summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2011-02-12 07:13:44 -0800
committerKenny Root <kroot@google.com>2011-02-14 09:32:56 -0800
commitf582340a6a48588aa50da17e1620e8f91b146941 (patch)
treed30c0ffb648b7a43ff469f89f05850f5008482ec
parent72f9a5c374bf559e9c69a62c1d95304f913ef6b2 (diff)
downloadbionic-f582340a6a48588aa50da17e1620e8f91b146941.zip
bionic-f582340a6a48588aa50da17e1620e8f91b146941.tar.gz
bionic-f582340a6a48588aa50da17e1620e8f91b146941.tar.bz2
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
-rw-r--r--libc/include/stdio.h34
-rw-r--r--libc/stdio/asprintf.c2
-rw-r--r--libc/stdio/clrerr.c5
-rw-r--r--libc/stdio/fclose.c8
-rw-r--r--libc/stdio/feof.c8
-rw-r--r--libc/stdio/fflush.c23
-rw-r--r--libc/stdio/fgetc.c2
-rw-r--r--libc/stdio/fgetln.c19
-rw-r--r--libc/stdio/fgets.c7
-rw-r--r--libc/stdio/fileno.c8
-rw-r--r--libc/stdio/findfp.c34
-rw-r--r--libc/stdio/fpurge.c3
-rw-r--r--libc/stdio/fputc.c5
-rw-r--r--libc/stdio/fputs.c6
-rw-r--r--libc/stdio/fread.c29
-rw-r--r--libc/stdio/freopen.c4
-rw-r--r--libc/stdio/fseek.c10
-rw-r--r--libc/stdio/ftell.c9
-rw-r--r--libc/stdio/fwalk.c5
-rw-r--r--libc/stdio/fwrite.c6
-rw-r--r--libc/stdio/getc.c5
-rw-r--r--libc/stdio/gets.c10
-rw-r--r--libc/stdio/local.h5
-rw-r--r--libc/stdio/putc.c4
-rw-r--r--libc/stdio/puts.c7
-rw-r--r--libc/stdio/refill.c13
-rw-r--r--libc/stdio/setvbuf.c3
-rw-r--r--libc/stdio/snprintf.c2
-rw-r--r--libc/stdio/sprintf.c2
-rw-r--r--libc/stdio/ungetc.c16
-rw-r--r--libc/stdio/vasprintf.c2
-rw-r--r--libc/stdio/vfprintf.c15
-rw-r--r--libc/stdio/vfscanf.c10
-rw-r--r--libc/stdio/vsnprintf.c2
-rw-r--r--libc/stdio/vsprintf.c2
-rw-r--r--libc/stdio/wbuf.c6
36 files changed, 239 insertions, 92 deletions
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 @@ __END_DECLS
#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 @@ static __inline int __sputc(int _c, FILE *_p) {
#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 @@ asprintf(char **str, const char *fmt, ...)
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 @@ fclose(FILE *fp)
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 @@ fclose(FILE *fp)
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 @@ __sflush(FILE *fp)
}
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 @@ char *
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 @@ fgetln(FILE *fp, size_t *lenp)
fp->_flags |= __SMOD;
fp->_r -= len;
fp->_p = p;
+ FUNLOCKFILE(fp);
return (ret);
}
@@ -139,12 +139,15 @@ fgetln(FILE *fp, size_t *lenp)
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 @@ fgets(char *buf, int n, FILE *fp)
if (n <= 0) /* sanity check */
return (NULL);
+ FLOCKFILE(fp);
_SET_ORIENTATION(fp, -1);
s = buf;
n--; /* leave space for NUL */
@@ -61,8 +62,10 @@ fgets(char *buf, int n, FILE *fp)
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 @@ fgets(char *buf, int n, FILE *fp)
fp->_p = t;
(void)memcpy((void *)s, (void *)p, len);
s[len] = '\0';
+ FUNLOCKFILE(fp);
return (buf);
}
fp->_r -= len;
@@ -93,5 +97,6 @@ fgets(char *buf, int n, FILE *fp)
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 @@ int __sdidinit;
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 @@ __sfp(void)
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 @@ f_prealloc(void)
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 @@ _cleanup(void)
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 @@ fpurge(FILE *fp)
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 @@ fputs(const char *s, FILE *fp)
{
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 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
*/
if ((resid = count * size) == 0)
return (0);
+ FLOCKFILE(fp);
if (fp->_r < 0)
fp->_r = 0;
total = resid;
@@ -79,20 +79,25 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
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 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
* 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 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
else {
fp->_flags |= __SERR;
}
+ FUNLOCKFILE(fp);
return ((total - resid) / size);
}
p += len;
resid -= len;
}
+ FUNLOCKFILE(fp);
return (count);
}
else
@@ -146,6 +161,7 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
resid -= r;
if (__srefill(fp)) {
/* no more input: return partial result */
+ FUNLOCKFILE(fp);
return ((total - resid) / size);
}
}
@@ -154,5 +170,6 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
(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 @@ freopen(const char *file, const char *mode, FILE *fp)
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 @@ freopen(const char *file, const char *mode, FILE *fp)
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 @@ freopen(const char *file, const char *mode, FILE *fp)
*/
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 @@ fseeko(FILE *fp, off_t offset, int whence)
* 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 @@ fseeko(FILE *fp, off_t offset, int whence)
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 @@ fseeko(FILE *fp, off_t offset, int whence)
break;
default:
+ FUNLOCKFILE(fp);
errno = EINVAL;
return (EOF);
}
@@ -189,6 +193,7 @@ fseeko(FILE *fp, off_t offset, int whence)
if (HASUB(fp))
FREEUB(fp);
fp->_flags &= ~__SEOF;
+ FUNLOCKFILE(fp);
return (0);
}
@@ -215,6 +220,7 @@ fseeko(FILE *fp, off_t offset, int whence)
fp->_p += n;
fp->_r -= n;
}
+ FUNLOCKFILE(fp);
return (0);
/*
@@ -224,6 +230,7 @@ fseeko(FILE *fp, off_t offset, int whence)
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 @@ dumb:
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 @@ ftello(FILE *fp)
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 @@ ftello(FILE *fp)
*/
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 @@ _fwalk(int (*function)(FILE *))
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 @@ fwrite(const void *buf, size_t size, size_t count, FILE *fp)
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 @@ fwrite(const void *buf, size_t size, size_t count, FILE *fp)
* 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 @@ getc(FILE *fp)
{
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 @@ gets(char *buf)
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 __swhatbuf(FILE *, size_t *, int *);
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 @@ extern int __sdidinit;
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 @@ putc(int c, FILE *fp)
{
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 @@ puts(const char *s)
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 @@ puts(const char *s)
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 @@ __srefill(FILE *fp)
* 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 @@ setvbuf(FILE *fp, char *buf, int mode, size_t size)
* 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 @@ nbf:
fp->_w = 0;
fp->_bf._base = fp->_p = fp->_nbuf;
fp->_bf._size = 1;
+ FUNLOCKFILE(fp);
return (ret);
}
flags |= __SMBF;
@@ -145,6 +147,7 @@ nbf:
/* 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 @@ snprintf(char *str, size_t n, const char *fmt, ...)
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 @@ sprintf(char *str, const char *fmt, ...)
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 @@ ungetc(int c, FILE *fp)
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 @@ ungetc(int c, FILE *fp)
*/
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 @@ ungetc(int c, FILE *fp)
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 @@ ungetc(int c, FILE *fp)
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 @@ vasprintf(char **str, const char *fmt, __va_list ap)
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 @@ __sbprintf(FILE *fp, const char *fmt, va_list ap)
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 @@ static int _my_isnan(double);
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 @@ VFSCANF(FILE *fp, const char *fmt0, __va_list ap)
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 @@ VFSCANF(FILE *fp, const char *fmt0, __va_list ap)
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 @@ literal:
* Disgusting backwards compatibility hacks. XXX
*/
case '\0': /* compat */
+ FUNLOCKFILE(fp);
return (EOF);
default: /* compat */
@@ -689,8 +693,10 @@ literal:
}
}
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 @@ vsnprintf(char *str, size_t n, const char *fmt, __va_list ap)
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 @@ vsprintf(char *str, const char *fmt, __va_list ap)
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 @@ __swbuf(int c, FILE *fp)
* 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);
}