2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A#include "lint.h"
2N/A#include "mtlib.h"
2N/A#include "file64.h"
2N/A#include <sys/types.h>
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <thread.h>
2N/A#include <synch.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include "stdiom.h"
2N/A#include <wchar.h>
2N/A#include <sys/stat.h>
2N/A#include <stddef.h>
2N/A#include <errno.h>
2N/A#include <fcntl.h>
2N/A
2N/A#define _iob __iob
2N/A
2N/A#undef end
2N/A
2N/A#define FILE_ARY_SZ 8 /* a nice size for FILE array & end_buffer_ptrs */
2N/A
2N/A#ifdef _LP64
2N/A
2N/A/*
2N/A * Macros to declare and loop over a fp or fp/xfp combo to
2N/A * avoid some of the _LP64 ifdef hell.
2N/A */
2N/A
2N/A#define FPDECL(fp) FILE *fp
2N/A#define FIRSTFP(lp, fp) fp = lp->iobp
2N/A#define NEXTFP(fp) fp++
2N/A#define FPLOCK(fp) &fp->_lock
2N/A#define FPSTATE(fp) &fp->_state
2N/A
2N/A#define xFILE FILE
2N/A
2N/A#else
2N/A
2N/A#define FPDECL(fp) FILE *fp; xFILE *x##fp
2N/A#define FIRSTFP(lp, fp) x##fp = lp->iobp; \
2N/A fp = x##fp ? &x##fp->_iob : &_iob[0]
2N/A#define NEXTFP(fp) (x##fp ? fp = &(++x##fp)->_iob : ++fp)
2N/A#define FPLOCK(fp) x##fp ? \
2N/A &x##fp->xlock : &_xftab[IOPIND(fp)]._lock
2N/A#define FPSTATE(fp) x##fp ? \
2N/A &x##fp->xstate : &_xftab[IOPIND(fp)]._state
2N/A
2N/A/* The extended 32-bit file structure for use in link buffers */
2N/Atypedef struct xFILE {
2N/A FILE _iob; /* must be first! */
2N/A struct xFILEdata _xdat;
2N/A} xFILE;
2N/A
2N/A#define xmagic _xdat._magic
2N/A#define xend _xdat._end
2N/A#define xlock _xdat._lock
2N/A#define xstate _xdat._state
2N/A
2N/A#define FILEx(fp) ((struct xFILE *)(uintptr_t)fp)
2N/A
2N/A/*
2N/A * The magic number stored is actually the pointer scrambled with
2N/A * a magic number. Pointers to data items live everywhere in memory
2N/A * so we scramble the pointer in order to avoid accidental collisions.
2N/A */
2N/A#define XFILEMAGIC 0x63687367
2N/A#define XMAGIC(xfp) ((uintptr_t)(xfp) ^ XFILEMAGIC)
2N/A
2N/A#endif /* _LP64 */
2N/A
2N/Astruct _link_ /* manages a list of streams */
2N/A{
2N/A xFILE *iobp; /* the array of (x)FILE's */
2N/A /* NULL for the __first_link in ILP32 */
2N/A int niob; /* length of the arrays */
2N/A struct _link_ *next; /* next in the list */
2N/A};
2N/A
2N/A/*
2N/A * With dynamic linking, iob may be in either the library or in the user's
2N/A * a.out, so the run time linker fixes up the first entry in __first_link at
2N/A * process startup time.
2N/A *
2N/A * In 32 bit processes, we don't have xFILE[FILE_ARY_SZ] but FILE[],
2N/A * and _xftab[] instead; this is denoted by having iobp set to NULL in
2N/A * 32 bit mode for the first link entry.
2N/A */
2N/Astruct _link_ __first_link = /* first in linked list */
2N/A{
2N/A#if !defined(_LP64)
2N/A NULL,
2N/A#else
2N/A &_iob[0],
2N/A#endif
2N/A _NFILE,
2N/A NULL
2N/A};
2N/A
2N/A/*
2N/A * Information cached to speed up searches. We remember where we
2N/A * last found a free FILE* and we remember whether we saw any fcloses
2N/A * in between. We also count the number of chunks we allocated, see
2N/A * _findiop() for an explanation.
2N/A * These variables are all protected by _first_link_lock.
2N/A */
2N/Astatic struct _link_ *lastlink = NULL;
2N/Astatic int fcloses;
2N/Astatic int nchunks;
2N/A
2N/Astatic mutex_t _first_link_lock = DEFAULTMUTEX;
2N/A
2N/Astatic int _fflush_l_iops(void);
2N/Astatic FILE *getiop(FILE *, rmutex_t *, mbstate_t *);
2N/A
2N/A/*
2N/A * All functions that understand the linked list of iob's follow.
2N/A */
2N/A#pragma weak _cleanup = __cleanup
2N/Avoid
2N/A__cleanup(void) /* called at process end to flush ouput streams */
2N/A{
2N/A (void) fflush(NULL);
2N/A}
2N/A
2N/A/*
2N/A * For fork1-safety (see libc_prepare_atfork(), etc).
2N/A */
2N/Avoid
2N/Astdio_locks()
2N/A{
2N/A (void) mutex_lock(&_first_link_lock);
2N/A /*
2N/A * XXX: We should acquire all of the iob locks here.
2N/A */
2N/A}
2N/A
2N/Avoid
2N/Astdio_unlocks()
2N/A{
2N/A /*
2N/A * XXX: We should release all of the iob locks here.
2N/A */
2N/A (void) mutex_unlock(&_first_link_lock);
2N/A}
2N/A
2N/Avoid
2N/A_flushlbf(void) /* fflush() all line-buffered streams */
2N/A{
2N/A FPDECL(fp);
2N/A int i;
2N/A struct _link_ *lp;
2N/A /* Allow compiler to optimize the loop */
2N/A int threaded = __libc_threaded;
2N/A
2N/A if (threaded)
2N/A cancel_safe_mutex_lock(&_first_link_lock);
2N/A
2N/A lp = &__first_link;
2N/A do {
2N/A FIRSTFP(lp, fp);
2N/A for (i = lp->niob; --i >= 0; NEXTFP(fp)) {
2N/A /*
2N/A * The additional _IONBF check guards againsts
2N/A * allocated but uninitialized iops (see _findiop).
2N/A * We also automatically skip non allocated iop's.
2N/A * Don't block on locks.
2N/A */
2N/A if ((fp->_flag & (_IOLBF | _IOWRT | _IONBF)) ==
2N/A (_IOLBF | _IOWRT)) {
2N/A if (threaded) {
2N/A rmutex_t *lk = FPLOCK(fp);
2N/A if (cancel_safe_mutex_trylock(lk) != 0)
2N/A continue;
2N/A /* Recheck after locking */
2N/A if ((fp->_flag & (_IOLBF | _IOWRT)) ==
2N/A (_IOLBF | _IOWRT)) {
2N/A (void) _fflush_u(fp);
2N/A }
2N/A cancel_safe_mutex_unlock(lk);
2N/A } else {
2N/A (void) _fflush_u(fp);
2N/A }
2N/A }
2N/A }
2N/A } while ((lp = lp->next) != NULL);
2N/A
2N/A if (threaded)
2N/A cancel_safe_mutex_unlock(&_first_link_lock);
2N/A}
2N/A
2N/A/* allocate an unused stream; NULL if cannot */
2N/AFILE *
2N/A_findiop(void)
2N/A{
2N/A struct _link_ *lp, **prev;
2N/A
2N/A /* used so there only needs to be one malloc() */
2N/A#ifdef _LP64
2N/A typedef struct {
2N/A struct _link_ hdr;
2N/A FILE iob[FILE_ARY_SZ];
2N/A } Pkg;
2N/A#else
2N/A typedef union {
2N/A struct { /* Normal */
2N/A struct _link_ hdr;
2N/A xFILE iob[FILE_ARY_SZ];
2N/A } Pkgn;
2N/A struct { /* Reversed */
2N/A xFILE iob[FILE_ARY_SZ];
2N/A struct _link_ hdr;
2N/A } Pkgr;
2N/A } Pkg;
2N/A uintptr_t delta;
2N/A#endif
2N/A Pkg *pkgp;
2N/A struct _link_ *hdr;
2N/A FPDECL(fp);
2N/A int i;
2N/A int threaded = __libc_threaded;
2N/A
2N/A if (threaded)
2N/A cancel_safe_mutex_lock(&_first_link_lock);
2N/A
2N/A if (lastlink == NULL) {
2N/Arescan:
2N/A fcloses = 0;
2N/A lastlink = &__first_link;
2N/A }
2N/A
2N/A lp = lastlink;
2N/A
2N/A /*
2N/A * lock to make testing of fp->_flag == 0 and acquiring the fp atomic
2N/A * and for allocation of new links
2N/A * low contention expected on _findiop(), hence coarse locking.
2N/A * for finer granularity, use fp->_lock for allocating an iop
2N/A * and make the testing of lp->next and allocation of new link atomic
2N/A * using lp->_lock
2N/A */
2N/A
2N/A do {
2N/A prev = &lp->next;
2N/A FIRSTFP(lp, fp);
2N/A
2N/A for (i = lp->niob; --i >= 0; NEXTFP(fp)) {
2N/A FILE *ret;
2N/A if (threaded) {
2N/A ret = getiop(fp, FPLOCK(fp), FPSTATE(fp));
2N/A if (ret != NULL) {
2N/A cancel_safe_mutex_unlock(
2N/A &_first_link_lock);
2N/A return (ret);
2N/A }
2N/A } else {
2N/A ret = getiop(fp, NULL, FPSTATE(fp));
2N/A if (ret != NULL)
2N/A return (ret);
2N/A }
2N/A }
2N/A } while ((lastlink = lp = lp->next) != NULL);
2N/A
2N/A /*
2N/A * If there was a sufficient number of fcloses since we last started
2N/A * at __first_link, we rescan all fp's again. We do not rescan for
2N/A * all fcloses; that would simplify the algorithm but would make
2N/A * search times near O(n) again.
2N/A * Worst case behaviour would still be pretty bad (open a full set,
2N/A * then continously opening and closing one FILE * gets you a full
2N/A * scan each time). That's why we over allocate 1 FILE for each
2N/A * 32 chunks. More over allocation is better; this is a nice
2N/A * empirical value which doesn't cost a lot of memory, doesn't
2N/A * overallocate until we reach 256 FILE *s and keeps the performance
2N/A * pretty close to the optimum.
2N/A */
2N/A if (fcloses > nchunks/32)
2N/A goto rescan;
2N/A
2N/A /*
2N/A * Need to allocate another and put it in the linked list.
2N/A */
2N/A if ((pkgp = malloc(sizeof (Pkg))) == NULL) {
2N/A if (threaded)
2N/A cancel_safe_mutex_unlock(&_first_link_lock);
2N/A return (NULL);
2N/A }
2N/A
2N/A (void) memset(pkgp, 0, sizeof (Pkg));
2N/A
2N/A#ifdef _LP64
2N/A hdr = &pkgp->hdr;
2N/A hdr->iobp = &pkgp->iob[0];
2N/A#else
2N/A /*
2N/A * The problem with referencing a word after a FILE* is the possibility
2N/A * of a SIGSEGV if a non-stdio issue FILE structure ends on a page
2N/A * boundary. We run this check so we never need to run an expensive
2N/A * check like mincore() in order to know whether it is
2N/A * safe to dereference ((xFILE*)fp)->xmagic.
2N/A * We allocate the block with two alternative layouts; if one
2N/A * layout is not properly aligned for our purposes, the other layout
2N/A * will be because the size of _link_ is small compared to
2N/A * sizeof (xFILE).
2N/A * The check performed is this:
2N/A * If the distance from pkgp to the end of the page is
2N/A * less than the the offset of the last xmagic field in the
2N/A * xFILE structure, (the 0x1000 boundary is inside our just
2N/A * allocated structure) and the distance modulo the size of xFILE
2N/A * is identical to the offset of the first xmagic in the
2N/A * structure (i.e., XXXXXX000 points to an xmagic field),
2N/A * we need to use the reverse structure.
2N/A */
2N/A if ((delta = 0x1000 - ((uintptr_t)pkgp & 0xfff)) <=
2N/A offsetof(Pkg, Pkgn.iob[FILE_ARY_SZ-1].xmagic) &&
2N/A delta % sizeof (struct xFILE) ==
2N/A offsetof(Pkg, Pkgn.iob[0].xmagic)) {
2N/A /* Use reversed structure */
2N/A hdr = &pkgp->Pkgr.hdr;
2N/A hdr->iobp = &pkgp->Pkgr.iob[0];
2N/A } else {
2N/A /* Use normal structure */
2N/A hdr = &pkgp->Pkgn.hdr;
2N/A hdr->iobp = &pkgp->Pkgn.iob[0];
2N/A }
2N/A#endif /* _LP64 */
2N/A
2N/A hdr->niob = FILE_ARY_SZ;
2N/A nchunks++;
2N/A
2N/A#ifdef _LP64
2N/A fp = hdr->iobp;
2N/A for (i = 0; i < FILE_ARY_SZ; i++)
2N/A (void) mutex_init(&fp[i]._lock,
2N/A USYNC_THREAD | LOCK_RECURSIVE, NULL);
2N/A#else
2N/A xfp = hdr->iobp;
2N/A fp = &xfp->_iob;
2N/A
2N/A for (i = 0; i < FILE_ARY_SZ; i++) {
2N/A xfp[i].xmagic = XMAGIC(&xfp[i]);
2N/A (void) mutex_init(&xfp[i].xlock,
2N/A USYNC_THREAD | LOCK_RECURSIVE, NULL);
2N/A }
2N/A#endif /* _LP64 */
2N/A
2N/A lastlink = *prev = hdr;
2N/A fp->_ptr = 0;
2N/A fp->_base = 0;
2N/A fp->_flag = 0377; /* claim the fp by setting low 8 bits */
2N/A if (threaded)
2N/A cancel_safe_mutex_unlock(&_first_link_lock);
2N/A
2N/A return (fp);
2N/A}
2N/A
2N/Astatic void
2N/Aisseekable(FILE *iop)
2N/A{
2N/A struct stat64 fstatbuf;
2N/A int save_errno;
2N/A
2N/A save_errno = errno;
2N/A
2N/A if (fstat64(GET_FD(iop), &fstatbuf) != 0) {
2N/A /*
2N/A * when we don't know what it is we'll
2N/A * do the old behaviour and flush
2N/A * the stream
2N/A */
2N/A SET_SEEKABLE(iop);
2N/A errno = save_errno;
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * check for what is non-SEEKABLE
2N/A * otherwise assume it's SEEKABLE so we get the old
2N/A * behaviour and flush the stream
2N/A */
2N/A
2N/A if (S_ISFIFO(fstatbuf.st_mode) || S_ISCHR(fstatbuf.st_mode) ||
2N/A S_ISSOCK(fstatbuf.st_mode) || S_ISDOOR(fstatbuf.st_mode)) {
2N/A CLEAR_SEEKABLE(iop);
2N/A } else {
2N/A SET_SEEKABLE(iop);
2N/A }
2N/A
2N/A errno = save_errno;
2N/A}
2N/A
2N/A#ifdef _LP64
2N/Avoid
2N/A_setbufend(FILE *iop, Uchar *end) /* set the end pointer for this iop */
2N/A{
2N/A iop->_end = end;
2N/A
2N/A isseekable(iop);
2N/A}
2N/A
2N/A#undef _realbufend
2N/A
2N/AUchar *
2N/A_realbufend(FILE *iop) /* get the end pointer for this iop */
2N/A{
2N/A return (iop->_end);
2N/A}
2N/A
2N/A#else /* _LP64 */
2N/A
2N/A/*
2N/A * Awkward functions not needed for the sane 64 bit environment.
2N/A */
2N/A/*
2N/A * xmagic must not be aligned on a 4K boundary. We guarantee this in
2N/A * _findiop().
2N/A */
2N/A#define VALIDXFILE(xfp) \
2N/A (((uintptr_t)&(xfp)->xmagic & 0xfff) && \
2N/A (xfp)->xmagic == XMAGIC(FILEx(xfp)))
2N/A
2N/Astatic struct xFILEdata *
2N/Agetxfdat(FILE *iop)
2N/A{
2N/A if (STDIOP(iop))
2N/A return (&_xftab[IOPIND(iop)]);
2N/A else if (VALIDXFILE(FILEx(iop)))
2N/A return (&FILEx(iop)->_xdat);
2N/A else
2N/A return (NULL);
2N/A}
2N/A
2N/Avoid
2N/A_setbufend(FILE *iop, Uchar *end) /* set the end pointer for this iop */
2N/A{
2N/A struct xFILEdata *dat = getxfdat(iop);
2N/A
2N/A if (dat != NULL)
2N/A dat->_end = end;
2N/A
2N/A isseekable(iop);
2N/A
2N/A /*
2N/A * For binary compatibility with user programs using the
2N/A * old _bufend macro. This is *so* broken, fileno()
2N/A * is not the proper index.
2N/A */
2N/A if (iop->_magic < _NFILE)
2N/A _bufendtab[iop->_magic] = end;
2N/A
2N/A}
2N/A
2N/AUchar *
2N/A_realbufend(FILE *iop) /* get the end pointer for this iop */
2N/A{
2N/A struct xFILEdata *dat = getxfdat(iop);
2N/A
2N/A if (dat != NULL)
2N/A return (dat->_end);
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * _reallock() is invoked in each stdio call through the IOB_LCK() macro,
2N/A * it is therefor extremely performance sensitive. We get better performance
2N/A * by inlining the STDIOP check in IOB_LCK and inlining a custom version
2N/A * of getfxdat() here.
2N/A */
2N/Armutex_t *
2N/A_reallock(FILE *iop)
2N/A{
2N/A if (VALIDXFILE(FILEx(iop)))
2N/A return (&FILEx(iop)->xlock);
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A#endif /* _LP64 */
2N/A
2N/A/* make sure _cnt, _ptr are correct */
2N/Avoid
2N/A_bufsync(FILE *iop, Uchar *bufend)
2N/A{
2N/A ssize_t spaceleft;
2N/A
2N/A spaceleft = bufend - iop->_ptr;
2N/A if (bufend < iop->_ptr) {
2N/A iop->_ptr = bufend;
2N/A iop->_cnt = 0;
2N/A } else if (spaceleft < iop->_cnt)
2N/A iop->_cnt = spaceleft;
2N/A}
2N/A
2N/A/* really write out current buffer contents */
2N/Aint
2N/A_xflsbuf(FILE *iop)
2N/A{
2N/A ssize_t n;
2N/A Uchar *base = iop->_base;
2N/A Uchar *bufend;
2N/A ssize_t num_wrote;
2N/A
2N/A /*
2N/A * Hopefully, be stable with respect to interrupts...
2N/A */
2N/A n = iop->_ptr - base;
2N/A iop->_ptr = base;
2N/A bufend = _bufend(iop);
2N/A if (iop->_flag & (_IOLBF | _IONBF))
2N/A iop->_cnt = 0; /* always go to a flush */
2N/A else
2N/A iop->_cnt = bufend - base;
2N/A
2N/A if (_needsync(iop, bufend)) /* recover from interrupts */
2N/A _bufsync(iop, bufend);
2N/A
2N/A if (n > 0) {
2N/A int fd = GET_FD(iop);
2N/A while ((num_wrote = write(fd, base, (size_t)n)) != n) {
2N/A if (num_wrote <= 0) {
2N/A if (!cancel_active())
2N/A iop->_flag |= _IOERR;
2N/A return (EOF);
2N/A }
2N/A n -= num_wrote;
2N/A base += num_wrote;
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/* flush (write) buffer */
2N/Aint
2N/Afflush(FILE *iop)
2N/A{
2N/A int res;
2N/A rmutex_t *lk;
2N/A
2N/A if (iop) {
2N/A FLOCKFILE(lk, iop);
2N/A res = _fflush_u(iop);
2N/A FUNLOCKFILE(lk);
2N/A } else {
2N/A res = _fflush_l_iops(); /* flush all iops */
2N/A }
2N/A return (res);
2N/A}
2N/A
2N/Astatic int
2N/A_fflush_l_iops(void) /* flush all buffers */
2N/A{
2N/A FPDECL(iop);
2N/A
2N/A int i;
2N/A struct _link_ *lp;
2N/A int res = 0;
2N/A rmutex_t *lk;
2N/A /* Allow the compiler to optimize the load out of the loop */
2N/A int threaded = __libc_threaded;
2N/A
2N/A if (threaded)
2N/A cancel_safe_mutex_lock(&_first_link_lock);
2N/A
2N/A lp = &__first_link;
2N/A
2N/A do {
2N/A /*
2N/A * We need to grab the file locks or file corruption
2N/A * will happen. But we first check the flags field
2N/A * knowing that when it is 0, it isn't allocated and
2N/A * cannot be allocated while we're holding the
2N/A * _first_link_lock. And when _IONBF is set (also the
2N/A * case when _flag is 0377, or alloc in progress), we
2N/A * also ignore it.
2N/A *
2N/A * Ignore locked streams; it will appear as if
2N/A * concurrent updates happened after fflush(NULL). Note
2N/A * that we even attempt to lock if the locking is set to
2N/A * "by caller". We don't want to penalize callers of
2N/A * __fsetlocking() by not flushing their files. Note: if
2N/A * __fsetlocking() callers don't employ any locking, they
2N/A * may still face corruption in fflush(NULL); but that's
2N/A * no change from earlier releases.
2N/A */
2N/A FIRSTFP(lp, iop);
2N/A for (i = lp->niob; --i >= 0; NEXTFP(iop)) {
2N/A unsigned int flag = iop->_flag;
2N/A
2N/A /* flag 0, flag 0377, or _IONBF set */
2N/A if (flag == 0 || (flag & _IONBF) != 0)
2N/A continue;
2N/A
2N/A if (threaded) {
2N/A lk = FPLOCK(iop);
2N/A if (cancel_safe_mutex_trylock(lk) != 0)
2N/A continue;
2N/A }
2N/A
2N/A if (!(iop->_flag & _IONBF)) {
2N/A /*
2N/A * don't need to worry about the _IORW case
2N/A * since the iop will also marked with _IOREAD
2N/A * or _IOWRT whichever we are really doing
2N/A */
2N/A if (iop->_flag & _IOWRT) {
2N/A /* Flush write buffers */
2N/A res |= _fflush_u(iop);
2N/A } else if (iop->_flag & _IOREAD) {
2N/A /*
2N/A * flush seekable read buffers
2N/A * don't flush non-seekable read buffers
2N/A */
2N/A if (GET_SEEKABLE(iop)) {
2N/A res |= _fflush_u(iop);
2N/A }
2N/A }
2N/A }
2N/A if (threaded)
2N/A cancel_safe_mutex_unlock(lk);
2N/A }
2N/A } while ((lp = lp->next) != NULL);
2N/A if (threaded)
2N/A cancel_safe_mutex_unlock(&_first_link_lock);
2N/A return (res);
2N/A}
2N/A
2N/A/* flush buffer */
2N/Aint
2N/A_fflush_u(FILE *iop)
2N/A{
2N/A int res = 0;
2N/A
2N/A /* this portion is always assumed locked */
2N/A if (!(iop->_flag & _IOWRT)) {
2N/A (void) lseek64(GET_FD(iop), -iop->_cnt, SEEK_CUR);
2N/A iop->_cnt = 0;
2N/A /* needed for ungetc & multibyte pushbacks */
2N/A iop->_ptr = iop->_base;
2N/A if (iop->_flag & _IORW) {
2N/A iop->_flag &= ~_IOREAD;
2N/A }
2N/A return (0);
2N/A }
2N/A if (iop->_base != NULL && iop->_ptr > iop->_base) {
2N/A res = _xflsbuf(iop);
2N/A }
2N/A if (iop->_flag & _IORW) {
2N/A iop->_flag &= ~_IOWRT;
2N/A iop->_cnt = 0;
2N/A }
2N/A return (res);
2N/A}
2N/A
2N/A/* flush buffer and close stream */
2N/Aint
2N/Afclose(FILE *iop)
2N/A{
2N/A int res = 0;
2N/A rmutex_t *lk;
2N/A
2N/A if (iop == NULL) {
2N/A return (EOF); /* avoid passing zero to FLOCKFILE */
2N/A }
2N/A
2N/A FLOCKFILE(lk, iop);
2N/A if (iop->_flag == 0) {
2N/A FUNLOCKFILE(lk);
2N/A return (EOF);
2N/A }
2N/A /* Is not unbuffered and opened for read and/or write ? */
2N/A if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
2N/A res = _fflush_u(iop);
2N/A if (close(GET_FD(iop)) < 0)
2N/A res = EOF;
2N/A if (iop->_flag & _IOMYBUF) {
2N/A (void) free((char *)iop->_base - PUSHBACK);
2N/A }
2N/A iop->_base = NULL;
2N/A iop->_ptr = NULL;
2N/A iop->_cnt = 0;
2N/A iop->_flag = 0; /* marks it as available */
2N/A FUNLOCKFILE(lk);
2N/A
2N/A if (__libc_threaded)
2N/A cancel_safe_mutex_lock(&_first_link_lock);
2N/A fcloses++;
2N/A if (__libc_threaded)
2N/A cancel_safe_mutex_unlock(&_first_link_lock);
2N/A
2N/A return (res);
2N/A}
2N/A
2N/A/* close all open streams */
2N/Aint
2N/Afcloseall(void)
2N/A{
2N/A FPDECL(iop);
2N/A
2N/A struct _link_ *lp;
2N/A rmutex_t *lk;
2N/A
2N/A if (__libc_threaded)
2N/A cancel_safe_mutex_lock(&_first_link_lock);
2N/A
2N/A lp = &__first_link;
2N/A
2N/A do {
2N/A int i;
2N/A
2N/A FIRSTFP(lp, iop);
2N/A for (i = lp->niob; --i >= 0; NEXTFP(iop)) {
2N/A /* code stolen from fclose(), above */
2N/A
2N/A FLOCKFILE(lk, iop);
2N/A if (iop->_flag == 0) {
2N/A FUNLOCKFILE(lk);
2N/A continue;
2N/A }
2N/A
2N/A /* Not unbuffered and opened for read and/or write? */
2N/A if (!(iop->_flag & _IONBF) &&
2N/A (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
2N/A (void) _fflush_u(iop);
2N/A (void) close(GET_FD(iop));
2N/A if (iop->_flag & _IOMYBUF)
2N/A free((char *)iop->_base - PUSHBACK);
2N/A iop->_base = NULL;
2N/A iop->_ptr = NULL;
2N/A iop->_cnt = 0;
2N/A iop->_flag = 0; /* marks it as available */
2N/A FUNLOCKFILE(lk);
2N/A fcloses++;
2N/A }
2N/A } while ((lp = lp->next) != NULL);
2N/A
2N/A if (__libc_threaded)
2N/A cancel_safe_mutex_unlock(&_first_link_lock);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/* flush buffer, close fd but keep the stream used by freopen() */
2N/Aint
2N/Aclose_fd(FILE *iop)
2N/A{
2N/A int res = 0;
2N/A mbstate_t *mb;
2N/A
2N/A if (iop == NULL || iop->_flag == 0)
2N/A return (EOF);
2N/A /* Is not unbuffered and opened for read and/or write ? */
2N/A if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
2N/A res = _fflush_u(iop);
2N/A if (close(GET_FD(iop)) < 0)
2N/A res = EOF;
2N/A if (iop->_flag & _IOMYBUF) {
2N/A (void) free((char *)iop->_base - PUSHBACK);
2N/A }
2N/A iop->_base = NULL;
2N/A iop->_ptr = NULL;
2N/A mb = _getmbstate(iop);
2N/A if (mb != NULL)
2N/A (void) memset(mb, 0, sizeof (mbstate_t));
2N/A iop->_cnt = 0;
2N/A _setorientation(iop, _NO_MODE);
2N/A return (res);
2N/A}
2N/A
2N/Astatic FILE *
2N/Agetiop(FILE *fp, rmutex_t *lk, mbstate_t *mb)
2N/A{
2N/A if (lk != NULL && cancel_safe_mutex_trylock(lk) != 0)
2N/A return (NULL); /* locked: fp in use */
2N/A
2N/A if (fp->_flag == 0) { /* unused */
2N/A#ifndef _LP64
2N/A fp->__orientation = 0;
2N/A#endif /* _LP64 */
2N/A fp->_cnt = 0;
2N/A fp->_ptr = NULL;
2N/A fp->_base = NULL;
2N/A fp->_flag = 0377; /* claim the fp by setting low 8 bits */
2N/A (void) memset(mb, 0, sizeof (mbstate_t));
2N/A FUNLOCKFILE(lk);
2N/A return (fp);
2N/A }
2N/A FUNLOCKFILE(lk);
2N/A return (NULL);
2N/A}
2N/A
2N/A#ifndef _LP64
2N/A/*
2N/A * DESCRIPTION:
2N/A * This function gets the pointer to the mbstate_t structure associated
2N/A * with the specified iop.
2N/A *
2N/A * RETURNS:
2N/A * If the associated mbstate_t found, the pointer to the mbstate_t is
2N/A * returned. Otherwise, NULL is returned.
2N/A */
2N/Ambstate_t *
2N/A_getmbstate(FILE *iop)
2N/A{
2N/A struct xFILEdata *dat = getxfdat(iop);
2N/A
2N/A if (dat != NULL)
2N/A return (&dat->_state);
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * More 32-bit only functions.
2N/A * They lookup/set large fd's for extended FILE support.
2N/A */
2N/A
2N/A/*
2N/A * The negative value indicates that Extended fd FILE's has not
2N/A * been enabled by the user.
2N/A */
2N/Astatic int bad_fd = -1;
2N/A
2N/Aint
2N/A_file_get(FILE *iop)
2N/A{
2N/A int altfd;
2N/A
2N/A /*
2N/A * Failure indicates a FILE * not allocated through stdio;
2N/A * it means the flag values are probably bogus and that if
2N/A * a file descriptor is set, it's in _magic.
2N/A * Inline getxfdat() for performance reasons.
2N/A */
2N/A if (STDIOP(iop))
2N/A altfd = _xftab[IOPIND(iop)]._altfd;
2N/A else if (VALIDXFILE(FILEx(iop)))
2N/A altfd = FILEx(iop)->_xdat._altfd;
2N/A else
2N/A return (iop->_magic);
2N/A /*
2N/A * if this is not an internal extended FILE then check
2N/A * if _file is being changed from underneath us.
2N/A * It should not be because if
2N/A * it is then then we lose our ability to guard against
2N/A * silent data corruption.
2N/A */
2N/A if (!iop->__xf_nocheck && bad_fd > -1 && iop->_magic != bad_fd) {
2N/A (void) fprintf(stderr,
2N/A "Application violated extended FILE safety mechanism.\n"
2N/A "Please read the man page for extendedFILE.\nAborting\n");
2N/A abort();
2N/A }
2N/A return (altfd);
2N/A}
2N/A
2N/Aint
2N/A_file_set(FILE *iop, int fd, const char *type)
2N/A{
2N/A struct xFILEdata *dat;
2N/A int Fflag;
2N/A
2N/A /* Already known to contain at least one byte */
2N/A while (*++type != '\0')
2N/A ;
2N/A
2N/A Fflag = type[-1] == 'F';
2N/A if (!Fflag && bad_fd < 0) {
2N/A errno = EMFILE;
2N/A return (-1);
2N/A }
2N/A
2N/A dat = getxfdat(iop);
2N/A iop->__extendedfd = 1;
2N/A iop->__xf_nocheck = Fflag;
2N/A dat->_altfd = fd;
2N/A iop->_magic = (unsigned char)bad_fd;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Activates extended fd's in FILE's
2N/A */
2N/A
2N/Astatic const int tries[] = {196, 120, 60, 3};
2N/A#define NTRIES (sizeof (tries)/sizeof (int))
2N/A
2N/Aint
2N/Aenable_extended_FILE_stdio(int fd, int action)
2N/A{
2N/A int i;
2N/A
2N/A if (action < 0)
2N/A action = SIGABRT; /* default signal */
2N/A
2N/A if (fd < 0) {
2N/A /*
2N/A * search for an available fd and make it the badfd
2N/A */
2N/A for (i = 0; i < NTRIES; i++) {
2N/A fd = fcntl(tries[i], F_BADFD, action);
2N/A if (fd >= 0)
2N/A break;
2N/A }
2N/A if (fd < 0) /* failed to find an available fd */
2N/A return (-1);
2N/A } else {
2N/A /* caller requests that fd be the chosen badfd */
2N/A int nfd = fcntl(fd, F_BADFD, action);
2N/A if (nfd < 0 || nfd != fd)
2N/A return (-1);
2N/A }
2N/A bad_fd = fd;
2N/A return (0);
2N/A}
2N/A#endif