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