xref: /dragonfly/contrib/diffutils/lib/fcntl.c (revision c880cbaf)
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 #ifdef __KLIBC__
31 # define INCL_DOS
32 # include <os2.h>
33 #endif
34 
35 #if 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 /* Forward declarations, because we '#undef fcntl' in the middle of this
170    compilation unit.  */
171 /* Our implementation of fcntl (fd, F_DUPFD, target).  */
172 static int rpl_fcntl_DUPFD (int fd, int target);
173 /* Our implementation of fcntl (fd, F_DUPFD_CLOEXEC, target).  */
174 static int rpl_fcntl_DUPFD_CLOEXEC (int fd, int target);
175 #ifdef __KLIBC__
176 /* Adds support for fcntl on directories.  */
177 static int klibc_fcntl (int fd, int action, /* arg */...);
178 #endif
179 
180 
181 /* Perform the specified ACTION on the file descriptor FD, possibly
182    using the argument ARG further described below.  This replacement
183    handles the following actions, and forwards all others on to the
184    native fcntl.  An unrecognized ACTION returns -1 with errno set to
185    EINVAL.
186 
187    F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
188    If successful, return the duplicate, which will be inheritable;
189    otherwise return -1 and set errno.
190 
191    F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
192    target fd.  If successful, return the duplicate, which will not be
193    inheritable; otherwise return -1 and set errno.
194 
195    F_GETFD - ARG need not be present.  If successful, return a
196    non-negative value containing the descriptor flags of FD (only
197    FD_CLOEXEC is portable, but other flags may be present); otherwise
198    return -1 and set errno.  */
199 
200 int
201 fcntl (int fd, int action, /* arg */...)
202 #undef fcntl
203 #ifdef __KLIBC__
204 # define fcntl klibc_fcntl
205 #endif
206 {
207   va_list arg;
208   int result = -1;
209   va_start (arg, action);
210   switch (action)
211     {
212     case F_DUPFD:
213       {
214         int target = va_arg (arg, int);
215         result = rpl_fcntl_DUPFD (fd, target);
216         break;
217       }
218 
219     case F_DUPFD_CLOEXEC:
220       {
221         int target = va_arg (arg, int);
222         result = rpl_fcntl_DUPFD_CLOEXEC (fd, target);
223         break;
224       }
225 
226 #if !HAVE_FCNTL
227     case F_GETFD:
228       {
229 # if defined _WIN32 && ! defined __CYGWIN__
230         HANDLE handle = (HANDLE) _get_osfhandle (fd);
231         DWORD flags;
232         if (handle == INVALID_HANDLE_VALUE
233             || GetHandleInformation (handle, &flags) == 0)
234           errno = EBADF;
235         else
236           result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
237 # else /* !W32 */
238         /* Use dup2 to reject invalid file descriptors.  No way to
239            access this information, so punt.  */
240         if (0 <= dup2 (fd, fd))
241           result = 0;
242 # endif /* !W32 */
243         break;
244       } /* F_GETFD */
245 #endif /* !HAVE_FCNTL */
246 
247       /* Implementing F_SETFD on mingw is not trivial - there is no
248          API for changing the O_NOINHERIT bit on an fd, and merely
249          changing the HANDLE_FLAG_INHERIT bit on the underlying handle
250          can lead to odd state.  It may be possible by duplicating the
251          handle, using _open_osfhandle with the right flags, then
252          using dup2 to move the duplicate onto the original, but that
253          is not supported for now.  */
254 
255     default:
256       {
257 #if HAVE_FCNTL
258         switch (action)
259           {
260           #ifdef F_BARRIERFSYNC                  /* macOS */
261           case F_BARRIERFSYNC:
262           #endif
263           #ifdef F_CHKCLEAN                      /* macOS */
264           case F_CHKCLEAN:
265           #endif
266           #ifdef F_CLOSEM                        /* NetBSD, HP-UX */
267           case F_CLOSEM:
268           #endif
269           #ifdef F_FLUSH_DATA                    /* macOS */
270           case F_FLUSH_DATA:
271           #endif
272           #ifdef F_FREEZE_FS                     /* macOS */
273           case F_FREEZE_FS:
274           #endif
275           #ifdef F_FULLFSYNC                     /* macOS */
276           case F_FULLFSYNC:
277           #endif
278           #ifdef F_GETCONFINED                   /* macOS */
279           case F_GETCONFINED:
280           #endif
281           #ifdef F_GETDEFAULTPROTLEVEL           /* macOS */
282           case F_GETDEFAULTPROTLEVEL:
283           #endif
284           #ifdef F_GETFD                         /* POSIX */
285           case F_GETFD:
286           #endif
287           #ifdef F_GETFL                         /* POSIX */
288           case F_GETFL:
289           #endif
290           #ifdef F_GETLEASE                      /* Linux */
291           case F_GETLEASE:
292           #endif
293           #ifdef F_GETNOSIGPIPE                  /* macOS */
294           case F_GETNOSIGPIPE:
295           #endif
296           #ifdef F_GETOWN                        /* POSIX */
297           case F_GETOWN:
298           #endif
299           #ifdef F_GETPIPE_SZ                    /* Linux */
300           case F_GETPIPE_SZ:
301           #endif
302           #ifdef F_GETPROTECTIONCLASS            /* macOS */
303           case F_GETPROTECTIONCLASS:
304           #endif
305           #ifdef F_GETPROTECTIONLEVEL            /* macOS */
306           case F_GETPROTECTIONLEVEL:
307           #endif
308           #ifdef F_GET_SEALS                     /* Linux */
309           case F_GET_SEALS:
310           #endif
311           #ifdef F_GETSIG                        /* Linux */
312           case F_GETSIG:
313           #endif
314           #ifdef F_MAXFD                         /* NetBSD */
315           case F_MAXFD:
316           #endif
317           #ifdef F_RECYCLE                       /* macOS */
318           case F_RECYCLE:
319           #endif
320           #ifdef F_SETFIFOENH                    /* HP-UX */
321           case F_SETFIFOENH:
322           #endif
323           #ifdef F_THAW_FS                       /* macOS */
324           case F_THAW_FS:
325           #endif
326             /* These actions take no argument.  */
327             result = fcntl (fd, action);
328             break;
329 
330           #ifdef F_ADD_SEALS                     /* Linux */
331           case F_ADD_SEALS:
332           #endif
333           #ifdef F_BADFD                         /* Solaris */
334           case F_BADFD:
335           #endif
336           #ifdef F_CHECK_OPENEVT                 /* macOS */
337           case F_CHECK_OPENEVT:
338           #endif
339           #ifdef F_DUP2FD                        /* FreeBSD, AIX, Solaris */
340           case F_DUP2FD:
341           #endif
342           #ifdef F_DUP2FD_CLOEXEC                /* FreeBSD, Solaris */
343           case F_DUP2FD_CLOEXEC:
344           #endif
345           #ifdef F_DUP2FD_CLOFORK                /* Solaris */
346           case F_DUP2FD_CLOFORK:
347           #endif
348           #ifdef F_DUPFD                         /* POSIX */
349           case F_DUPFD:
350           #endif
351           #ifdef F_DUPFD_CLOEXEC                 /* POSIX */
352           case F_DUPFD_CLOEXEC:
353           #endif
354           #ifdef F_DUPFD_CLOFORK                 /* Solaris */
355           case F_DUPFD_CLOFORK:
356           #endif
357           #ifdef F_GETXFL                        /* Solaris */
358           case F_GETXFL:
359           #endif
360           #ifdef F_GLOBAL_NOCACHE                /* macOS */
361           case F_GLOBAL_NOCACHE:
362           #endif
363           #ifdef F_MAKECOMPRESSED                /* macOS */
364           case F_MAKECOMPRESSED:
365           #endif
366           #ifdef F_MOVEDATAEXTENTS               /* macOS */
367           case F_MOVEDATAEXTENTS:
368           #endif
369           #ifdef F_NOCACHE                       /* macOS */
370           case F_NOCACHE:
371           #endif
372           #ifdef F_NODIRECT                      /* macOS */
373           case F_NODIRECT:
374           #endif
375           #ifdef F_NOTIFY                        /* Linux */
376           case F_NOTIFY:
377           #endif
378           #ifdef F_OPLKACK                       /* IRIX */
379           case F_OPLKACK:
380           #endif
381           #ifdef F_OPLKREG                       /* IRIX */
382           case F_OPLKREG:
383           #endif
384           #ifdef F_RDAHEAD                       /* macOS */
385           case F_RDAHEAD:
386           #endif
387           #ifdef F_SETBACKINGSTORE               /* macOS */
388           case F_SETBACKINGSTORE:
389           #endif
390           #ifdef F_SETCONFINED                   /* macOS */
391           case F_SETCONFINED:
392           #endif
393           #ifdef F_SETFD                         /* POSIX */
394           case F_SETFD:
395           #endif
396           #ifdef F_SETFL                         /* POSIX */
397           case F_SETFL:
398           #endif
399           #ifdef F_SETLEASE                      /* Linux */
400           case F_SETLEASE:
401           #endif
402           #ifdef F_SETNOSIGPIPE                  /* macOS */
403           case F_SETNOSIGPIPE:
404           #endif
405           #ifdef F_SETOWN                        /* POSIX */
406           case F_SETOWN:
407           #endif
408           #ifdef F_SETPIPE_SZ                    /* Linux */
409           case F_SETPIPE_SZ:
410           #endif
411           #ifdef F_SETPROTECTIONCLASS            /* macOS */
412           case F_SETPROTECTIONCLASS:
413           #endif
414           #ifdef F_SETSIG                        /* Linux */
415           case F_SETSIG:
416           #endif
417           #ifdef F_SINGLE_WRITER                 /* macOS */
418           case F_SINGLE_WRITER:
419           #endif
420             /* These actions take an 'int' argument.  */
421             {
422               int x = va_arg (arg, int);
423               result = fcntl (fd, action, x);
424             }
425             break;
426 
427           default:
428             /* Other actions take a pointer argument.  */
429             {
430               void *p = va_arg (arg, void *);
431               result = fcntl (fd, action, p);
432             }
433             break;
434           }
435 #else
436         errno = EINVAL;
437 #endif
438         break;
439       }
440     }
441   va_end (arg);
442   return result;
443 }
444 
445 static int
446 rpl_fcntl_DUPFD (int fd, int target)
447 {
448   int result;
449 #if !HAVE_FCNTL
450   result = dupfd (fd, target, 0);
451 #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
452   /* Detect invalid target; needed for cygwin 1.5.x.  */
453   if (target < 0 || getdtablesize () <= target)
454     {
455       result = -1;
456       errno = EINVAL;
457     }
458   else
459     {
460       /* Haiku alpha 2 loses fd flags on original.  */
461       int flags = fcntl (fd, F_GETFD);
462       if (flags < 0)
463         result = -1;
464       else
465         {
466           result = fcntl (fd, F_DUPFD, target);
467           if (0 <= result && fcntl (fd, F_SETFD, flags) == -1)
468             {
469               int saved_errno = errno;
470               close (result);
471               result = -1;
472               errno = saved_errno;
473             }
474 # if REPLACE_FCHDIR
475           if (0 <= result)
476             result = _gl_register_dup (fd, result);
477 # endif
478         }
479     }
480 #else
481   result = fcntl (fd, F_DUPFD, target);
482 #endif
483   return result;
484 }
485 
486 static int
487 rpl_fcntl_DUPFD_CLOEXEC (int fd, int target)
488 {
489   int result;
490 #if !HAVE_FCNTL
491   result = dupfd (fd, target, O_CLOEXEC);
492 #else /* HAVE_FCNTL */
493 # if defined __HAIKU__
494   /* On Haiku, the system fcntl (fd, F_DUPFD_CLOEXEC, target) sets
495      the FD_CLOEXEC flag on fd, not on target.  Therefore avoid the
496      system fcntl in this case.  */
497 #  define have_dupfd_cloexec -1
498 # else
499   /* Try the system call first, if the headers claim it exists
500      (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
501      may be running with a glibc that has the macro but with an
502      older kernel that does not support it.  Cache the
503      information on whether the system call really works, but
504      avoid caching failure if the corresponding F_DUPFD fails
505      for any reason.  0 = unknown, 1 = yes, -1 = no.  */
506   static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
507   if (0 <= have_dupfd_cloexec)
508     {
509       result = fcntl (fd, F_DUPFD_CLOEXEC, target);
510       if (0 <= result || errno != EINVAL)
511         {
512           have_dupfd_cloexec = 1;
513 #  if REPLACE_FCHDIR
514           if (0 <= result)
515             result = _gl_register_dup (fd, result);
516 #  endif
517         }
518       else
519         {
520           result = rpl_fcntl_DUPFD (fd, target);
521           if (result >= 0)
522             have_dupfd_cloexec = -1;
523         }
524     }
525   else
526 # endif
527     result = rpl_fcntl_DUPFD (fd, target);
528   if (0 <= result && have_dupfd_cloexec == -1)
529     {
530       int flags = fcntl (result, F_GETFD);
531       if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
532         {
533           int saved_errno = errno;
534           close (result);
535           errno = saved_errno;
536           result = -1;
537         }
538     }
539 #endif /* HAVE_FCNTL */
540   return result;
541 }
542 
543 #undef fcntl
544 
545 #ifdef __KLIBC__
546 
547 static int
548 klibc_fcntl (int fd, int action, /* arg */...);
549 {
550   va_list arg_ptr;
551   int arg;
552   struct stat sbuf;
553   int result;
554 
555   va_start (arg_ptr, action);
556   arg = va_arg (arg_ptr, int);
557   result = fcntl (fd, action, arg);
558   /* EPERM for F_DUPFD, ENOTSUP for others */
559   if (result == -1 && (errno == EPERM || errno == ENOTSUP)
560       && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
561     {
562       ULONG ulMode;
563 
564       switch (action)
565         {
566         case F_DUPFD:
567           /* Find available fd */
568           while (fcntl (arg, F_GETFL) != -1 || errno != EBADF)
569             arg++;
570 
571           result = dup2 (fd, arg);
572           break;
573 
574         /* Using underlying APIs is right ? */
575         case F_GETFD:
576           if (DosQueryFHState (fd, &ulMode))
577             break;
578 
579           result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0;
580           break;
581 
582         case F_SETFD:
583           if (arg & ~FD_CLOEXEC)
584             break;
585 
586           if (DosQueryFHState (fd, &ulMode))
587             break;
588 
589           if (arg & FD_CLOEXEC)
590             ulMode |= OPEN_FLAGS_NOINHERIT;
591           else
592             ulMode &= ~OPEN_FLAGS_NOINHERIT;
593 
594           /* Filter supported flags.  */
595           ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR
596                      | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT);
597 
598           if (DosSetFHState (fd, ulMode))
599             break;
600 
601           result = 0;
602           break;
603 
604         case F_GETFL:
605           result = 0;
606           break;
607 
608         case F_SETFL:
609           if (arg != 0)
610             break;
611 
612           result = 0;
613           break;
614 
615         default:
616           errno = EINVAL;
617           break;
618         }
619     }
620 
621   va_end (arg_ptr);
622 
623   return result;
624 }
625 
626 #endif
627