Merge "Fix the handle locking in stdio"
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index c38ed5a..4006882 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -170,6 +170,7 @@
 #define	__SOFF	0x1000		/* set iff _offset is in fact correct */
 #define	__SMOD	0x2000		/* true => fgetln modified _p text */
 #define	__SALC	0x4000		/* allocate string space dynamically */
+#define	__SIGN	0x8000		/* ignore this file in _fwalk */
 
 /*
  * The following three definitions are for ANSI C, which took them
@@ -406,38 +407,43 @@
 #define	__sclearerr(p)	((void)((p)->_flags &= ~(__SERR|__SEOF)))
 #define	__sfileno(p)	((p)->_file)
 
-#define	feof(p)		__sfeof(p)
-#define	ferror(p)	__sferror(p)
+extern	int __isthreaded;
 
-#ifndef _POSIX_THREADS
-#define	clearerr(p)	__sclearerr(p)
-#endif
+#define	feof(p)		(!__isthreaded ? __sfeof(p) : (feof)(p))
+#define	ferror(p)	(!__isthreaded ? __sferror(p) : (ferror)(p))
+#define	clearerr(p)	(!__isthreaded ? __sclearerr(p) : (clearerr)(p))
 
 #if __POSIX_VISIBLE
-#define	fileno(p)	__sfileno(p)
+#define	fileno(p)	(!__isthreaded ? __sfileno(p) : (fileno)(p))
 #endif
 
+#define	getc(fp)	(!__isthreaded ? __sgetc(fp) : (getc)(fp))
+
+#if __BSD_VISIBLE
+/*
+ * The macro implementations of putc and putc_unlocked are not
+ * fully POSIX compliant; they do not set errno on failure
+ */
+#define putc(x, fp)	(!__isthreaded ? __sputc(x, fp) : (putc)(x, fp))
+#endif /* __BSD_VISIBLE */
+
 #ifndef lint
-#ifndef _POSIX_THREADS
-#define	getc(fp)	__sgetc(fp)
-#endif /* _POSIX_THREADS */
+#if __POSIX_VISIBLE >= 199506
 #define	getc_unlocked(fp)	__sgetc(fp)
 /*
  * The macro implementations of putc and putc_unlocked are not
  * fully POSIX compliant; they do not set errno on failure
  */
 #if __BSD_VISIBLE
-#ifndef _POSIX_THREADS
-#define putc(x, fp)	__sputc(x, fp)
-#endif /* _POSIX_THREADS */
 #define putc_unlocked(x, fp)	__sputc(x, fp)
 #endif /* __BSD_VISIBLE */
+#endif /* __POSIX_VISIBLE >= 199506 */
 #endif /* lint */
 
 #define	getchar()	getc(stdin)
 #define	putchar(x)	putc(x, stdout)
-#define getchar_unlocked()	getc_unlocked(stdin)
-#define putchar_unlocked(c)	putc_unlocked(c, stdout)
+#define	getchar_unlocked()	getc_unlocked(stdin)
+#define	putchar_unlocked(c)	putc_unlocked(c, stdout)
 
 #ifdef _GNU_SOURCE
 /*
diff --git a/libc/stdio/asprintf.c b/libc/stdio/asprintf.c
index 1257c7f..c3d8d61 100644
--- a/libc/stdio/asprintf.c
+++ b/libc/stdio/asprintf.c
@@ -38,7 +38,7 @@
 		goto err;
 	f._bf._size = f._w = 127;		/* Leave room for the NUL */
 	va_start(ap, fmt);
-	ret = vfprintf(&f, fmt, ap);
+	ret = __vfprintf(&f, fmt, ap);
 	va_end(ap);
 	if (ret == -1)
 		goto err;
diff --git a/libc/stdio/clrerr.c b/libc/stdio/clrerr.c
index 20f1994..cb6c4df 100644
--- a/libc/stdio/clrerr.c
+++ b/libc/stdio/clrerr.c
@@ -32,12 +32,13 @@
  */
 
 #include <stdio.h>
+#include "local.h"
 #undef	clearerr
 
 void
 clearerr(FILE *fp)
 {
-	flockfile(fp);
+	FLOCKFILE(fp);
 	__sclearerr(fp);
-	funlockfile(fp);
+	FUNLOCKFILE(fp);
 }
diff --git a/libc/stdio/fclose.c b/libc/stdio/fclose.c
index e94292b..8c3bac4 100644
--- a/libc/stdio/fclose.c
+++ b/libc/stdio/fclose.c
@@ -36,9 +36,6 @@
 #include <stdlib.h>
 #include "local.h"
 
-/* BIONIC: remove any file lock associated with a FILE* pointer */
-extern void __fremovelock(FILE *fp);
-
 int
 fclose(FILE *fp)
 {
@@ -48,6 +45,7 @@
 		errno = EBADF;
 		return (EOF);
 	}
+	FLOCKFILE(fp);
 	WCIO_FREE(fp);
 	r = fp->_flags & __SWR ? __sflush(fp) : 0;
 	if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0)
