1 /* Provide file descriptor control. 2 3 Copyright (C) 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 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 <stdlib.h> 29 #include <unistd.h> 30 31 #ifdef __KLIBC__ 32 # define INCL_DOS 33 # include <os2.h> 34 #endif 35 36 #if defined _WIN32 && ! defined __CYGWIN__ 37 /* Get declarations of the native Windows API functions. */ 38 # define WIN32_LEAN_AND_MEAN 39 # include <windows.h> 40 41 /* Get _get_osfhandle. */ 42 # if GNULIB_MSVC_NOTHROW 43 # include "msvc-nothrow.h" 44 # else 45 # include <io.h> 46 # endif 47 48 /* Upper bound on getdtablesize(). See lib/getdtablesize.c. */ 49 # define OPEN_MAX_MAX 0x10000 50 51 /* Duplicate OLDFD into the first available slot of at least NEWFD, 52 which must be positive, with FLAGS determining whether the duplicate 53 will be inheritable. */ 54 static int 55 dupfd (int oldfd, int newfd, int flags) 56 { 57 /* Mingw has no way to create an arbitrary fd. Iterate until all 58 file descriptors less than newfd are filled up. */ 59 HANDLE curr_process = GetCurrentProcess (); 60 HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd); 61 unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT]; 62 unsigned int fds_to_close_bound = 0; 63 int result; 64 BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE; 65 int mode; 66 67 if (newfd < 0 || getdtablesize () <= newfd) 68 { 69 errno = EINVAL; 70 return -1; 71 } 72 if (old_handle == INVALID_HANDLE_VALUE 73 || (mode = _setmode (oldfd, O_BINARY)) == -1) 74 { 75 /* oldfd is not open, or is an unassigned standard file 76 descriptor. */ 77 errno = EBADF; 78 return -1; 79 } 80 _setmode (oldfd, mode); 81 flags |= mode; 82 83 for (;;) 84 { 85 HANDLE new_handle; 86 int duplicated_fd; 87 unsigned int index; 88 89 if (!DuplicateHandle (curr_process, /* SourceProcessHandle */ 90 old_handle, /* SourceHandle */ 91 curr_process, /* TargetProcessHandle */ 92 (PHANDLE) &new_handle, /* TargetHandle */ 93 (DWORD) 0, /* DesiredAccess */ 94 inherit, /* InheritHandle */ 95 DUPLICATE_SAME_ACCESS)) /* Options */ 96 { 97 switch (GetLastError ()) 98 { 99 case ERROR_TOO_MANY_OPEN_FILES: 100 errno = EMFILE; 101 break; 102 case ERROR_INVALID_HANDLE: 103 case ERROR_INVALID_TARGET_HANDLE: 104 case ERROR_DIRECT_ACCESS_HANDLE: 105 errno = EBADF; 106 break; 107 case ERROR_INVALID_PARAMETER: 108 case ERROR_INVALID_FUNCTION: 109 case ERROR_INVALID_ACCESS: 110 errno = EINVAL; 111 break; 112 default: 113 errno = EACCES; 114 break; 115 } 116 result = -1; 117 break; 118 } 119 duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags); 120 if (duplicated_fd < 0) 121 { 122 CloseHandle (new_handle); 123 result = -1; 124 break; 125 } 126 if (newfd <= duplicated_fd) 127 { 128 result = duplicated_fd; 129 break; 130 } 131 132 /* Set the bit duplicated_fd in fds_to_close[]. */ 133 index = (unsigned int) duplicated_fd / CHAR_BIT; 134 if (fds_to_close_bound <= index) 135 { 136 if (sizeof fds_to_close <= index) 137 /* Need to increase OPEN_MAX_MAX. */ 138 abort (); 139 memset (fds_to_close + fds_to_close_bound, '\0', 140 index + 1 - fds_to_close_bound); 141 fds_to_close_bound = index + 1; 142 } 143 fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT); 144 } 145 146 /* Close the previous fds that turned out to be too small. */ 147 { 148 int saved_errno = errno; 149 unsigned int duplicated_fd; 150 151 for (duplicated_fd = 0; 152 duplicated_fd < fds_to_close_bound * CHAR_BIT; 153 duplicated_fd++) 154 if ((fds_to_close[duplicated_fd / CHAR_BIT] 155 >> (duplicated_fd % CHAR_BIT)) 156 & 1) 157 close (duplicated_fd); 158 159 errno = saved_errno; 160 } 161 162 # if REPLACE_FCHDIR 163 if (0 <= result) 164 result = _gl_register_dup (oldfd, result); 165 # endif 166 return result; 167 } 168 #endif /* W32 */ 169 170 /* Forward declarations, because we '#undef fcntl' in the middle of this 171 compilation unit. */ 172 /* Our implementation of fcntl (fd, F_DUPFD, target). */ 173 static int rpl_fcntl_DUPFD (int fd, int target); 174 /* Our implementation of fcntl (fd, F_DUPFD_CLOEXEC, target). */ 175 static int rpl_fcntl_DUPFD_CLOEXEC (int fd, int target); 176 #ifdef __KLIBC__ 177 /* Adds support for fcntl on directories. */ 178 static int klibc_fcntl (int fd, int action, /* arg */...); 179 #endif 180 181 182 /* Perform the specified ACTION on the file descriptor FD, possibly 183 using the argument ARG further described below. This replacement 184 handles the following actions, and forwards all others on to the 185 native fcntl. An unrecognized ACTION returns -1 with errno set to 186 EINVAL. 187 188 F_DUPFD - duplicate FD, with int ARG being the minimum target fd. 189 If successful, return the duplicate, which will be inheritable; 190 otherwise return -1 and set errno. 191 192 F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum 193 target fd. If successful, return the duplicate, which will not be 194 inheritable; otherwise return -1 and set errno. 195 196 F_GETFD - ARG need not be present. If successful, return a 197 non-negative value containing the descriptor flags of FD (only 198 FD_CLOEXEC is portable, but other flags may be present); otherwise 199 return -1 and set errno. */ 200 201 int 202 fcntl (int fd, int action, /* arg */...) 203 #undef fcntl 204 #ifdef __KLIBC__ 205 # define fcntl klibc_fcntl 206 #endif 207 { 208 va_list arg; 209 int result = -1; 210 va_start (arg, action); 211 switch (action) 212 { 213 case F_DUPFD: 214 { 215 int target = va_arg (arg, int); 216 result = rpl_fcntl_DUPFD (fd, target); 217 break; 218 } 219 220 case F_DUPFD_CLOEXEC: 221 { 222 int target = va_arg (arg, int); 223 result = rpl_fcntl_DUPFD_CLOEXEC (fd, target); 224 break; 225 } 226 227 #if !HAVE_FCNTL 228 case F_GETFD: 229 { 230 # if defined _WIN32 && ! defined __CYGWIN__ 231 HANDLE handle = (HANDLE) _get_osfhandle (fd); 232 DWORD flags; 233 if (handle == INVALID_HANDLE_VALUE 234 || GetHandleInformation (handle, &flags) == 0) 235 errno = EBADF; 236 else 237 result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC; 238 # else /* !W32 */ 239 /* Use dup2 to reject invalid file descriptors. No way to 240 access this information, so punt. */ 241 if (0 <= dup2 (fd, fd)) 242 result = 0; 243 # endif /* !W32 */ 244 break; 245 } /* F_GETFD */ 246 #endif /* !HAVE_FCNTL */ 247 248 /* Implementing F_SETFD on mingw is not trivial - there is no 249 API for changing the O_NOINHERIT bit on an fd, and merely 250 changing the HANDLE_FLAG_INHERIT bit on the underlying handle 251 can lead to odd state. It may be possible by duplicating the 252 handle, using _open_osfhandle with the right flags, then 253 using dup2 to move the duplicate onto the original, but that 254 is not supported for now. */ 255 256 default: 257 { 258 #if HAVE_FCNTL 259 switch (action) 260 { 261 #ifdef F_BARRIERFSYNC /* macOS */ 262 case F_BARRIERFSYNC: 263 #endif 264 #ifdef F_CHKCLEAN /* macOS */ 265 case F_CHKCLEAN: 266 #endif 267 #ifdef F_CLOSEM /* NetBSD, HP-UX */ 268 case F_CLOSEM: 269 #endif 270 #ifdef F_FLUSH_DATA /* macOS */ 271 case F_FLUSH_DATA: 272 #endif 273 #ifdef F_FREEZE_FS /* macOS */ 274 case F_FREEZE_FS: 275 #endif 276 #ifdef F_FULLFSYNC /* macOS */ 277 case F_FULLFSYNC: 278 #endif 279 #ifdef F_GETCONFINED /* macOS */ 280 case F_GETCONFINED: 281 #endif 282 #ifdef F_GETDEFAULTPROTLEVEL /* macOS */ 283 case F_GETDEFAULTPROTLEVEL: 284 #endif 285 #ifdef F_GETFD /* POSIX */ 286 case F_GETFD: 287 #endif 288 #ifdef F_GETFL /* POSIX */ 289 case F_GETFL: 290 #endif 291 #ifdef F_GETLEASE /* Linux */ 292 case F_GETLEASE: 293 #endif 294 #ifdef F_GETNOSIGPIPE /* macOS */ 295 case F_GETNOSIGPIPE: 296 #endif 297 #ifdef F_GETOWN /* POSIX */ 298 case F_GETOWN: 299 #endif 300 #ifdef F_GETPIPE_SZ /* Linux */ 301 case F_GETPIPE_SZ: 302 #endif 303 #ifdef F_GETPROTECTIONCLASS /* macOS */ 304 case F_GETPROTECTIONCLASS: 305 #endif 306 #ifdef F_GETPROTECTIONLEVEL /* macOS */ 307 case F_GETPROTECTIONLEVEL: 308 #endif 309 #ifdef F_GET_SEALS /* Linux */ 310 case F_GET_SEALS: 311 #endif 312 #ifdef F_GETSIG /* Linux */ 313 case F_GETSIG: 314 #endif 315 #ifdef F_MAXFD /* NetBSD */ 316 case F_MAXFD: 317 #endif 318 #ifdef F_RECYCLE /* macOS */ 319 case F_RECYCLE: 320 #endif 321 #ifdef F_SETFIFOENH /* HP-UX */ 322 case F_SETFIFOENH: 323 #endif 324 #ifdef F_THAW_FS /* macOS */ 325 case F_THAW_FS: 326 #endif 327 /* These actions take no argument. */ 328 result = fcntl (fd, action); 329 break; 330 331 #ifdef F_ADD_SEALS /* Linux */ 332 case F_ADD_SEALS: 333 #endif 334 #ifdef F_BADFD /* Solaris */ 335 case F_BADFD: 336 #endif 337 #ifdef F_CHECK_OPENEVT /* macOS */ 338 case F_CHECK_OPENEVT: 339 #endif 340 #ifdef F_DUP2FD /* FreeBSD, AIX, Solaris */ 341 case F_DUP2FD: 342 #endif 343 #ifdef F_DUP2FD_CLOEXEC /* FreeBSD, Solaris */ 344 case F_DUP2FD_CLOEXEC: 345 #endif 346 #ifdef F_DUP2FD_CLOFORK /* Solaris */ 347 case F_DUP2FD_CLOFORK: 348 #endif 349 #ifdef F_DUPFD /* POSIX */ 350 case F_DUPFD: 351 #endif 352 #ifdef F_DUPFD_CLOEXEC /* POSIX */ 353 case F_DUPFD_CLOEXEC: 354 #endif 355 #ifdef F_DUPFD_CLOFORK /* Solaris */ 356 case F_DUPFD_CLOFORK: 357 #endif 358 #ifdef F_GETXFL /* Solaris */ 359 case F_GETXFL: 360 #endif 361 #ifdef F_GLOBAL_NOCACHE /* macOS */ 362 case F_GLOBAL_NOCACHE: 363 #endif 364 #ifdef F_MAKECOMPRESSED /* macOS */ 365 case F_MAKECOMPRESSED: 366 #endif 367 #ifdef F_MOVEDATAEXTENTS /* macOS */ 368 case F_MOVEDATAEXTENTS: 369 #endif 370 #ifdef F_NOCACHE /* macOS */ 371 case F_NOCACHE: 372 #endif 373 #ifdef F_NODIRECT /* macOS */ 374 case F_NODIRECT: 375 #endif 376 #ifdef F_NOTIFY /* Linux */ 377 case F_NOTIFY: 378 #endif 379 #ifdef F_OPLKACK /* IRIX */ 380 case F_OPLKACK: 381 #endif 382 #ifdef F_OPLKREG /* IRIX */ 383 case F_OPLKREG: 384 #endif 385 #ifdef F_RDAHEAD /* macOS */ 386 case F_RDAHEAD: 387 #endif 388 #ifdef F_SETBACKINGSTORE /* macOS */ 389 case F_SETBACKINGSTORE: 390 #endif 391 #ifdef F_SETCONFINED /* macOS */ 392 case F_SETCONFINED: 393 #endif 394 #ifdef F_SETFD /* POSIX */ 395 case F_SETFD: 396 #endif 397 #ifdef F_SETFL /* POSIX */ 398 case F_SETFL: 399 #endif 400 #ifdef F_SETLEASE /* Linux */ 401 case F_SETLEASE: 402 #endif 403 #ifdef F_SETNOSIGPIPE /* macOS */ 404 case F_SETNOSIGPIPE: 405 #endif 406 #ifdef F_SETOWN /* POSIX */ 407 case F_SETOWN: 408 #endif 409 #ifdef F_SETPIPE_SZ /* Linux */ 410 case F_SETPIPE_SZ: 411 #endif 412 #ifdef F_SETPROTECTIONCLASS /* macOS */ 413 case F_SETPROTECTIONCLASS: 414 #endif 415 #ifdef F_SETSIG /* Linux */ 416 case F_SETSIG: 417 #endif 418 #ifdef F_SINGLE_WRITER /* macOS */ 419 case F_SINGLE_WRITER: 420 #endif 421 /* These actions take an 'int' argument. */ 422 { 423 int x = va_arg (arg, int); 424 result = fcntl (fd, action, x); 425 } 426 break; 427 428 default: 429 /* Other actions take a pointer argument. */ 430 { 431 void *p = va_arg (arg, void *); 432 result = fcntl (fd, action, p); 433 } 434 break; 435 } 436 #else 437 errno = EINVAL; 438 #endif 439 break; 440 } 441 } 442 va_end (arg); 443 return result; 444 } 445 446 static int 447 rpl_fcntl_DUPFD (int fd, int target) 448 { 449 int result; 450 #if !HAVE_FCNTL 451 result = dupfd (fd, target, 0); 452 #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR 453 /* Detect invalid target; needed for cygwin 1.5.x. */ 454 if (target < 0 || getdtablesize () <= target) 455 { 456 result = -1; 457 errno = EINVAL; 458 } 459 else 460 { 461 /* Haiku alpha 2 loses fd flags on original. */ 462 int flags = fcntl (fd, F_GETFD); 463 if (flags < 0) 464 result = -1; 465 else 466 { 467 result = fcntl (fd, F_DUPFD, target); 468 if (0 <= result && fcntl (fd, F_SETFD, flags) == -1) 469 { 470 int saved_errno = errno; 471 close (result); 472 result = -1; 473 errno = saved_errno; 474 } 475 # if REPLACE_FCHDIR 476 if (0 <= result) 477 result = _gl_register_dup (fd, result); 478 # endif 479 } 480 } 481 #else 482 result = fcntl (fd, F_DUPFD, target); 483 #endif 484 return result; 485 } 486 487 static int 488 rpl_fcntl_DUPFD_CLOEXEC (int fd, int target) 489 { 490 int result; 491 #if !HAVE_FCNTL 492 result = dupfd (fd, target, O_CLOEXEC); 493 #else /* HAVE_FCNTL */ 494 # if defined __NetBSD__ || defined __HAIKU__ 495 /* On NetBSD 9.0, the system fcntl (fd, F_DUPFD_CLOEXEC, target) 496 has only the same effect as fcntl (fd, F_DUPFD, target). */ 497 /* On Haiku, the system fcntl (fd, F_DUPFD_CLOEXEC, target) sets 498 the FD_CLOEXEC flag on fd, not on target. Therefore avoid the 499 system fcntl in this case. */ 500 # define have_dupfd_cloexec -1 501 # else 502 /* Try the system call first, if the headers claim it exists 503 (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we 504 may be running with a glibc that has the macro but with an 505 older kernel that does not support it. Cache the 506 information on whether the system call really works, but 507 avoid caching failure if the corresponding F_DUPFD fails 508 for any reason. 0 = unknown, 1 = yes, -1 = no. */ 509 static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0; 510 if (0 <= have_dupfd_cloexec) 511 { 512 result = fcntl (fd, F_DUPFD_CLOEXEC, target); 513 if (0 <= result || errno != EINVAL) 514 { 515 have_dupfd_cloexec = 1; 516 # if REPLACE_FCHDIR 517 if (0 <= result) 518 result = _gl_register_dup (fd, result); 519 # endif 520 } 521 else 522 { 523 result = rpl_fcntl_DUPFD (fd, target); 524 if (result >= 0) 525 have_dupfd_cloexec = -1; 526 } 527 } 528 else 529 # endif 530 result = rpl_fcntl_DUPFD (fd, target); 531 if (0 <= result && have_dupfd_cloexec == -1) 532 { 533 int flags = fcntl (result, F_GETFD); 534 if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1) 535 { 536 int saved_errno = errno; 537 close (result); 538 errno = saved_errno; 539 result = -1; 540 } 541 } 542 #endif /* HAVE_FCNTL */ 543 return result; 544 } 545 546 #undef fcntl 547 548 #ifdef __KLIBC__ 549 550 static int 551 klibc_fcntl (int fd, int action, /* arg */...) 552 { 553 va_list arg_ptr; 554 int arg; 555 struct stat sbuf; 556 int result; 557 558 va_start (arg_ptr, action); 559 arg = va_arg (arg_ptr, int); 560 result = fcntl (fd, action, arg); 561 /* EPERM for F_DUPFD, ENOTSUP for others */ 562 if (result == -1 && (errno == EPERM || errno == ENOTSUP) 563 && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode)) 564 { 565 ULONG ulMode; 566 567 switch (action) 568 { 569 case F_DUPFD: 570 /* Find available fd */ 571 while (fcntl (arg, F_GETFL) != -1 || errno != EBADF) 572 arg++; 573 574 result = dup2 (fd, arg); 575 break; 576 577 /* Using underlying APIs is right ? */ 578 case F_GETFD: 579 if (DosQueryFHState (fd, &ulMode)) 580 break; 581 582 result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0; 583 break; 584 585 case F_SETFD: 586 if (arg & ~FD_CLOEXEC) 587 break; 588 589 if (DosQueryFHState (fd, &ulMode)) 590 break; 591 592 if (arg & FD_CLOEXEC) 593 ulMode |= OPEN_FLAGS_NOINHERIT; 594 else 595 ulMode &= ~OPEN_FLAGS_NOINHERIT; 596 597 /* Filter supported flags. */ 598 ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR 599 | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT); 600 601 if (DosSetFHState (fd, ulMode)) 602 break; 603 604 result = 0; 605 break; 606 607 case F_GETFL: 608 result = 0; 609 break; 610 611 case F_SETFL: 612 if (arg != 0) 613 break; 614 615 result = 0; 616 break; 617 618 default: 619 errno = EINVAL; 620 break; 621 } 622 } 623 624 va_end (arg_ptr); 625 626 return result; 627 } 628 629 #endif 630