Blame lib/fcntl.c

Packit 8f70b4
/* Provide file descriptor control.
Packit 8f70b4
Packit 8f70b4
   Copyright (C) 2009-2018 Free Software Foundation, Inc.
Packit 8f70b4
Packit 8f70b4
   This program is free software: you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3 of the License, or
Packit 8f70b4
   (at your option) any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License
Packit 8f70b4
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
/* Written by Eric Blake <ebb9@byu.net>.  */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
Packit 8f70b4
/* Specification.  */
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <limits.h>
Packit 8f70b4
#include <stdarg.h>
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
Packit 8f70b4
#if !HAVE_FCNTL
Packit 8f70b4
# define rpl_fcntl fcntl
Packit 8f70b4
#endif
Packit 8f70b4
#undef fcntl
Packit 8f70b4
Packit 8f70b4
#if defined _WIN32 && ! defined __CYGWIN__
Packit 8f70b4
/* Get declarations of the native Windows API functions.  */
Packit 8f70b4
# define WIN32_LEAN_AND_MEAN
Packit 8f70b4
# include <windows.h>
Packit 8f70b4
Packit 8f70b4
/* Get _get_osfhandle.  */
Packit 8f70b4
# if GNULIB_MSVC_NOTHROW
Packit 8f70b4
#  include "msvc-nothrow.h"
Packit 8f70b4
# else
Packit 8f70b4
#  include <io.h>
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
/* Upper bound on getdtablesize().  See lib/getdtablesize.c.  */
Packit 8f70b4
# define OPEN_MAX_MAX 0x10000
Packit 8f70b4
Packit 8f70b4
/* Duplicate OLDFD into the first available slot of at least NEWFD,
Packit 8f70b4
   which must be positive, with FLAGS determining whether the duplicate
Packit 8f70b4
   will be inheritable.  */
Packit 8f70b4
static int
Packit 8f70b4
dupfd (int oldfd, int newfd, int flags)
Packit 8f70b4
{
Packit 8f70b4
  /* Mingw has no way to create an arbitrary fd.  Iterate until all
Packit 8f70b4
     file descriptors less than newfd are filled up.  */
Packit 8f70b4
  HANDLE curr_process = GetCurrentProcess ();
Packit 8f70b4
  HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
Packit 8f70b4
  unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
Packit 8f70b4
  unsigned int fds_to_close_bound = 0;
Packit 8f70b4
  int result;
Packit 8f70b4
  BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE;
Packit 8f70b4
  int mode;
Packit 8f70b4
Packit 8f70b4
  if (newfd < 0 || getdtablesize () <= newfd)
Packit 8f70b4
    {
Packit 8f70b4
      errno = EINVAL;
Packit 8f70b4
      return -1;
Packit 8f70b4
    }
Packit 8f70b4
  if (old_handle == INVALID_HANDLE_VALUE
Packit 8f70b4
      || (mode = setmode (oldfd, O_BINARY)) == -1)
Packit 8f70b4
    {
Packit 8f70b4
      /* oldfd is not open, or is an unassigned standard file
Packit 8f70b4
         descriptor.  */
Packit 8f70b4
      errno = EBADF;
Packit 8f70b4
      return -1;
Packit 8f70b4
    }
Packit 8f70b4
  setmode (oldfd, mode);
Packit 8f70b4
  flags |= mode;
Packit 8f70b4
Packit 8f70b4
  for (;;)
Packit 8f70b4
    {
Packit 8f70b4
      HANDLE new_handle;
Packit 8f70b4
      int duplicated_fd;
Packit 8f70b4
      unsigned int index;
Packit 8f70b4
Packit 8f70b4
      if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
Packit 8f70b4
                            old_handle,             /* SourceHandle */
Packit 8f70b4
                            curr_process,           /* TargetProcessHandle */
Packit 8f70b4
                            (PHANDLE) &new_handle,  /* TargetHandle */
Packit 8f70b4
                            (DWORD) 0,              /* DesiredAccess */
Packit 8f70b4
                            inherit,                /* InheritHandle */
Packit 8f70b4
                            DUPLICATE_SAME_ACCESS)) /* Options */
