Home | History | Annotate | Line # | Download | only in import
dup2.c revision 1.1
      1 /* Duplicate an open file descriptor to a specified file descriptor.
      2 
      3    Copyright (C) 1999, 2004-2007, 2009-2020 Free Software Foundation, Inc.
      4 
      5    This program is free software: you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 3 of the License, or
      8    (at your option) any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
     17 
     18 /* written by Paul Eggert */
     19 
     20 #include <config.h>
     21 
     22 /* Specification.  */
     23 #include <unistd.h>
     24 
     25 #include <errno.h>
     26 #include <fcntl.h>
     27 
     28 #if HAVE_DUP2
     29 
     30 # undef dup2
     31 
     32 # if defined _WIN32 && ! defined __CYGWIN__
     33 
     34 /* Get declarations of the native Windows API functions.  */
     35 #  define WIN32_LEAN_AND_MEAN
     36 #  include <windows.h>
     37 
     38 #  if HAVE_MSVC_INVALID_PARAMETER_HANDLER
     39 #   include "msvc-inval.h"
     40 #  endif
     41 
     42 /* Get _get_osfhandle.  */
     43 #  if GNULIB_MSVC_NOTHROW
     44 #   include "msvc-nothrow.h"
     45 #  else
     46 #   include <io.h>
     47 #  endif
     48 
     49 #  if HAVE_MSVC_INVALID_PARAMETER_HANDLER
     50 static int
     51 dup2_nothrow (int fd, int desired_fd)
     52 {
     53   int result;
     54 
     55   TRY_MSVC_INVAL
     56     {
     57       result = dup2 (fd, desired_fd);
     58     }
     59   CATCH_MSVC_INVAL
     60     {
     61       errno = EBADF;
     62       result = -1;
     63     }
     64   DONE_MSVC_INVAL;
     65 
     66   return result;
     67 }
     68 #  else
     69 #   define dup2_nothrow dup2
     70 #  endif
     71 
     72 static int
     73 ms_windows_dup2 (int fd, int desired_fd)
     74 {
     75   int result;
     76 
     77   /* If fd is closed, mingw hangs on dup2 (fd, fd).  If fd is open,
     78      dup2 (fd, fd) returns 0, but all further attempts to use fd in
     79      future dup2 calls will hang.  */
     80   if (fd == desired_fd)
     81     {
     82       if ((HANDLE) _get_osfhandle (fd) == INVALID_HANDLE_VALUE)
     83         {
     84           errno = EBADF;
     85           return -1;
     86         }
     87       return fd;
     88     }
     89 
     90   /* Wine 1.0.1 return 0 when desired_fd is negative but not -1:
     91      https://bugs.winehq.org/show_bug.cgi?id=21289 */
     92   if (desired_fd < 0)
     93     {
     94       errno = EBADF;
     95       return -1;
     96     }
     97 
     98   result = dup2_nothrow (fd, desired_fd);
     99 
    100   if (result == 0)
    101     result = desired_fd;
    102 
    103   return result;
    104 }
    105 
    106 #  define dup2 ms_windows_dup2
    107 
    108 # elif defined __KLIBC__
    109 
    110 #  include <InnoTekLIBC/backend.h>
    111 
    112 static int
    113 klibc_dup2dirfd (int fd, int desired_fd)
    114 {
    115   int tempfd;
    116   int dupfd;
    117 
    118   tempfd = open ("NUL", O_RDONLY);
    119   if (tempfd == -1)
    120     return -1;
    121 
    122   if (tempfd == desired_fd)
    123     {
    124       close (tempfd);
    125 
    126       char path[_MAX_PATH];
    127       if (__libc_Back_ioFHToPath (fd, path, sizeof (path)))
    128         return -1;
    129 
    130       return open(path, O_RDONLY);
    131     }
    132 
    133   dupfd = klibc_dup2dirfd (fd, desired_fd);
    134 
    135   close (tempfd);
    136 
    137   return dupfd;
    138 }
    139 
    140 static int
    141 klibc_dup2 (int fd, int desired_fd)
    142 {
    143   int dupfd;
    144   struct stat sbuf;
    145 
    146   dupfd = dup2 (fd, desired_fd);
    147   if (dupfd == -1 && errno == ENOTSUP \
    148       && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
    149     {
    150       close (desired_fd);
    151 
    152       return klibc_dup2dirfd (fd, desired_fd);
    153     }
    154 
    155   return dupfd;
    156 }
    157 
    158 #  define dup2 klibc_dup2
    159 # endif
    160 
    161 int
    162 rpl_dup2 (int fd, int desired_fd)
    163 {
    164   int result;
    165 
    166 # ifdef F_GETFL
    167   /* On Linux kernels 2.6.26-2.6.29, dup2 (fd, fd) returns -EBADF.
    168      On Cygwin 1.5.x, dup2 (1, 1) returns 0.
    169      On Cygwin 1.7.17, dup2 (1, -1) dumps core.
    170      On Cygwin 1.7.25, dup2 (1, 256) can dump core.
    171      On Haiku, dup2 (fd, fd) mistakenly clears FD_CLOEXEC.  */
    172 #  if HAVE_SETDTABLESIZE
    173   setdtablesize (desired_fd + 1);
    174 #  endif
    175   if (desired_fd < 0)
    176     fd = desired_fd;
    177   if (fd == desired_fd)
    178     return fcntl (fd, F_GETFL) == -1 ? -1 : fd;
    179 # endif
    180 
    181   result = dup2 (fd, desired_fd);
    182 
    183   /* Correct an errno value on FreeBSD 6.1 and Cygwin 1.5.x.  */
    184   if (result == -1 && errno == EMFILE)
    185     errno = EBADF;
    186 # if REPLACE_FCHDIR
    187   if (fd != desired_fd && result != -1)
    188     result = _gl_register_dup (fd, result);
    189 # endif
    190   return result;
    191 }
    192 
    193 #else /* !HAVE_DUP2 */
    194 
    195 /* On older platforms, dup2 did not exist.  */
    196 
    197 # ifndef F_DUPFD
    198 static int
    199 dupfd (int fd, int desired_fd)
    200 {
    201   int duplicated_fd = dup (fd);
    202   if (duplicated_fd < 0 || duplicated_fd == desired_fd)
    203     return duplicated_fd;
    204   else
    205     {
    206       int r = dupfd (fd, desired_fd);
    207       int e = errno;
    208       close (duplicated_fd);
    209       errno = e;
    210       return r;
    211     }
    212 }
    213 # endif
    214 
    215 int
    216 dup2 (int fd, int desired_fd)
    217 {
    218   int result = fcntl (fd, F_GETFL) < 0 ? -1 : fd;
    219   if (result == -1 || fd == desired_fd)
    220     return result;
    221   close (desired_fd);
    222 # ifdef F_DUPFD
    223   result = fcntl (fd, F_DUPFD, desired_fd);
    224 #  if REPLACE_FCHDIR
    225   if (0 <= result)
    226     result = _gl_register_dup (fd, result);
    227 #  endif
    228 # else
    229   result = dupfd (fd, desired_fd);
    230 # endif
    231   if (result == -1 && (errno == EMFILE || errno == EINVAL))
    232     errno = EBADF;
    233   return result;
    234 }
    235 #endif /* !HAVE_DUP2 */
    236