Blame lib/fcntl.c

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