Packit 8f70b4
        {
Packit 8f70b4
          switch (GetLastError ())
Packit 8f70b4
            {
Packit 8f70b4
              case ERROR_TOO_MANY_OPEN_FILES:
Packit 8f70b4
                errno = EMFILE;
Packit 8f70b4
                break;
Packit 8f70b4
              case ERROR_INVALID_HANDLE:
Packit 8f70b4
              case ERROR_INVALID_TARGET_HANDLE:
Packit 8f70b4
              case ERROR_DIRECT_ACCESS_HANDLE:
Packit 8f70b4
                errno = EBADF;
Packit 8f70b4
                break;
Packit 8f70b4
              case ERROR_INVALID_PARAMETER:
Packit 8f70b4
              case ERROR_INVALID_FUNCTION:
Packit 8f70b4
              case ERROR_INVALID_ACCESS:
Packit 8f70b4
                errno = EINVAL;
Packit 8f70b4
                break;
Packit 8f70b4
              default:
Packit 8f70b4
                errno = EACCES;
Packit 8f70b4
                break;
Packit 8f70b4
            }
Packit 8f70b4
          result = -1;
Packit 8f70b4
          break;
Packit 8f70b4
        }
Packit 8f70b4
      duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags);
Packit 8f70b4
      if (duplicated_fd < 0)
Packit 8f70b4
        {
Packit 8f70b4
          CloseHandle (new_handle);
Packit 8f70b4
          result = -1;
Packit 8f70b4
          break;
Packit 8f70b4
        }
Packit 8f70b4
      if (newfd <= duplicated_fd)
Packit 8f70b4
        {
Packit 8f70b4
          result = duplicated_fd;
Packit 8f70b4
          break;
Packit 8f70b4
        }
Packit 8f70b4
Packit 8f70b4
      /* Set the bit duplicated_fd in fds_to_close[].  */
Packit 8f70b4
      index = (unsigned int) duplicated_fd / CHAR_BIT;
Packit 8f70b4
      if (fds_to_close_bound <= index)
Packit 8f70b4
        {
Packit 8f70b4
          if (sizeof fds_to_close <= index)
Packit 8f70b4
            /* Need to increase OPEN_MAX_MAX.  */
Packit 8f70b4
            abort ();
Packit 8f70b4
          memset (fds_to_close + fds_to_close_bound, '\0',
Packit 8f70b4
                  index + 1 - fds_to_close_bound);
Packit 8f70b4
          fds_to_close_bound = index + 1;
Packit 8f70b4
        }
Packit 8f70b4
      fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  /* Close the previous fds that turned out to be too small.  */
Packit 8f70b4
  {
Packit 8f70b4
    int saved_errno = errno;
Packit 8f70b4
    unsigned int duplicated_fd;
Packit 8f70b4
Packit 8f70b4
    for (duplicated_fd = 0;
Packit 8f70b4
         duplicated_fd < fds_to_close_bound * CHAR_BIT;
Packit 8f70b4
         duplicated_fd++)
Packit 8f70b4
      if ((fds_to_close[duplicated_fd / CHAR_BIT]
Packit 8f70b4
           >> (duplicated_fd % CHAR_BIT))
Packit 8f70b4
          & 1)
Packit 8f70b4
        close (duplicated_fd);
Packit 8f70b4
Packit 8f70b4
    errno = saved_errno;
Packit 8f70b4
  }
Packit 8f70b4
Packit 8f70b4
# if REPLACE_FCHDIR
Packit 8f70b4
  if (0 <= result)
Packit 8f70b4
    result = _gl_register_dup (oldfd, result);
Packit 8f70b4
# endif
Packit 8f70b4
  return result;
