xref: /netbsd-src/external/gpl3/gdb/dist/gnulib/import/fcntl.c (revision 4b169a6ba595ae283ca507b26b15fdff40495b1c)
18dffb485Schristos /* Provide file descriptor control.
28dffb485Schristos 
3*4b169a6bSchristos    Copyright (C) 2009-2022 Free Software Foundation, Inc.
48dffb485Schristos 
5*4b169a6bSchristos    This file is free software: you can redistribute it and/or modify
6*4b169a6bSchristos    it under the terms of the GNU Lesser General Public License as
7*4b169a6bSchristos    published by the Free Software Foundation; either version 2.1 of the
8*4b169a6bSchristos    License, or (at your option) any later version.
98dffb485Schristos 
10*4b169a6bSchristos    This file is distributed in the hope that it will be useful,
118dffb485Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
128dffb485Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*4b169a6bSchristos    GNU Lesser General Public License for more details.
148dffb485Schristos 
15*4b169a6bSchristos    You should have received a copy of the GNU Lesser General Public License
168dffb485Schristos    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
178dffb485Schristos 
188dffb485Schristos /* Written by Eric Blake <ebb9@byu.net>.  */
198dffb485Schristos 
208dffb485Schristos #include <config.h>
218dffb485Schristos 
228dffb485Schristos /* Specification.  */
238dffb485Schristos #include <fcntl.h>
248dffb485Schristos 
258dffb485Schristos #include <errno.h>
268dffb485Schristos #include <limits.h>
278dffb485Schristos #include <stdarg.h>
288dffb485Schristos #include <stdlib.h>
298dffb485Schristos #include <unistd.h>
308dffb485Schristos 
318dffb485Schristos #ifdef __KLIBC__
328dffb485Schristos # define INCL_DOS
338dffb485Schristos # include <os2.h>
348dffb485Schristos #endif
358dffb485Schristos 
368dffb485Schristos #if defined _WIN32 && ! defined __CYGWIN__
378dffb485Schristos /* Get declarations of the native Windows API functions.  */
388dffb485Schristos # define WIN32_LEAN_AND_MEAN
398dffb485Schristos # include <windows.h>
408dffb485Schristos 
418dffb485Schristos /* Get _get_osfhandle.  */
428dffb485Schristos # if GNULIB_MSVC_NOTHROW
438dffb485Schristos #  include "msvc-nothrow.h"
448dffb485Schristos # else
458dffb485Schristos #  include <io.h>
468dffb485Schristos # endif
478dffb485Schristos 
488dffb485Schristos /* Upper bound on getdtablesize().  See lib/getdtablesize.c.  */
498dffb485Schristos # define OPEN_MAX_MAX 0x10000
508dffb485Schristos 
518dffb485Schristos /* Duplicate OLDFD into the first available slot of at least NEWFD,
528dffb485Schristos    which must be positive, with FLAGS determining whether the duplicate
538dffb485Schristos    will be inheritable.  */
548dffb485Schristos static int
dupfd(int oldfd,int newfd,int flags)558dffb485Schristos dupfd (int oldfd, int newfd, int flags)
568dffb485Schristos {
578dffb485Schristos   /* Mingw has no way to create an arbitrary fd.  Iterate until all
588dffb485Schristos      file descriptors less than newfd are filled up.  */
598dffb485Schristos   HANDLE curr_process = GetCurrentProcess ();
608dffb485Schristos   HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
618dffb485Schristos   unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
628dffb485Schristos   unsigned int fds_to_close_bound = 0;
638dffb485Schristos   int result;
648dffb485Schristos   BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE;
658dffb485Schristos   int mode;
668dffb485Schristos 
678dffb485Schristos   if (newfd < 0 || getdtablesize () <= newfd)
688dffb485Schristos     {
698dffb485Schristos       errno = EINVAL;
708dffb485Schristos       return -1;
718dffb485Schristos     }
728dffb485Schristos   if (old_handle == INVALID_HANDLE_VALUE
73*4b169a6bSchristos       || (mode = _setmode (oldfd, O_BINARY)) == -1)
748dffb485Schristos     {
758dffb485Schristos       /* oldfd is not open, or is an unassigned standard file
768dffb485Schristos          descriptor.  */
778dffb485Schristos       errno = EBADF;
788dffb485Schristos       return -1;
798dffb485Schristos     }
80*4b169a6bSchristos   _setmode (oldfd, mode);
818dffb485Schristos   flags |= mode;
828dffb485Schristos 
838dffb485Schristos   for (;;)
848dffb485Schristos     {
858dffb485Schristos       HANDLE new_handle;
868dffb485Schristos       int duplicated_fd;
878dffb485Schristos       unsigned int index;
888dffb485Schristos 
898dffb485Schristos       if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
908dffb485Schristos                             old_handle,             /* SourceHandle */
918dffb485Schristos                             curr_process,           /* TargetProcessHandle */
928dffb485Schristos                             (PHANDLE) &new_handle,  /* TargetHandle */
938dffb485Schristos                             (DWORD) 0,              /* DesiredAccess */
948dffb485Schristos                             inherit,                /* InheritHandle */
958dffb485Schristos                             DUPLICATE_SAME_ACCESS)) /* Options */
968dffb485Schristos         {
978dffb485Schristos           switch (GetLastError ())
988dffb485Schristos             {
998dffb485Schristos               case ERROR_TOO_MANY_OPEN_FILES:
1008dffb485Schristos                 errno = EMFILE;
1018dffb485Schristos                 break;
1028dffb485Schristos               case ERROR_INVALID_HANDLE:
1038dffb485Schristos               case ERROR_INVALID_TARGET_HANDLE:
1048dffb485Schristos               case ERROR_DIRECT_ACCESS_HANDLE:
1058dffb485Schristos                 errno = EBADF;
1068dffb485Schristos                 break;
1078dffb485Schristos               case ERROR_INVALID_PARAMETER:
1088dffb485Schristos               case ERROR_INVALID_FUNCTION:
1098dffb485Schristos               case ERROR_INVALID_ACCESS:
1108dffb485Schristos                 errno = EINVAL;
1118dffb485Schristos                 break;
1128dffb485Schristos               default:
1138dffb485Schristos                 errno = EACCES;
1148dffb485Schristos                 break;
1158dffb485Schristos             }
1168dffb485Schristos           result = -1;
1178dffb485Schristos           break;
1188dffb485Schristos         }
1198dffb485Schristos       duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags);
1208dffb485Schristos       if (duplicated_fd < 0)
1218dffb485Schristos         {
1228dffb485Schristos           CloseHandle (new_handle);
1238dffb485Schristos           result = -1;
1248dffb485Schristos           break;
1258dffb485Schristos         }
1268dffb485Schristos       if (newfd <= duplicated_fd)
1278dffb485Schristos         {
1288dffb485Schristos           result = duplicated_fd;
1298dffb485Schristos           break;
1308dffb485Schristos         }
1318dffb485Schristos 
1328dffb485Schristos       /* Set the bit duplicated_fd in fds_to_close[].  */
1338dffb485Schristos       index = (unsigned int) duplicated_fd / CHAR_BIT;
1348dffb485Schristos       if (fds_to_close_bound <= index)
1358dffb485Schristos         {
1368dffb485Schristos           if (sizeof fds_to_close <= index)
1378dffb485Schristos             /* Need to increase OPEN_MAX_MAX.  */
1388dffb485Schristos             abort ();
1398dffb485Schristos           memset (fds_to_close + fds_to_close_bound, '\0',
1408dffb485Schristos                   index + 1 - fds_to_close_bound);
1418dffb485Schristos           fds_to_close_bound = index + 1;
1428dffb485Schristos         }
1438dffb485Schristos       fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
1448dffb485Schristos     }
1458dffb485Schristos 
1468dffb485Schristos   /* Close the previous fds that turned out to be too small.  */
1478dffb485Schristos   {
1488dffb485Schristos     int saved_errno = errno;
1498dffb485Schristos     unsigned int duplicated_fd;
1508dffb485Schristos 
1518dffb485Schristos     for (duplicated_fd = 0;
1528dffb485Schristos          duplicated_fd < fds_to_close_bound * CHAR_BIT;
1538dffb485Schristos          duplicated_fd++)
1548dffb485Schristos       if ((fds_to_close[duplicated_fd / CHAR_BIT]
1558dffb485Schristos            >> (duplicated_fd % CHAR_BIT))
1568dffb485Schristos           & 1)
1578dffb485Schristos         close (duplicated_fd);
1588dffb485Schristos 
1598dffb485Schristos     errno = saved_errno;
1608dffb485Schristos   }
1618dffb485Schristos 
1628dffb485Schristos # if REPLACE_FCHDIR
1638dffb485Schristos   if (0 <= result)
1648dffb485Schristos     result = _gl_register_dup (oldfd, result);
1658dffb485Schristos # endif
1668dffb485Schristos   return result;
1678dffb485Schristos }
1688dffb485Schristos #endif /* W32 */
1698dffb485Schristos 
1708dffb485Schristos /* Forward declarations, because we '#undef fcntl' in the middle of this
1718dffb485Schristos    compilation unit.  */
1728dffb485Schristos /* Our implementation of fcntl (fd, F_DUPFD, target).  */
1738dffb485Schristos static int rpl_fcntl_DUPFD (int fd, int target);
1748dffb485Schristos /* Our implementation of fcntl (fd, F_DUPFD_CLOEXEC, target).  */
1758dffb485Schristos static int rpl_fcntl_DUPFD_CLOEXEC (int fd, int target);
1768dffb485Schristos #ifdef __KLIBC__
1778dffb485Schristos /* Adds support for fcntl on directories.  */
1788dffb485Schristos static int klibc_fcntl (int fd, int action, /* arg */...);
1798dffb485Schristos #endif
1808dffb485Schristos 
1818dffb485Schristos 
1828dffb485Schristos /* Perform the specified ACTION on the file descriptor FD, possibly
1838dffb485Schristos    using the argument ARG further described below.  This replacement
1848dffb485Schristos    handles the following actions, and forwards all others on to the
1858dffb485Schristos    native fcntl.  An unrecognized ACTION returns -1 with errno set to
1868dffb485Schristos    EINVAL.
1878dffb485Schristos 
1888dffb485Schristos    F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
1898dffb485Schristos    If successful, return the duplicate, which will be inheritable;
1908dffb485Schristos    otherwise return -1 and set errno.
1918dffb485Schristos 
1928dffb485Schristos    F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
1938dffb485Schristos    target fd.  If successful, return the duplicate, which will not be
1948dffb485Schristos    inheritable; otherwise return -1 and set errno.
1958dffb485Schristos 
1968dffb485Schristos    F_GETFD - ARG need not be present.  If successful, return a
1978dffb485Schristos    non-negative value containing the descriptor flags of FD (only
1988dffb485Schristos    FD_CLOEXEC is portable, but other flags may be present); otherwise
1998dffb485Schristos    return -1 and set errno.  */
2008dffb485Schristos 
2018dffb485Schristos int
fcntl(int fd,int action,...)2028dffb485Schristos fcntl (int fd, int action, /* arg */...)
2038dffb485Schristos #undef fcntl
2048dffb485Schristos #ifdef __KLIBC__
2058dffb485Schristos # define fcntl klibc_fcntl
2068dffb485Schristos #endif
2078dffb485Schristos {
2088dffb485Schristos   va_list arg;
2098dffb485Schristos   int result = -1;
2108dffb485Schristos   va_start (arg, action);
2118dffb485Schristos   switch (action)
2128dffb485Schristos     {
2138dffb485Schristos     case F_DUPFD:
2148dffb485Schristos       {
2158dffb485Schristos         int target = va_arg (arg, int);
2168dffb485Schristos         result = rpl_fcntl_DUPFD (fd, target);
2178dffb485Schristos         break;
2188dffb485Schristos       }
2198dffb485Schristos 
2208dffb485Schristos     case F_DUPFD_CLOEXEC:
2218dffb485Schristos       {
2228dffb485Schristos         int target = va_arg (arg, int);
2238dffb485Schristos         result = rpl_fcntl_DUPFD_CLOEXEC (fd, target);
2248dffb485Schristos         break;
2258dffb485Schristos       }
2268dffb485Schristos 
2278dffb485Schristos #if !HAVE_FCNTL
2288dffb485Schristos     case F_GETFD:
2298dffb485Schristos       {
2308dffb485Schristos # if defined _WIN32 && ! defined __CYGWIN__
2318dffb485Schristos         HANDLE handle = (HANDLE) _get_osfhandle (fd);
2328dffb485Schristos         DWORD flags;
2338dffb485Schristos         if (handle == INVALID_HANDLE_VALUE
2348dffb485Schristos             || GetHandleInformation (handle, &flags) == 0)
2358dffb485Schristos           errno = EBADF;
2368dffb485Schristos         else
2378dffb485Schristos           result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
2388dffb485Schristos # else /* !W32 */
2398dffb485Schristos         /* Use dup2 to reject invalid file descriptors.  No way to
2408dffb485Schristos            access this information, so punt.  */
2418dffb485Schristos         if (0 <= dup2 (fd, fd))
2428dffb485Schristos           result = 0;
2438dffb485Schristos # endif /* !W32 */
2448dffb485Schristos         break;
2458dffb485Schristos       } /* F_GETFD */
2468dffb485Schristos #endif /* !HAVE_FCNTL */
2478dffb485Schristos 
2488dffb485Schristos       /* Implementing F_SETFD on mingw is not trivial - there is no
2498dffb485Schristos          API for changing the O_NOINHERIT bit on an fd, and merely
2508dffb485Schristos          changing the HANDLE_FLAG_INHERIT bit on the underlying handle
2518dffb485Schristos          can lead to odd state.  It may be possible by duplicating the
2528dffb485Schristos          handle, using _open_osfhandle with the right flags, then
2538dffb485Schristos          using dup2 to move the duplicate onto the original, but that
2548dffb485Schristos          is not supported for now.  */
2558dffb485Schristos 
2568dffb485Schristos     default:
2578dffb485Schristos       {
2588dffb485Schristos #if HAVE_FCNTL
2598dffb485Schristos         switch (action)
2608dffb485Schristos           {
2618dffb485Schristos           #ifdef F_BARRIERFSYNC                  /* macOS */
2628dffb485Schristos           case F_BARRIERFSYNC:
2638dffb485Schristos           #endif
2648dffb485Schristos           #ifdef F_CHKCLEAN                      /* macOS */
2658dffb485Schristos           case F_CHKCLEAN:
2668dffb485Schristos           #endif
2678dffb485Schristos           #ifdef F_CLOSEM                        /* NetBSD, HP-UX */
2688dffb485Schristos           case F_CLOSEM:
2698dffb485Schristos           #endif
2708dffb485Schristos           #ifdef F_FLUSH_DATA                    /* macOS */
2718dffb485Schristos           case F_FLUSH_DATA:
2728dffb485Schristos           #endif
2738dffb485Schristos           #ifdef F_FREEZE_FS                     /* macOS */
2748dffb485Schristos           case F_FREEZE_FS:
2758dffb485Schristos           #endif
2768dffb485Schristos           #ifdef F_FULLFSYNC                     /* macOS */
2778dffb485Schristos           case F_FULLFSYNC:
2788dffb485Schristos           #endif
2798dffb485Schristos           #ifdef F_GETCONFINED                   /* macOS */
2808dffb485Schristos           case F_GETCONFINED:
2818dffb485Schristos           #endif
2828dffb485Schristos           #ifdef F_GETDEFAULTPROTLEVEL           /* macOS */
2838dffb485Schristos           case F_GETDEFAULTPROTLEVEL:
2848dffb485Schristos           #endif
2858dffb485Schristos           #ifdef F_GETFD                         /* POSIX */
2868dffb485Schristos           case F_GETFD:
2878dffb485Schristos           #endif
2888dffb485Schristos           #ifdef F_GETFL                         /* POSIX */
2898dffb485Schristos           case F_GETFL:
2908dffb485Schristos           #endif
2918dffb485Schristos           #ifdef F_GETLEASE                      /* Linux */
2928dffb485Schristos           case F_GETLEASE:
2938dffb485Schristos           #endif
2948dffb485Schristos           #ifdef F_GETNOSIGPIPE                  /* macOS */
2958dffb485Schristos           case F_GETNOSIGPIPE:
2968dffb485Schristos           #endif
2978dffb485Schristos           #ifdef F_GETOWN                        /* POSIX */
2988dffb485Schristos           case F_GETOWN:
2998dffb485Schristos           #endif
3008dffb485Schristos           #ifdef F_GETPIPE_SZ                    /* Linux */
3018dffb485Schristos           case F_GETPIPE_SZ:
3028dffb485Schristos           #endif
3038dffb485Schristos           #ifdef F_GETPROTECTIONCLASS            /* macOS */
3048dffb485Schristos           case F_GETPROTECTIONCLASS:
3058dffb485Schristos           #endif
3068dffb485Schristos           #ifdef F_GETPROTECTIONLEVEL            /* macOS */
3078dffb485Schristos           case F_GETPROTECTIONLEVEL:
3088dffb485Schristos           #endif
3098dffb485Schristos           #ifdef F_GET_SEALS                     /* Linux */
3108dffb485Schristos           case F_GET_SEALS:
3118dffb485Schristos           #endif
3128dffb485Schristos           #ifdef F_GETSIG                        /* Linux */
3138dffb485Schristos           case F_GETSIG:
3148dffb485Schristos           #endif
3158dffb485Schristos           #ifdef F_MAXFD                         /* NetBSD */
3168dffb485Schristos           case F_MAXFD:
3178dffb485Schristos           #endif
3188dffb485Schristos           #ifdef F_RECYCLE                       /* macOS */
3198dffb485Schristos           case F_RECYCLE:
3208dffb485Schristos           #endif
3218dffb485Schristos           #ifdef F_SETFIFOENH                    /* HP-UX */
3228dffb485Schristos           case F_SETFIFOENH:
3238dffb485Schristos           #endif
3248dffb485Schristos           #ifdef F_THAW_FS                       /* macOS */
3258dffb485Schristos           case F_THAW_FS:
3268dffb485Schristos           #endif
3278dffb485Schristos             /* These actions take no argument.  */
3288dffb485Schristos             result = fcntl (fd, action);
3298dffb485Schristos             break;
3308dffb485Schristos 
3318dffb485Schristos           #ifdef F_ADD_SEALS                     /* Linux */
3328dffb485Schristos           case F_ADD_SEALS:
3338dffb485Schristos           #endif
3348dffb485Schristos           #ifdef F_BADFD                         /* Solaris */
3358dffb485Schristos           case F_BADFD:
3368dffb485Schristos           #endif
3378dffb485Schristos           #ifdef F_CHECK_OPENEVT                 /* macOS */
3388dffb485Schristos           case F_CHECK_OPENEVT:
3398dffb485Schristos           #endif
3408dffb485Schristos           #ifdef F_DUP2FD                        /* FreeBSD, AIX, Solaris */
3418dffb485Schristos           case F_DUP2FD:
3428dffb485Schristos           #endif
3438dffb485Schristos           #ifdef F_DUP2FD_CLOEXEC                /* FreeBSD, Solaris */
3448dffb485Schristos           case F_DUP2FD_CLOEXEC:
3458dffb485Schristos           #endif
3468dffb485Schristos           #ifdef F_DUP2FD_CLOFORK                /* Solaris */
3478dffb485Schristos           case F_DUP2FD_CLOFORK:
3488dffb485Schristos           #endif
3498dffb485Schristos           #ifdef F_DUPFD                         /* POSIX */
3508dffb485Schristos           case F_DUPFD:
3518dffb485Schristos           #endif
3528dffb485Schristos           #ifdef F_DUPFD_CLOEXEC                 /* POSIX */
3538dffb485Schristos           case F_DUPFD_CLOEXEC:
3548dffb485Schristos           #endif
3558dffb485Schristos           #ifdef F_DUPFD_CLOFORK                 /* Solaris */
3568dffb485Schristos           case F_DUPFD_CLOFORK:
3578dffb485Schristos           #endif
3588dffb485Schristos           #ifdef F_GETXFL                        /* Solaris */
3598dffb485Schristos           case F_GETXFL:
3608dffb485Schristos           #endif
3618dffb485Schristos           #ifdef F_GLOBAL_NOCACHE                /* macOS */
3628dffb485Schristos           case F_GLOBAL_NOCACHE:
3638dffb485Schristos           #endif
3648dffb485Schristos           #ifdef F_MAKECOMPRESSED                /* macOS */
3658dffb485Schristos           case F_MAKECOMPRESSED:
3668dffb485Schristos           #endif
3678dffb485Schristos           #ifdef F_MOVEDATAEXTENTS               /* macOS */
3688dffb485Schristos           case F_MOVEDATAEXTENTS:
3698dffb485Schristos           #endif
3708dffb485Schristos           #ifdef F_NOCACHE                       /* macOS */
3718dffb485Schristos           case F_NOCACHE:
3728dffb485Schristos           #endif
3738dffb485Schristos           #ifdef F_NODIRECT                      /* macOS */
3748dffb485Schristos           case F_NODIRECT:
3758dffb485Schristos           #endif
3768dffb485Schristos           #ifdef F_NOTIFY                        /* Linux */
3778dffb485Schristos           case F_NOTIFY:
3788dffb485Schristos           #endif
3798dffb485Schristos           #ifdef F_OPLKACK                       /* IRIX */
3808dffb485Schristos           case F_OPLKACK:
3818dffb485Schristos           #endif
3828dffb485Schristos           #ifdef F_OPLKREG                       /* IRIX */
3838dffb485Schristos           case F_OPLKREG:
3848dffb485Schristos           #endif
3858dffb485Schristos           #ifdef F_RDAHEAD                       /* macOS */
3868dffb485Schristos           case F_RDAHEAD:
3878dffb485Schristos           #endif
3888dffb485Schristos           #ifdef F_SETBACKINGSTORE               /* macOS */
3898dffb485Schristos           case F_SETBACKINGSTORE:
3908dffb485Schristos           #endif
3918dffb485Schristos           #ifdef F_SETCONFINED                   /* macOS */
3928dffb485Schristos           case F_SETCONFINED:
3938dffb485Schristos           #endif
3948dffb485Schristos           #ifdef F_SETFD                         /* POSIX */
3958dffb485Schristos           case F_SETFD:
3968dffb485Schristos           #endif
3978dffb485Schristos           #ifdef F_SETFL                         /* POSIX */
3988dffb485Schristos           case F_SETFL:
3998dffb485Schristos           #endif
4008dffb485Schristos           #ifdef F_SETLEASE                      /* Linux */
4018dffb485Schristos           case F_SETLEASE:
4028dffb485Schristos           #endif
4038dffb485Schristos           #ifdef F_SETNOSIGPIPE                  /* macOS */
4048dffb485Schristos           case F_SETNOSIGPIPE:
4058dffb485Schristos           #endif
4068dffb485Schristos           #ifdef F_SETOWN                        /* POSIX */
4078dffb485Schristos           case F_SETOWN:
4088dffb485Schristos           #endif
4098dffb485Schristos           #ifdef F_SETPIPE_SZ                    /* Linux */
4108dffb485Schristos           case F_SETPIPE_SZ:
4118dffb485Schristos           #endif
4128dffb485Schristos           #ifdef F_SETPROTECTIONCLASS            /* macOS */
4138dffb485Schristos           case F_SETPROTECTIONCLASS:
4148dffb485Schristos           #endif
4158dffb485Schristos           #ifdef F_SETSIG                        /* Linux */
4168dffb485Schristos           case F_SETSIG:
4178dffb485Schristos           #endif
4188dffb485Schristos           #ifdef F_SINGLE_WRITER                 /* macOS */
4198dffb485Schristos           case F_SINGLE_WRITER:
4208dffb485Schristos           #endif
4218dffb485Schristos             /* These actions take an 'int' argument.  */
4228dffb485Schristos             {
4238dffb485Schristos               int x = va_arg (arg, int);
4248dffb485Schristos               result = fcntl (fd, action, x);
4258dffb485Schristos             }
4268dffb485Schristos             break;
4278dffb485Schristos 
4288dffb485Schristos           default:
4298dffb485Schristos             /* Other actions take a pointer argument.  */
4308dffb485Schristos             {
4318dffb485Schristos               void *p = va_arg (arg, void *);
4328dffb485Schristos               result = fcntl (fd, action, p);
4338dffb485Schristos             }
4348dffb485Schristos             break;
4358dffb485Schristos           }
4368dffb485Schristos #else
4378dffb485Schristos         errno = EINVAL;
4388dffb485Schristos #endif
4398dffb485Schristos         break;
4408dffb485Schristos       }
4418dffb485Schristos     }
4428dffb485Schristos   va_end (arg);
4438dffb485Schristos   return result;
4448dffb485Schristos }
4458dffb485Schristos 
4468dffb485Schristos static int
rpl_fcntl_DUPFD(int fd,int target)4478dffb485Schristos rpl_fcntl_DUPFD (int fd, int target)
4488dffb485Schristos {
4498dffb485Schristos   int result;
4508dffb485Schristos #if !HAVE_FCNTL
4518dffb485Schristos   result = dupfd (fd, target, 0);
4528dffb485Schristos #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
4538dffb485Schristos   /* Detect invalid target; needed for cygwin 1.5.x.  */
4548dffb485Schristos   if (target < 0 || getdtablesize () <= target)
4558dffb485Schristos     {
4568dffb485Schristos       result = -1;
4578dffb485Schristos       errno = EINVAL;
4588dffb485Schristos     }
4598dffb485Schristos   else
4608dffb485Schristos     {
4618dffb485Schristos       /* Haiku alpha 2 loses fd flags on original.  */
4628dffb485Schristos       int flags = fcntl (fd, F_GETFD);
4638dffb485Schristos       if (flags < 0)
4648dffb485Schristos         result = -1;
4658dffb485Schristos       else
4668dffb485Schristos         {
4678dffb485Schristos           result = fcntl (fd, F_DUPFD, target);
4688dffb485Schristos           if (0 <= result && fcntl (fd, F_SETFD, flags) == -1)
4698dffb485Schristos             {
4708dffb485Schristos               int saved_errno = errno;
4718dffb485Schristos               close (result);
4728dffb485Schristos               result = -1;
4738dffb485Schristos               errno = saved_errno;
4748dffb485Schristos             }
4758dffb485Schristos # if REPLACE_FCHDIR
4768dffb485Schristos           if (0 <= result)
4778dffb485Schristos             result = _gl_register_dup (fd, result);
4788dffb485Schristos # endif
4798dffb485Schristos         }
4808dffb485Schristos     }
4818dffb485Schristos #else
4828dffb485Schristos   result = fcntl (fd, F_DUPFD, target);
4838dffb485Schristos #endif
4848dffb485Schristos   return result;
4858dffb485Schristos }
4868dffb485Schristos 
4878dffb485Schristos static int
rpl_fcntl_DUPFD_CLOEXEC(int fd,int target)4888dffb485Schristos rpl_fcntl_DUPFD_CLOEXEC (int fd, int target)
4898dffb485Schristos {
4908dffb485Schristos   int result;
4918dffb485Schristos #if !HAVE_FCNTL
4928dffb485Schristos   result = dupfd (fd, target, O_CLOEXEC);
4938dffb485Schristos #else /* HAVE_FCNTL */
494*4b169a6bSchristos # if defined __NetBSD__ || defined __HAIKU__
495*4b169a6bSchristos   /* On NetBSD 9.0, the system fcntl (fd, F_DUPFD_CLOEXEC, target)
496*4b169a6bSchristos      has only the same effect as fcntl (fd, F_DUPFD, target).  */
4978dffb485Schristos   /* On Haiku, the system fcntl (fd, F_DUPFD_CLOEXEC, target) sets
4988dffb485Schristos      the FD_CLOEXEC flag on fd, not on target.  Therefore avoid the
4998dffb485Schristos      system fcntl in this case.  */
5008dffb485Schristos #  define have_dupfd_cloexec -1
5018dffb485Schristos # else
5028dffb485Schristos   /* Try the system call first, if the headers claim it exists
5038dffb485Schristos      (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
5048dffb485Schristos      may be running with a glibc that has the macro but with an
5058dffb485Schristos      older kernel that does not support it.  Cache the
5068dffb485Schristos      information on whether the system call really works, but
5078dffb485Schristos      avoid caching failure if the corresponding F_DUPFD fails
5088dffb485Schristos      for any reason.  0 = unknown, 1 = yes, -1 = no.  */
5098dffb485Schristos   static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
5108dffb485Schristos   if (0 <= have_dupfd_cloexec)
5118dffb485Schristos     {
5128dffb485Schristos       result = fcntl (fd, F_DUPFD_CLOEXEC, target);
5138dffb485Schristos       if (0 <= result || errno != EINVAL)
5148dffb485Schristos         {
5158dffb485Schristos           have_dupfd_cloexec = 1;
5168dffb485Schristos #  if REPLACE_FCHDIR
5178dffb485Schristos           if (0 <= result)
5188dffb485Schristos             result = _gl_register_dup (fd, result);
5198dffb485Schristos #  endif
5208dffb485Schristos         }
5218dffb485Schristos       else
5228dffb485Schristos         {
5238dffb485Schristos           result = rpl_fcntl_DUPFD (fd, target);
5248dffb485Schristos           if (result >= 0)
5258dffb485Schristos             have_dupfd_cloexec = -1;
5268dffb485Schristos         }
5278dffb485Schristos     }
5288dffb485Schristos   else
5298dffb485Schristos # endif
5308dffb485Schristos     result = rpl_fcntl_DUPFD (fd, target);
5318dffb485Schristos   if (0 <= result && have_dupfd_cloexec == -1)
5328dffb485Schristos     {
5338dffb485Schristos       int flags = fcntl (result, F_GETFD);
5348dffb485Schristos       if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
5358dffb485Schristos         {
5368dffb485Schristos           int saved_errno = errno;
5378dffb485Schristos           close (result);
5388dffb485Schristos           errno = saved_errno;
5398dffb485Schristos           result = -1;
5408dffb485Schristos         }
5418dffb485Schristos     }
5428dffb485Schristos #endif /* HAVE_FCNTL */
5438dffb485Schristos   return result;
5448dffb485Schristos }
5458dffb485Schristos 
5468dffb485Schristos #undef fcntl
5478dffb485Schristos 
5488dffb485Schristos #ifdef __KLIBC__
5498dffb485Schristos 
5508dffb485Schristos static int
klibc_fcntl(int fd,int action,...)5518dffb485Schristos klibc_fcntl (int fd, int action, /* arg */...)
5528dffb485Schristos {
5538dffb485Schristos   va_list arg_ptr;
5548dffb485Schristos   int arg;
5558dffb485Schristos   struct stat sbuf;
5568dffb485Schristos   int result;
5578dffb485Schristos 
5588dffb485Schristos   va_start (arg_ptr, action);
5598dffb485Schristos   arg = va_arg (arg_ptr, int);
5608dffb485Schristos   result = fcntl (fd, action, arg);
5618dffb485Schristos   /* EPERM for F_DUPFD, ENOTSUP for others */
5628dffb485Schristos   if (result == -1 && (errno == EPERM || errno == ENOTSUP)
5638dffb485Schristos       && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
5648dffb485Schristos     {
5658dffb485Schristos       ULONG ulMode;
5668dffb485Schristos 
5678dffb485Schristos       switch (action)
5688dffb485Schristos         {
5698dffb485Schristos         case F_DUPFD:
5708dffb485Schristos           /* Find available fd */
5718dffb485Schristos           while (fcntl (arg, F_GETFL) != -1 || errno != EBADF)
5728dffb485Schristos             arg++;
5738dffb485Schristos 
5748dffb485Schristos           result = dup2 (fd, arg);
5758dffb485Schristos           break;
5768dffb485Schristos 
5778dffb485Schristos         /* Using underlying APIs is right ? */
5788dffb485Schristos         case F_GETFD:
5798dffb485Schristos           if (DosQueryFHState (fd, &ulMode))
5808dffb485Schristos             break;
5818dffb485Schristos 
5828dffb485Schristos           result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0;
5838dffb485Schristos           break;
5848dffb485Schristos 
5858dffb485Schristos         case F_SETFD:
5868dffb485Schristos           if (arg & ~FD_CLOEXEC)
5878dffb485Schristos             break;
5888dffb485Schristos 
5898dffb485Schristos           if (DosQueryFHState (fd, &ulMode))
5908dffb485Schristos             break;
5918dffb485Schristos 
5928dffb485Schristos           if (arg & FD_CLOEXEC)
5938dffb485Schristos             ulMode |= OPEN_FLAGS_NOINHERIT;
5948dffb485Schristos           else
5958dffb485Schristos             ulMode &= ~OPEN_FLAGS_NOINHERIT;
5968dffb485Schristos 
5978dffb485Schristos           /* Filter supported flags.  */
5988dffb485Schristos           ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR
5998dffb485Schristos                      | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT);
6008dffb485Schristos 
6018dffb485Schristos           if (DosSetFHState (fd, ulMode))
6028dffb485Schristos             break;
6038dffb485Schristos 
6048dffb485Schristos           result = 0;
6058dffb485Schristos           break;
6068dffb485Schristos 
6078dffb485Schristos         case F_GETFL:
6088dffb485Schristos           result = 0;
6098dffb485Schristos           break;
6108dffb485Schristos 
6118dffb485Schristos         case F_SETFL:
6128dffb485Schristos           if (arg != 0)
6138dffb485Schristos             break;
6148dffb485Schristos 
6158dffb485Schristos           result = 0;
6168dffb485Schristos           break;
6178dffb485Schristos 
6188dffb485Schristos         default:
6198dffb485Schristos           errno = EINVAL;
6208dffb485Schristos           break;
6218dffb485Schristos         }
6228dffb485Schristos     }
6238dffb485Schristos 
6248dffb485Schristos   va_end (arg_ptr);
6258dffb485Schristos 
6268dffb485Schristos   return result;
6278dffb485Schristos }
6288dffb485Schristos 
6298dffb485Schristos #endif
630