Fix the handle locking in stdio

Fix the handle locking in stdio to use flockfile/funlockfile
internally when and where required.  Macros in <stdio.h> are updated
to automatically call the underlying functions when the process is
threaded to obtain the necessary locking.  A private mutex is added
to protect __sglue, the internal list of FILE handles, and another
to protect the one-time initialization.  Some routines in libc that
use getc() change to use getc_unlocked() as they're either protected
by their own lock or aren't thread-safe routines anyway.

Based on OpenBSD change by guenther@openbsd.org
http://www.mail-archive.com/source-changes@cvs.openbsd.org/msg01015.html

Bug: 3446659
Change-Id: Ie82116e358c541718d6709ec45ca6796be5a007b
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);
 }