1 /* Provide file descriptor control. 2 3 Copyright (C) 2009-2011 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 <http://www.gnu.org/licenses/>. */ 17 18 /* Written by Eric Blake <ebb9@byu.net>. */ 19 20 #include <config.h> 21 22 /* Specification. */ 23 #include <fcntl.h> 24 25 #include <errno.h> 26 #include <limits.h> 27 #include <stdarg.h> 28 #include <unistd.h> 29 30 #if !HAVE_FCNTL 31 # define rpl_fcntl fcntl 32 #endif 33 #undef fcntl 34 35 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ 36 /* Get declarations of the Win32 API functions. */ 37 # define WIN32_LEAN_AND_MEAN 38 # include <windows.h> 39 40 /* Upper bound on getdtablesize(). See lib/getdtablesize.c. */ 41 # define OPEN_MAX_MAX 0x10000 42 43 /* Duplicate OLDFD into the first available slot of at least NEWFD, 44 which must be positive, with FLAGS determining whether the duplicate 45 will be inheritable. */ 46 static int 47 dupfd (int oldfd, int newfd, int flags) 48 { 49 /* Mingw has no way to create an arbitrary fd. Iterate until all 50 file descriptors less than newfd are filled up. */ 51 HANDLE curr_process = GetCurrentProcess (); 52 HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd); 53 unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT]; 54 unsigned int fds_to_close_bound = 0; 55 int result; 56 BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE; 57 int mode; 58 59 if (newfd < 0 || getdtablesize () <= newfd) 60 { 61 errno = EINVAL; 62 return -1; 63 } 64 if (old_handle == INVALID_HANDLE_VALUE 65 || (mode = setmode (oldfd, O_BINARY)) == -1) 66 { 67 /* oldfd is not open, or is an unassigned standard file 68 descriptor. */ 69 errno = EBADF; 70 return -1; 71 } 72 setmode (oldfd, mode); 73 flags |= mode; 74 75 for (;;) 76 { 77 HANDLE new_handle; 78 int duplicated_fd; 79 unsigned int index; 80 81 if (!DuplicateHandle (curr_process, /* SourceProcessHandle */ 82 old_handle, /* SourceHandle */ 83 curr_process, /* TargetProcessHandle */ 84 (PHANDLE) &new_handle, /* TargetHandle */ 85 (DWORD) 0, /* DesiredAccess */ 86 inherit, /* InheritHandle */ 87 DUPLICATE_SAME_ACCESS)) /* Options */ 88 { 89 /* TODO: Translate GetLastError () into errno. */ 90 errno = EMFILE; 91 result = -1; 92 break; 93 } 94 duplicated_fd = _open_osfhandle ((long) new_handle, flags); 95 if (duplicated_fd < 0) 96 { 97 CloseHandle (new_handle); 98 errno = EMFILE; 99 result = -1; 100 break; 101 } 102 if (newfd <= duplicated_fd) 103 { 104 result = duplicated_fd; 105 break; 106 } 107 108 /* Set the bit duplicated_fd in fds_to_close[]. */ 109 index = (unsigned int) duplicated_fd / CHAR_BIT; 110 if (fds_to_close_bound <= index) 111 { 112 if (sizeof fds_to_close <= index) 113 /* Need to increase OPEN_MAX_MAX. */ 114 abort (); 115 memset (fds_to_close + fds_to_close_bound, '\0', 116 index + 1 - fds_to_close_bound); 117 fds_to_close_bound = index + 1; 118 } 119 fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT); 120 } 121 122 /* Close the previous fds that turned out to be too small. */ 123 { 124 int saved_errno = errno; 125 unsigned int duplicated_fd; 126 127 for (duplicated_fd = 0; 128 duplicated_fd < fds_to_close_bound * CHAR_BIT; 129 duplicated_fd++) 130 if ((fds_to_close[duplicated_fd / CHAR_BIT] 131 >> (duplicated_fd % CHAR_BIT)) 132 & 1) 133 close (duplicated_fd); 134 135 errno = saved_errno; 136 } 137 138 # if REPLACE_FCHDIR 139 if (0 <= result) 140 result = _gl_register_dup (oldfd, result); 141 # endif 142 return result; 143 } 144 #endif /* W32 */ 145 146 /* Perform the specified ACTION on the file descriptor FD, possibly 147 using the argument ARG further described below. This replacement 148 handles the following actions, and forwards all others on to the 149 native fcntl. An unrecognized ACTION returns -1 with errno set to 150 EINVAL. 151 152 F_DUPFD - duplicate FD, with int ARG being the minimum target fd. 153 If successful, return the duplicate, which will be inheritable; 154 otherwise return -1 and set errno. 155 156 F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum 157 target fd. If successful, return the duplicate, which will not be 158 inheritable; otherwise return -1 and set errno. 159 160 F_GETFD - ARG need not be present. If successful, return a 161 non-negative value containing the descriptor flags of FD (only 162 FD_CLOEXEC is portable, but other flags may be present); otherwise 163 return -1 and set errno. */ 164 165 int 166 rpl_fcntl (int fd, int action, /* arg */...) 167 { 168 va_list arg; 169 int result = -1; 170 va_start (arg, action); 171 switch (action) 172 { 173 174 #if !HAVE_FCNTL 175 case F_DUPFD: 176 { 177 int target = va_arg (arg, int); 178 result = dupfd (fd, target, 0); 179 break; 180 } 181 #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR 182 case F_DUPFD: 183 { 184 int target = va_arg (arg, int); 185 /* Detect invalid target; needed for cygwin 1.5.x. */ 186 if (target < 0 || getdtablesize () <= target) 187 errno = EINVAL; 188 else 189 { 190 /* Haiku alpha 2 loses fd flags on original. */ 191 int flags = fcntl (fd, F_GETFD); 192 if (flags < 0) 193 { 194 result = -1; 195 break; 196 } 197 result = fcntl (fd, action, target); 198 if (0 <= result && fcntl (fd, F_SETFD, flags) == -1) 199 { 200 int saved_errno = errno; 201 close (result); 202 result = -1; 203 errno = saved_errno; 204 } 205 # if REPLACE_FCHDIR 206 if (0 <= result) 207 result = _gl_register_dup (fd, result); 208 # endif 209 } 210 break; 211 } /* F_DUPFD */ 212 #endif /* FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR */ 213 214 case F_DUPFD_CLOEXEC: 215 { 216 int target = va_arg (arg, int); 217 218 #if !HAVE_FCNTL 219 result = dupfd (fd, target, O_CLOEXEC); 220 break; 221 #else /* HAVE_FCNTL */ 222 /* Try the system call first, if the headers claim it exists 223 (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we 224 may be running with a glibc that has the macro but with an 225 older kernel that does not support it. Cache the 226 information on whether the system call really works, but 227 avoid caching failure if the corresponding F_DUPFD fails 228 for any reason. 0 = unknown, 1 = yes, -1 = no. */ 229 static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0; 230 if (0 <= have_dupfd_cloexec) 231 { 232 result = fcntl (fd, action, target); 233 if (0 <= result || errno != EINVAL) 234 { 235 have_dupfd_cloexec = 1; 236 # if REPLACE_FCHDIR 237 if (0 <= result) 238 result = _gl_register_dup (fd, result); 239 # endif 240 } 241 else 242 { 243 result = rpl_fcntl (fd, F_DUPFD, target); 244 if (result < 0) 245 break; 246 have_dupfd_cloexec = -1; 247 } 248 } 249 else 250 result = rpl_fcntl (fd, F_DUPFD, target); 251 if (0 <= result && have_dupfd_cloexec == -1) 252 { 253 int flags = fcntl (result, F_GETFD); 254 if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1) 255 { 256 int saved_errno = errno; 257 close (result); 258 errno = saved_errno; 259 result = -1; 260 } 261 } 262 break; 263 #endif /* HAVE_FCNTL */ 264 } /* F_DUPFD_CLOEXEC */ 265 266 #if !HAVE_FCNTL 267 case F_GETFD: 268 { 269 # if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ 270 HANDLE handle = (HANDLE) _get_osfhandle (fd); 271 DWORD flags; 272 if (handle == INVALID_HANDLE_VALUE 273 || GetHandleInformation (handle, &flags) == 0) 274 errno = EBADF; 275 else 276 result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC; 277 # else /* !W32 */ 278 /* Use dup2 to reject invalid file descriptors. No way to 279 access this information, so punt. */ 280 if (0 <= dup2 (fd, fd)) 281 result = 0; 282 # endif /* !W32 */ 283 break; 284 } /* F_GETFD */ 285 #endif /* !HAVE_FCNTL */ 286 287 /* Implementing F_SETFD on mingw is not trivial - there is no 288 API for changing the O_NOINHERIT bit on an fd, and merely 289 changing the HANDLE_FLAG_INHERIT bit on the underlying handle 290 can lead to odd state. It may be possible by duplicating the 291 handle, using _open_osfhandle with the right flags, then 292 using dup2 to move the duplicate onto the original, but that 293 is not supported for now. */ 294 295 default: 296 { 297 #if HAVE_FCNTL 298 void *p = va_arg (arg, void *); 299 result = fcntl (fd, action, p); 300 #else 301 errno = EINVAL; 302 #endif 303 break; 304 } 305 } 306 va_end (arg); 307 return result; 308 } 309