Blame lib/fcntl.c

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