@@ -58,8 +56,8 @@
 		FREEUB(fp);
 	if (HASLB(fp))
 		FREELB(fp);
-	fp->_flags = 0;		/* Release this FILE for reuse. */
 	fp->_r = fp->_w = 0;	/* Mess up if reaccessed. */
-	__fremovelock(fp);
+	fp->_flags = 0;		/* Release this FILE for reuse. */
+	FUNLOCKFILE(fp);
 	return (r);
 }
diff --git a/libc/stdio/feof.c b/libc/stdio/feof.c
index eb742da..0fa65b0 100644
--- a/libc/stdio/feof.c
+++ b/libc/stdio/feof.c
@@ -32,6 +32,7 @@
  */
 
 #include <stdio.h>
+#include "local.h"
 
 /*
  * A subroutine version of the macro feof.
@@ -41,5 +42,10 @@
 int
 feof(FILE *fp)
 {
-	return (__sfeof(fp));
+	int ret;
+
+	FLOCKFILE(fp);
+	ret = __sfeof(fp);
+	FUNLOCKFILE(fp);
+	return (ret);
 }
diff --git a/libc/stdio/fflush.c b/libc/stdio/fflush.c
index 3f72ad8..e69bdcc 100644
--- a/libc/stdio/fflush.c
+++ b/libc/stdio/fflush.c
@@ -39,14 +39,18 @@
 int
 fflush(FILE *fp)
 {
+	int r;
 
 	if (fp == NULL)
-		return (_fwalk(__sflush));
+		return (_fwalk(__sflush_locked));
+	FLOCKFILE(fp);
 	if ((fp->_flags & (__SWR | __SRW)) == 0) {
 		errno = EBADF;
-		return (EOF);
-	}
-	return (__sflush(fp));
+		r = EOF;
+	} else
+		r = __sflush(fp);
+	FUNLOCKFILE(fp);
+	return (r);
 }
 
 int
@@ -80,3 +84,14 @@
 	}
 	return (0);
 }
+
+int
+__sflush_locked(FILE *fp)
+{
+	int r;
+
+	FLOCKFILE(fp);
+	r = __sflush(fp);
+	FUNLOCKFILE(fp);
+	return (r);
+}
diff --git a/libc/stdio/fgetc.c b/libc/stdio/fgetc.c
index 53e2948..0a6d54e 100644
--- a/libc/stdio/fgetc.c
+++ b/libc/stdio/fgetc.c
@@ -36,5 +36,5 @@
 int
 fgetc(FILE *fp)
 {
-	return (__sgetc(fp));
+	return (getc(fp));
 }
diff --git a/libc/stdio/fgetln.c b/libc/stdio/fgetln.c
index 95a5b31..0947dd8 100644
--- a/libc/stdio/fgetln.c
+++ b/libc/stdio/fgetln.c
@@ -71,19 +71,18 @@
 fgetln(FILE *fp, size_t *lenp)
 {
 	unsigned char *p;
+	char *ret;
 	size_t len;
 	size_t off;
 
+	FLOCKFILE(fp);
+
 	/* make sure there is input */
-	if (fp->_r <= 0 && __srefill(fp)) {
-		*lenp = 0;
-		return (NULL);
-	}
+	if (fp->_r <= 0 && __srefill(fp))
+		goto error;
 
 	/* look for a newline in the input */
 	if ((p = memchr((void *)fp->_p, '\n', fp->_r)) != NULL) {
-		char *ret;
-
 		/*
 		 * Found one.  Flag buffer as modified to keep fseek from
 		 * `optimising' a backward seek, in case the user stomps on
@@ -95,6 +94,7 @@
 		fp->_flags |= __SMOD;
 		fp->_r -= len;
 		fp->_p = p;
+		FUNLOCKFILE(fp);
 		return (ret);
 	}
 
@@ -139,12 +139,15 @@
 		break;
 	}
 	*lenp = len;
+	ret = (char *)fp->_lb._base;
 #ifdef notdef
-	fp->_lb._base[len] = '\0';
+	ret[len] = '\0';
 #endif
-	return ((char *)fp->_lb._base);
+	FUNLOCKFILE(fp);
+	return (ret);
 
 error:
 	*lenp = 0;		/* ??? */
+	FUNLOCKFILE(fp);
 	return (NULL);		/* ??? */
 }
diff --git a/libc/stdio/fgets.c b/libc/stdio/fgets.c
index f26385d..311b7b2 100644
--- a/libc/stdio/fgets.c
+++ b/libc/stdio/fgets.c
@@ -51,6 +51,7 @@
 	if (n <= 0)		/* sanity check */
 		return (NULL);
 
+	FLOCKFILE(fp);
 	_SET_ORIENTATION(fp, -1);
 	s = buf;
 	n--;			/* leave space for NUL */
@@ -61,8 +62,10 @@
 		if (fp->_r <= 0) {
 			if (__srefill(fp)) {
 				/* EOF/error: stop with partial or no line */
-				if (s == buf)
+				if (s == buf) {
+					FUNLOCKFILE(fp);
 					return (NULL);
+                                }
 				break;
 			}
 		}
