xref: /illumos-gate/usr/src/lib/libc/port/stdio/flush.c (revision 4abb96737d15cd2d6530b0aa7b8404ec911ad940)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 #include "synonyms.h"
34 #include "mtlib.h"
35 #include "file64.h"
36 #include "../gen/_libc_gettext.h"
37 
38 #define	_iob	__iob
39 
40 #include <sys/types.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <thread.h>
44 #include <synch.h>
45 #include <unistd.h>
46 #include <string.h>
47 #include "stdiom.h"
48 #include <wchar.h>
49 #include <sys/stat.h>
50 #include <stddef.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 
54 #undef end
55 
56 #define	FILE_ARY_SZ	8 /* a nice size for FILE array & end_buffer_ptrs */
57 
58 #ifdef	_LP64
59 
60 /*
61  * Macros to declare and loop over a fp or fp/xfp combo to
62  * avoid some of the _LP64 ifdef hell.
63  */
64 
65 #define	FPDECL(fp)		FILE *fp
66 #define	FIRSTFP(lp, fp)		fp = lp->iobp
67 #define	NEXTFP(fp)		fp++
68 #define	FPLOCK(fp)		&fp->_lock
69 #define	FPSTATE(fp)		&fp->_state
70 
71 #define	xFILE			FILE
72 
73 #else
74 
75 #define	FPDECL(fp)		FILE *fp; xFILE *x##fp
76 #define	FIRSTFP(lp, fp)		x##fp = lp->iobp; \
77 				fp = x##fp ? &x##fp->_iob : &_iob[0]
78 #define	NEXTFP(fp)		(x##fp ? fp = &(++x##fp)->_iob : ++fp)
79 #define	FPLOCK(fp)		x##fp ? \
80 				    &x##fp->xlock : &_xftab[IOPIND(fp)]._lock
81 #define	FPSTATE(fp)		x##fp ? \
82 				    &x##fp->xstate : &_xftab[IOPIND(fp)]._state
83 
84 /* The extended 32-bit file structure for use in link buffers */
85 typedef struct xFILE {
86 	FILE			_iob;		/* must be first! */
87 	struct xFILEdata	_xdat;
88 } xFILE;
89 
90 #define	xmagic			_xdat._magic
91 #define	xend			_xdat._end
92 #define	xlock			_xdat._lock
93 #define	xstate			_xdat._state
94 
95 #define	FILEx(fp)		((struct xFILE *)(uintptr_t)fp)
96 
97 /*
98  * The magic number stored is actually the pointer scrambled with
99  * a magic number.  Pointers to data items live everywhere in memory
100  * so we scramble the pointer in order to avoid accidental collisions.
101  */
102 #define	XFILEMAGIC		0x63687367
103 #define	XMAGIC(xfp)		((uintptr_t)(xfp) ^ XFILEMAGIC)
104 
105 #endif /* _LP64 */
106 
107 struct _link_	/* manages a list of streams */
108 {
109 	xFILE *iobp;		/* the array of (x)FILE's */
110 				/* NULL for the __first_link in ILP32 */
111 	int	niob;		/* length of the arrays */
112 	struct _link_	*next;	/* next in the list */
113 };
114 
115 /*
116  * With dynamic linking, iob may be in either the library or in the user's
117  * a.out, so the run time linker fixes up the first entry in __first_link at
118  * process startup time.
119  *
120  * In 32 bit processes, we don't have xFILE[FILE_ARY_SZ] but FILE[],
121  * and _xftab[] instead; this is denoted by having iobp set to NULL in
122  * 32 bit mode for the first link entry.
123  */
124 struct _link_ __first_link =	/* first in linked list */
125 {
126 #if !defined(_LP64)
127 	NULL,
128 #else
129 	&_iob[0],
130 #endif
131 	_NFILE,
132 	NULL
133 };
134 
135 /*
136  * Information cached to speed up searches.  We remember where we
137  * last found a free FILE* and we remember whether we saw any fcloses
138  * in between.  We also count the number of chunks we allocated, see
139  * _findiop() for an explanation.
140  * These variables are all protected by _first_link_lock.
141  */
142 static struct _link_ *lastlink = NULL;
143 static int fcloses;
144 static int nchunks;
145 
146 static mutex_t _first_link_lock = DEFAULTMUTEX;
147 
148 static int _fflush_l_iops(void);
149 static FILE *getiop(FILE *, rmutex_t *, mbstate_t *);
150 
151 /*
152  * All functions that understand the linked list of iob's follow.
153  */
154 #pragma weak _cleanup = __cleanup
155 void
156 __cleanup(void)		/* called at process end to flush ouput streams */
157 {
158 	(void) fflush(NULL);
159 }
160 
161 /*
162  * For fork1-safety (see libc_prepare_atfork(), etc).
163  */
164 void
165 stdio_locks()
166 {
167 	(void) _private_mutex_lock(&_first_link_lock);
168 	/*
169 	 * XXX: We should acquire all of the iob locks here.
170 	 */
171 }
172 
173 void
174 stdio_unlocks()
175 {
176 	/*
177 	 * XXX: We should release all of the iob locks here.
178 	 */
179 	(void) _private_mutex_unlock(&_first_link_lock);
180 }
181 
182 void
183 _flushlbf(void)		/* fflush() all line-buffered streams */
184 {
185 	FPDECL(fp);
186 	int i;
187 	struct _link_ *lp;
188 	/* Allow compiler to optimize the loop */
189 	int threaded = __libc_threaded;
190 
191 	if (threaded)
192 		(void) _private_mutex_lock(&_first_link_lock);
193 
194 	lp = &__first_link;
195 	do {
196 		FIRSTFP(lp, fp);
197 		for (i = lp->niob; --i >= 0; NEXTFP(fp)) {
198 			/*
199 			 * The additional _IONBF check guards againsts
200 			 * allocated but uninitialized iops (see _findiop).
201 			 * We also automatically skip non allocated iop's.
202 			 * Don't block on locks.
203 			 */
204 			if ((fp->_flag & (_IOLBF | _IOWRT | _IONBF)) ==
205 			    (_IOLBF | _IOWRT)) {
206 				if (threaded) {
207 					rmutex_t *lk = FPLOCK(fp);
208 					if (rmutex_trylock(lk) != 0)
209 						continue;
210 					/* Recheck after locking */
211 					if ((fp->_flag & (_IOLBF | _IOWRT)) ==
212 					    (_IOLBF | _IOWRT)) {
213 						(void) _fflush_u(fp);
214 					}
215 					(void) rmutex_unlock(lk);
216 				} else {
217 					(void) _fflush_u(fp);
218 				}
219 			}
220 		}
221 	} while ((lp = lp->next) != NULL);
222 
223 	if (threaded)
224 		(void) _private_mutex_unlock(&_first_link_lock);
225 }
226 
227 /* allocate an unused stream; NULL if cannot */
228 FILE *
229 _findiop(void)
230 {
231 	struct _link_ *lp, **prev;
232 
233 	/* used so there only needs to be one malloc() */
234 #ifdef _LP64
235 	typedef	struct	{
236 		struct _link_	hdr;
237 		FILE	iob[FILE_ARY_SZ];
238 	} Pkg;
239 #else
240 	typedef union {
241 		struct {				/* Normal */
242 			struct _link_	hdr;
243 			xFILE	iob[FILE_ARY_SZ];
244 		} Pkgn;
245 		struct {				/* Reversed */
246 			xFILE	iob[FILE_ARY_SZ];
247 			struct _link_	hdr;
248 		} Pkgr;
249 	} Pkg;
250 	uintptr_t delta;
251 #endif
252 	Pkg *pkgp;
253 	struct _link_ *hdr;
254 	FPDECL(fp);
255 	int i;
256 	int threaded = __libc_threaded;
257 
258 	if (threaded)
259 		(void) _private_mutex_lock(&_first_link_lock);
260 
261 	if (lastlink == NULL) {
262 rescan:
263 		fcloses = 0;
264 		lastlink = &__first_link;
265 	}
266 
267 	lp = lastlink;
268 
269 	/*
270 	 * lock to make testing of fp->_flag == 0 and acquiring the fp atomic
271 	 * and for allocation of new links
272 	 * low contention expected on _findiop(), hence coarse locking.
273 	 * for finer granularity, use fp->_lock for allocating an iop
274 	 * and make the testing of lp->next and allocation of new link atomic
275 	 * using lp->_lock
276 	 */
277 
278 	do {
279 		prev = &lp->next;
280 		FIRSTFP(lp, fp);
281 
282 		for (i = lp->niob; --i >= 0; NEXTFP(fp)) {
283 			FILE *ret;
284 			if (threaded) {
285 				ret = getiop(fp, FPLOCK(fp), FPSTATE(fp));
286 				if (ret != NULL) {
287 					(void) _private_mutex_unlock(
288 					    &_first_link_lock);
289 					return (ret);
290 				}
291 			} else {
292 				ret = getiop(fp, NULL, FPSTATE(fp));
293 				if (ret != NULL)
294 					return (ret);
295 			}
296 		}
297 	} while ((lastlink = lp = lp->next) != NULL);
298 
299 	/*
300 	 * If there was a sufficient number of  fcloses since we last started
301 	 * at __first_link, we rescan all fp's again.  We do not rescan for
302 	 * all fcloses; that would simplify the algorithm but would make
303 	 * search times near O(n) again.
304 	 * Worst case behaviour would still be pretty bad (open a full set,
305 	 * then continously opening and closing one FILE * gets you a full
306 	 * scan each time).  That's why we over allocate 1 FILE for each
307 	 * 32 chunks.  More over allocation is better; this is a nice
308 	 * empirical value which doesn't cost a lot of memory, doesn't
309 	 * overallocate until we reach 256 FILE *s and keeps the performance
310 	 * pretty close to the optimum.
311 	 */
312 	if (fcloses > nchunks/32)
313 		goto rescan;
314 
315 	/*
316 	 * Need to allocate another and put it in the linked list.
317 	 */
318 	if ((pkgp = malloc(sizeof (Pkg))) == NULL) {
319 		if (threaded)
320 			(void) _private_mutex_unlock(&_first_link_lock);
321 		return (NULL);
322 	}
323 
324 	(void) memset(pkgp, 0, sizeof (Pkg));
325 
326 #ifdef _LP64
327 	hdr = &pkgp->hdr;
328 	hdr->iobp = &pkgp->iob[0];
329 #else
330 	/*
331 	 * The problem with referencing a word after a FILE* is the possibility
332 	 * of a SIGSEGV if a non-stdio issue FILE structure ends on a page
333 	 * boundary.  We run this check so we never need to run an expensive
334 	 * check like mincore() in order to know whether it is
335 	 * safe to dereference ((xFILE*)fp)->xmagic.
336 	 * We allocate the block with two alternative layouts; if one
337 	 * layout is not properly aligned for our purposes, the other layout
338 	 * will be because the size of _link_ is small compared to
339 	 * sizeof (xFILE).
340 	 * The check performed is this:
341 	 *	If the distance from pkgp to the end of the page is
342 	 *	less than the the offset of the last xmagic field in the
343 	 *	xFILE structure, (the 0x1000 boundary is inside our just
344 	 *	allocated structure) and the distance modulo the size of xFILE
345 	 *	is identical to the offset of the first xmagic in the
346 	 *	structure (i.e., XXXXXX000 points to an xmagic field),
347 	 *	we need to use the reverse structure.
348 	 */
349 	if ((delta = 0x1000 - ((uintptr_t)pkgp & 0xfff)) <=
350 				offsetof(Pkg, Pkgn.iob[FILE_ARY_SZ-1].xmagic) &&
351 	    delta % sizeof (struct xFILE) ==
352 		    offsetof(Pkg, Pkgn.iob[0].xmagic)) {
353 		/* Use reversed structure */
354 		hdr = &pkgp->Pkgr.hdr;
355 		hdr->iobp = &pkgp->Pkgr.iob[0];
356 	} else {
357 		/* Use normal structure */
358 		hdr = &pkgp->Pkgn.hdr;
359 		hdr->iobp = &pkgp->Pkgn.iob[0];
360 	}
361 #endif /* _LP64 */
362 
363 	hdr->niob = FILE_ARY_SZ;
364 	nchunks++;
365 
366 #ifdef	_LP64
367 	fp = hdr->iobp;
368 	for (i = 0; i < FILE_ARY_SZ; i++)
369 		_private_mutex_init(&fp[i]._lock,
370 			USYNC_THREAD|LOCK_RECURSIVE, NULL);
371 #else
372 	xfp = hdr->iobp;
373 	fp = &xfp->_iob;
374 
375 	for (i = 0; i < FILE_ARY_SZ; i++) {
376 		xfp[i].xmagic = XMAGIC(&xfp[i]);
377 		_private_mutex_init(&xfp[i].xlock,
378 			USYNC_THREAD|LOCK_RECURSIVE, NULL);
379 	}
380 #endif	/*	_LP64	*/
381 
382 	lastlink = *prev = hdr;
383 	fp->_ptr = 0;
384 	fp->_base = 0;
385 	fp->_flag = 0377; /* claim the fp by setting low 8 bits */
386 	if (threaded)
387 		(void) _private_mutex_unlock(&_first_link_lock);
388 
389 	return (fp);
390 }
391 
392 static void
393 isseekable(FILE *iop)
394 {
395 	struct stat64 fstatbuf;
396 	int save_errno;
397 
398 	save_errno = errno;
399 
400 	if (fstat64(GET_FD(iop), &fstatbuf) != 0) {
401 		/*
402 		 * when we don't know what it is we'll
403 		 * do the old behaviour and flush
404 		 * the stream
405 		 */
406 		SET_SEEKABLE(iop);
407 		errno = save_errno;
408 		return;
409 	}
410 
411 	/*
412 	 * check for what is non-SEEKABLE
413 	 * otherwise assume it's SEEKABLE so we get the old
414 	 * behaviour and flush the stream
415 	 */
416 
417 	if (S_ISFIFO(fstatbuf.st_mode) || S_ISCHR(fstatbuf.st_mode) ||
418 	    S_ISSOCK(fstatbuf.st_mode) || S_ISDOOR(fstatbuf.st_mode)) {
419 		CLEAR_SEEKABLE(iop);
420 	} else {
421 		SET_SEEKABLE(iop);
422 	}
423 
424 	errno = save_errno;
425 }
426 
427 #ifdef	_LP64
428 void
429 _setbufend(FILE *iop, Uchar *end)	/* set the end pointer for this iop */
430 {
431 	iop->_end = end;
432 
433 	isseekable(iop);
434 }
435 
436 #undef _realbufend
437 
438 Uchar *
439 _realbufend(FILE *iop)		/* get the end pointer for this iop */
440 {
441 	return (iop->_end);
442 }
443 
444 #else /* _LP64 */
445 
446 /*
447  * Awkward functions not needed for the sane 64 bit environment.
448  */
449 /*
450  * xmagic must not be aligned on a 4K boundary. We guarantee this in
451  * _findiop().
452  */
453 #define	VALIDXFILE(xfp) \
454 	(((uintptr_t)&(xfp)->xmagic & 0xfff) && \
455 	    (xfp)->xmagic == XMAGIC(FILEx(xfp)))
456 
457 static struct xFILEdata *
458 getxfdat(FILE *iop)
459 {
460 	if (STDIOP(iop))
461 		return (&_xftab[IOPIND(iop)]);
462 	else if (VALIDXFILE(FILEx(iop)))
463 		return (&FILEx(iop)->_xdat);
464 	else
465 		return (NULL);
466 }
467 
468 void
469 _setbufend(FILE *iop, Uchar *end)	/* set the end pointer for this iop */
470 {
471 	struct xFILEdata *dat = getxfdat(iop);
472 
473 	if (dat != NULL)
474 		dat->_end = end;
475 
476 	isseekable(iop);
477 
478 	/*
479 	 * For binary compatibility with user programs using the
480 	 * old _bufend macro.  This is *so* broken, fileno()
481 	 * is not the proper index.
482 	 */
483 	if (iop->_magic < _NFILE)
484 		_bufendtab[iop->_magic] = end;
485 
486 }
487 
488 Uchar *
489 _realbufend(FILE *iop)		/* get the end pointer for this iop */
490 {
491 	struct xFILEdata *dat = getxfdat(iop);
492 
493 	if (dat != NULL)
494 		return (dat->_end);
495 
496 	return (NULL);
497 }
498 
499 /*
500  * _reallock() is invoked in each stdio call through the IOB_LCK() macro,
501  * it is therefor extremely performance sensitive.  We get better performance
502  * by inlining the STDIOP check in IOB_LCK and inlining a custom version
503  * of getfxdat() here.
504  */
505 rmutex_t *
506 _reallock(FILE *iop)
507 {
508 	if (VALIDXFILE(FILEx(iop)))
509 		return (&FILEx(iop)->xlock);
510 
511 	return (NULL);
512 }
513 
514 #endif	/*	_LP64	*/
515 
516 /* make sure _cnt, _ptr are correct */
517 void
518 _bufsync(FILE *iop, Uchar *bufend)
519 {
520 	ssize_t spaceleft;
521 
522 	spaceleft = bufend - iop->_ptr;
523 	if (bufend < iop->_ptr) {
524 		iop->_ptr = bufend;
525 		iop->_cnt = 0;
526 	} else if (spaceleft < iop->_cnt)
527 		iop->_cnt = spaceleft;
528 }
529 
530 /* really write out current buffer contents */
531 int
532 _xflsbuf(FILE *iop)
533 {
534 	ssize_t n;
535 	Uchar *base = iop->_base;
536 	Uchar *bufend;
537 	ssize_t num_wrote;
538 
539 	/*
540 	 * Hopefully, be stable with respect to interrupts...
541 	 */
542 	n = iop->_ptr - base;
543 	iop->_ptr = base;
544 	bufend = _bufend(iop);
545 	if (iop->_flag & (_IOLBF | _IONBF))
546 		iop->_cnt = 0;		/* always go to a flush */
547 	else
548 		iop->_cnt = bufend - base;
549 
550 	if (_needsync(iop, bufend))	/* recover from interrupts */
551 		_bufsync(iop, bufend);
552 
553 	if (n > 0) {
554 		int fd = GET_FD(iop);
555 		while ((num_wrote =
556 			write(fd, base, (size_t)n)) != n) {
557 			if (num_wrote <= 0) {
558 				iop->_flag |= _IOERR;
559 				return (EOF);
560 			}
561 			n -= num_wrote;
562 			base += num_wrote;
563 		}
564 	}
565 	return (0);
566 }
567 
568 /* flush (write) buffer */
569 int
570 fflush(FILE *iop)
571 {
572 	int res;
573 	rmutex_t *lk;
574 
575 	if (iop) {
576 		FLOCKFILE(lk, iop);
577 		res = _fflush_u(iop);
578 		FUNLOCKFILE(lk);
579 	} else {
580 		res = _fflush_l_iops();		/* flush all iops */
581 	}
582 	return (res);
583 }
584 
585 static int
586 _fflush_l_iops(void)		/* flush all buffers */
587 {
588 	FPDECL(iop);
589 
590 	int i;
591 	struct _link_ *lp;
592 	int res = 0;
593 	rmutex_t *lk;
594 	/* Allow the compiler to optimize the load out of the loop */
595 	int threaded = __libc_threaded;
596 
597 	if (threaded)
598 		(void) _private_mutex_lock(&_first_link_lock);
599 
600 	lp = &__first_link;
601 
602 	do {
603 		/*
604 		 * We need to grab the file locks or file corruption
605 		 * will happen.  But we first check the flags field
606 		 * knowing that when it is 0, it isn't allocated and
607 		 * cannot be allocated while we're holding the
608 		 * _first_link_lock.  And when _IONBF is set (also the
609 		 * case when _flag is 0377, or alloc in progress), we
610 		 * also ignore it.
611 		 *
612 		 * Ignore locked streams; it will appear as if
613 		 * concurrent updates happened after fflush(NULL).  Note
614 		 * that we even attempt to lock if the locking is set to
615 		 * "by caller".  We don't want to penalize callers of
616 		 * __fsetlocking() by not flushing their files.  Note: if
617 		 * __fsetlocking() callers don't employ any locking, they
618 		 * may still face corruption in fflush(NULL); but that's
619 		 * no change from earlier releases.
620 		 */
621 		FIRSTFP(lp, iop);
622 		for (i = lp->niob; --i >= 0; NEXTFP(iop)) {
623 			unsigned int flag = iop->_flag;
624 
625 			/* flag 0, flag 0377, or _IONBF set */
626 			if (flag == 0 || (flag & _IONBF) != 0)
627 				continue;
628 
629 			if (threaded) {
630 				lk = FPLOCK(iop);
631 				if (rmutex_trylock(lk) != 0)
632 					continue;
633 			}
634 
635 			if (!(iop->_flag & _IONBF)) {
636 				/*
637 				 * don't need to worry about the _IORW case
638 				 * since the iop will also marked with _IOREAD
639 				 * or _IOWRT whichever we are really doing
640 				 */
641 				if (iop->_flag & _IOWRT) {
642 					/* Flush write buffers */
643 					res |= _fflush_u(iop);
644 				} else if (iop->_flag & _IOREAD) {
645 					/*
646 					 * flush seekable read buffers
647 					 * don't flush non-seekable read buffers
648 					 */
649 					if (GET_SEEKABLE(iop)) {
650 						res |= _fflush_u(iop);
651 					}
652 				}
653 			}
654 			if (threaded)
655 				(void) rmutex_unlock(lk);
656 		}
657 	} while ((lp = lp->next) != NULL);
658 	if (threaded)
659 		(void) _private_mutex_unlock(&_first_link_lock);
660 	return (res);
661 }
662 
663 /* flush buffer */
664 int
665 _fflush_u(FILE *iop)
666 {
667 	int res = 0;
668 
669 	/* this portion is always assumed locked */
670 	if (!(iop->_flag & _IOWRT)) {
671 		(void) lseek64(GET_FD(iop), -iop->_cnt, SEEK_CUR);
672 		iop->_cnt = 0;
673 		/* needed for ungetc & multibyte pushbacks */
674 		iop->_ptr = iop->_base;
675 		if (iop->_flag & _IORW) {
676 			iop->_flag &= ~_IOREAD;
677 		}
678 		return (0);
679 	}
680 	if (iop->_base != NULL && iop->_ptr > iop->_base) {
681 		res = _xflsbuf(iop);
682 	}
683 	if (iop->_flag & _IORW) {
684 		iop->_flag &= ~_IOWRT;
685 		iop->_cnt = 0;
686 	}
687 	return (res);
688 }
689 
690 /* flush buffer and close stream */
691 int
692 fclose(FILE *iop)
693 {
694 	int res = 0;
695 	rmutex_t *lk;
696 
697 	if (iop == NULL) {
698 		return (EOF);		/* avoid passing zero to FLOCKFILE */
699 	}
700 
701 	FLOCKFILE(lk, iop);
702 	if (iop->_flag == 0) {
703 		FUNLOCKFILE(lk);
704 		return (EOF);
705 	}
706 	/* Is not unbuffered and opened for read and/or write ? */
707 	if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
708 		res = _fflush_u(iop);
709 	if (close(GET_FD(iop)) < 0)
710 		res = EOF;
711 	if (iop->_flag & _IOMYBUF) {
712 		(void) free((char *)iop->_base - PUSHBACK);
713 	}
714 	iop->_base = NULL;
715 	iop->_ptr = NULL;
716 	iop->_cnt = 0;
717 	iop->_flag = 0;			/* marks it as available */
718 	FUNLOCKFILE(lk);
719 
720 	if (__libc_threaded)
721 		(void) _private_mutex_lock(&_first_link_lock);
722 	fcloses++;
723 	if (__libc_threaded)
724 		(void) _private_mutex_unlock(&_first_link_lock);
725 
726 	return (res);
727 }
728 
729 /* flush buffer, close fd but keep the stream used by freopen() */
730 int
731 close_fd(FILE *iop)
732 {
733 	int res = 0;
734 	mbstate_t *mb;
735 
736 	if (iop == NULL || iop->_flag == 0)
737 		return (EOF);
738 	/* Is not unbuffered and opened for read and/or write ? */
739 	if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
740 		res = _fflush_u(iop);
741 	if (close(GET_FD(iop)) < 0)
742 		res = EOF;
743 	if (iop->_flag & _IOMYBUF) {
744 		(void) free((char *)iop->_base - PUSHBACK);
745 	}
746 	iop->_base = NULL;
747 	iop->_ptr = NULL;
748 	mb = _getmbstate(iop);
749 	if (mb != NULL)
750 		(void) memset(mb, 0, sizeof (mbstate_t));
751 	iop->_cnt = 0;
752 	_setorientation(iop, _NO_MODE);
753 	return (res);
754 }
755 
756 static FILE *
757 getiop(FILE *fp, rmutex_t *lk, mbstate_t *mb)
758 {
759 	if (lk != NULL && rmutex_trylock(lk))
760 		return (NULL);	/* locked: fp in use */
761 
762 	if (fp->_flag == 0) {	/* unused */
763 #ifndef	_LP64
764 		fp->__orientation = 0;
765 #endif /* _LP64 */
766 		fp->_cnt = 0;
767 		fp->_ptr = NULL;
768 		fp->_base = NULL;
769 		fp->_flag = 0377;	/* claim the fp by setting low 8 bits */
770 		(void) memset(mb, 0, sizeof (mbstate_t));
771 		FUNLOCKFILE(lk);
772 		return (fp);
773 	}
774 	FUNLOCKFILE(lk);
775 	return (NULL);
776 }
777 
778 #ifndef	_LP64
779 /*
780  * DESCRIPTION:
781  * This function gets the pointer to the mbstate_t structure associated
782  * with the specified iop.
783  *
784  * RETURNS:
785  * If the associated mbstate_t found, the pointer to the mbstate_t is
786  * returned.  Otherwise, NULL is returned.
787  */
788 mbstate_t *
789 _getmbstate(FILE *iop)
790 {
791 	struct xFILEdata *dat = getxfdat(iop);
792 
793 	if (dat != NULL)
794 		return (&dat->_state);
795 
796 	return (NULL);
797 }
798 
799 /*
800  * More 32-bit only functions.
801  * They lookup/set large fd's for extended FILE support.
802  */
803 
804 /*
805  * The negative value indicates that Extended fd FILE's has not
806  * been enabled by the user.
807  */
808 static int bad_fd = -1;
809 
810 int
811 _file_get(FILE *iop)
812 {
813 	int altfd;
814 
815 	/*
816 	 * Failure indicates a FILE * not allocated through stdio;
817 	 * it means the flag values are probably bogus and that if
818 	 * a file descriptor is set, it's in _magic.
819 	 * Inline getxfdat() for performance reasons.
820 	 */
821 	if (STDIOP(iop))
822 		altfd = _xftab[IOPIND(iop)]._altfd;
823 	else if (VALIDXFILE(FILEx(iop)))
824 		altfd = FILEx(iop)->_xdat._altfd;
825 	else
826 		return (iop->_magic);
827 	/*
828 	 * if this is not an internal extended FILE then check
829 	 * if _file is being changed from underneath us.
830 	 * It should not be because if
831 	 * it is then then we lose our ability to guard against
832 	 * silent data corruption.
833 	 */
834 	if (!iop->__xf_nocheck && bad_fd > -1 && iop->_magic != bad_fd) {
835 		/* LINTED: variable format specifier */
836 		(void) fprintf(stderr, _libc_gettext(
837 		    "Application violated extended FILE safety mechanism.\n"
838 		    "Please read the man page for extendedFILE.\nAborting\n"));
839 		abort();
840 	}
841 	return (altfd);
842 }
843 
844 int
845 _file_set(FILE *iop, int fd, const char *type)
846 {
847 	struct xFILEdata *dat;
848 	int Fflag;
849 
850 	/* Already known to contain at least one byte */
851 	while (*++type != '\0')
852 		;
853 
854 	Fflag = type[-1] == 'F';
855 	if (!Fflag && bad_fd < 0) {
856 		errno = EMFILE;
857 		return (-1);
858 	}
859 
860 	dat = getxfdat(iop);
861 	iop->__extendedfd = 1;
862 	iop->__xf_nocheck = Fflag;
863 	dat->_altfd = fd;
864 	iop->_magic = (unsigned char)bad_fd;
865 	return (0);
866 }
867 
868 /*
869  * Activates extended fd's in FILE's
870  */
871 
872 static const int tries[] = {196, 120, 60, 3};
873 #define	NTRIES	(sizeof (tries)/sizeof (int))
874 
875 int
876 enable_extended_FILE_stdio(int fd, int action)
877 {
878 	int i;
879 
880 	if (action < 0)
881 		action = SIGABRT;	/* default signal */
882 
883 	if (fd < 0) {
884 		/*
885 		 * search for an available fd and make it the badfd
886 		 */
887 		for (i = 0; i < NTRIES; i++) {
888 			fd = fcntl(tries[i], F_BADFD, action);
889 			if (fd >= 0)
890 				break;
891 		}
892 		if (fd < 0)	/* failed to find an available fd */
893 			return (-1);
894 	} else {
895 		/* caller requests that fd be the chosen badfd */
896 		int nfd = fcntl(fd, F_BADFD, action);
897 		if (nfd < 0 || nfd != fd)
898 			return (-1);
899 	}
900 	bad_fd = fd;
901 	return (0);
902 }
903 #endif
904