summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc/port/stdio/flush.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libc/port/stdio/flush.c')
-rw-r--r--usr/src/lib/libc/port/stdio/flush.c295
1 files changed, 229 insertions, 66 deletions
diff --git a/usr/src/lib/libc/port/stdio/flush.c b/usr/src/lib/libc/port/stdio/flush.c
index e0bdb699cd..02cb43249e 100644
--- a/usr/src/lib/libc/port/stdio/flush.c
+++ b/usr/src/lib/libc/port/stdio/flush.c
@@ -33,6 +33,7 @@
#include "synonyms.h"
#include "mtlib.h"
#include "file64.h"
+#include "../gen/_libc_gettext.h"
#define _iob __iob
@@ -48,6 +49,7 @@
#include <sys/stat.h>
#include <stddef.h>
#include <errno.h>
+#include <fcntl.h>
#undef end
@@ -63,6 +65,8 @@
#define FPDECL(fp) FILE *fp
#define FIRSTFP(lp, fp) fp = lp->iobp
#define NEXTFP(fp) fp++
+#define FPLOCK(fp) &fp->_lock
+#define FPSTATE(fp) &fp->_state
#define xFILE FILE
@@ -72,6 +76,10 @@
#define FIRSTFP(lp, fp) x##fp = lp->iobp; \
fp = x##fp ? &x##fp->_iob : &_iob[0]
#define NEXTFP(fp) (x##fp ? fp = &(++x##fp)->_iob : ++fp)
+#define FPLOCK(fp) x##fp ? \
+ &x##fp->xlock : &_xftab[IOPIND(fp)]._lock
+#define FPSTATE(fp) x##fp ? \
+ &x##fp->xstate : &_xftab[IOPIND(fp)]._state
/* The extended 32-bit file structure for use in link buffers */
typedef struct xFILE {
@@ -135,19 +143,11 @@ static struct _link_ *lastlink = NULL;
static int fcloses;
static int nchunks;
-static rwlock_t _first_link_lock = DEFAULTRWLOCK;
+static mutex_t _first_link_lock = DEFAULTMUTEX;
-static int _fflush_u_iops(void);
+static int _fflush_l_iops(void);
static FILE *getiop(FILE *, rmutex_t *, mbstate_t *);
-#define GETIOP(fp, lk, mb) {FILE *ret; \
- if ((ret = getiop((fp), __libc_threaded? (lk): NULL, (mb))) != NULL) { \
- if (__libc_threaded) \
- (void) __rw_unlock(&_first_link_lock); \
- return (ret); \
- }; \
- }
-
/*
* All functions that understand the linked list of iob's follow.
*/
@@ -164,7 +164,7 @@ __cleanup(void) /* called at process end to flush ouput streams */
void
stdio_locks()
{
- (void) __rw_wrlock(&_first_link_lock);
+ (void) __mutex_lock(&_first_link_lock);
/*
* XXX: We should acquire all of the iob locks here.
*/
@@ -176,7 +176,7 @@ stdio_unlocks()
/*
* XXX: We should release all of the iob locks here.
*/
- (void) __rw_unlock(&_first_link_lock);
+ (void) __mutex_unlock(&_first_link_lock);
}
void
@@ -185,22 +185,43 @@ _flushlbf(void) /* fflush() all line-buffered streams */
FPDECL(fp);
int i;
struct _link_ *lp;
+ /* Allow compiler to optimize the loop */
+ int threaded = __libc_threaded;
- if (__libc_threaded)
- (void) __rw_rdlock(&_first_link_lock);
+ if (threaded)
+ (void) __mutex_lock(&_first_link_lock);
lp = &__first_link;
do {
FIRSTFP(lp, fp);
for (i = lp->niob; --i >= 0; NEXTFP(fp)) {
- if ((fp->_flag & (_IOLBF | _IOWRT)) ==
- (_IOLBF | _IOWRT))
- (void) _fflush_u(fp);
+ /*
+ * The additional _IONBF check guards againsts
+ * allocated but uninitialized iops (see _findiop).
+ * We also automatically skip non allocated iop's.
+ * Don't block on locks.
+ */
+ if ((fp->_flag & (_IOLBF | _IOWRT | _IONBF)) ==
+ (_IOLBF | _IOWRT)) {
+ if (threaded) {
+ rmutex_t *lk = FPLOCK(fp);
+ if (rmutex_trylock(lk) != 0)
+ continue;
+ /* Recheck after locking */
+ if ((fp->_flag & (_IOLBF | _IOWRT)) ==
+ (_IOLBF | _IOWRT)) {
+ (void) _fflush_u(fp);
+ }
+ (void) rmutex_unlock(lk);
+ } else {
+ (void) _fflush_u(fp);
+ }
+ }
}
} while ((lp = lp->next) != NULL);
- if (__libc_threaded)
- (void) __rw_unlock(&_first_link_lock);
+ if (threaded)
+ (void) __mutex_unlock(&_first_link_lock);
}
/* allocate an unused stream; NULL if cannot */
@@ -232,9 +253,10 @@ _findiop(void)
struct _link_ *hdr;
FPDECL(fp);
int i;
+ int threaded = __libc_threaded;
- if (__libc_threaded)
- (void) __rw_wrlock(&_first_link_lock);
+ if (threaded)
+ (void) __mutex_lock(&_first_link_lock);
if (lastlink == NULL) {
rescan:
@@ -258,13 +280,18 @@ rescan:
FIRSTFP(lp, fp);
for (i = lp->niob; --i >= 0; NEXTFP(fp)) {
-#ifdef _LP64
- GETIOP(fp, &fp->_lock, &fp->_state);
-#else
- GETIOP(fp,
- xfp ? &xfp->xlock : &_xftab[IOPIND(fp)]._lock,
- xfp ? &xfp->xstate : &_xftab[IOPIND(fp)]._state);
-#endif /* _LP64 */
+ FILE *ret;
+ if (threaded) {
+ ret = getiop(fp, FPLOCK(fp), FPSTATE(fp));
+ if (ret != NULL) {
+ (void) __mutex_unlock(&_first_link_lock);
+ return (ret);
+ }
+ } else {
+ ret = getiop(fp, NULL, FPSTATE(fp));
+ if (ret != NULL)
+ return (ret);
+ }
}
} while ((lastlink = lp = lp->next) != NULL);
@@ -288,8 +315,8 @@ rescan:
* Need to allocate another and put it in the linked list.
*/
if ((pkgp = malloc(sizeof (Pkg))) == NULL) {
- if (__libc_threaded)
- (void) __rw_unlock(&_first_link_lock);
+ if (threaded)
+ (void) __mutex_unlock(&_first_link_lock);
return (NULL);
}
@@ -355,8 +382,8 @@ rescan:
fp->_ptr = 0;
fp->_base = 0;
fp->_flag = 0377; /* claim the fp by setting low 8 bits */
- if (__libc_threaded)
- (void) __rw_unlock(&_first_link_lock);
+ if (threaded)
+ (void) __mutex_unlock(&_first_link_lock);
return (fp);
}
@@ -369,7 +396,7 @@ isseekable(FILE *iop)
save_errno = errno;
- if (fstat64(iop->_file, &fstatbuf) != 0) {
+ if (fstat64(GET_FD(iop), &fstatbuf) != 0) {
/*
* when we don't know what it is we'll
* do the old behaviour and flush
@@ -452,8 +479,8 @@ _setbufend(FILE *iop, Uchar *end) /* set the end pointer for this iop */
* old _bufend macro. This is *so* broken, fileno()
* is not the proper index.
*/
- if (iop->_file < _NFILE)
- _bufendtab[iop->_file] = end;
+ if (iop->_magic < _NFILE)
+ _bufendtab[iop->_magic] = end;
}
@@ -523,8 +550,9 @@ _xflsbuf(FILE *iop)
_bufsync(iop, bufend);
if (n > 0) {
+ int fd = GET_FD(iop);
while ((num_wrote =
- write(iop->_file, base, (size_t)n)) != n) {
+ write(fd, base, (size_t)n)) != n) {
if (num_wrote <= 0) {
iop->_flag |= _IOERR;
return (EOF);
@@ -548,56 +576,86 @@ fflush(FILE *iop)
res = _fflush_u(iop);
FUNLOCKFILE(lk);
} else {
- res = _fflush_u_iops(); /* flush all iops */
+ res = _fflush_l_iops(); /* flush all iops */
}
return (res);
}
static int
-_fflush_u_iops(void) /* flush all buffers */
+_fflush_l_iops(void) /* flush all buffers */
{
FPDECL(iop);
int i;
struct _link_ *lp;
int res = 0;
+ rmutex_t *lk;
+ /* Allow the compiler to optimize the load out of the loop */
+ int threaded = __libc_threaded;
- if (__libc_threaded)
- (void) __rw_rdlock(&_first_link_lock);
+ if (threaded)
+ (void) __mutex_lock(&_first_link_lock);
lp = &__first_link;
do {
/*
- * Don't grab the locks for these file pointers
- * since they are supposed to be flushed anyway
- * It could also be the case in which the 2nd
- * portion (base and lock) are not initialized
+ * We need to grab the file locks or file corruption
+ * will happen. But we first check the flags field
+ * knowing that when it is 0, it isn't allocated and
+ * cannot be allocated while we're holding the
+ * _first_link_lock. And when _IONBF is set (also the
+ * case when _flag is 0377, or alloc in progress), we
+ * also ignore it.
+ *
+ * Ignore locked streams; it will appear as if
+ * concurrent updates happened after fflush(NULL). Note
+ * that we even attempt to lock if the locking is set to
+ * "by caller". We don't want to penalize callers of
+ * __fsetlocking() by not flushing their files. Note: if
+ * __fsetlocking() callers don't employ any locking, they
+ * may still face corruption in fflush(NULL); but that's
+ * no change from earlier releases.
*/
FIRSTFP(lp, iop);
for (i = lp->niob; --i >= 0; NEXTFP(iop)) {
- if (!(iop->_flag & _IONBF)) {
- /*
- * don't need to worry about the _IORW case
- * since the iop will also marked with _IOREAD
- * or _IOWRT whichever we are really doing
- */
- if (iop->_flag & _IOWRT) { /* flush write buffers */
- res |= _fflush_u(iop);
- } else if (iop->_flag & _IOREAD) {
+ unsigned int flag = iop->_flag;
+
+ /* flag 0, flag 0377, or _IONBF set */
+ if (flag == 0 || (flag & _IONBF) != 0)
+ continue;
+
+ if (threaded) {
+ lk = FPLOCK(iop);
+ if (rmutex_trylock(lk) != 0)
+ continue;
+ }
+
+ if (!(iop->_flag & _IONBF)) {
/*
- * flush seekable read buffers
- * don't flush non-seekable read buffers
+ * don't need to worry about the _IORW case
+ * since the iop will also marked with _IOREAD
+ * or _IOWRT whichever we are really doing
*/
- if (GET_SEEKABLE(iop)) {
- res |= _fflush_u(iop);
- }
+ if (iop->_flag & _IOWRT) {
+ /* Flush write buffers */
+ res |= _fflush_u(iop);
+ } else if (iop->_flag & _IOREAD) {
+ /*
+ * flush seekable read buffers
+ * don't flush non-seekable read buffers
+ */
+ if (GET_SEEKABLE(iop)) {
+ res |= _fflush_u(iop);
+ }
+ }
}
- }
+ if (threaded)
+ (void) rmutex_unlock(lk);
}
} while ((lp = lp->next) != NULL);
- if (__libc_threaded)
- (void) __rw_unlock(&_first_link_lock);
+ if (threaded)
+ (void) __mutex_unlock(&_first_link_lock);
return (res);
}
@@ -609,7 +667,7 @@ _fflush_u(FILE *iop)
/* this portion is always assumed locked */
if (!(iop->_flag & _IOWRT)) {
- (void) lseek64(iop->_file, -iop->_cnt, SEEK_CUR);
+ (void) lseek64(GET_FD(iop), -iop->_cnt, SEEK_CUR);
iop->_cnt = 0;
/* needed for ungetc & multibyte pushbacks */
iop->_ptr = iop->_base;
@@ -647,7 +705,7 @@ fclose(FILE *iop)
/* Is not unbuffered and opened for read and/or write ? */
if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
res = _fflush_u(iop);
- if (close(iop->_file) < 0)
+ if (close(GET_FD(iop)) < 0)
res = EOF;
if (iop->_flag & _IOMYBUF) {
(void) free((char *)iop->_base - PUSHBACK);
@@ -659,10 +717,10 @@ fclose(FILE *iop)
FUNLOCKFILE(lk);
if (__libc_threaded)
- (void) __rw_wrlock(&_first_link_lock);
+ (void) __mutex_lock(&_first_link_lock);
fcloses++;
if (__libc_threaded)
- (void) __rw_unlock(&_first_link_lock);
+ (void) __mutex_unlock(&_first_link_lock);
return (res);
}
@@ -679,7 +737,7 @@ close_fd(FILE *iop)
/* Is not unbuffered and opened for read and/or write ? */
if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
res = _fflush_u(iop);
- if (close(iop->_file) < 0)
+ if (close(GET_FD(iop)) < 0)
res = EOF;
if (iop->_flag & _IOMYBUF) {
(void) free((char *)iop->_base - PUSHBACK);
@@ -736,4 +794,109 @@ _getmbstate(FILE *iop)
return (NULL);
}
+
+/*
+ * More 32-bit only functions.
+ * They lookup/set large fd's for extended FILE support.
+ */
+
+/*
+ * The negative value indicates that Extended fd FILE's has not
+ * been enabled by the user.
+ */
+static int bad_fd = -1;
+
+int
+_file_get(FILE *iop)
+{
+ int altfd;
+
+ /*
+ * Failure indicates a FILE * not allocated through stdio;
+ * it means the flag values are probably bogus and that if
+ * a file descriptor is set, it's in _magic.
+ * Inline getxfdat() for performance reasons.
+ */
+ if (STDIOP(iop))
+ altfd = _xftab[IOPIND(iop)]._altfd;
+ else if (VALIDXFILE(FILEx(iop)))
+ altfd = FILEx(iop)->_xdat._altfd;
+ else
+ return (iop->_magic);
+ /*
+ * if this is not an internal extended FILE then check
+ * if _file is being changed from underneath us.
+ * It should not be because if
+ * it is then then we lose our ability to guard against
+ * silent data corruption.
+ */
+ if (!iop->__xf_nocheck && bad_fd > -1 && iop->_magic != bad_fd) {
+ /* LINTED: variable format specifier */
+ (void) fprintf(stderr, _libc_gettext(
+ "Application violated extended FILE safety mechanism.\n"
+ "Please read the man page for extendedFILE.\nAborting\n"));
+ abort();
+ }
+ return (altfd);
+}
+
+int
+_file_set(FILE *iop, int fd, const char *type)
+{
+ struct xFILEdata *dat;
+ int Fflag;
+
+ /* Already known to contain at least one byte */
+ while (*++type != '\0')
+ ;
+
+ Fflag = type[-1] == 'F';
+ if (!Fflag && bad_fd < 0) {
+ errno = EMFILE;
+ return (-1);
+ }
+
+ dat = getxfdat(iop);
+ iop->__extendedfd = 1;
+ iop->__xf_nocheck = Fflag;
+ dat->_altfd = fd;
+ iop->_magic = (unsigned char)bad_fd;
+ return (0);
+}
+
+/*
+ * Activates extended fd's in FILE's
+ */
+
+static const int tries[] = {196, 120, 60, 3};
+#define NTRIES (sizeof (tries)/sizeof (int))
+
+int
+enable_extended_FILE_stdio(int fd, int action)
+{
+ int i;
+
+ if (action < 0)
+ action = SIGABRT; /* default signal */
+
+ if (fd < 0) {
+ /*
+ * search for an available fd and make it the badfd
+ */
+ for (i = 0; i < NTRIES; i++) {
+ fd = fcntl(tries[i], F_BADFD, action);
+ if (fd >= 0)
+ break;
+ }
+ if (fd < 0) /* failed to find an available fd */
+ return (-1);
+ } else {
+ /* caller requests that fd be the chosen badfd */
+ int nfd = fcntl(fd, F_BADFD, action);
+ if (nfd < 0 || nfd != fd)
+ return (-1);
+ }
+ bad_fd = fd;
+ return (0);
+}
#endif