@@ -84,6 +87,7 @@
 			fp->_p = t;
 			(void)memcpy((void *)s, (void *)p, len);
 			s[len] = '\0';
+			FUNLOCKFILE(fp);
 			return (buf);
 		}
 		fp->_r -= len;
@@ -93,5 +97,6 @@
 		n -= len;
 	}
 	*s = '\0';
+	FUNLOCKFILE(fp);
 	return (buf);
 }
diff --git a/libc/stdio/fileno.c b/libc/stdio/fileno.c
index 0fd985b..cbefdeb 100644
--- a/libc/stdio/fileno.c
+++ b/libc/stdio/fileno.c
@@ -32,6 +32,7 @@
  */
 
 #include <stdio.h>
+#include "local.h"
 
 /*
  * A subroutine version of the macro fileno.
@@ -41,5 +42,10 @@
 int
 fileno(FILE *fp)
 {
-	return (__sfileno(fp));
+	int ret;
+
+	FLOCKFILE(fp);
+	ret = __sfileno(fp);
+	FUNLOCKFILE(fp);
+	return (ret);
 }
diff --git a/libc/stdio/findfp.c b/libc/stdio/findfp.c
index 1d0f9c5..a659c87 100644
--- a/libc/stdio/findfp.c
+++ b/libc/stdio/findfp.c
@@ -39,6 +39,7 @@
 #include <string.h>
 #include "local.h"
 #include "glue.h"
+#include "thread_private.h"
 
 int	__sdidinit;
 
@@ -54,6 +55,8 @@
 static FILE usual[FOPEN_MAX - 3];
 static struct __sfileext usualext[FOPEN_MAX - 3];
 static struct glue uglue = { 0, FOPEN_MAX - 3, usual };
+static struct glue *lastglue = &uglue;
+_THREAD_PRIVATE_MUTEX(__sfp_mutex);
 
 static struct __sfileext __sFext[3];
 FILE __sF[3] = {
@@ -104,16 +107,25 @@
 
 	if (!__sdidinit)
 		__sinit();
-	for (g = &__sglue;; g = g->next) {
+
+	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
+	for (g = &__sglue; g != NULL; g = g->next) {
 		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
 			if (fp->_flags == 0)
 				goto found;
-		if (g->next == NULL && (g->next = moreglue(NDYNAMIC)) == NULL)
-			break;
 	}
-	return (NULL);
+
+	/* release lock while mallocing */
+	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
+	if ((g = moreglue(NDYNAMIC)) == NULL)
+		return (NULL);
+	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
+	lastglue->next = g;
+	lastglue = g;
+	fp = g->iobs;
 found:
 	fp->_flags = 1;		/* reserve this slot; caller sets real flags */
+	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
 	fp->_p = NULL;		/* no current pointer */
 	fp->_w = 0;		/* nothing to read or write */
 	fp->_r = 0;
@@ -144,8 +156,12 @@
 	n = getdtablesize() - FOPEN_MAX + 20;		/* 20 for slop. */
 	for (g = &__sglue; (n -= g->niobs) > 0 && g->next; g = g->next)
 		/* void */;
-	if (n > 0)
-		g->next = moreglue(n);
+	if (n > 0 && ((g = moreglue(n)) != NULL)) {
+		_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
+		lastglue->next = g;
+		lastglue = g;
+		_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
+	}
 }
 #endif
 
@@ -170,12 +186,18 @@
 void
 __sinit(void)
 {
+	_THREAD_PRIVATE_MUTEX(__sinit_mutex);
 	int i;
 
+	_THREAD_PRIVATE_MUTEX_LOCK(__sinit_mutex);
+	if (__sdidinit)
+		goto out;	/* bail out if caller lost the race */
 	for (i = 0; i < FOPEN_MAX - 3; i++) {
 		_FILEEXT_SETUP(usual+i, usualext+i);
 	}
 	/* make sure we clean up on exit */
 	__atexit_register_cleanup(_cleanup); /* conservative */
 	__sdidinit = 1;
+out:
+	_THREAD_PRIVATE_MUTEX_UNLOCK(__sinit_mutex);
 }
diff --git a/libc/stdio/fpurge.c b/libc/stdio/fpurge.c
index fa0213a..e04c4fe 100644
--- a/libc/stdio/fpurge.c
+++ b/libc/stdio/fpurge.c
@@ -43,7 +43,9 @@
 int
 fpurge(FILE *fp)
 {
+	FLOCKFILE(fp);
 	if (!fp->_flags) {
+		FUNLOCKFILE(fp);
 		errno = EBADF;
 		return(EOF);
 	}
@@ -54,5 +56,6 @@
 	fp->_p = fp->_bf._base;
 	fp->_r = 0;
 	fp->_w = fp->_flags & (__SLBF|__SNBF) ? 0 : fp->_bf._size;
+	FUNLOCKFILE(fp);
 	return (0);
 }
