/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1990, 19934* The Regents of the University of California. All rights reserved.5*6* This code is derived from software contributed to Berkeley by7* Chris Torek.8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12* 1. Redistributions of source code must retain the above copyright13* notice, this list of conditions and the following disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17* 3. Neither the name of the University nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334#include "namespace.h"35#include <sys/types.h>36#include <sys/stat.h>37#include <fcntl.h>38#include <errno.h>39#include <limits.h>40#include <unistd.h>41#include <stdio.h>42#include <stdlib.h>43#include "un-namespace.h"44#include "libc_private.h"45#include "local.h"4647/*48* Re-direct an existing, open (probably) file to some other file.49* ANSI is written such that the original file gets closed if at50* all possible, no matter what.51*/52FILE *53freopen(const char * __restrict file, const char * __restrict mode,54FILE * __restrict fp)55{56int f;57int dflags, fdflags, flags, isopen, oflags, sverrno, wantfd;5859if ((flags = __sflags(mode, &oflags)) == 0) {60sverrno = errno;61(void) fclose(fp);62errno = sverrno;63return (NULL);64}6566FLOCKFILE_CANCELSAFE(fp);6768if (!__sdidinit)69__sinit();7071/*72* If the filename is a NULL pointer, the caller is asking us to73* re-open the same file with a different mode. We allow this only74* if the modes are compatible.75*/76if (file == NULL) {77/* See comment below regarding freopen() of closed files. */78if (fp->_flags == 0) {79errno = EINVAL;80fp = NULL;81goto end;82}83if ((dflags = _fcntl(fp->_file, F_GETFL)) < 0) {84sverrno = errno;85fclose(fp);86errno = sverrno;87fp = NULL;88goto end;89}90/* Work around incorrect O_ACCMODE. */91if ((dflags & O_ACCMODE) != O_RDWR &&92(dflags & (O_ACCMODE | O_EXEC)) != (oflags & O_ACCMODE)) {93fclose(fp);94errno = EBADF;95fp = NULL;96goto end;97}98if (fp->_flags & __SWR)99(void) __sflush(fp);100if ((oflags ^ dflags) & O_APPEND) {101dflags &= ~O_APPEND;102dflags |= oflags & O_APPEND;103if (_fcntl(fp->_file, F_SETFL, dflags) < 0) {104sverrno = errno;105fclose(fp);106errno = sverrno;107fp = NULL;108goto end;109}110}111if (oflags & O_TRUNC)112(void) ftruncate(fp->_file, (off_t)0);113if (!(oflags & O_APPEND))114(void) _sseek(fp, (fpos_t)0, SEEK_SET);115if ((oflags & O_CLOEXEC) != 0) {116fdflags = _fcntl(fp->_file, F_GETFD, 0);117if (fdflags != -1 && (fdflags & FD_CLOEXEC) == 0)118(void) _fcntl(fp->_file, F_SETFD,119fdflags | FD_CLOEXEC);120}121f = fp->_file;122isopen = 0;123wantfd = -1;124goto finish;125}126127/*128* There are actually programs that depend on being able to "freopen"129* descriptors that weren't originally open. Keep this from breaking.130* Remember whether the stream was open to begin with, and which file131* descriptor (if any) was associated with it. If it was attached to132* a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin)133* should work. This is unnecessary if it was not a Unix file.134*/135if (fp->_flags == 0) {136fp->_flags = __SEOF; /* hold on to it */137isopen = 0;138wantfd = -1;139} else {140/* flush the stream; ANSI doesn't require this. */141if (fp->_flags & __SWR)142(void) __sflush(fp);143/* if close is NULL, closing is a no-op, hence pointless */144isopen = fp->_close != NULL;145if ((wantfd = fp->_file) < 0 && isopen) {146(void) (*fp->_close)(fp->_cookie);147isopen = 0;148}149}150151/* Get a new descriptor to refer to the new file. */152f = _open(file, oflags, DEFFILEMODE);153/* If out of fd's close the old one and try again. */154if (f < 0 && isopen && wantfd > STDERR_FILENO &&155(errno == ENFILE || errno == EMFILE)) {156(void) (*fp->_close)(fp->_cookie);157isopen = 0;158wantfd = -1;159f = _open(file, oflags, DEFFILEMODE);160}161sverrno = errno;162163finish:164/*165* Finish closing fp. Even if the open succeeded above, we cannot166* keep fp->_base: it may be the wrong size. This loses the effect167* of any setbuffer calls, but stdio has always done this before.168*169* Leave the existing file descriptor open until dup2() is called170* below to avoid races where a concurrent open() in another thread171* could claim the existing descriptor.172*/173if (fp->_flags & __SMBF)174free((char *)fp->_bf._base);175fp->_w = 0;176fp->_r = 0;177fp->_p = NULL;178fp->_bf._base = NULL;179fp->_bf._size = 0;180fp->_lbfsize = 0;181if (HASUB(fp))182FREEUB(fp);183fp->_ub._size = 0;184if (HASLB(fp))185FREELB(fp);186fp->_lb._size = 0;187fp->_orientation = 0;188memset(&fp->_mbstate, 0, sizeof(mbstate_t));189fp->_flags2 = 0;190191if (f < 0) { /* did not get it after all */192if (isopen)193(void) (*fp->_close)(fp->_cookie);194fp->_flags = 0; /* set it free */195errno = sverrno; /* restore in case _close clobbered */196fp = NULL;197goto end;198}199200/*201* If reopening something that was open before on a real file, try202* to maintain the descriptor. Various C library routines (perror)203* assume stderr is always fd STDERR_FILENO, even if being freopen'd.204*/205if (wantfd >= 0) {206if ((oflags & O_CLOEXEC ? _fcntl(f, F_DUP2FD_CLOEXEC, wantfd) :207_dup2(f, wantfd)) >= 0) {208(void)_close(f);209f = wantfd;210} else211(void)_close(fp->_file);212}213214/*215* File descriptors are a full int, but _file is only a short.216* If we get a valid file descriptor that is greater than217* SHRT_MAX, then the fd will get sign-extended into an218* invalid file descriptor. Handle this case by failing the219* open.220*/221if (f > SHRT_MAX) {222fp->_flags = 0; /* set it free */223errno = EMFILE;224fp = NULL;225goto end;226}227228fp->_flags = flags;229fp->_file = f;230fp->_cookie = fp;231fp->_read = __sread;232fp->_write = __swrite;233fp->_seek = __sseek;234fp->_close = __sclose;235/*236* When opening in append mode, even though we use O_APPEND,237* we need to seek to the end so that ftell() gets the right238* answer. If the user then alters the seek pointer, or239* the file extends, this will fail, but there is not much240* we can do about this. (We could set __SAPP and check in241* fseek and ftell.)242*/243if (oflags & O_APPEND) {244fp->_flags2 |= __S2OAP;245(void) _sseek(fp, (fpos_t)0, SEEK_END);246}247end:248FUNLOCKFILE_CANCELSAFE();249return (fp);250}251252253