New upstream version 1.9
[debian/gzip] / lib / fcntl.c
1 /* Provide file descriptor control.
2
3    Copyright (C) 2009-2018 Free Software Foundation, Inc.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
17
18 /* Written by Eric Blake <ebb9@byu.net>.  */
19
20 #include <config.h>
21
22 /* Specification.  */
23 #include <fcntl.h>
24
25 #include <errno.h>
26 #include <limits.h>
27 #include <stdarg.h>
28 #include <unistd.h>
29
30 #if !HAVE_FCNTL
31 # define rpl_fcntl fcntl
32 #endif
33 #undef fcntl
34
35 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
36 /* Get declarations of the native Windows API functions.  */
37 # define WIN32_LEAN_AND_MEAN
38 # include <windows.h>
39
40 /* Get _get_osfhandle.  */
41 # if GNULIB_MSVC_NOTHROW
42 #  include "msvc-nothrow.h"
43 # else
44 #  include <io.h>
45 # endif
46
47 /* Upper bound on getdtablesize().  See lib/getdtablesize.c.  */
48 # define OPEN_MAX_MAX 0x10000
49
50 /* Duplicate OLDFD into the first available slot of at least NEWFD,
51    which must be positive, with FLAGS determining whether the duplicate
52    will be inheritable.  */
53 static int
54 dupfd (int oldfd, int newfd, int flags)
55 {
56   /* Mingw has no way to create an arbitrary fd.  Iterate until all
57      file descriptors less than newfd are filled up.  */
58   HANDLE curr_process = GetCurrentProcess ();
59   HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
60   unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
61   unsigned int fds_to_close_bound = 0;
62   int result;
63   BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE;
64   int mode;
65
66   if (newfd < 0 || getdtablesize () <= newfd)
67     {
68       errno = EINVAL;
69       return -1;
70     }
71   if (old_handle == INVALID_HANDLE_VALUE
72       || (mode = setmode (oldfd, O_BINARY)) == -1)
73     {
74       /* oldfd is not open, or is an unassigned standard file
75          descriptor.  */
76       errno = EBADF;
77       return -1;
78     }
79   setmode (oldfd, mode);
80   flags |= mode;
81
82   for (;;)
83     {
84       HANDLE new_handle;
85       int duplicated_fd;
86       unsigned int index;
87
88       if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
89                             old_handle,             /* SourceHandle */
90                             curr_process,           /* TargetProcessHandle */
91                             (PHANDLE) &new_handle,  /* TargetHandle */
92                             (DWORD) 0,              /* DesiredAccess */
93                             inherit,                /* InheritHandle */
94                             DUPLICATE_SAME_ACCESS)) /* Options */
95         {
96           switch (GetLastError ())
97             {
98               case ERROR_TOO_MANY_OPEN_FILES:
99                 errno = EMFILE;
100                 break;
101               case ERROR_INVALID_HANDLE:
102               case ERROR_INVALID_TARGET_HANDLE:
103               case ERROR_DIRECT_ACCESS_HANDLE:
104                 errno = EBADF;
105                 break;
106               case ERROR_INVALID_PARAMETER:
107               case ERROR_INVALID_FUNCTION:
108               case ERROR_INVALID_ACCESS:
109                 errno = EINVAL;
110                 break;
111               default:
112                 errno = EACCES;
113                 break;
114             }
115           result = -1;
116           break;
117         }
118       duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags);
119       if (duplicated_fd < 0)
120         {
121           CloseHandle (new_handle);
122           result = -1;
123           break;
124         }
125       if (newfd <= duplicated_fd)
126         {
127           result = duplicated_fd;
128           break;
129         }
130
131       /* Set the bit duplicated_fd in fds_to_close[].  */
132       index = (unsigned int) duplicated_fd / CHAR_BIT;
133       if (fds_to_close_bound <= index)
134         {
135           if (sizeof fds_to_close <= index)
136             /* Need to increase OPEN_MAX_MAX.  */
137             abort ();
138           memset (fds_to_close + fds_to_close_bound, '\0',
139                   index + 1 - fds_to_close_bound);
140           fds_to_close_bound = index + 1;
141         }
142       fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
143     }
144
145   /* Close the previous fds that turned out to be too small.  */
146   {
147     int saved_errno = errno;
148     unsigned int duplicated_fd;
149
150     for (duplicated_fd = 0;
151          duplicated_fd < fds_to_close_bound * CHAR_BIT;
152          duplicated_fd++)
153       if ((fds_to_close[duplicated_fd / CHAR_BIT]
154            >> (duplicated_fd % CHAR_BIT))
155           & 1)
156         close (duplicated_fd);
157
158     errno = saved_errno;
159   }
160
161 # if REPLACE_FCHDIR
162   if (0 <= result)
163     result = _gl_register_dup (oldfd, result);
164 # endif
165   return result;
166 }
167 #endif /* W32 */
168
169 #ifdef __KLIBC__
170
171 # define INCL_DOS
172 # include <os2.h>
173
174 static int
175 klibc_fcntl (int fd, int action, /* arg */...)
176 {
177   va_list arg_ptr;
178   int arg;
179   struct stat sbuf;
180   int result = -1;
181
182   va_start (arg_ptr, action);
183   arg = va_arg (arg_ptr, int);
184   result = fcntl (fd, action, arg);
185   /* EPERM for F_DUPFD, ENOTSUP for others */
186   if (result == -1 && (errno == EPERM || errno == ENOTSUP)
187       && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
188   {
189     ULONG ulMode;
190
191     switch (action)
192       {
193       case F_DUPFD:
194         /* Find available fd */
195         while (fcntl (arg, F_GETFL) != -1 || errno != EBADF)
196           arg++;
197
198         result = dup2 (fd, arg);
199         break;
200
201       /* Using underlying APIs is right ? */
202       case F_GETFD:
203         if (DosQueryFHState (fd, &ulMode))
204           break;
205
206         result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0;
207         break;
208
209       case F_SETFD:
210         if (arg & ~FD_CLOEXEC)
211           break;
212
213         if (DosQueryFHState (fd, &ulMode))
214           break;
215
216         if (arg & FD_CLOEXEC)
217           ulMode |= OPEN_FLAGS_NOINHERIT;
218         else
219           ulMode &= ~OPEN_FLAGS_NOINHERIT;
220
221         /* Filter supported flags.  */
222         ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR
223                    | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT);
224
225         if (DosSetFHState (fd, ulMode))
226           break;
227
228         result = 0;
229         break;
230
231       case F_GETFL:
232         result = 0;
233         break;
234
235       case F_SETFL:
236         if (arg != 0)
237           break;
238
239         result = 0;
240         break;
241
242       default :
243         errno = EINVAL;
244         break;
245       }
246   }
247
248   va_end (arg_ptr);
249
250   return result;
251 }
252
253 # define fcntl klibc_fcntl
254 #endif
255
256 /* Perform the specified ACTION on the file descriptor FD, possibly
257    using the argument ARG further described below.  This replacement
258    handles the following actions, and forwards all others on to the
259    native fcntl.  An unrecognized ACTION returns -1 with errno set to
260    EINVAL.
261
262    F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
263    If successful, return the duplicate, which will be inheritable;
264    otherwise return -1 and set errno.
265
266    F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
267    target fd.  If successful, return the duplicate, which will not be
268    inheritable; otherwise return -1 and set errno.
269
270    F_GETFD - ARG need not be present.  If successful, return a
271    non-negative value containing the descriptor flags of FD (only
272    FD_CLOEXEC is portable, but other flags may be present); otherwise
273    return -1 and set errno.  */
274
275 int
276 rpl_fcntl (int fd, int action, /* arg */...)
277 {
278   va_list arg;
279   int result = -1;
280   va_start (arg, action);
281   switch (action)
282     {
283
284 #if !HAVE_FCNTL
285     case F_DUPFD:
286       {
287         int target = va_arg (arg, int);
288         result = dupfd (fd, target, 0);
289         break;
290       }
291 #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
292     case F_DUPFD:
293       {
294         int target = va_arg (arg, int);
295         /* Detect invalid target; needed for cygwin 1.5.x.  */
296         if (target < 0 || getdtablesize () <= target)
297           errno = EINVAL;
298         else
299           {
300             /* Haiku alpha 2 loses fd flags on original.  */
301             int flags = fcntl (fd, F_GETFD);
302             if (flags < 0)
303               {
304                 result = -1;
305                 break;
306               }
307             result = fcntl (fd, action, target);
308             if (0 <= result && fcntl (fd, F_SETFD, flags) == -1)
309               {
310                 int saved_errno = errno;
311                 close (result);
312                 result = -1;
313                 errno = saved_errno;
314               }
315 # if REPLACE_FCHDIR
316             if (0 <= result)
317               result = _gl_register_dup (fd, result);
318 # endif
319           }
320         break;
321       } /* F_DUPFD */
322 #endif /* FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR */
323
324     case F_DUPFD_CLOEXEC:
325       {
326         int target = va_arg (arg, int);
327
328 #if !HAVE_FCNTL
329         result = dupfd (fd, target, O_CLOEXEC);
330         break;
331 #else /* HAVE_FCNTL */
332         /* Try the system call first, if the headers claim it exists
333            (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
334            may be running with a glibc that has the macro but with an
335            older kernel that does not support it.  Cache the
336            information on whether the system call really works, but
337            avoid caching failure if the corresponding F_DUPFD fails
338            for any reason.  0 = unknown, 1 = yes, -1 = no.  */
339         static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
340         if (0 <= have_dupfd_cloexec)
341           {
342             result = fcntl (fd, action, target);
343             if (0 <= result || errno != EINVAL)
344               {
345                 have_dupfd_cloexec = 1;
346 # if REPLACE_FCHDIR
347                 if (0 <= result)
348                   result = _gl_register_dup (fd, result);
349 # endif
350               }
351             else
352               {
353                 result = rpl_fcntl (fd, F_DUPFD, target);
354                 if (result < 0)
355                   break;
356                 have_dupfd_cloexec = -1;
357               }
358           }
359         else
360           result = rpl_fcntl (fd, F_DUPFD, target);
361         if (0 <= result && have_dupfd_cloexec == -1)
362           {
363             int flags = fcntl (result, F_GETFD);
364             if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
365               {
366                 int saved_errno = errno;
367                 close (result);
368                 errno = saved_errno;
369                 result = -1;
370               }
371           }
372         break;
373 #endif /* HAVE_FCNTL */
374       } /* F_DUPFD_CLOEXEC */
375
376 #if !HAVE_FCNTL
377     case F_GETFD:
378       {
379 # if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
380         HANDLE handle = (HANDLE) _get_osfhandle (fd);
381         DWORD flags;
382         if (handle == INVALID_HANDLE_VALUE
383             || GetHandleInformation (handle, &flags) == 0)
384           errno = EBADF;
385         else
386           result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
387 # else /* !W32 */
388         /* Use dup2 to reject invalid file descriptors.  No way to
389            access this information, so punt.  */
390         if (0 <= dup2 (fd, fd))
391           result = 0;
392 # endif /* !W32 */
393         break;
394       } /* F_GETFD */
395 #endif /* !HAVE_FCNTL */
396
397       /* Implementing F_SETFD on mingw is not trivial - there is no
398          API for changing the O_NOINHERIT bit on an fd, and merely
399          changing the HANDLE_FLAG_INHERIT bit on the underlying handle
400          can lead to odd state.  It may be possible by duplicating the
401          handle, using _open_osfhandle with the right flags, then
402          using dup2 to move the duplicate onto the original, but that
403          is not supported for now.  */
404
405     default:
406       {
407 #if HAVE_FCNTL
408         void *p = va_arg (arg, void *);
409         result = fcntl (fd, action, p);
410 #else
411         errno = EINVAL;
412 #endif
413         break;
414       }
415     }
416   va_end (arg);
417   return result;
418 }