diff --git a/libc/stdio/fputc.c b/libc/stdio/fputc.c
index 2a6e7b7..90809e2 100644
--- a/libc/stdio/fputc.c
+++ b/libc/stdio/fputc.c
@@ -33,14 +33,9 @@
 
 #include <stdio.h>
 #include <errno.h>
-#include "local.h"
 
 int
 fputc(int c, FILE *fp)
 {
-	if (cantwrite(fp)) {
-		errno = EBADF;
-		return (EOF);
-	}
 	return (putc(c, fp));
 }
diff --git a/libc/stdio/fputs.c b/libc/stdio/fputs.c
index 7434ca8..c2462ba 100644
--- a/libc/stdio/fputs.c
+++ b/libc/stdio/fputs.c
@@ -44,11 +44,15 @@
 {
 	struct __suio uio;
 	struct __siov iov;
+	int ret;
 
 	iov.iov_base = (void *)s;
 	iov.iov_len = uio.uio_resid = strlen(s);
 	uio.uio_iov = &iov;
 	uio.uio_iovcnt = 1;
+	FLOCKFILE(fp);
 	_SET_ORIENTATION(fp, -1);
-	return (__sfvwrite(fp, &uio));
+	ret = __sfvwrite(fp, &uio);
+	FUNLOCKFILE(fp);
+	return (ret);
 }
diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c
index 69c40b3..649db17 100644
--- a/libc/stdio/fread.c
+++ b/libc/stdio/fread.c
@@ -39,9 +39,8 @@
 static int
 lflush(FILE *fp)
 {
-
     if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
-        return (__sflush(fp));
+        return (__sflush_locked(fp));
     return (0);
 }
 
@@ -60,6 +59,7 @@
      */
     if ((resid = count * size) == 0)
         return (0);
+    FLOCKFILE(fp);
     if (fp->_r < 0)
         fp->_r = 0;
     total = resid;
@@ -79,20 +79,25 @@
         fp->_r = 0;     /* largely a convenience for callers */
 
         /* SysV does not make this test; take it out for compatibility */
-        if (fp->_flags & __SEOF)
+        if (fp->_flags & __SEOF) {
+            FUNLOCKFILE(fp);
             return (EOF);
+        }
 
         /* if not already reading, have to be reading and writing */
         if ((fp->_flags & __SRD) == 0) {
             if ((fp->_flags & __SRW) == 0) {
-                errno = EBADF;
                 fp->_flags |= __SERR;
+                FUNLOCKFILE(fp);
+                errno = EBADF;
                 return (EOF);
             }
             /* switch to reading */
             if (fp->_flags & __SWR) {
-                if (__sflush(fp))
+                if (__sflush(fp)) {
+                    FUNLOCKFILE(fp);
                     return (EOF);
+                }
                 fp->_flags &= ~__SWR;
                 fp->_w = 0;
                 fp->_lbfsize = 0;
@@ -116,8 +121,16 @@
          * standard.
          */
 
-        if (fp->_flags & (__SLBF|__SNBF))
+        if (fp->_flags & (__SLBF|__SNBF)) {
+            /* Ignore this file in _fwalk to deadlock. */
+            fp->_flags |= __SIGN;
             (void) _fwalk(lflush);
+            fp->_flags &= ~__SIGN;
+
+            /* Now flush this file without locking it. */
+            if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
+                __sflush(fp);
+        }
 
         while (resid > 0) {
             int   len = (*fp->_read)(fp->_cookie, p, resid );
@@ -128,11 +141,13 @@
                 else {
                     fp->_flags |= __SERR;
                 }
+                FUNLOCKFILE(fp);
                 return ((total - resid) / size);
             }
             p     += len;
             resid -= len;
         }
+        FUNLOCKFILE(fp);
         return (count);
     }
     else
@@ -146,6 +161,7 @@
             resid -= r;
             if (__srefill(fp)) {
                 /* no more input: return partial result */
+                FUNLOCKFILE(fp);
                 return ((total - resid) / size);
             }
         }
@@ -154,5 +170,6 @@
     (void)memcpy((void *)p, (void *)fp->_p, resid);
     fp->_r -= resid;
     fp->_p += resid;
+    FUNLOCKFILE(fp);
     return (count);
 }
diff --git a/libc/stdio/freopen.c b/libc/stdio/freopen.c
index 59b2228..da3a674 100644
--- a/libc/stdio/freopen.c
+++ b/libc/stdio/freopen.c
@@ -59,6 +59,8 @@
 	if (!__sdidinit)
 		__sinit();
 
+	FLOCKFILE(fp);
+
 	/*
 	 * There are actually programs that depend on being able to "freopen"
 	 * descriptors that weren't originally open.  Keep this from breaking.
@@ -120,6 +122,7 @@
 
 	if (f < 0) {			/* did not get it after all */
 		fp->_flags = 0;		/* set it free */
+		FUNLOCKFILE(fp);
 		errno = sverrno;	/* restore in case _close clobbered */
 		return (NULL);
 	}
