Blame lib/fcntl.c

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