1 /* Copyright (C) 1993-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.
17 
18    As a special exception, if you link the code in this file with
19    files compiled with a GNU compiler to produce an executable,
20    that does not cause the resulting executable to be covered by
21    the GNU Lesser General Public License.  This exception does not
22    however invalidate any other reasons why the executable file
23    might be covered by the GNU Lesser General Public License.
24    This exception applies to code released by its copyright holders
25    in files containing the exception.  */
26 
27 #include <stdlib.h>
28 #include "libioP.h"
29 #include <fcntl.h>
30 
31 #include <shlib-compat.h>
32 
33 FILE *
_IO_new_fdopen(int fd,const char * mode)34 _IO_new_fdopen (int fd, const char *mode)
35 {
36   int read_write;
37   struct locked_FILE
38   {
39     struct _IO_FILE_plus fp;
40 #ifdef _IO_MTSAFE_IO
41     _IO_lock_t lock;
42 #endif
43     struct _IO_wide_data wd;
44   } *new_f;
45   int i;
46   int use_mmap = 0;
47 
48   /* Decide whether we modify the offset of the file we attach to and seek to
49      the end of file.  We only do this if the mode is 'a' and if the file
50      descriptor did not have O_APPEND in its flags already.  */
51   bool do_seek = false;
52 
53   switch (*mode)
54     {
55     case 'r':
56       read_write = _IO_NO_WRITES;
57       break;
58     case 'w':
59       read_write = _IO_NO_READS;
60       break;
61     case 'a':
62       read_write = _IO_NO_READS|_IO_IS_APPENDING;
63       break;
64     default:
65       __set_errno (EINVAL);
66       return NULL;
67   }
68   for (i = 1; i < 5; ++i)
69     {
70       switch (*++mode)
71 	{
72 	case '\0':
73 	  break;
74 	case '+':
75 	  read_write &= _IO_IS_APPENDING;
76 	  break;
77 	case 'm':
78 	  use_mmap = 1;
79 	  continue;
80 	case 'x':
81 	case 'b':
82 	default:
83 	  /* Ignore */
84 	  continue;
85 	}
86       break;
87     }
88   int fd_flags = __fcntl (fd, F_GETFL);
89   if (fd_flags == -1)
90     return NULL;
91 
92   if (((fd_flags & O_ACCMODE) == O_RDONLY && !(read_write & _IO_NO_WRITES))
93       || ((fd_flags & O_ACCMODE) == O_WRONLY && !(read_write & _IO_NO_READS)))
94     {
95       __set_errno (EINVAL);
96       return NULL;
97     }
98 
99   /* The May 93 draft of P1003.4/D14.1 (redesignated as 1003.1b)
100      [System Application Program Interface (API) Amendment 1:
101      Realtime Extensions], Rationale B.8.3.3
102      Open a Stream on a File Descriptor says:
103 
104 	 Although not explicitly required by POSIX.1, a good
105 	 implementation of append ("a") mode would cause the
106 	 O_APPEND flag to be set.
107 
108      (Historical implementations [such as Solaris2] do a one-time
109      seek in fdopen.)
110 
111      However, we do not turn O_APPEND off if the mode is "w" (even
112      though that would seem consistent) because that would be more
113      likely to break historical programs.
114      */
115   if ((read_write & _IO_IS_APPENDING) && !(fd_flags & O_APPEND))
116     {
117       do_seek = true;
118       if (__fcntl (fd, F_SETFL, fd_flags | O_APPEND) == -1)
119 	return NULL;
120     }
121 
122   new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));
123   if (new_f == NULL)
124     return NULL;
125 #ifdef _IO_MTSAFE_IO
126   new_f->fp.file._lock = &new_f->lock;
127 #endif
128   _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd,
129 #if _G_HAVE_MMAP
130 	       (use_mmap && (read_write & _IO_NO_WRITES))
131 	       ? &_IO_wfile_jumps_maybe_mmap :
132 #endif
133 	       &_IO_wfile_jumps);
134   _IO_JUMPS (&new_f->fp) =
135 #if _G_HAVE_MMAP
136     (use_mmap && (read_write & _IO_NO_WRITES)) ? &_IO_file_jumps_maybe_mmap :
137 #endif
138       &_IO_file_jumps;
139   _IO_new_file_init_internal (&new_f->fp);
140   /* We only need to record the fd because _IO_file_init_internal will
141      have unset the offset.  It is important to unset the cached
142      offset because the real offset in the file could change between
143      now and when the handle is activated and we would then mislead
144      ftell into believing that we have a valid offset.  */
145   new_f->fp.file._fileno = fd;
146   new_f->fp.file._flags &= ~_IO_DELETE_DONT_CLOSE;
147 
148   _IO_mask_flags (&new_f->fp.file, read_write,
149 		  _IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
150 
151   /* For append mode, set the file offset to the end of the file if we added
152      O_APPEND to the file descriptor flags.  Don't update the offset cache
153      though, since the file handle is not active.  */
154   if (do_seek && ((read_write & (_IO_IS_APPENDING | _IO_NO_READS))
155 		  == (_IO_IS_APPENDING | _IO_NO_READS)))
156     {
157       off64_t new_pos = _IO_SYSSEEK (&new_f->fp.file, 0, _IO_seek_end);
158       if (new_pos == _IO_pos_BAD && errno != ESPIPE)
159 	return NULL;
160     }
161   return &new_f->fp.file;
162 }
163 libc_hidden_ver (_IO_new_fdopen, _IO_fdopen)
164 
165 strong_alias (_IO_new_fdopen, __new_fdopen)
166 versioned_symbol (libc, _IO_new_fdopen, _IO_fdopen, GLIBC_2_1);
167 versioned_symbol (libc, __new_fdopen, fdopen, GLIBC_2_1);
168