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