Home | History | Annotate | Download | only in stdio
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1988 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 /*
     31  * University Copyright- Copyright (c) 1982, 1986, 1988
     32  * The Regents of the University of California
     33  * All Rights Reserved
     34  *
     35  * University Acknowledgment- Portions of this document are derived from
     36  * software developed by the University of California, Berkeley, and its
     37  * contributors.
     38  */
     39 
     40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     41 
     42 #include "lint.h"
     43 #include "file64.h"
     44 #include <sys/types.h>
     45 #include <stdio.h>
     46 #include <mtlib.h>
     47 #include <fcntl.h>
     48 #include <unistd.h>
     49 #include <limits.h>
     50 #include <thread.h>
     51 #include <synch.h>
     52 #include <stdlib.h>
     53 #include <errno.h>
     54 #include "stdiom.h"
     55 #include "xpg6.h"
     56 
     57 /* Final argument to _endopen depends on build environment */
     58 #define	LARGE_OPEN		(_FILE_OFFSET_BITS == 64)
     59 
     60 FILE *
     61 fopen(const char *name, const char *type) /* open name, return new stream */
     62 {
     63 	FILE *iop;
     64 	FILE  *rc;
     65 
     66 	iop = _findiop();
     67 	/*
     68 	 * Note that iop is not locked here, since no other thread could
     69 	 * possibly call _endopen with the same iop at this point.
     70 	 */
     71 	rc = _endopen(name, type, iop, LARGE_OPEN);
     72 
     73 	if (rc == NULL && iop != NULL)
     74 		iop->_flag = 0; /* release iop */
     75 
     76 	return (rc);
     77 }
     78 
     79 static FILE *
     80 _freopen_null(const char *type, FILE *iop)
     81 {
     82 	char	plus, mode;
     83 	int	oflag, nflag, fd, accmode;
     84 	mbstate_t	*mb;
     85 
     86 	if (iop == NULL || iop->_flag == 0) {
     87 		errno = EBADF;
     88 		return (NULL);
     89 	}
     90 
     91 	if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
     92 		(void) _fflush_u(iop);
     93 
     94 	if (iop->_flag & _IOMYBUF) {
     95 		free((char *)iop->_base - PUSHBACK);
     96 	}
     97 	iop->_base = NULL;
     98 	iop->_ptr = NULL;
     99 	/*
    100 	 * Clear stream orientation, clear stream encoding rule, and set
    101 	 * stream's mbstate_t object to describe an initial conversion state.
    102 	 */
    103 	mb = _getmbstate(iop);
    104 	if (mb != NULL)
    105 		(void) memset(mb, 0, sizeof (mbstate_t));
    106 	iop->_cnt = 0;
    107 	_setorientation(iop, _NO_MODE);
    108 
    109 	fd = FILENO(iop);
    110 	mode = type[0];
    111 	if (mode != 'r' && mode != 'w' && mode != 'a') {
    112 		errno = EINVAL;
    113 		goto errret;
    114 	}
    115 
    116 	if ((oflag = fcntl(fd, F_GETFL)) == -1)
    117 		goto errret;
    118 
    119 	if ((plus = type[1]) == 'b')
    120 		plus = type[2];
    121 
    122 	/*
    123 	 * Because the filename has not been specified, the underlying file
    124 	 * will not be closed and reopened.  The access modes of an open
    125 	 * file descriptor can't be changed via fcntl().  When '+' is
    126 	 * specified, the old access mode needs to be O_RDWR.  When 'r' is
    127 	 * specified, the old access mode needs to be O_RDONLY or O_RDWR.
    128 	 * When 'a' or 'w' is specified, the old access mode needs to be
    129 	 * O_WRONLY or O_RDWR.  Otherwise, fail with EBADF, indicating that
    130 	 * the underlying file descriptor was not opened with a mode that
    131 	 * would allow the stream to do successful I/O with the requested mode.
    132 	 */
    133 
    134 	accmode = oflag & O_ACCMODE;
    135 	if ((accmode == O_RDONLY && (mode != 'r' || plus == '+')) ||
    136 	    (accmode == O_WRONLY && (mode == 'r' || plus == '+'))) {
    137 		(void) close(fd);
    138 		errno = EBADF;
    139 		goto errret_noclose;
    140 	}
    141 
    142 #ifdef	_LP64
    143 	iop->_flag &= ~0377;	/* clear lower 8-bits */
    144 	if (mode == 'r') {
    145 		iop->_flag |= _IOREAD;
    146 		nflag = oflag & ~O_APPEND;
    147 	} else if (mode == 'w') {
    148 		iop->_flag |= _IOWRT;
    149 		nflag = oflag & ~O_APPEND;
    150 	} else {
    151 		iop->_flag |= _IOWRT;
    152 		nflag = oflag | O_APPEND;
    153 	}
    154 	if (plus == '+') {
    155 		iop->_flag = (iop->_flag & ~(_IOREAD | _IOWRT)) | _IORW;
    156 	}
    157 #else
    158 	if (mode == 'r') {
    159 		iop->_flag = _IOREAD;
    160 		nflag = oflag & ~O_APPEND;
    161 	} else if (mode == 'w') {
    162 		iop->_flag = _IOWRT;
    163 		nflag = oflag & ~O_APPEND;
    164 	} else {
    165 		iop->_flag = _IOWRT;
    166 		nflag = oflag | O_APPEND;
    167 	}
    168 	if (plus == '+') {
    169 		iop->_flag = _IORW;
    170 	}
    171 #endif
    172 	/*
    173 	 * Change mode of underlying fd as much as possible without closing
    174 	 * and reopening it.  Ignore truncate failures, eg. with stdout.
    175 	 */
    176 	if (mode == 'w')
    177 		(void) ftruncate64(fd, (off64_t)0);
    178 
    179 	if (fcntl(fd, F_SETFL, nflag) == -1)
    180 		goto errret;
    181 
    182 	/* ignore seek failures, eg. with pipes */
    183 	(void) lseek64(fd, (off64_t)0, SEEK_SET);
    184 
    185 	return (iop);
    186 
    187 errret:
    188 	if (errno != EBADF)
    189 		(void) close(fd);
    190 
    191 errret_noclose:
    192 	iop->_flag = 0;		/* release iop */
    193 	return (NULL);
    194 }
    195 
    196 FILE *
    197 freopen(const char *name, const char *type, FILE *iop)
    198 {
    199 	FILE *rc;
    200 	rmutex_t *lk;
    201 
    202 	if (name == NULL && __xpg6 & _C99SUSv3_freopen_NULL_filename) {
    203 		/*
    204 		 * XPG6:  If name is a null pointer, freopen will attempt to
    205 		 * change the mode of the stream to that specified by type.
    206 		 */
    207 		FLOCKFILE(lk, iop);
    208 		rc = _freopen_null(type, iop);
    209 		FUNLOCKFILE(lk);
    210 		return (rc);
    211 	}
    212 	/*
    213 	 * there may be concurrent calls to reopen the same stream - need
    214 	 * to make freopen() atomic
    215 	 */
    216 	FLOCKFILE(lk, iop);
    217 	/*
    218 	 * new function to do everything that fclose() does, except
    219 	 * to release the iop - this cannot yet be released since
    220 	 * _endopen() is yet to be called on this iop
    221 	 */
    222 
    223 	(void) close_fd(iop);
    224 
    225 	rc = _endopen(name, type, iop, LARGE_OPEN);
    226 
    227 	if (rc == NULL)
    228 		iop->_flag = 0; /* release iop */
    229 
    230 	FUNLOCKFILE(lk);
    231 	return (rc);
    232 }
    233