Home | History | Annotate | Line # | Download | only in import
      1      1.1  christos /* Provide file descriptor control.
      2      1.1  christos 
      3  1.1.1.2  christos    Copyright (C) 2009-2022 Free Software Foundation, Inc.
      4      1.1  christos 
      5  1.1.1.2  christos    This file is free software: you can redistribute it and/or modify
      6  1.1.1.2  christos    it under the terms of the GNU Lesser General Public License as
      7  1.1.1.2  christos    published by the Free Software Foundation; either version 2.1 of the
      8  1.1.1.2  christos    License, or (at your option) any later version.
      9      1.1  christos 
     10  1.1.1.2  christos    This file is distributed in the hope that it will be useful,
     11      1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12      1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  1.1.1.2  christos    GNU Lesser General Public License for more details.
     14      1.1  christos 
     15  1.1.1.2  christos    You should have received a copy of the GNU Lesser General Public License
     16      1.1  christos    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
     17      1.1  christos 
     18      1.1  christos /* Written by Eric Blake <ebb9 (at) byu.net>.  */
     19      1.1  christos 
     20      1.1  christos #include <config.h>
     21      1.1  christos 
     22      1.1  christos /* Specification.  */
     23      1.1  christos #include <fcntl.h>
     24      1.1  christos 
     25      1.1  christos #include <errno.h>
     26      1.1  christos #include <limits.h>
     27      1.1  christos #include <stdarg.h>
     28      1.1  christos #include <stdlib.h>
     29      1.1  christos #include <unistd.h>
     30      1.1  christos 
     31      1.1  christos #ifdef __KLIBC__
     32      1.1  christos # define INCL_DOS
     33      1.1  christos # include <os2.h>
     34      1.1  christos #endif
     35      1.1  christos 
     36      1.1  christos #if defined _WIN32 && ! defined __CYGWIN__
     37      1.1  christos /* Get declarations of the native Windows API functions.  */
     38      1.1  christos # define WIN32_LEAN_AND_MEAN
     39      1.1  christos # include <windows.h>
     40      1.1  christos 
     41      1.1  christos /* Get _get_osfhandle.  */
     42      1.1  christos # if GNULIB_MSVC_NOTHROW
     43      1.1  christos #  include "msvc-nothrow.h"
     44      1.1  christos # else
     45      1.1  christos #  include <io.h>
     46      1.1  christos # endif
     47      1.1  christos 
     48      1.1  christos /* Upper bound on getdtablesize().  See lib/getdtablesize.c.  */
     49      1.1  christos # define OPEN_MAX_MAX 0x10000
     50      1.1  christos 
     51      1.1  christos /* Duplicate OLDFD into the first available slot of at least NEWFD,
     52      1.1  christos    which must be positive, with FLAGS determining whether the duplicate
     53      1.1  christos    will be inheritable.  */
     54      1.1  christos static int
     55      1.1  christos dupfd (int oldfd, int newfd, int flags)
     56      1.1  christos {
     57      1.1  christos   /* Mingw has no way to create an arbitrary fd.  Iterate until all
     58      1.1  christos      file descriptors less than newfd are filled up.  */
     59      1.1  christos   HANDLE curr_process = GetCurrentProcess ();
     60      1.1  christos   HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
     61      1.1  christos   unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
     62      1.1  christos   unsigned int fds_to_close_bound = 0;
     63      1.1  christos   int result;
     64      1.1  christos   BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE;
     65      1.1  christos   int mode;
     66      1.1  christos 
     67      1.1  christos   if (newfd < 0 || getdtablesize () <= newfd)
     68      1.1  christos     {
     69      1.1  christos       errno = EINVAL;
     70      1.1  christos       return -1;
     71      1.1  christos     }
     72      1.1  christos   if (old_handle == INVALID_HANDLE_VALUE
     73  1.1.1.2  christos       || (mode = _setmode (oldfd, O_BINARY)) == -1)
     74      1.1  christos     {
     75      1.1  christos       /* oldfd is not open, or is an unassigned standard file
     76      1.1  christos          descriptor.  */
     77      1.1  christos       errno = EBADF;
     78      1.1  christos       return -1;
     79      1.1  christos     }
     80  1.1.1.2  christos   _setmode (oldfd, mode);
     81      1.1  christos   flags |= mode;
     82      1.1  christos 
     83      1.1  christos   for (;;)
     84      1.1  christos     {
     85      1.1  christos       HANDLE new_handle;
     86      1.1  christos       int duplicated_fd;
     87      1.1  christos       unsigned int index;
     88      1.1  christos 
     89      1.1  christos       if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
     90      1.1  christos                             old_handle,             /* SourceHandle */
     91      1.1  christos                             curr_process,           /* TargetProcessHandle */
     92      1.1  christos                             (PHANDLE) &new_handle,  /* TargetHandle */
     93      1.1  christos                             (DWORD) 0,              /* DesiredAccess */
     94      1.1  christos                             inherit,                /* InheritHandle */
     95      1.1  christos                             DUPLICATE_SAME_ACCESS)) /* Options */
     96      1.1  christos         {
     97      1.1  christos           switch (GetLastError ())
     98      1.1  christos             {
     99      1.1  christos               case ERROR_TOO_MANY_OPEN_FILES:
    100      1.1  christos                 errno = EMFILE;
    101      1.1  christos                 break;
    102      1.1  christos               case ERROR_INVALID_HANDLE:
    103      1.1  christos               case ERROR_INVALID_TARGET_HANDLE:
    104      1.1  christos               case ERROR_DIRECT_ACCESS_HANDLE:
    105      1.1  christos                 errno = EBADF;
    106      1.1  christos                 break;
    107      1.1  christos               case ERROR_INVALID_PARAMETER:
    108      1.1  christos               case ERROR_INVALID_FUNCTION:
    109      1.1  christos               case ERROR_INVALID_ACCESS:
    110      1.1  christos                 errno = EINVAL;
    111      1.1  christos                 break;
    112      1.1  christos               default:
    113      1.1  christos                 errno = EACCES;
    114      1.1  christos                 break;
    115      1.1  christos             }
    116      1.1  christos           result = -1;
    117      1.1  christos           break;
    118      1.1  christos         }
    119      1.1  christos       duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags);
    120      1.1  christos       if (duplicated_fd < 0)
    121      1.1  christos         {
    122      1.1  christos           CloseHandle (new_handle);
    123      1.1  christos           result = -1;
    124      1.1  christos           break;
    125      1.1  christos         }
    126      1.1  christos       if (newfd <= duplicated_fd)
    127      1.1  christos         {
    128      1.1  christos           result = duplicated_fd;
    129      1.1  christos           break;
    130      1.1  christos         }
    131      1.1  christos 
    132      1.1  christos       /* Set the bit duplicated_fd in fds_to_close[].  */
    133      1.1  christos       index = (unsigned int) duplicated_fd / CHAR_BIT;
    134      1.1  christos       if (fds_to_close_bound <= index)
    135      1.1  christos         {
    136      1.1  christos           if (sizeof fds_to_close <= index)
    137      1.1  christos             /* Need to increase OPEN_MAX_MAX.  */
    138      1.1  christos             abort ();
    139      1.1  christos           memset (fds_to_close + fds_to_close_bound, '\0',
    140      1.1  christos                   index + 1 - fds_to_close_bound);
    141      1.1  christos           fds_to_close_bound = index + 1;
    142      1.1  christos         }
    143      1.1  christos       fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
    144      1.1  christos     }
    145      1.1  christos 
    146      1.1  christos   /* Close the previous fds that turned out to be too small.  */
    147      1.1  christos   {
    148      1.1  christos     int saved_errno = errno;
    149      1.1  christos     unsigned int duplicated_fd;
    150      1.1  christos 
    151      1.1  christos     for (duplicated_fd = 0;
    152      1.1  christos          duplicated_fd < fds_to_close_bound * CHAR_BIT;
    153      1.1  christos          duplicated_fd++)
    154      1.1  christos       if ((fds_to_close[duplicated_fd / CHAR_BIT]
    155      1.1  christos            >> (duplicated_fd % CHAR_BIT))
    156      1.1  christos           & 1)
    157      1.1  christos         close (duplicated_fd);
    158      1.1  christos 
    159      1.1  christos     errno = saved_errno;
    160      1.1  christos   }
    161      1.1  christos 
    162      1.1  christos # if REPLACE_FCHDIR
    163      1.1  christos   if (0 <= result)
    164      1.1  christos     result = _gl_register_dup (oldfd, result);
    165      1.1  christos # endif
    166      1.1  christos   return result;
    167      1.1  christos }
    168      1.1  christos #endif /* W32 */
    169      1.1  christos 
    170      1.1  christos /* Forward declarations, because we '#undef fcntl' in the middle of this
    171      1.1  christos    compilation unit.  */
    172      1.1  christos /* Our implementation of fcntl (fd, F_DUPFD, target).  */
    173      1.1  christos static int rpl_fcntl_DUPFD (int fd, int target);
    174      1.1  christos /* Our implementation of fcntl (fd, F_DUPFD_CLOEXEC, target).  */
    175      1.1  christos static int rpl_fcntl_DUPFD_CLOEXEC (int fd, int target);
    176      1.1  christos #ifdef __KLIBC__
    177      1.1  christos /* Adds support for fcntl on directories.  */
    178      1.1  christos static int klibc_fcntl (int fd, int action, /* arg */...);
    179      1.1  christos #endif
    180      1.1  christos 
    181      1.1  christos 
    182      1.1  christos /* Perform the specified ACTION on the file descriptor FD, possibly
    183      1.1  christos    using the argument ARG further described below.  This replacement
    184      1.1  christos    handles the following actions, and forwards all others on to the
    185      1.1  christos    native fcntl.  An unrecognized ACTION returns -1 with errno set to
    186      1.1  christos    EINVAL.
    187      1.1  christos 
    188      1.1  christos    F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
    189      1.1  christos    If successful, return the duplicate, which will be inheritable;
    190      1.1  christos    otherwise return -1 and set errno.
    191      1.1  christos 
    192      1.1  christos    F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
    193      1.1  christos    target fd.  If successful, return the duplicate, which will not be
    194      1.1  christos    inheritable; otherwise return -1 and set errno.
    195      1.1  christos 
    196      1.1  christos    F_GETFD - ARG need not be present.  If successful, return a
    197      1.1  christos    non-negative value containing the descriptor flags of FD (only
    198      1.1  christos    FD_CLOEXEC is portable, but other flags may be present); otherwise
    199      1.1  christos    return -1 and set errno.  */
    200      1.1  christos 
    201      1.1  christos int
    202      1.1  christos fcntl (int fd, int action, /* arg */...)
    203      1.1  christos #undef fcntl
    204      1.1  christos #ifdef __KLIBC__
    205      1.1  christos # define fcntl klibc_fcntl
    206      1.1  christos #endif
    207      1.1  christos {
    208      1.1  christos   va_list arg;
    209      1.1  christos   int result = -1;
    210      1.1  christos   va_start (arg, action);
    211      1.1  christos   switch (action)
    212      1.1  christos     {
    213      1.1  christos     case F_DUPFD:
    214      1.1  christos       {
    215      1.1  christos         int target = va_arg (arg, int);
    216      1.1  christos         result = rpl_fcntl_DUPFD (fd, target);
    217      1.1  christos         break;
    218      1.1  christos       }
    219      1.1  christos 
    220      1.1  christos     case F_DUPFD_CLOEXEC:
    221      1.1  christos       {
    222      1.1  christos         int target = va_arg (arg, int);
    223      1.1  christos         result = rpl_fcntl_DUPFD_CLOEXEC (fd, target);
    224      1.1  christos         break;
    225      1.1  christos       }
    226      1.1  christos 
    227      1.1  christos #if !HAVE_FCNTL
    228      1.1  christos     case F_GETFD:
    229      1.1  christos       {
    230      1.1  christos # if defined _WIN32 && ! defined __CYGWIN__
    231      1.1  christos         HANDLE handle = (HANDLE) _get_osfhandle (fd);
    232      1.1  christos         DWORD flags;
    233      1.1  christos         if (handle == INVALID_HANDLE_VALUE
    234      1.1  christos             || GetHandleInformation (handle, &flags) == 0)
    235      1.1  christos           errno = EBADF;
    236      1.1  christos         else
    237      1.1  christos           result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
    238      1.1  christos # else /* !W32 */
    239      1.1  christos         /* Use dup2 to reject invalid file descriptors.  No way to
    240      1.1  christos            access this information, so punt.  */
    241      1.1  christos         if (0 <= dup2 (fd, fd))
    242      1.1  christos           result = 0;
    243      1.1  christos # endif /* !W32 */
    244      1.1  christos         break;
    245      1.1  christos       } /* F_GETFD */
    246      1.1  christos #endif /* !HAVE_FCNTL */
    247      1.1  christos 
    248      1.1  christos       /* Implementing F_SETFD on mingw is not trivial - there is no
    249      1.1  christos          API for changing the O_NOINHERIT bit on an fd, and merely
    250      1.1  christos          changing the HANDLE_FLAG_INHERIT bit on the underlying handle
    251      1.1  christos          can lead to odd state.  It may be possible by duplicating the
    252      1.1  christos          handle, using _open_osfhandle with the right flags, then
    253      1.1  christos          using dup2 to move the duplicate onto the original, but that
    254      1.1  christos          is not supported for now.  */
    255      1.1  christos 
    256      1.1  christos     default:
    257      1.1  christos       {
    258      1.1  christos #if HAVE_FCNTL
    259      1.1  christos         switch (action)
    260      1.1  christos           {
    261      1.1  christos           #ifdef F_BARRIERFSYNC                  /* macOS */
    262      1.1  christos           case F_BARRIERFSYNC:
    263      1.1  christos           #endif
    264      1.1  christos           #ifdef F_CHKCLEAN                      /* macOS */
    265      1.1  christos           case F_CHKCLEAN:
    266      1.1  christos           #endif
    267      1.1  christos           #ifdef F_CLOSEM                        /* NetBSD, HP-UX */
    268      1.1  christos           case F_CLOSEM:
    269      1.1  christos           #endif
    270      1.1  christos           #ifdef F_FLUSH_DATA                    /* macOS */
    271      1.1  christos           case F_FLUSH_DATA:
    272      1.1  christos           #endif
    273      1.1  christos           #ifdef F_FREEZE_FS                     /* macOS */
    274      1.1  christos           case F_FREEZE_FS:
    275      1.1  christos           #endif
    276      1.1  christos           #ifdef F_FULLFSYNC                     /* macOS */
    277      1.1  christos           case F_FULLFSYNC:
    278      1.1  christos           #endif
    279      1.1  christos           #ifdef F_GETCONFINED                   /* macOS */
    280      1.1  christos           case F_GETCONFINED:
    281      1.1  christos           #endif
    282      1.1  christos           #ifdef F_GETDEFAULTPROTLEVEL           /* macOS */
    283      1.1  christos           case F_GETDEFAULTPROTLEVEL:
    284      1.1  christos           #endif
    285      1.1  christos           #ifdef F_GETFD                         /* POSIX */
    286      1.1  christos           case F_GETFD:
    287      1.1  christos           #endif
    288      1.1  christos           #ifdef F_GETFL                         /* POSIX */
    289      1.1  christos           case F_GETFL:
    290      1.1  christos           #endif
    291      1.1  christos           #ifdef F_GETLEASE                      /* Linux */
    292      1.1  christos           case F_GETLEASE:
    293      1.1  christos           #endif
    294      1.1  christos           #ifdef F_GETNOSIGPIPE                  /* macOS */
    295      1.1  christos           case F_GETNOSIGPIPE:
    296      1.1  christos           #endif
    297      1.1  christos           #ifdef F_GETOWN                        /* POSIX */
    298      1.1  christos           case F_GETOWN:
    299      1.1  christos           #endif
    300      1.1  christos           #ifdef F_GETPIPE_SZ                    /* Linux */
    301      1.1  christos           case F_GETPIPE_SZ:
    302      1.1  christos           #endif
    303      1.1  christos           #ifdef F_GETPROTECTIONCLASS            /* macOS */
    304      1.1  christos           case F_GETPROTECTIONCLASS:
    305      1.1  christos           #endif
    306      1.1  christos           #ifdef F_GETPROTECTIONLEVEL            /* macOS */
    307      1.1  christos           case F_GETPROTECTIONLEVEL:
    308      1.1  christos           #endif
    309      1.1  christos           #ifdef F_GET_SEALS                     /* Linux */
    310      1.1  christos           case F_GET_SEALS:
    311      1.1  christos           #endif
    312      1.1  christos           #ifdef F_GETSIG                        /* Linux */
    313      1.1  christos           case F_GETSIG:
    314      1.1  christos           #endif
    315      1.1  christos           #ifdef F_MAXFD                         /* NetBSD */
    316      1.1  christos           case F_MAXFD:
    317      1.1  christos           #endif
    318      1.1  christos           #ifdef F_RECYCLE                       /* macOS */
    319      1.1  christos           case F_RECYCLE:
    320      1.1  christos           #endif
    321      1.1  christos           #ifdef F_SETFIFOENH                    /* HP-UX */
    322      1.1  christos           case F_SETFIFOENH:
    323      1.1  christos           #endif
    324      1.1  christos           #ifdef F_THAW_FS                       /* macOS */
    325      1.1  christos           case F_THAW_FS:
    326      1.1  christos           #endif
    327      1.1  christos             /* These actions take no argument.  */
    328      1.1  christos             result = fcntl (fd, action);
    329      1.1  christos             break;
    330      1.1  christos 
    331      1.1  christos           #ifdef F_ADD_SEALS                     /* Linux */
    332      1.1  christos           case F_ADD_SEALS:
    333      1.1  christos           #endif
    334      1.1  christos           #ifdef F_BADFD                         /* Solaris */
    335      1.1  christos           case F_BADFD:
    336      1.1  christos           #endif
    337      1.1  christos           #ifdef F_CHECK_OPENEVT                 /* macOS */
    338      1.1  christos           case F_CHECK_OPENEVT:
    339      1.1  christos           #endif
    340      1.1  christos           #ifdef F_DUP2FD                        /* FreeBSD, AIX, Solaris */
    341      1.1  christos           case F_DUP2FD:
    342      1.1  christos           #endif
    343      1.1  christos           #ifdef F_DUP2FD_CLOEXEC                /* FreeBSD, Solaris */
    344      1.1  christos           case F_DUP2FD_CLOEXEC:
    345      1.1  christos           #endif
    346      1.1  christos           #ifdef F_DUP2FD_CLOFORK                /* Solaris */
    347      1.1  christos           case F_DUP2FD_CLOFORK:
    348      1.1  christos           #endif
    349      1.1  christos           #ifdef F_DUPFD                         /* POSIX */
    350      1.1  christos           case F_DUPFD:
    351      1.1  christos           #endif
    352      1.1  christos           #ifdef F_DUPFD_CLOEXEC                 /* POSIX */
    353      1.1  christos           case F_DUPFD_CLOEXEC:
    354      1.1  christos           #endif
    355      1.1  christos           #ifdef F_DUPFD_CLOFORK                 /* Solaris */
    356      1.1  christos           case F_DUPFD_CLOFORK:
    357      1.1  christos           #endif
    358      1.1  christos           #ifdef F_GETXFL                        /* Solaris */
    359      1.1  christos           case F_GETXFL:
    360      1.1  christos           #endif
    361      1.1  christos           #ifdef F_GLOBAL_NOCACHE                /* macOS */
    362      1.1  christos           case F_GLOBAL_NOCACHE:
    363      1.1  christos           #endif
    364      1.1  christos           #ifdef F_MAKECOMPRESSED                /* macOS */
    365      1.1  christos           case F_MAKECOMPRESSED:
    366      1.1  christos           #endif
    367      1.1  christos           #ifdef F_MOVEDATAEXTENTS               /* macOS */
    368      1.1  christos           case F_MOVEDATAEXTENTS:
    369      1.1  christos           #endif
    370      1.1  christos           #ifdef F_NOCACHE                       /* macOS */
    371      1.1  christos           case F_NOCACHE:
    372      1.1  christos           #endif
    373      1.1  christos           #ifdef F_NODIRECT                      /* macOS */
    374      1.1  christos           case F_NODIRECT:
    375      1.1  christos           #endif
    376      1.1  christos           #ifdef F_NOTIFY                        /* Linux */
    377      1.1  christos           case F_NOTIFY:
    378      1.1  christos           #endif
    379      1.1  christos           #ifdef F_OPLKACK                       /* IRIX */
    380      1.1  christos           case F_OPLKACK:
    381      1.1  christos           #endif
    382      1.1  christos           #ifdef F_OPLKREG                       /* IRIX */
    383      1.1  christos           case F_OPLKREG:
    384      1.1  christos           #endif
    385      1.1  christos           #ifdef F_RDAHEAD                       /* macOS */
    386      1.1  christos           case F_RDAHEAD:
    387      1.1  christos           #endif
    388      1.1  christos           #ifdef F_SETBACKINGSTORE               /* macOS */
    389      1.1  christos           case F_SETBACKINGSTORE:
    390      1.1  christos           #endif
    391      1.1  christos           #ifdef F_SETCONFINED                   /* macOS */
    392      1.1  christos           case F_SETCONFINED:
    393      1.1  christos           #endif
    394      1.1  christos           #ifdef F_SETFD                         /* POSIX */
    395      1.1  christos           case F_SETFD:
    396      1.1  christos           #endif
    397      1.1  christos           #ifdef F_SETFL                         /* POSIX */
    398      1.1  christos           case F_SETFL:
    399      1.1  christos           #endif
    400      1.1  christos           #ifdef F_SETLEASE                      /* Linux */
    401      1.1  christos           case F_SETLEASE:
    402      1.1  christos           #endif
    403      1.1  christos           #ifdef F_SETNOSIGPIPE                  /* macOS */
    404      1.1  christos           case F_SETNOSIGPIPE:
    405      1.1  christos           #endif
    406      1.1  christos           #ifdef F_SETOWN                        /* POSIX */
    407      1.1  christos           case F_SETOWN:
    408      1.1  christos           #endif
    409      1.1  christos           #ifdef F_SETPIPE_SZ                    /* Linux */
    410      1.1  christos           case F_SETPIPE_SZ:
    411      1.1  christos           #endif
    412      1.1  christos           #ifdef F_SETPROTECTIONCLASS            /* macOS */
    413      1.1  christos           case F_SETPROTECTIONCLASS:
    414      1.1  christos           #endif
    415      1.1  christos           #ifdef F_SETSIG                        /* Linux */
    416      1.1  christos           case F_SETSIG:
    417      1.1  christos           #endif
    418      1.1  christos           #ifdef F_SINGLE_WRITER                 /* macOS */
    419      1.1  christos           case F_SINGLE_WRITER:
    420      1.1  christos           #endif
    421      1.1  christos             /* These actions take an 'int' argument.  */
    422      1.1  christos             {
    423      1.1  christos               int x = va_arg (arg, int);
    424      1.1  christos               result = fcntl (fd, action, x);
    425      1.1  christos             }
    426      1.1  christos             break;
    427      1.1  christos 
    428      1.1  christos           default:
    429      1.1  christos             /* Other actions take a pointer argument.  */
    430      1.1  christos             {
    431      1.1  christos               void *p = va_arg (arg, void *);
    432      1.1  christos               result = fcntl (fd, action, p);
    433      1.1  christos             }
    434      1.1  christos             break;
    435      1.1  christos           }
    436      1.1  christos #else
    437      1.1  christos         errno = EINVAL;
    438      1.1  christos #endif
    439      1.1  christos         break;
    440      1.1  christos       }
    441      1.1  christos     }
    442      1.1  christos   va_end (arg);
    443      1.1  christos   return result;
    444      1.1  christos }
    445      1.1  christos 
    446      1.1  christos static int
    447      1.1  christos rpl_fcntl_DUPFD (int fd, int target)
    448      1.1  christos {
    449      1.1  christos   int result;
    450      1.1  christos #if !HAVE_FCNTL
    451      1.1  christos   result = dupfd (fd, target, 0);
    452      1.1  christos #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
    453      1.1  christos   /* Detect invalid target; needed for cygwin 1.5.x.  */
    454      1.1  christos   if (target < 0 || getdtablesize () <= target)
    455      1.1  christos     {
    456      1.1  christos       result = -1;
    457      1.1  christos       errno = EINVAL;
    458      1.1  christos     }
    459      1.1  christos   else
    460      1.1  christos     {
    461      1.1  christos       /* Haiku alpha 2 loses fd flags on original.  */
    462      1.1  christos       int flags = fcntl (fd, F_GETFD);
    463      1.1  christos       if (flags < 0)
    464      1.1  christos         result = -1;
    465      1.1  christos       else
    466      1.1  christos         {
    467      1.1  christos           result = fcntl (fd, F_DUPFD, target);
    468      1.1  christos           if (0 <= result && fcntl (fd, F_SETFD, flags) == -1)
    469      1.1  christos             {
    470      1.1  christos               int saved_errno = errno;
    471      1.1  christos               close (result);
    472      1.1  christos               result = -1;
    473      1.1  christos               errno = saved_errno;
    474      1.1  christos             }
    475      1.1  christos # if REPLACE_FCHDIR
    476      1.1  christos           if (0 <= result)
    477      1.1  christos             result = _gl_register_dup (fd, result);
    478      1.1  christos # endif
    479      1.1  christos         }
    480      1.1  christos     }
    481      1.1  christos #else
    482      1.1  christos   result = fcntl (fd, F_DUPFD, target);
    483      1.1  christos #endif
    484      1.1  christos   return result;
    485      1.1  christos }
    486      1.1  christos 
    487      1.1  christos static int
    488      1.1  christos rpl_fcntl_DUPFD_CLOEXEC (int fd, int target)
    489      1.1  christos {
    490      1.1  christos   int result;
    491      1.1  christos #if !HAVE_FCNTL
    492      1.1  christos   result = dupfd (fd, target, O_CLOEXEC);
    493      1.1  christos #else /* HAVE_FCNTL */
    494  1.1.1.2  christos # if defined __NetBSD__ || defined __HAIKU__
    495  1.1.1.2  christos   /* On NetBSD 9.0, the system fcntl (fd, F_DUPFD_CLOEXEC, target)
    496  1.1.1.2  christos      has only the same effect as fcntl (fd, F_DUPFD, target).  */
    497      1.1  christos   /* On Haiku, the system fcntl (fd, F_DUPFD_CLOEXEC, target) sets
    498      1.1  christos      the FD_CLOEXEC flag on fd, not on target.  Therefore avoid the
    499      1.1  christos      system fcntl in this case.  */
    500      1.1  christos #  define have_dupfd_cloexec -1
    501      1.1  christos # else
    502      1.1  christos   /* Try the system call first, if the headers claim it exists
    503      1.1  christos      (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
    504      1.1  christos      may be running with a glibc that has the macro but with an
    505      1.1  christos      older kernel that does not support it.  Cache the
    506      1.1  christos      information on whether the system call really works, but
    507      1.1  christos      avoid caching failure if the corresponding F_DUPFD fails
    508      1.1  christos      for any reason.  0 = unknown, 1 = yes, -1 = no.  */
    509      1.1  christos   static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
    510      1.1  christos   if (0 <= have_dupfd_cloexec)
    511      1.1  christos     {
    512      1.1  christos       result = fcntl (fd, F_DUPFD_CLOEXEC, target);
    513      1.1  christos       if (0 <= result || errno != EINVAL)
    514      1.1  christos         {
    515      1.1  christos           have_dupfd_cloexec = 1;
    516      1.1  christos #  if REPLACE_FCHDIR
    517      1.1  christos           if (0 <= result)
    518      1.1  christos             result = _gl_register_dup (fd, result);
    519      1.1  christos #  endif
    520      1.1  christos         }
    521      1.1  christos       else
    522      1.1  christos         {
    523      1.1  christos           result = rpl_fcntl_DUPFD (fd, target);
    524      1.1  christos           if (result >= 0)
    525      1.1  christos             have_dupfd_cloexec = -1;
    526      1.1  christos         }
    527      1.1  christos     }
    528      1.1  christos   else
    529      1.1  christos # endif
    530      1.1  christos     result = rpl_fcntl_DUPFD (fd, target);
    531      1.1  christos   if (0 <= result && have_dupfd_cloexec == -1)
    532      1.1  christos     {
    533      1.1  christos       int flags = fcntl (result, F_GETFD);
    534      1.1  christos       if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
    535      1.1  christos         {
    536      1.1  christos           int saved_errno = errno;
    537      1.1  christos           close (result);
    538      1.1  christos           errno = saved_errno;
    539      1.1  christos           result = -1;
    540      1.1  christos         }
    541      1.1  christos     }
    542      1.1  christos #endif /* HAVE_FCNTL */
    543      1.1  christos   return result;
    544      1.1  christos }
    545      1.1  christos 
    546      1.1  christos #undef fcntl
    547      1.1  christos 
    548      1.1  christos #ifdef __KLIBC__
    549      1.1  christos 
    550      1.1  christos static int
    551      1.1  christos klibc_fcntl (int fd, int action, /* arg */...)
    552      1.1  christos {
    553      1.1  christos   va_list arg_ptr;
    554      1.1  christos   int arg;
    555      1.1  christos   struct stat sbuf;
    556      1.1  christos   int result;
    557      1.1  christos 
    558      1.1  christos   va_start (arg_ptr, action);
    559      1.1  christos   arg = va_arg (arg_ptr, int);
    560      1.1  christos   result = fcntl (fd, action, arg);
    561      1.1  christos   /* EPERM for F_DUPFD, ENOTSUP for others */
    562      1.1  christos   if (result == -1 && (errno == EPERM || errno == ENOTSUP)
    563      1.1  christos       && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
    564      1.1  christos     {
    565      1.1  christos       ULONG ulMode;
    566      1.1  christos 
    567      1.1  christos       switch (action)
    568      1.1  christos         {
    569      1.1  christos         case F_DUPFD:
    570      1.1  christos           /* Find available fd */
    571      1.1  christos           while (fcntl (arg, F_GETFL) != -1 || errno != EBADF)
    572      1.1  christos             arg++;
    573      1.1  christos 
    574      1.1  christos           result = dup2 (fd, arg);
    575      1.1  christos           break;
    576      1.1  christos 
    577      1.1  christos         /* Using underlying APIs is right ? */
    578      1.1  christos         case F_GETFD:
    579      1.1  christos           if (DosQueryFHState (fd, &ulMode))
    580      1.1  christos             break;
    581      1.1  christos 
    582      1.1  christos           result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0;
    583      1.1  christos           break;
    584      1.1  christos 
    585      1.1  christos         case F_SETFD:
    586      1.1  christos           if (arg & ~FD_CLOEXEC)
    587      1.1  christos             break;
    588      1.1  christos 
    589      1.1  christos           if (DosQueryFHState (fd, &ulMode))
    590      1.1  christos             break;
    591      1.1  christos 
    592      1.1  christos           if (arg & FD_CLOEXEC)
    593      1.1  christos             ulMode |= OPEN_FLAGS_NOINHERIT;
    594      1.1  christos           else
    595      1.1  christos             ulMode &= ~OPEN_FLAGS_NOINHERIT;
    596      1.1  christos 
    597      1.1  christos           /* Filter supported flags.  */
    598      1.1  christos           ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR
    599      1.1  christos                      | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT);
    600      1.1  christos 
    601      1.1  christos           if (DosSetFHState (fd, ulMode))
    602      1.1  christos             break;
    603      1.1  christos 
    604      1.1  christos           result = 0;
    605      1.1  christos           break;
    606      1.1  christos 
    607      1.1  christos         case F_GETFL:
    608      1.1  christos           result = 0;
    609      1.1  christos           break;
    610      1.1  christos 
    611      1.1  christos         case F_SETFL:
    612      1.1  christos           if (arg != 0)
    613      1.1  christos             break;
    614      1.1  christos 
    615      1.1  christos           result = 0;
    616      1.1  christos           break;
    617      1.1  christos 
    618      1.1  christos         default:
    619      1.1  christos           errno = EINVAL;
    620      1.1  christos           break;
    621      1.1  christos         }
    622      1.1  christos     }
    623      1.1  christos 
    624      1.1  christos   va_end (arg_ptr);
    625      1.1  christos 
    626      1.1  christos   return result;
    627      1.1  christos }
    628      1.1  christos 
    629      1.1  christos #endif
    630