diff options
author | Kenny Root <kroot@google.com> | 2011-02-12 07:13:44 -0800 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2011-02-14 09:32:56 -0800 |
commit | f582340a6a48588aa50da17e1620e8f91b146941 (patch) | |
tree | d30c0ffb648b7a43ff469f89f05850f5008482ec | |
parent | 72f9a5c374bf559e9c69a62c1d95304f913ef6b2 (diff) | |
download | bionic-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
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); } |