@@ -154,5 +157,6 @@
 	 */
 	if (oflags & O_APPEND)
 		(void) __sseek((void *)fp, (fpos_t)0, SEEK_END);
+	FUNLOCKFILE(fp);
 	return (fp);
 }
diff --git a/libc/stdio/fseek.c b/libc/stdio/fseek.c
index 8581b62..38697f5 100644
--- a/libc/stdio/fseek.c
+++ b/libc/stdio/fseek.c
@@ -70,6 +70,7 @@
 	 * Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
 	 * After this, whence is either SEEK_SET or SEEK_END.
 	 */
+	FLOCKFILE(fp);
 	switch (whence) {
 
 	case SEEK_CUR:
@@ -83,8 +84,10 @@
 			curoff = fp->_offset;
 		else {
 			curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR);
-			if (curoff == (fpos_t)-1)
+			if (curoff == (fpos_t)-1) {
+				FUNLOCKFILE(fp);
 				return (EOF);
+			}
 		}
 		if (fp->_flags & __SRD) {
 			curoff -= fp->_r;
@@ -105,6 +108,7 @@
 		break;
 
 	default:
+		FUNLOCKFILE(fp);
 		errno = EINVAL;
 		return (EOF);
 	}
@@ -189,6 +193,7 @@
 		if (HASUB(fp))
 			FREEUB(fp);
 		fp->_flags &= ~__SEOF;
+		FUNLOCKFILE(fp);
 		return (0);
 	}
 
@@ -215,6 +220,7 @@
 		fp->_p += n;
 		fp->_r -= n;
 	}
+	FUNLOCKFILE(fp);
 	return (0);
 
 	/*
@@ -224,6 +230,7 @@
 dumb:
 	if (__sflush(fp) ||
 	    (*seekfn)(fp->_cookie, (fpos_t)offset, whence) == POS_ERR) {
+		FUNLOCKFILE(fp);
 		return (EOF);
 	}
 	/* success: clear EOF indicator and discard ungetc() data */
@@ -233,6 +240,7 @@
 	fp->_r = 0;
 	/* fp->_w = 0; */	/* unnecessary (I think...) */
 	fp->_flags &= ~__SEOF;
+	FUNLOCKFILE(fp);
 	return (0);
 }
 
diff --git a/libc/stdio/ftell.c b/libc/stdio/ftell.c
index b7d449e..9f850ee 100644
--- a/libc/stdio/ftell.c
+++ b/libc/stdio/ftell.c
@@ -45,20 +45,22 @@
 
 	if (fp->_seek == NULL) {
 		errno = ESPIPE;			/* historic practice */
-		return ((off_t)-1);
+		pos = -1;
+                goto out;
 	}
 
 	/*
 	 * Find offset of underlying I/O object, then
 	 * adjust for buffered bytes.
 	 */
+        FLOCKFILE(fp);
 	__sflush(fp);		/* may adjust seek offset on append stream */
 	if (fp->_flags & __SOFF)
 		pos = fp->_offset;
 	else {
 		pos = (*fp->_seek)(fp->_cookie, (fpos_t)0, SEEK_CUR);
-		if (pos == -1L)
-			return (pos);
+		if (pos == -1)
+			goto out;
 	}
 	if (fp->_flags & __SRD) {
 		/*
@@ -77,6 +79,7 @@
 		 */
 		pos += fp->_p - fp->_bf._base;
 	}
+out:	FUNLOCKFILE(fp);
 	return (pos);
 }
 
diff --git a/libc/stdio/fwalk.c b/libc/stdio/fwalk.c
index 5606cf1..b1df891 100644
--- a/libc/stdio/fwalk.c
+++ b/libc/stdio/fwalk.c
@@ -45,8 +45,9 @@
 
 	ret = 0;
 	for (g = &__sglue; g != NULL; g = g->next)
-		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
-			if (fp->_flags != 0)
+		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) {
+			if ((fp->_flags != 0) && ((fp->_flags & __SIGN) == 0))
 				ret |= (*function)(fp);
+		}
 	return (ret);
 }
diff --git a/libc/stdio/fwrite.c b/libc/stdio/fwrite.c
index 8a508dc..a97313e 100644
--- a/libc/stdio/fwrite.c
+++ b/libc/stdio/fwrite.c
@@ -45,6 +45,7 @@
 	size_t n;
 	struct __suio uio;
 	struct __siov iov;
+	int ret;
 
 	iov.iov_base = (void *)buf;
 	uio.uio_resid = iov.iov_len = n = count * size;
@@ -56,7 +57,10 @@
 	 * skip the divide if this happens, since divides are
 	 * generally slow and since this occurs whenever size==0.
 	 */
-	if (__sfvwrite(fp, &uio) == 0)
+	FLOCKFILE(fp);
+	ret = __sfvwrite(fp, &uio);
+	FUNLOCKFILE(fp);
+	if (ret == 0)
 		return (count);
 	return ((n - uio.uio_resid) / size);
 }
