Blame lib/fcntl.c

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