1 /* Provide file descriptor control.
3 Copyright (C) 2009-2023 Free Software Foundation, Inc.
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.
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.
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/>. */
18 /* Written by Eric Blake <ebb9@byu.net>. */
36 #if defined _WIN32 && ! defined __CYGWIN__
37 /* Get declarations of the native Windows API functions. */
38 # define WIN32_LEAN_AND_MEAN
41 /* Get _get_osfhandle. */
42 # if GNULIB_MSVC_NOTHROW
43 # include "msvc-nothrow.h"
48 /* Upper bound on getdtablesize(). See lib/getdtablesize.c. */
49 # define OPEN_MAX_MAX 0x10000
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. */
55 dupfd (int oldfd
, int newfd
, int flags
)
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;
64 BOOL inherit
= flags
& O_CLOEXEC
? FALSE
: TRUE
;
67 if (newfd
< 0 || getdtablesize () <= newfd
)
72 if (old_handle
== INVALID_HANDLE_VALUE
73 || (mode
= _setmode (oldfd
, O_BINARY
)) == -1)
75 /* oldfd is not open, or is an unassigned standard file
80 _setmode (oldfd
, mode
);
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 */
97 switch (GetLastError ())
99 case ERROR_TOO_MANY_OPEN_FILES
:
102 case ERROR_INVALID_HANDLE
:
103 case ERROR_INVALID_TARGET_HANDLE
:
104 case ERROR_DIRECT_ACCESS_HANDLE
:
107 case ERROR_INVALID_PARAMETER
:
108 case ERROR_INVALID_FUNCTION
:
109 case ERROR_INVALID_ACCESS
:
119 duplicated_fd
= _open_osfhandle ((intptr_t) new_handle
, flags
);
120 if (duplicated_fd
< 0)
122 CloseHandle (new_handle
);
126 if (newfd
<= duplicated_fd
)
128 result
= duplicated_fd
;
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
)
136 if (sizeof fds_to_close
<= index
)
137 /* Need to increase OPEN_MAX_MAX. */
139 memset (fds_to_close
+ fds_to_close_bound
, '\0',
140 index
+ 1 - fds_to_close_bound
);
141 fds_to_close_bound
= index
+ 1;
143 fds_to_close
[index
] |= 1 << ((unsigned int) duplicated_fd
% CHAR_BIT
);
146 /* Close the previous fds that turned out to be too small. */
148 int saved_errno
= errno
;
149 unsigned int duplicated_fd
;
151 for (duplicated_fd
= 0;
152 duplicated_fd
< fds_to_close_bound
* CHAR_BIT
;
154 if ((fds_to_close
[duplicated_fd
/ CHAR_BIT
]
155 >> (duplicated_fd
% CHAR_BIT
))
157 close (duplicated_fd
);
164 result
= _gl_register_dup (oldfd
, result
);
170 /* Forward declarations, because we '#undef fcntl' in the middle of this
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
);
177 /* Adds support for fcntl on directories. */
178 static int klibc_fcntl (int fd
, int action
, /* arg */...);
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
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.
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.
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. */
202 fcntl (int fd
, int action
, /* arg */...)
205 # define fcntl klibc_fcntl
210 va_start (arg
, action
);
215 int target
= va_arg (arg
, int);
216 result
= rpl_fcntl_DUPFD (fd
, target
);
220 case F_DUPFD_CLOEXEC
:
222 int target
= va_arg (arg
, int);
223 result
= rpl_fcntl_DUPFD_CLOEXEC (fd
, target
);
230 # if defined _WIN32 && ! defined __CYGWIN__
231 HANDLE handle
= (HANDLE
) _get_osfhandle (fd
);
233 if (handle
== INVALID_HANDLE_VALUE
234 || GetHandleInformation (handle
, &flags
) == 0)
237 result
= (flags
& HANDLE_FLAG_INHERIT
) ? 0 : FD_CLOEXEC
;
239 /* Use dup2 to reject invalid file descriptors. No way to
240 access this information, so punt. */
241 if (0 <= dup2 (fd
, fd
))
246 #endif /* !HAVE_FCNTL */
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. */
261 #ifdef F_BARRIERFSYNC /* macOS */
264 #ifdef F_CHKCLEAN /* macOS */
267 #ifdef F_CLOSEM /* NetBSD, HP-UX */
270 #ifdef F_FLUSH_DATA /* macOS */
273 #ifdef F_FREEZE_FS /* macOS */
276 #ifdef F_FULLFSYNC /* macOS */
279 #ifdef F_GETCONFINED /* macOS */
282 #ifdef F_GETDEFAULTPROTLEVEL /* macOS */
283 case F_GETDEFAULTPROTLEVEL
:
285 #ifdef F_GETFD /* POSIX */
288 #ifdef F_GETFL /* POSIX */
291 #ifdef F_GETLEASE /* Linux */
294 #ifdef F_GETNOSIGPIPE /* macOS */
297 #ifdef F_GETOWN /* POSIX */
300 #ifdef F_GETPIPE_SZ /* Linux */
303 #ifdef F_GETPROTECTIONCLASS /* macOS */
304 case F_GETPROTECTIONCLASS
:
306 #ifdef F_GETPROTECTIONLEVEL /* macOS */
307 case F_GETPROTECTIONLEVEL
:
309 #ifdef F_GET_SEALS /* Linux */
312 #ifdef F_GETSIG /* Linux */
315 #ifdef F_MAXFD /* NetBSD */
318 #ifdef F_RECYCLE /* macOS */
321 #ifdef F_SETFIFOENH /* HP-UX */
324 #ifdef F_THAW_FS /* macOS */
327 /* These actions take no argument. */
328 result
= fcntl (fd
, action
);
331 #ifdef F_ADD_SEALS /* Linux */
334 #ifdef F_BADFD /* Solaris */
337 #ifdef F_CHECK_OPENEVT /* macOS */
338 case F_CHECK_OPENEVT
:
340 #ifdef F_DUP2FD /* FreeBSD, AIX, Solaris */
343 #ifdef F_DUP2FD_CLOEXEC /* FreeBSD, Solaris */
344 case F_DUP2FD_CLOEXEC
:
346 #ifdef F_DUP2FD_CLOFORK /* Solaris */
347 case F_DUP2FD_CLOFORK
:
349 #ifdef F_DUPFD /* POSIX */
352 #ifdef F_DUPFD_CLOEXEC /* POSIX */
353 case F_DUPFD_CLOEXEC
:
355 #ifdef F_DUPFD_CLOFORK /* Solaris */
356 case F_DUPFD_CLOFORK
:
358 #ifdef F_GETXFL /* Solaris */
361 #ifdef F_GLOBAL_NOCACHE /* macOS */
362 case F_GLOBAL_NOCACHE
:
364 #ifdef F_MAKECOMPRESSED /* macOS */
365 case F_MAKECOMPRESSED
:
367 #ifdef F_MOVEDATAEXTENTS /* macOS */
368 case F_MOVEDATAEXTENTS
:
370 #ifdef F_NOCACHE /* macOS */
373 #ifdef F_NODIRECT /* macOS */
376 #ifdef F_NOTIFY /* Linux */
379 #ifdef F_OPLKACK /* IRIX */
382 #ifdef F_OPLKREG /* IRIX */
385 #ifdef F_RDAHEAD /* macOS */
388 #ifdef F_SETBACKINGSTORE /* macOS */
389 case F_SETBACKINGSTORE
:
391 #ifdef F_SETCONFINED /* macOS */
394 #ifdef F_SETFD /* POSIX */
397 #ifdef F_SETFL /* POSIX */
400 #ifdef F_SETLEASE /* Linux */
403 #ifdef F_SETNOSIGPIPE /* macOS */
406 #ifdef F_SETOWN /* POSIX */
409 #ifdef F_SETPIPE_SZ /* Linux */
412 #ifdef F_SETPROTECTIONCLASS /* macOS */
413 case F_SETPROTECTIONCLASS
:
415 #ifdef F_SETSIG /* Linux */
418 #ifdef F_SINGLE_WRITER /* macOS */
419 case F_SINGLE_WRITER
:
421 /* These actions take an 'int' argument. */
423 int x
= va_arg (arg
, int);
424 result
= fcntl (fd
, action
, x
);
429 /* Other actions take a pointer argument. */
431 void *p
= va_arg (arg
, void *);
432 result
= fcntl (fd
, action
, p
);
447 rpl_fcntl_DUPFD (int fd
, int target
)
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
)
461 /* Haiku alpha 2 loses fd flags on original. */
462 int flags
= fcntl (fd
, F_GETFD
);
467 result
= fcntl (fd
, F_DUPFD
, target
);
468 if (0 <= result
&& fcntl (fd
, F_SETFD
, flags
) == -1)
470 int saved_errno
= errno
;
477 result
= _gl_register_dup (fd
, result
);
482 result
= fcntl (fd
, F_DUPFD
, target
);
488 rpl_fcntl_DUPFD_CLOEXEC (int fd
, int target
)
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
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
)
512 result
= fcntl (fd
, F_DUPFD_CLOEXEC
, target
);
513 if (0 <= result
|| errno
!= EINVAL
)
515 have_dupfd_cloexec
= 1;
518 result
= _gl_register_dup (fd
, result
);
523 result
= rpl_fcntl_DUPFD (fd
, target
);
525 have_dupfd_cloexec
= -1;
530 result
= rpl_fcntl_DUPFD (fd
, target
);
531 if (0 <= result
&& have_dupfd_cloexec
== -1)
533 int flags
= fcntl (result
, F_GETFD
);
534 if (flags
< 0 || fcntl (result
, F_SETFD
, flags
| FD_CLOEXEC
) == -1)
536 int saved_errno
= errno
;
542 #endif /* HAVE_FCNTL */
551 klibc_fcntl (int fd
, int action
, /* arg */...)
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
))
570 /* Find available fd */
571 while (fcntl (arg
, F_GETFL
) != -1 || errno
!= EBADF
)
574 result
= dup2 (fd
, arg
);
577 /* Using underlying APIs is right ? */
579 if (DosQueryFHState (fd
, &ulMode
))
582 result
= (ulMode
& OPEN_FLAGS_NOINHERIT
) ? FD_CLOEXEC
: 0;
586 if (arg
& ~FD_CLOEXEC
)
589 if (DosQueryFHState (fd
, &ulMode
))
592 if (arg
& FD_CLOEXEC
)
593 ulMode
|= OPEN_FLAGS_NOINHERIT
;
595 ulMode
&= ~OPEN_FLAGS_NOINHERIT
;
597 /* Filter supported flags. */
598 ulMode
&= (OPEN_FLAGS_WRITE_THROUGH
| OPEN_FLAGS_FAIL_ON_ERROR
599 | OPEN_FLAGS_NO_CACHE
| OPEN_FLAGS_NOINHERIT
);
601 if (DosSetFHState (fd
, ulMode
))