diff --git a/libc/stdio/getc.c b/libc/stdio/getc.c
index cdd5722..16a5b1d 100644
--- a/libc/stdio/getc.c
+++ b/libc/stdio/getc.c
@@ -32,6 +32,7 @@
  */
 
 #include <stdio.h>
+#include "local.h"
 
 /*
  * A subroutine version of the macro getc_unlocked.
@@ -54,8 +55,8 @@
 {
 	int c;
 
-	flockfile(fp);
+	FLOCKFILE(fp);
 	c = __sgetc(fp);
-	funlockfile(fp);
+	FUNLOCKFILE(fp);
 	return (c);
 }
diff --git a/libc/stdio/gets.c b/libc/stdio/gets.c
index 004eb99..93e2edd 100644
--- a/libc/stdio/gets.c
+++ b/libc/stdio/gets.c
@@ -32,6 +32,7 @@
  */
 
 #include <stdio.h>
+#include "local.h"
 
 __warn_references(gets,
     "warning: gets() is very unsafe; consider using fgets()");
@@ -42,14 +43,17 @@
 	int c;
 	char *s;
 
-	for (s = buf; (c = getchar()) != '\n';)
+	FLOCKFILE(stdin);
+	for (s = buf; (c = getchar_unlocked()) != '\n';)
 		if (c == EOF)
-			if (s == buf)
+			if (s == buf) {
+				FUNLOCKFILE(stdin);
 				return (NULL);
-			else
+			} else
 				break;
 		else
 			*s++ = c;
 	*s = '\0';
+	FUNLOCKFILE(stdin);
 	return (buf);
 }
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 3db1fc5..6b2111a 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -46,6 +46,7 @@
  */
 
 int	__sflush(FILE *);
+int	__sflush_locked(FILE *);
 FILE	*__sfp(void);
 int	__srefill(FILE *);
 int	__sread(void *, char *, int);
@@ -59,6 +60,7 @@
 int	_fwalk(int (*)(FILE *));
 int	__swsetup(FILE *);
 int	__sflags(const char *, int *);
+int	__vfprintf(FILE *, const char *, __va_list);
 
 extern void __atexit_register_cleanup(void (*)(void));
 extern int __sdidinit;
@@ -89,3 +91,6 @@
 	free((char *)(fp)->_lb._base); \
 	(fp)->_lb._base = NULL; \
 }
+
+#define FLOCKFILE(fp)   do { if (__isthreaded) flockfile(fp); } while (0)
+#define FUNLOCKFILE(fp) do { if (__isthreaded) funlockfile(fp); } while (0)
diff --git a/libc/stdio/putc.c b/libc/stdio/putc.c
index 9250215..2b05504 100644
--- a/libc/stdio/putc.c
+++ b/libc/stdio/putc.c
@@ -60,8 +60,8 @@
 {
 	int ret;
 
-	flockfile(fp);
+	FLOCKFILE(fp);
 	ret = putc_unlocked(c, fp);
-	funlockfile(fp);
+	FUNLOCKFILE(fp);
 	return (ret);
 }
diff --git a/libc/stdio/puts.c b/libc/stdio/puts.c
index c6ecc24..4603a3d 100644
--- a/libc/stdio/puts.c
+++ b/libc/stdio/puts.c
@@ -33,6 +33,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include "local.h"
 #include "fvwrite.h"
 
 /*
@@ -44,6 +45,7 @@
 	size_t c = strlen(s);
 	struct __suio uio;
 	struct __siov iov[2];
+	int ret;
 
 	iov[0].iov_base = (void *)s;
 	iov[0].iov_len = c;
@@ -52,5 +54,8 @@
 	uio.uio_resid = c + 1;
 	uio.uio_iov = &iov[0];
 	uio.uio_iovcnt = 2;
-	return (__sfvwrite(stdout, &uio) ? EOF : '\n');
+	FLOCKFILE(stdout);
+	ret = __sfvwrite(stdout, &uio);
+	FUNLOCKFILE(stdout);
+	return (ret ? EOF : '\n');
 }
diff --git a/libc/stdio/refill.c b/libc/stdio/refill.c
index 74b378e..7cb6b78 100644
--- a/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -39,9 +39,8 @@
 static int
 lflush(FILE *fp)
 {
-
 	if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
-		return (__sflush(fp));
+		return (__sflush_locked(fp)); /* ignored... */
 	return (0);
 }
 
@@ -103,8 +102,16 @@
 	 * flush all line buffered output files, per the ANSI C
 	 * standard.
 	 */
-	if (fp->_flags & (__SLBF|__SNBF))
+	if (fp->_flags & (__SLBF|__SNBF)) {
+		/* Ignore this file in _fwalk to avoid potential deadlock. */
+		fp->_flags |= __SIGN;
 		(void) _fwalk(lflush);
+		fp->_flags &= ~__SIGN;
+
+		/* Now flush this file without locking it. */
+		if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
+		    __sflush(fp);
+	}
 	fp->_p = fp->_bf._base;
 	fp->_r = (*fp->_read)(fp->_cookie, (char *)fp->_p, fp->_bf._size);
 	fp->_flags &= ~__SMOD;	/* buffer contents are again pristine */
