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