(root)/
tar-1.35/
gnu/
fcntl.c
       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