diff --git a/libc/stdio/setvbuf.c b/libc/stdio/setvbuf.c
index 9b92bf0..2fb76af 100644
--- a/libc/stdio/setvbuf.c
+++ b/libc/stdio/setvbuf.c
@@ -61,6 +61,7 @@
 	 * malloc()ed.  We also clear any eof condition, as if this were
 	 * a seek.
 	 */
+	FLOCKFILE(fp);
 	ret = 0;
 	(void)__sflush(fp);
 	if (HASUB(fp))
@@ -107,6 +108,7 @@
 			fp->_w = 0;
 			fp->_bf._base = fp->_p = fp->_nbuf;
 			fp->_bf._size = 1;
+			FUNLOCKFILE(fp);
 			return (ret);
 		}
 		flags |= __SMBF;
@@ -145,6 +147,7 @@
 		/* begin/continue reading, or stay in intermediate state */
 		fp->_w = 0;
 	}
+	FUNLOCKFILE(fp);
 	__atexit_register_cleanup(_cleanup);
 
 	return (ret);
diff --git a/libc/stdio/snprintf.c b/libc/stdio/snprintf.c
index 45ef7eb..5aa54be 100644
--- a/libc/stdio/snprintf.c
+++ b/libc/stdio/snprintf.c
@@ -60,7 +60,7 @@
 	f._bf._base = f._p = (unsigned char *)str;
 	f._bf._size = f._w = n - 1;
 	va_start(ap, fmt);
-	ret = vfprintf(&f, fmt, ap);
+	ret = __vfprintf(&f, fmt, ap);
 	va_end(ap);
 	*f._p = '\0';
 	return (ret);
diff --git a/libc/stdio/sprintf.c b/libc/stdio/sprintf.c
index 67f924b..3cf7952 100644
--- a/libc/stdio/sprintf.c
+++ b/libc/stdio/sprintf.c
@@ -56,7 +56,7 @@
 	f._bf._base = f._p = (unsigned char *)str;
 	f._bf._size = f._w = INT_MAX;
 	va_start(ap, fmt);
-	ret = vfprintf(&f, fmt, ap);
+	ret = __vfprintf(&f, fmt, ap);
 	va_end(ap);
 	*f._p = '\0';
 	return (ret);
diff --git a/libc/stdio/ungetc.c b/libc/stdio/ungetc.c
index fe05258..b493d21 100644
--- a/libc/stdio/ungetc.c
+++ b/libc/stdio/ungetc.c
@@ -82,17 +82,20 @@
 		return (EOF);
 	if (!__sdidinit)
 		__sinit();
