root/lib/fcntl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dupfd
  2. rpl_fcntl_DUPFD
  3. rpl_fcntl_DUPFD_CLOEXEC
  4. klibc_fcntl

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

/* [<][>][^][v][top][bottom][index][help] */