Packit 8f70b4
}
Packit 8f70b4
#endif /* W32 */
Packit 8f70b4
Packit 8f70b4
#ifdef __KLIBC__
Packit 8f70b4
Packit 8f70b4
# define INCL_DOS
Packit 8f70b4
# include <os2.h>
Packit 8f70b4
Packit 8f70b4
static int
Packit 8f70b4
klibc_fcntl (int fd, int action, /* arg */...)
Packit 8f70b4
{
Packit 8f70b4
  va_list arg_ptr;
Packit 8f70b4
  int arg;
Packit 8f70b4
  struct stat sbuf;
Packit 8f70b4
  int result = -1;
Packit 8f70b4
Packit 8f70b4
  va_start (arg_ptr, action);
Packit 8f70b4
  arg = va_arg (arg_ptr, int);
Packit 8f70b4
  result = fcntl (fd, action, arg);
Packit 8f70b4
  /* EPERM for F_DUPFD, ENOTSUP for others */
Packit 8f70b4
  if (result == -1 && (errno == EPERM || errno == ENOTSUP)
Packit 8f70b4
      && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
Packit 8f70b4
  {
Packit 8f70b4
    ULONG ulMode;
Packit 8f70b4
Packit 8f70b4
    switch (action)
Packit 8f70b4
      {
Packit 8f70b4
      case F_DUPFD:
Packit 8f70b4
        /* Find available fd */
Packit 8f70b4
        while (fcntl (arg, F_GETFL) != -1 || errno != EBADF)
Packit 8f70b4
          arg++;
Packit 8f70b4
Packit 8f70b4
        result = dup2 (fd, arg);
Packit 8f70b4
        break;
Packit 8f70b4
Packit 8f70b4
      /* Using underlying APIs is right ? */
Packit 8f70b4
      case F_GETFD:
Packit 8f70b4
        if (DosQueryFHState (fd, &ulMode))
Packit 8f70b4
          break;
Packit 8f70b4
Packit 8f70b4
        result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0;
Packit 8f70b4
        break;
Packit 8f70b4
Packit 8f70b4
      case F_SETFD:
Packit 8f70b4
        if (arg & ~FD_CLOEXEC)
Packit 8f70b4
          break;
Packit 8f70b4
Packit 8f70b4
        if (DosQueryFHState (fd, &ulMode))
Packit 8f70b4
          break;
Packit 8f70b4
Packit 8f70b4
        if (arg & FD_CLOEXEC)
Packit 8f70b4
          ulMode |= OPEN_FLAGS_NOINHERIT;
Packit 8f70b4
        else
Packit 8f70b4
          ulMode &= ~OPEN_FLAGS_NOINHERIT;
Packit 8f70b4
Packit 8f70b4
        /* Filter supported flags.  */
Packit 8f70b4
        ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR
Packit 8f70b4
                   | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT);
Packit 8f70b4
Packit 8f70b4
        if (DosSetFHState (fd, ulMode))
Packit 8f70b4
          break;
Packit 8f70b4
Packit 8f70b4
        result = 0;
Packit 8f70b4
        break;
Packit 8f70b4
Packit 8f70b4
      case F_GETFL:
Packit 8f70b4
        result = 0;
Packit 8f70b4
        break;
Packit 8f70b4
Packit 8f70b4
      case F_SETFL:
Packit 8f70b4
        if (arg != 0)
Packit 8f70b4
          break;
Packit 8f70b4
Packit 8f70b4
        result = 0;
Packit 8f70b4
        break;
Packit 8f70b4
Packit 8f70b4
      default :
Packit 8f70b4
        errno = EINVAL;
Packit 8f70b4
        break;
Packit 8f70b4
      }
Packit 8f70b4
  }
Packit 8f70b4
Packit 8f70b4
  va_end (arg_ptr);
Packit 8f70b4
Packit 8f70b4
  return result;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
# define fcntl klibc_fcntl
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* Perform the specified ACTION on the file descriptor FD, possibly
Packit 8f70b4
   using the argument ARG further described below.  This replacement
Packit 8f70b4
   handles the following actions, and forwards all others on to the
Packit 8f70b4
   native fcntl.  An unrecognized ACTION returns -1 with errno set to
Packit 8f70b4
   EINVAL.
Packit 8f70b4
Packit 8f70b4
   F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
Packit 8f70b4
   If successful, return the duplicate, which will be inheritable;
Packit 8f70b4
   otherwise return -1 and set errno.
Packit 8f70b4
Packit 8f70b4
   F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
Packit 8f70b4
   target fd.  If successful, return the duplicate, which will not be
Packit 8f70b4
   inheritable; otherwise return -1 and set errno.
Packit 8f70b4
Packit 8f70b4
   F_GETFD - ARG need not be present.  If successful, return a
Packit 8f70b4
   non-negative value containing the descriptor flags of FD (only
Packit 8f70b4
   FD_CLOEXEC is portable, but other flags may be present); otherwise
Packit 8f70b4
   return -1 and set errno.  */
Packit 8f70b4
Packit 8f70b4
int
Packit 8f70b4
rpl_fcntl (int fd, int action, /* arg */...)
Packit 8f70b4
{
Packit 8f70b4
  va_list arg;
Packit 8f70b4
  int result = -1;
Packit 8f70b4
  va_start (arg, action);
Packit 8f70b4
  switch (action)
Packit 8f70b4
    {
Packit 8f70b4
Packit 8f70b4
#if !HAVE_FCNTL
Packit 8f70b4
    case F_DUPFD:
Packit 8f70b4
      {
Packit 8f70b4
        int target = va_arg (arg, int);
Packit 8f70b4
        result = dupfd (fd, target, 0);
Packit 8f70b4
        break;
Packit 8f70b4
      }
Packit 8f70b4
#elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
Packit 8f70b4
    case F_DUPFD:
Packit 8f70b4
      {
Packit 8f70b4
        int target = va_arg (arg, int);
Packit 8f70b4
        /* Detect invalid target; needed for cygwin 1.5.x.  */
Packit 8f70b4
        if (target < 0 || getdtablesize () <= target)
Packit 8f70b4
          errno = EINVAL;
Packit 8f70b4
        else
Packit 8f70b4
          {
Packit 8f70b4
            /* Haiku alpha 2 loses fd flags on original.  */
Packit 8f70b4
            int flags = fcntl (fd, F_GETFD);
Packit 8f70b4
            if (flags < 0)
Packit 8f70b4
              {
Packit 8f70b4
                result = -1;
Packit 8f70b4
                break;
Packit 8f70b4
              }
Packit 8f70b4
            result = fcntl (fd, action, target);
Packit 8f70b4
            if (0 <= result && fcntl (fd, F_SETFD, flags) == -1)
Packit 8f70b4
              {
Packit 8f70b4
                int saved_errno = errno;
Packit 8f70b4
                close (result);
Packit 8f70b4
                result = -1;
Packit 8f70b4
                errno = saved_errno;
Packit 8f70b4
              }
Packit 8f70b4
# if REPLACE_FCHDIR
Packit 8f70b4
            if (0 <= result)
Packit 8f70b4
              result = _gl_register_dup (fd, result);
Packit 8f70b4
# endif
Packit 8f70b4
          }
Packit 8f70b4
        break;
Packit 8f70b4
      } /* F_DUPFD */
Packit 8f70b4
#endif /* FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR */
Packit 8f70b4
Packit 8f70b4
    case F_DUPFD_CLOEXEC:
Packit 8f70b4
      {
Packit 8f70b4
        int target = va_arg (arg, int);
Packit 8f70b4
Packit 8f70b4
#if !HAVE_FCNTL
Packit 8f70b4
        result = dupfd (fd, target, O_CLOEXEC);
Packit 8f70b4
        break;
Packit 8f70b4
#else /* HAVE_FCNTL */
Packit 8f70b4
        /* Try the system call first, if the headers claim it exists
Packit 8f70b4
           (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
Packit 8f70b4
           may be running with a glibc that has the macro but with an
Packit 8f70b4
           older kernel that does not support it.  Cache the
Packit 8f70b4
           information on whether the system call really works, but
Packit 8f70b4
           avoid caching failure if the corresponding F_DUPFD fails
Packit 8f70b4
           for any reason.  0 = unknown, 1 = yes, -1 = no.  */
Packit 8f70b4
        static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
Packit 8f70b4
        if (0 <= have_dupfd_cloexec)
Packit 8f70b4
          {
Packit 8f70b4
            result = fcntl (fd, action, target);
Packit 8f70b4
            if (0 <= result || errno != EINVAL)
Packit 8f70b4
              {
Packit 8f70b4
                have_dupfd_cloexec = 1;
Packit 8f70b4
# if REPLACE_FCHDIR
Packit 8f70b4
                if (0 <= result)
Packit 8f70b4
                  result = _gl_register_dup (fd, result);
Packit 8f70b4
# endif
Packit 8f70b4
              }
Packit 8f70b4
            else
Packit 8f70b4
              {
Packit 8f70b4
                result = rpl_fcntl (fd, F_DUPFD, target);
Packit 8f70b4
                if (result < 0)
Packit 8f70b4
                  break;
Packit 8f70b4
                have_dupfd_cloexec = -1;
Packit 8f70b4
              }
Packit 8f70b4
          }
Packit 8f70b4
        else
Packit 8f70b4
          result = rpl_fcntl (fd, F_DUPFD, target);
Packit 8f70b4
        if (0 <= result && have_dupfd_cloexec == -1)
Packit 8f70b4
          {
Packit 8f70b4
            int flags = fcntl (result, F_GETFD);
Packit 8f70b4
            if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
Packit 8f70b4
              {
Packit 8f70b4
                int saved_errno = errno;
Packit 8f70b4
                close (result);
Packit 8f70b4
                errno = saved_errno;
Packit 8f70b4
                result = -1;
Packit 8f70b4
              }
Packit 8f70b4
          }
Packit 8f70b4
        break;
Packit 8f70b4
#endif /* HAVE_FCNTL */
Packit 8f70b4
      } /* F_DUPFD_CLOEXEC */
Packit 8f70b4
Packit 8f70b4
#if !HAVE_FCNTL
Packit 8f70b4
    case F_GETFD:
Packit 8f70b4
      {
Packit 8f70b4
# if defined _WIN32 && ! defined __CYGWIN__
Packit 8f70b4
        HANDLE handle = (HANDLE) _get_osfhandle (fd);
Packit 8f70b4
        DWORD flags;
Packit 8f70b4
        if (handle == INVALID_HANDLE_VALUE
Packit 8f70b4
            || GetHandleInformation (handle, &flags) == 0)
Packit 8f70b4
          errno = EBADF;
Packit 8f70b4
        else
Packit 8f70b4
          result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
Packit 8f70b4
# else /* !W32 */
Packit 8f70b4
        /* Use dup2 to reject invalid file descriptors.  No way to
Packit 8f70b4
           access this information, so punt.  */
Packit 8f70b4
        if (0 <= dup2 (fd, fd))
Packit 8f70b4
          result = 0;
Packit 8f70b4
# endif /* !W32 */
Packit 8f70b4
        break;
Packit 8f70b4
      } /* F_GETFD */
Packit 8f70b4
#endif /* !HAVE_FCNTL */
Packit 8f70b4
Packit 8f70b4
      /* Implementing F_SETFD on mingw is not trivial - there is no
Packit 8f70b4
         API for changing the O_NOINHERIT bit on an fd, and merely
Packit 8f70b4
         changing the HANDLE_FLAG_INHERIT bit on the underlying handle
Packit 8f70b4
         can lead to odd state.  It may be possible by duplicating the
Packit 8f70b4
         handle, using _open_osfhandle with the right flags, then
Packit 8f70b4
         using dup2 to move the duplicate onto the original, but that
Packit 8f70b4
         is not supported for now.  */
Packit 8f70b4
Packit 8f70b4
    default:
Packit 8f70b4
      {
Packit 8f70b4
#if HAVE_FCNTL
Packit 8f70b4
        void *p = va_arg (arg, void *);
Packit 8f70b4
        result = fcntl (fd, action, p);
Packit 8f70b4
#else
Packit 8f70b4
        errno = EINVAL;
Packit 8f70b4
#endif
Packit 8f70b4
        break;
Packit 8f70b4
      }
Packit 8f70b4
    }
Packit 8f70b4
  va_end (arg);
Packit 8f70b4
  return result;
Packit 8f70b4
}