1 /* Duplicate an open file descriptor to a specified file descriptor. 2 3 Copyright (C) 1999, 2004-2007, 2009-2022 Free Software Foundation, Inc. 4 5 This file is free software: you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1 of the 8 License, or (at your option) any later version. 9 10 This file 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 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser 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 #undef dup2 29 30 #if defined _WIN32 && ! defined __CYGWIN__ 31 32 /* Get declarations of the native Windows API functions. */ 33 # define WIN32_LEAN_AND_MEAN 34 # include <windows.h> 35 36 # if HAVE_MSVC_INVALID_PARAMETER_HANDLER 37 # include "msvc-inval.h" 38 # endif 39 40 /* Get _get_osfhandle. */ 41 # if GNULIB_MSVC_NOTHROW 42 # include "msvc-nothrow.h" 43 # else 44 # include <io.h> 45 # endif 46 47 # if HAVE_MSVC_INVALID_PARAMETER_HANDLER 48 static int 49 dup2_nothrow (int fd, int desired_fd) 50 { 51 int result; 52 53 TRY_MSVC_INVAL 54 { 55 result = _dup2 (fd, desired_fd); 56 } 57 CATCH_MSVC_INVAL 58 { 59 errno = EBADF; 60 result = -1; 61 } 62 DONE_MSVC_INVAL; 63 64 return result; 65 } 66 # else 67 # define dup2_nothrow _dup2 68 # endif 69 70 static int 71 ms_windows_dup2 (int fd, int desired_fd) 72 { 73 int result; 74 75 /* If fd is closed, mingw hangs on dup2 (fd, fd). If fd is open, 76 dup2 (fd, fd) returns 0, but all further attempts to use fd in 77 future dup2 calls will hang. */ 78 if (fd == desired_fd) 79 { 80 if ((HANDLE) _get_osfhandle (fd) == INVALID_HANDLE_VALUE) 81 { 82 errno = EBADF; 83 return -1; 84 } 85 return fd; 86 } 87 88 /* Wine 1.0.1 return 0 when desired_fd is negative but not -1: 89 https://bugs.winehq.org/show_bug.cgi?id=21289 */ 90 if (desired_fd < 0) 91 { 92 errno = EBADF; 93 return -1; 94 } 95 96 result = dup2_nothrow (fd, desired_fd); 97 98 if (result == 0) 99 result = desired_fd; 100 101 return result; 102 } 103 104 # define dup2 ms_windows_dup2 105 106 #elif defined __KLIBC__ 107 108 # include <InnoTekLIBC/backend.h> 109 110 static int 111 klibc_dup2dirfd (int fd, int desired_fd) 112 { 113 int tempfd; 114 int dupfd; 115 116 tempfd = open ("NUL", O_RDONLY); 117 if (tempfd == -1) 118 return -1; 119 120 if (tempfd == desired_fd) 121 { 122 close (tempfd); 123 124 char path[_MAX_PATH]; 125 if (__libc_Back_ioFHToPath (fd, path, sizeof (path))) 126 return -1; 127 128 return open(path, O_RDONLY); 129 } 130 131 dupfd = klibc_dup2dirfd (fd, desired_fd); 132 133 close (tempfd); 134 135 return dupfd; 136 } 137 138 static int 139 klibc_dup2 (int fd, int desired_fd) 140 { 141 int dupfd; 142 struct stat sbuf; 143 144 dupfd = dup2 (fd, desired_fd); 145 if (dupfd == -1 && errno == ENOTSUP \ 146 && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode)) 147 { 148 close (desired_fd); 149 150 return klibc_dup2dirfd (fd, desired_fd); 151 } 152 153 return dupfd; 154 } 155 156 # define dup2 klibc_dup2 157 #endif 158 159 int 160 rpl_dup2 (int fd, int desired_fd) 161 { 162 int result; 163 164 #ifdef F_GETFL 165 /* On Linux kernels 2.6.26-2.6.29, dup2 (fd, fd) returns -EBADF. 166 On Cygwin 1.5.x, dup2 (1, 1) returns 0. 167 On Cygwin 1.7.17, dup2 (1, -1) dumps core. 168 On Cygwin 1.7.25, dup2 (1, 256) can dump core. 169 On Haiku, dup2 (fd, fd) mistakenly clears FD_CLOEXEC. */ 170 # if HAVE_SETDTABLESIZE 171 setdtablesize (desired_fd + 1); 172 # endif 173 if (desired_fd < 0) 174 fd = desired_fd; 175 if (fd == desired_fd) 176 return fcntl (fd, F_GETFL) == -1 ? -1 : fd; 177 #endif 178 179 result = dup2 (fd, desired_fd); 180 181 /* Correct an errno value on FreeBSD 6.1 and Cygwin 1.5.x. */ 182 if (result == -1 && errno == EMFILE) 183 errno = EBADF; 184 #if REPLACE_FCHDIR 185 if (fd != desired_fd && result != -1) 186 result = _gl_register_dup (fd, result); 187 #endif 188 return result; 189 } 190