+	FLOCKFILE(fp);
 	_SET_ORIENTATION(fp, -1);
 	if ((fp->_flags & __SRD) == 0) {
 		/*
 		 * Not already reading: no good unless reading-and-writing.
 		 * Otherwise, flush any current write stuff.
 		 */
-		if ((fp->_flags & __SRW) == 0)
+		if ((fp->_flags & __SRW) == 0) {
+error:			FUNLOCKFILE(fp);
 			return (EOF);
+		}
 		if (fp->_flags & __SWR) {
 			if (__sflush(fp))
-				return (EOF);
+				goto error;
 			fp->_flags &= ~__SWR;
 			fp->_w = 0;
 			fp->_lbfsize = 0;
@@ -107,9 +110,10 @@
 	 */
 	if (HASUB(fp)) {
 		if (fp->_r >= _UB(fp)._size && __submore(fp))
-			return (EOF);
+			goto error;
 		*--fp->_p = c;
-		fp->_r++;
+inc_ret:	fp->_r++;
+		FUNLOCKFILE(fp);
 		return (c);
 	}
 	fp->_flags &= ~__SEOF;
@@ -122,8 +126,7 @@
 	if (fp->_bf._base != NULL && fp->_p > fp->_bf._base &&
 	    fp->_p[-1] == c) {
 		fp->_p--;
-		fp->_r++;
-		return (c);
+		goto inc_ret;
 	}
 
 	/*
@@ -137,5 +140,6 @@
 	fp->_ubuf[sizeof(fp->_ubuf) - 1] = c;
 	fp->_p = &fp->_ubuf[sizeof(fp->_ubuf) - 1];
 	fp->_r = 1;
+	FUNLOCKFILE(fp);
 	return (c);
 }
diff --git a/libc/stdio/vasprintf.c b/libc/stdio/vasprintf.c
index 54c46b3..1630ccb 100644
--- a/libc/stdio/vasprintf.c
+++ b/libc/stdio/vasprintf.c
@@ -37,7 +37,7 @@
 	if (f._bf._base == NULL)
 		goto err;
 	f._bf._size = f._w = 127;		/* Leave room for the NUL */
-	ret = vfprintf(&f, fmt, ap);
+	ret = __vfprintf(&f, fmt, ap);
 	if (ret == -1)
 		goto err;
 	*f._p = '\0';
diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.c
index 9c36b79..dac8496 100644
--- a/libc/stdio/vfprintf.c
+++ b/libc/stdio/vfprintf.c
@@ -100,8 +100,8 @@
 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
 
 	/* do the work, then copy any error status */
-	ret = vfprintf(&fake, fmt, ap);
-	if (ret >= 0 && fflush(&fake))
+	ret = __vfprintf(&fake, fmt, ap);
+	if (ret >= 0 && __sflush(&fake))
 		ret = EOF;
 	if (fake._flags & __SERR)
 		fp->_flags |= __SERR;
@@ -158,6 +158,17 @@
 int
 vfprintf(FILE *fp, const char *fmt0, __va_list ap)
 {
+	int ret;
+
+	FLOCKFILE(fp);
+	ret = __vfprintf(fp, fmt0, ap);
+	FUNLOCKFILE(fp);
+	return (ret);
+}
+
+int
+__vfprintf(FILE *fp, const char *fmt0, __va_list ap)
+{
 	char *fmt;	/* format string */
 	int ch;	/* character from fmt */
 	int n, m, n2;	/* handy integers (short term usage) */
diff --git a/libc/stdio/vfscanf.c b/libc/stdio/vfscanf.c
index dbd0a8b..b16e3c7 100644
--- a/libc/stdio/vfscanf.c
+++ b/libc/stdio/vfscanf.c
@@ -117,6 +117,7 @@
 	static short basefix[17] =
 		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
 
+	FLOCKFILE(fp);
 	_SET_ORIENTATION(fp, -1);
 
 	nassigned = 0;
@@ -124,8 +125,10 @@
 	base = 0;		/* XXX just to keep gcc happy */
 	for (;;) {
 		c = *fmt++;
-		if (c == 0)
+		if (c == 0) {
+			FUNLOCKFILE(fp);
 			return (nassigned);
+		}
 		if (isspace(c)) {
 			while ((fp->_r > 0 || __srefill(fp) == 0) &&
 			    isspace(*fp->_p))
@@ -292,6 +295,7 @@
 		 * Disgusting backwards compatibility hacks.	XXX
 		 */
 		case '\0':	/* compat */
+			FUNLOCKFILE(fp);
 			return (EOF);
 
 		default:	/* compat */
@@ -689,8 +693,10 @@
 		}
 	}
 input_failure:
-	return (nassigned ? nassigned : -1);
+	if (nassigned == 0)
+		nassigned = -1;
 match_failure:
+	FUNLOCKFILE(fp);
 	return (nassigned);
 }
 
diff --git a/libc/stdio/vsnprintf.c b/libc/stdio/vsnprintf.c
index e6dd009..ca30f94 100644
--- a/libc/stdio/vsnprintf.c
+++ b/libc/stdio/vsnprintf.c
@@ -58,7 +58,7 @@
 	f._flags = __SWR | __SSTR;
 	f._bf._base = f._p = (unsigned char *)str;
 	f._bf._size = f._w = n - 1;
-	ret = vfprintf(&f, fmt, ap);
+	ret = __vfprintf(&f, fmt, ap);
 	*f._p = '\0';
 	return (ret);
 }
diff --git a/libc/stdio/vsprintf.c b/libc/stdio/vsprintf.c
index 67a53a1..846ee8a 100644
--- a/libc/stdio/vsprintf.c
+++ b/libc/stdio/vsprintf.c
@@ -53,7 +53,7 @@
 	f._flags = __SWR | __SSTR;
 	f._bf._base = f._p = (unsigned char *)str;
 	f._bf._size = f._w = INT_MAX;
-	ret = vfprintf(&f, fmt, ap);
+	ret = __vfprintf(&f, fmt, ap);
 	*f._p = '\0';
 	return (ret);
 }
diff --git a/libc/stdio/wbuf.c b/libc/stdio/wbuf.c
index c757799..e09ac59 100644
--- a/libc/stdio/wbuf.c
+++ b/libc/stdio/wbuf.c
@@ -65,20 +65,20 @@
 	 * stuff c into the buffer.  If this causes the buffer to fill
 	 * completely, or if c is '\n' and the file is line buffered,
 	 * flush it (perhaps a second time).  The second flush will always
-	 * happen on unbuffered streams, where _bf._size==1; fflush()
+	 * happen on unbuffered streams, where _bf._size==1; __sflush()
 	 * guarantees that putc() will always call wbuf() by setting _w
 	 * to 0, so we need not do anything else.
 	 */
 	n = fp->_p - fp->_bf._base;
 	if (n >= fp->_bf._size) {
-		if (fflush(fp))
+		if (__sflush(fp))
 			return (EOF);
 		n = 0;
 	}
 	fp->_w--;
 	*fp->_p++ = c;
 	if (++n == fp->_bf._size || (fp->_flags & __SLBF && c == '\n'))
-		if (fflush(fp))
+		if (__sflush(fp))
 			return (EOF);
 	return (c);
 }