18dffb485Schristos /* Provide file descriptor control.
28dffb485Schristos
3*4b169a6bSchristos Copyright (C) 2009-2022 Free Software Foundation, Inc.
48dffb485Schristos
5*4b169a6bSchristos This file is free software: you can redistribute it and/or modify
6*4b169a6bSchristos it under the terms of the GNU Lesser General Public License as
7*4b169a6bSchristos published by the Free Software Foundation; either version 2.1 of the
8*4b169a6bSchristos License, or (at your option) any later version.
98dffb485Schristos
10*4b169a6bSchristos This file is distributed in the hope that it will be useful,
118dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
128dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13*4b169a6bSchristos GNU Lesser General Public License for more details.
148dffb485Schristos
15*4b169a6bSchristos You should have received a copy of the GNU Lesser General Public License
168dffb485Schristos along with this program. If not, see <https://www.gnu.org/licenses/>. */
178dffb485Schristos
188dffb485Schristos /* Written by Eric Blake <ebb9@byu.net>. */
198dffb485Schristos
208dffb485Schristos #include <config.h>
218dffb485Schristos
228dffb485Schristos /* Specification. */
238dffb485Schristos #include <fcntl.h>
248dffb485Schristos
258dffb485Schristos #include <errno.h>
268dffb485Schristos #include <limits.h>
278dffb485Schristos #include <stdarg.h>
288dffb485Schristos #include <stdlib.h>
298dffb485Schristos #include <unistd.h>
308dffb485Schristos
318dffb485Schristos #ifdef __KLIBC__
328dffb485Schristos # define INCL_DOS
338dffb485Schristos # include <os2.h>
348dffb485Schristos #endif
358dffb485Schristos
368dffb485Schristos #if defined _WIN32 && ! defined __CYGWIN__
378dffb485Schristos /* Get declarations of the native Windows API functions. */
388dffb485Schristos # define WIN32_LEAN_AND_MEAN
398dffb485Schristos # include <windows.h>
408dffb485Schristos
418dffb485Schristos /* Get _get_osfhandle. */
428dffb485Schristos # if GNULIB_MSVC_NOTHROW
438dffb485Schristos # include "msvc-nothrow.h"
448dffb485Schristos # else
458dffb485Schristos # include <io.h>
468dffb485Schristos # endif
478dffb485Schristos
488dffb485Schristos /* Upper bound on getdtablesize(). See lib/getdtablesize.c. */
498dffb485Schristos # define OPEN_MAX_MAX 0x10000
508dffb485Schristos
518dffb485Schristos /* Duplicate OLDFD into the first available slot of at least NEWFD,
528dffb485Schristos which must be positive, with FLAGS determining whether the duplicate
538dffb485Schristos will be inheritable. */
548dffb485Schristos static int
dupfd(int oldfd,int newfd,int flags)558dffb485Schristos dupfd (int oldfd, int newfd, int flags)
568dffb485Schristos {
578dffb485Schristos /* Mingw has no way to create an arbitrary fd. Iterate until all
588dffb485Schristos file descriptors less than newfd are filled up. */
598dffb485Schristos HANDLE curr_process = GetCurrentProcess ();
608dffb485Schristos HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
618dffb485Schristos unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
628dffb485Schristos unsigned int fds_to_close_bound = 0;
638dffb485Schristos int result;
648dffb485Schristos BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE;
658dffb485Schristos int mode;
668dffb485Schristos
678dffb485Schristos if (newfd < 0 || getdtablesize () <= newfd)
688dffb485Schristos {
698dffb485Schristos errno = EINVAL;
708dffb485Schristos return -1;
718dffb485Schristos }
728dffb485Schristos if (old_handle == INVALID_HANDLE_VALUE
73*4b169a6bSchristos || (mode = _setmode (oldfd, O_BINARY)) == -1)
748dffb485Schristos {
758dffb485Schristos /* oldfd is not open, or is an unassigned standard file
768dffb485Schristos descriptor. */
778dffb485Schristos errno = EBADF;
788dffb485Schristos return -1;
798dffb485Schristos }
80*4b169a6bSchristos _setmode (oldfd, mode);
818dffb485Schristos flags |= mode;
828dffb485Schristos
838dffb485Schristos for (;;)
848dffb485Schristos {
858dffb485Schristos HANDLE new_handle;
868dffb485Schristos int duplicated_fd;
878dffb485Schristos unsigned int index;
888dffb485Schristos
898dffb485Schristos if (!DuplicateHandle (curr_process, /* SourceProcessHandle */
908dffb485Schristos old_handle, /* SourceHandle */
918dffb485Schristos curr_process, /* TargetProcessHandle */
928dffb485Schristos (PHANDLE) &new_handle, /* TargetHandle */
938dffb485Schristos (DWORD) 0, /* DesiredAccess */
948dffb485Schristos inherit, /* InheritHandle */
958dffb485Schristos DUPLICATE_SAME_ACCESS)) /* Options */
968dffb485Schristos {
978dffb485Schristos switch (GetLastError ())
988dffb485Schristos {
998dffb485Schristos case ERROR_TOO_MANY_OPEN_FILES:
1008dffb485Schristos errno = EMFILE;
1018dffb485Schristos break;
1028dffb485Schristos case ERROR_INVALID_HANDLE:
1038dffb485Schristos case ERROR_INVALID_TARGET_HANDLE:
1048dffb485Schristos case ERROR_DIRECT_ACCESS_HANDLE:
1058dffb485Schristos errno = EBADF;
1068dffb485Schristos break;
1078dffb485Schristos case ERROR_INVALID_PARAMETER:
1088dffb485Schristos case ERROR_INVALID_FUNCTION:
1098dffb485Schristos case ERROR_INVALID_ACCESS:
1108dffb485Schristos errno = EINVAL;
1118dffb485Schristos break;
1128dffb485Schristos default:
1138dffb485Schristos errno = EACCES;
1148dffb485Schristos break;
1158dffb485Schristos }
1168dffb485Schristos result = -1;
1178dffb485Schristos break;
1188dffb485Schristos }
1198dffb485Schristos duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags);
1208dffb485Schristos if (duplicated_fd < 0)
1218dffb485Schristos {
1228dffb485Schristos CloseHandle (new_handle);
1238dffb485Schristos result = -1;
1248dffb485Schristos break;
1258dffb485Schristos }
1268dffb485Schristos if (newfd <= duplicated_fd)
1278dffb485Schristos {
1288dffb485Schristos result = duplicated_fd;
1298dffb485Schristos break;
1308dffb485Schristos }
1318dffb485Schristos
1328dffb485Schristos /* Set the bit duplicated_fd in fds_to_close[]. */
1338dffb485Schristos index = (unsigned int) duplicated_fd / CHAR_BIT;
1348dffb485Schristos if (fds_to_close_bound <= index)
1358dffb485Schristos {
1368dffb485Schristos if (sizeof fds_to_close <= index)
1378dffb485Schristos /* Need to increase OPEN_MAX_MAX. */
1388dffb485Schristos abort ();
1398dffb485Schristos memset (fds_to_close + fds_to_close_bound, '\0',
1408dffb485Schristos index + 1 - fds_to_close_bound);
1418dffb485Schristos fds_to_close_bound = index + 1;
1428dffb485Schristos }
1438dffb485Schristos fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
1448dffb485Schristos }
1458dffb485Schristos
1468dffb485Schristos /* Close the previous fds that turned out to be too small. */
1478dffb485Schristos {
1488dffb485Schristos int saved_errno = errno;
1498dffb485Schristos unsigned int duplicated_fd;
1508dffb485Schristos
1518dffb485Schristos for (duplicated_fd = 0;
1528dffb485Schristos duplicated_fd < fds_to_close_bound * CHAR_BIT;
1538dffb485Schristos duplicated_fd++)
1548dffb485Schristos if ((fds_to_close[duplicated_fd / CHAR_BIT]
1558dffb485Schristos >> (duplicated_fd % CHAR_BIT))
1568dffb485Schristos & 1)
1578dffb485Schristos close (duplicated_fd);
1588dffb485Schristos
1598dffb485Schristos errno = saved_errno;
1608dffb485Schristos }
1618dffb485Schristos
1628dffb485Schristos # if REPLACE_FCHDIR
1638dffb485Schristos if (0 <= result)
1648dffb485Schristos result = _gl_register_dup (oldfd, result);
1658dffb485Schristos # endif
1668dffb485Schristos return result;
1678dffb485Schristos }
1688dffb485Schristos #endif /* W32 */
1698dffb485Schristos
1708dffb485Schristos /* Forward declarations, because we '#undef fcntl' in the middle of this
1718dffb485Schristos compilation unit. */
1728dffb485Schristos /* Our implementation of fcntl (fd, F_DUPFD, target). */
1738dffb485Schristos static int rpl_fcntl_DUPFD (int fd, int target);
1748dffb485Schristos /* Our implementation of fcntl (fd, F_DUPFD_CLOEXEC, target). */
1758dffb485Schristos static int rpl_fcntl_DUPFD_CLOEXEC (int fd, int target);
1768dffb485Schristos #ifdef __KLIBC__
1778dffb485Schristos /* Adds support for fcntl on directories. */
1788dffb485Schristos static int klibc_fcntl (int fd, int action, /* arg */...);
1798dffb485Schristos #endif
1808dffb485Schristos
1818dffb485Schristos
1828dffb485Schristos /* Perform the specified ACTION on the file descriptor FD, possibly
1838dffb485Schristos using the argument ARG further described below. This replacement
1848dffb485Schristos handles the following actions, and forwards all others on to the
1858dffb485Schristos native fcntl. An unrecognized ACTION returns -1 with errno set to
1868dffb485Schristos EINVAL.
1878dffb485Schristos
1888dffb485Schristos F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
1898dffb485Schristos If successful, return the duplicate, which will be inheritable;
1908dffb485Schristos otherwise return -1 and set errno.
1918dffb485Schristos
1928dffb485Schristos F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
1938dffb485Schristos target fd. If successful, return the duplicate, which will not be
1948dffb485Schristos inheritable; otherwise return -1 and set errno.
1958dffb485Schristos
1968dffb485Schristos F_GETFD - ARG need not be present. If successful, return a
1978dffb485Schristos non-negative value containing the descriptor flags of FD (only
1988dffb485Schristos FD_CLOEXEC is portable, but other flags may be present); otherwise
1998dffb485Schristos return -1 and set errno. */
2008dffb485Schristos
2018dffb485Schristos int
fcntl(int fd,int action,...)2028dffb485Schristos fcntl (int fd, int action, /* arg */...)
2038dffb485Schristos #undef fcntl
2048dffb485Schristos #ifdef __KLIBC__
2058dffb485Schristos # define fcntl klibc_fcntl
2068dffb485Schristos #endif
2078dffb485Schristos {
2088dffb485Schristos va_list arg;
2098dffb485Schristos int result = -1;
2108dffb485Schristos va_start (arg, action);
2118dffb485Schristos switch (action)
2128dffb485Schristos {
2138dffb485Schristos case F_DUPFD:
2148dffb485Schristos {
2158dffb485Schristos int target = va_arg (arg, int);
2168dffb485Schristos result = rpl_fcntl_DUPFD (fd, target);
2178dffb485Schristos break;
2188dffb485Schristos }
2198dffb485Schristos
2208dffb485Schristos case F_DUPFD_CLOEXEC:
2218dffb485Schristos {
2228dffb485Schristos int target = va_arg (arg, int);
2238dffb485Schristos result = rpl_fcntl_DUPFD_CLOEXEC (fd, target);
2248dffb485Schristos break;
2258dffb485Schristos }
2268dffb485Schristos
2278dffb485Schristos #if !HAVE_FCNTL
2288dffb485Schristos case F_GETFD:
2298dffb485Schristos {
2308dffb485Schristos # if defined _WIN32 && ! defined __CYGWIN__
2318dffb485Schristos HANDLE handle = (HANDLE) _get_osfhandle (fd);
2328dffb485Schristos DWORD flags;
2338dffb485Schristos if (handle == INVALID_HANDLE_VALUE
2348dffb485Schristos || GetHandleInformation (handle, &flags) == 0)
2358dffb485Schristos errno = EBADF;
2368dffb485Schristos else
2378dffb485Schristos result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
2388dffb485Schristos # else /* !W32 */
2398dffb485Schristos /* Use dup2 to reject invalid file descriptors. No way to
2408dffb485Schristos access this information, so punt. */
2418dffb485Schristos if (0 <= dup2 (fd, fd))
2428dffb485Schristos result = 0;
2438dffb485Schristos # endif /* !W32 */
2448dffb485Schristos break;
2458dffb485Schristos } /* F_GETFD */
2468dffb485Schristos #endif /* !HAVE_FCNTL */
2478dffb485Schristos
2488dffb485Schristos /* Implementing F_SETFD on mingw is not trivial - there is no
2498dffb485Schristos API for changing the O_NOINHERIT bit on an fd, and merely
2508dffb485Schristos changing the HANDLE_FLAG_INHERIT bit on the underlying handle
2518dffb485Schristos can lead to odd state. It may be possible by duplicating the
2528dffb485Schristos handle, using _open_osfhandle with the right flags, then
2538dffb485Schristos using dup2 to move the duplicate onto the original, but that
2548dffb485Schristos is not supported for now. */
2558dffb485Schristos
2568dffb485Schristos default:
2578dffb485Schristos {
2588dffb485Schristos #if HAVE_FCNTL
2598dffb485Schristos switch (action)
2608dffb485Schristos {
2618dffb485Schristos #ifdef F_BARRIERFSYNC /* macOS */
2628dffb485Schristos case F_BARRIERFSYNC:
2638dffb485Schristos #endif
2648dffb485Schristos #ifdef F_CHKCLEAN /* macOS */
2658dffb485Schristos case F_CHKCLEAN:
2668dffb485Schristos #endif
2678dffb485Schristos #ifdef F_CLOSEM /* NetBSD, HP-UX */
2688dffb485Schristos case F_CLOSEM:
2698dffb485Schristos #endif
2708dffb485Schristos #ifdef F_FLUSH_DATA /* macOS */
2718dffb485Schristos case F_FLUSH_DATA:
2728dffb485Schristos #endif
2738dffb485Schristos #ifdef F_FREEZE_FS /* macOS */
2748dffb485Schristos case F_FREEZE_FS:
2758dffb485Schristos #endif
2768dffb485Schristos #ifdef F_FULLFSYNC /* macOS */
2778dffb485Schristos case F_FULLFSYNC:
2788dffb485Schristos #endif
2798dffb485Schristos #ifdef F_GETCONFINED /* macOS */
2808dffb485Schristos case F_GETCONFINED:
2818dffb485Schristos #endif
2828dffb485Schristos #ifdef F_GETDEFAULTPROTLEVEL /* macOS */
2838dffb485Schristos case F_GETDEFAULTPROTLEVEL:
2848dffb485Schristos #endif
2858dffb485Schristos #ifdef F_GETFD /* POSIX */
2868dffb485Schristos case F_GETFD:
2878dffb485Schristos #endif
2888dffb485Schristos #ifdef F_GETFL /* POSIX */
2898dffb485Schristos case F_GETFL:
2908dffb485Schristos #endif
2918dffb485Schristos #ifdef F_GETLEASE /* Linux */
2928dffb485Schristos case F_GETLEASE:
2938dffb485Schristos #endif
2948dffb485Schristos #ifdef F_GETNOSIGPIPE /* macOS */
2958dffb485Schristos case F_GETNOSIGPIPE:
2968dffb485Schristos #endif
2978dffb485Schristos #ifdef F_GETOWN /* POSIX */
2988dffb485Schristos case F_GETOWN:
2998dffb485Schristos #endif
3008dffb485Schristos #ifdef F_GETPIPE_SZ /* Linux */
3018dffb485Schristos case F_GETPIPE_SZ:
3028dffb485Schristos #endif
3038dffb485Schristos #ifdef F_GETPROTECTIONCLASS /* macOS */
3048dffb485Schristos case F_GETPROTECTIONCLASS:
3058dffb485Schristos #endif
3068dffb485Schristos #ifdef F_GETPROTECTIONLEVEL /* macOS */
3078dffb485Schristos case F_GETPROTECTIONLEVEL:
3088dffb485Schristos #endif
3098dffb485Schristos #ifdef F_GET_SEALS /* Linux */
3108dffb485Schristos case F_GET_SEALS:
3118dffb485Schristos #endif
3128dffb485Schristos #ifdef F_GETSIG /* Linux */
3138dffb485Schristos case F_GETSIG:
3148dffb485Schristos #endif
3158dffb485Schristos #ifdef F_MAXFD /* NetBSD */
3168dffb485Schristos case F_MAXFD:
3178dffb485Schristos #endif
3188dffb485Schristos #ifdef F_RECYCLE /* macOS */
3198dffb485Schristos case F_RECYCLE:
3208dffb485Schristos #endif
3218dffb485Schristos #ifdef F_SETFIFOENH /* HP-UX */
3228dffb485Schristos case F_SETFIFOENH:
3238dffb485Schristos #endif
3248dffb485Schristos #ifdef F_THAW_FS /* macOS */
3258dffb485Schristos case F_THAW_FS:
3268dffb485Schristos #endif
3278dffb485Schristos /* These actions take no argument. */
3288dffb485Schristos result = fcntl (fd, action);
3298dffb485Schristos break;
3308dffb485Schristos
3318dffb485Schristos #ifdef F_ADD_SEALS /* Linux */
3328dffb485Schristos case F_ADD_SEALS:
3338dffb485Schristos #endif
3348dffb485Schristos #ifdef F_BADFD /* Solaris */
3358dffb485Schristos case F_BADFD:
3368dffb485Schristos #endif
3378dffb485Schristos #ifdef F_CHECK_OPENEVT /* macOS */
3388dffb485Schristos case F_CHECK_OPENEVT:
3398dffb485Schristos #endif
3408dffb485Schristos #ifdef F_DUP2FD /* FreeBSD, AIX, Solaris */
3418dffb485Schristos case F_DUP2FD:
3428dffb485Schristos #endif
3438dffb485Schristos #ifdef F_DUP2FD_CLOEXEC /* FreeBSD, Solaris */
3448dffb485Schristos case F_DUP2FD_CLOEXEC:
3458dffb485Schristos #endif
3468dffb485Schristos #ifdef F_DUP2FD_CLOFORK /* Solaris */
3478dffb485Schristos case F_DUP2FD_CLOFORK:
3488dffb485Schristos #endif
3498dffb485Schristos #ifdef F_DUPFD /* POSIX */
3508dffb485Schristos case F_DUPFD:
3518dffb485Schristos #endif
3528dffb485Schristos #ifdef F_DUPFD_CLOEXEC /* POSIX */
3538dffb485Schristos case F_DUPFD_CLOEXEC:
3548dffb485Schristos #endif
3558dffb485Schristos #ifdef F_DUPFD_CLOFORK /* Solaris */
3568dffb485Schristos case F_DUPFD_CLOFORK:
3578dffb485Schristos #endif
3588dffb485Schristos #ifdef F_GETXFL /* Solaris */
3598dffb485Schristos case F_GETXFL:
3608dffb485Schristos #endif
3618dffb485Schristos #ifdef F_GLOBAL_NOCACHE /* macOS */
3628dffb485Schristos case F_GLOBAL_NOCACHE:
3638dffb485Schristos #endif
3648dffb485Schristos #ifdef F_MAKECOMPRESSED /* macOS */
3658dffb485Schristos case F_MAKECOMPRESSED:
3668dffb485Schristos #endif
3678dffb485Schristos #ifdef F_MOVEDATAEXTENTS /* macOS */
3688dffb485Schristos case F_MOVEDATAEXTENTS:
3698dffb485Schristos #endif
3708dffb485Schristos #ifdef F_NOCACHE /* macOS */
3718dffb485Schristos case F_NOCACHE:
3728dffb485Schristos #endif
3738dffb485Schristos #ifdef F_NODIRECT /* macOS */
3748dffb485Schristos case F_NODIRECT:
3758dffb485Schristos #endif
3768dffb485Schristos #ifdef F_NOTIFY /* Linux */
3778dffb485Schristos case F_NOTIFY:
3788dffb485Schristos #endif
3798dffb485Schristos #ifdef F_OPLKACK /* IRIX */
3808dffb485Schristos case F_OPLKACK:
3818dffb485Schristos #endif
3828dffb485Schristos #ifdef F_OPLKREG /* IRIX */
3838dffb485Schristos case F_OPLKREG:
3848dffb485Schristos #endif
3858dffb485Schristos #ifdef F_RDAHEAD /* macOS */
3868dffb485Schristos case F_RDAHEAD:
3878dffb485Schristos #endif
3888dffb485Schristos #ifdef F_SETBACKINGSTORE /* macOS */
3898dffb485Schristos case F_SETBACKINGSTORE:
3908dffb485Schristos #endif
3918dffb485Schristos #ifdef F_SETCONFINED /* macOS */
3928dffb485Schristos case F_SETCONFINED:
3938dffb485Schristos #endif
3948dffb485Schristos #ifdef F_SETFD /* POSIX */
3958dffb485Schristos case F_SETFD:
3968dffb485Schristos #endif
3978dffb485Schristos #ifdef F_SETFL /* POSIX */
3988dffb485Schristos case F_SETFL:
3998dffb485Schristos #endif
4008dffb485Schristos #ifdef F_SETLEASE /* Linux */
4018dffb485Schristos case F_SETLEASE:
4028dffb485Schristos #endif
4038dffb485Schristos #ifdef F_SETNOSIGPIPE /* macOS */
4048dffb485Schristos case F_SETNOSIGPIPE:
4058dffb485Schristos #endif
4068dffb485Schristos #ifdef F_SETOWN /* POSIX */
4078dffb485Schristos case F_SETOWN:
4088dffb485Schristos #endif
4098dffb485Schristos #ifdef F_SETPIPE_SZ /* Linux */
4108dffb485Schristos case F_SETPIPE_SZ:
4118dffb485Schristos #endif
4128dffb485Schristos #ifdef F_SETPROTECTIONCLASS /* macOS */
4138dffb485Schristos case F_SETPROTECTIONCLASS:
4148dffb485Schristos #endif
4158dffb485Schristos #ifdef F_SETSIG /* Linux */
4168dffb485Schristos case F_SETSIG:
4178dffb485Schristos #endif
4188dffb485Schristos #ifdef F_SINGLE_WRITER /* macOS */
4198dffb485Schristos case F_SINGLE_WRITER:
4208dffb485Schristos #endif
4218dffb485Schristos /* These actions take an 'int' argument. */
4228dffb485Schristos {
4238dffb485Schristos int x = va_arg (arg, int);
4248dffb485Schristos result = fcntl (fd, action, x);
4258dffb485Schristos }
4268dffb485Schristos break;
4278dffb485Schristos
4288dffb485Schristos default:
4298dffb485Schristos /* Other actions take a pointer argument. */
4308dffb485Schristos {
4318dffb485Schristos void *p = va_arg (arg, void *);
4328dffb485Schristos result = fcntl (fd, action, p);
4338dffb485Schristos }
4348dffb485Schristos break;
4358dffb485Schristos }
4368dffb485Schristos #else
4378dffb485Schristos errno = EINVAL;
4388dffb485Schristos #endif
4398dffb485Schristos break;
4408dffb485Schristos }
4418dffb485Schristos }
4428dffb485Schristos va_end (arg);
4438dffb485Schristos return result;
4448dffb485Schristos }
4458dffb485Schristos
4468dffb485Schristos static int
rpl_fcntl_DUPFD(int fd,int target)4478dffb485Schristos rpl_fcntl_DUPFD (int fd, int target)
4488dffb485Schristos {
4498dffb485Schristos int result;
4508dffb485Schristos #if !HAVE_FCNTL
4518dffb485Schristos result = dupfd (fd, target, 0);
4528dffb485Schristos #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
4538dffb485Schristos /* Detect invalid target; needed for cygwin 1.5.x. */
4548dffb485Schristos if (target < 0 || getdtablesize () <= target)
4558dffb485Schristos {
4568dffb485Schristos result = -1;
4578dffb485Schristos errno = EINVAL;
4588dffb485Schristos }
4598dffb485Schristos else
4608dffb485Schristos {
4618dffb485Schristos /* Haiku alpha 2 loses fd flags on original. */
4628dffb485Schristos int flags = fcntl (fd, F_GETFD);
4638dffb485Schristos if (flags < 0)
4648dffb485Schristos result = -1;
4658dffb485Schristos else
4668dffb485Schristos {
4678dffb485Schristos result = fcntl (fd, F_DUPFD, target);
4688dffb485Schristos if (0 <= result && fcntl (fd, F_SETFD, flags) == -1)
4698dffb485Schristos {
4708dffb485Schristos int saved_errno = errno;
4718dffb485Schristos close (result);
4728dffb485Schristos result = -1;
4738dffb485Schristos errno = saved_errno;
4748dffb485Schristos }
4758dffb485Schristos # if REPLACE_FCHDIR
4768dffb485Schristos if (0 <= result)
4778dffb485Schristos result = _gl_register_dup (fd, result);
4788dffb485Schristos # endif
4798dffb485Schristos }
4808dffb485Schristos }
4818dffb485Schristos #else
4828dffb485Schristos result = fcntl (fd, F_DUPFD, target);
4838dffb485Schristos #endif
4848dffb485Schristos return result;
4858dffb485Schristos }
4868dffb485Schristos
4878dffb485Schristos static int
rpl_fcntl_DUPFD_CLOEXEC(int fd,int target)4888dffb485Schristos rpl_fcntl_DUPFD_CLOEXEC (int fd, int target)
4898dffb485Schristos {
4908dffb485Schristos int result;
4918dffb485Schristos #if !HAVE_FCNTL
4928dffb485Schristos result = dupfd (fd, target, O_CLOEXEC);
4938dffb485Schristos #else /* HAVE_FCNTL */
494*4b169a6bSchristos # if defined __NetBSD__ || defined __HAIKU__
495*4b169a6bSchristos /* On NetBSD 9.0, the system fcntl (fd, F_DUPFD_CLOEXEC, target)
496*4b169a6bSchristos has only the same effect as fcntl (fd, F_DUPFD, target). */
4978dffb485Schristos /* On Haiku, the system fcntl (fd, F_DUPFD_CLOEXEC, target) sets
4988dffb485Schristos the FD_CLOEXEC flag on fd, not on target. Therefore avoid the
4998dffb485Schristos system fcntl in this case. */
5008dffb485Schristos # define have_dupfd_cloexec -1
5018dffb485Schristos # else
5028dffb485Schristos /* Try the system call first, if the headers claim it exists
5038dffb485Schristos (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
5048dffb485Schristos may be running with a glibc that has the macro but with an
5058dffb485Schristos older kernel that does not support it. Cache the
5068dffb485Schristos information on whether the system call really works, but
5078dffb485Schristos avoid caching failure if the corresponding F_DUPFD fails
5088dffb485Schristos for any reason. 0 = unknown, 1 = yes, -1 = no. */
5098dffb485Schristos static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
5108dffb485Schristos if (0 <= have_dupfd_cloexec)
5118dffb485Schristos {
5128dffb485Schristos result = fcntl (fd, F_DUPFD_CLOEXEC, target);
5138dffb485Schristos if (0 <= result || errno != EINVAL)
5148dffb485Schristos {
5158dffb485Schristos have_dupfd_cloexec = 1;
5168dffb485Schristos # if REPLACE_FCHDIR
5178dffb485Schristos if (0 <= result)
5188dffb485Schristos result = _gl_register_dup (fd, result);
5198dffb485Schristos # endif
5208dffb485Schristos }
5218dffb485Schristos else
5228dffb485Schristos {
5238dffb485Schristos result = rpl_fcntl_DUPFD (fd, target);
5248dffb485Schristos if (result >= 0)
5258dffb485Schristos have_dupfd_cloexec = -1;
5268dffb485Schristos }
5278dffb485Schristos }
5288dffb485Schristos else
5298dffb485Schristos # endif
5308dffb485Schristos result = rpl_fcntl_DUPFD (fd, target);
5318dffb485Schristos if (0 <= result && have_dupfd_cloexec == -1)
5328dffb485Schristos {
5338dffb485Schristos int flags = fcntl (result, F_GETFD);
5348dffb485Schristos if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
5358dffb485Schristos {
5368dffb485Schristos int saved_errno = errno;
5378dffb485Schristos close (result);
5388dffb485Schristos errno = saved_errno;
5398dffb485Schristos result = -1;
5408dffb485Schristos }
5418dffb485Schristos }
5428dffb485Schristos #endif /* HAVE_FCNTL */
5438dffb485Schristos return result;
5448dffb485Schristos }
5458dffb485Schristos
5468dffb485Schristos #undef fcntl
5478dffb485Schristos
5488dffb485Schristos #ifdef __KLIBC__
5498dffb485Schristos
5508dffb485Schristos static int
klibc_fcntl(int fd,int action,...)5518dffb485Schristos klibc_fcntl (int fd, int action, /* arg */...)
5528dffb485Schristos {
5538dffb485Schristos va_list arg_ptr;
5548dffb485Schristos int arg;
5558dffb485Schristos struct stat sbuf;
5568dffb485Schristos int result;
5578dffb485Schristos
5588dffb485Schristos va_start (arg_ptr, action);
5598dffb485Schristos arg = va_arg (arg_ptr, int);
5608dffb485Schristos result = fcntl (fd, action, arg);
5618dffb485Schristos /* EPERM for F_DUPFD, ENOTSUP for others */
5628dffb485Schristos if (result == -1 && (errno == EPERM || errno == ENOTSUP)
5638dffb485Schristos && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
5648dffb485Schristos {
5658dffb485Schristos ULONG ulMode;
5668dffb485Schristos
5678dffb485Schristos switch (action)
5688dffb485Schristos {
5698dffb485Schristos case F_DUPFD:
5708dffb485Schristos /* Find available fd */
5718dffb485Schristos while (fcntl (arg, F_GETFL) != -1 || errno != EBADF)
5728dffb485Schristos arg++;
5738dffb485Schristos
5748dffb485Schristos result = dup2 (fd, arg);
5758dffb485Schristos break;
5768dffb485Schristos
5778dffb485Schristos /* Using underlying APIs is right ? */
5788dffb485Schristos case F_GETFD:
5798dffb485Schristos if (DosQueryFHState (fd, &ulMode))
5808dffb485Schristos break;
5818dffb485Schristos
5828dffb485Schristos result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0;
5838dffb485Schristos break;
5848dffb485Schristos
5858dffb485Schristos case F_SETFD:
5868dffb485Schristos if (arg & ~FD_CLOEXEC)
5878dffb485Schristos break;
5888dffb485Schristos
5898dffb485Schristos if (DosQueryFHState (fd, &ulMode))
5908dffb485Schristos break;
5918dffb485Schristos
5928dffb485Schristos if (arg & FD_CLOEXEC)
5938dffb485Schristos ulMode |= OPEN_FLAGS_NOINHERIT;
5948dffb485Schristos else
5958dffb485Schristos ulMode &= ~OPEN_FLAGS_NOINHERIT;
5968dffb485Schristos
5978dffb485Schristos /* Filter supported flags. */
5988dffb485Schristos ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR
5998dffb485Schristos | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT);
6008dffb485Schristos
6018dffb485Schristos if (DosSetFHState (fd, ulMode))
6028dffb485Schristos break;
6038dffb485Schristos
6048dffb485Schristos result = 0;
6058dffb485Schristos break;
6068dffb485Schristos
6078dffb485Schristos case F_GETFL:
6088dffb485Schristos result = 0;
6098dffb485Schristos break;
6108dffb485Schristos
6118dffb485Schristos case F_SETFL:
6128dffb485Schristos if (arg != 0)
6138dffb485Schristos break;
6148dffb485Schristos
6158dffb485Schristos result = 0;
6168dffb485Schristos break;
6178dffb485Schristos
6188dffb485Schristos default:
6198dffb485Schristos errno = EINVAL;
6208dffb485Schristos break;
6218dffb485Schristos }
6228dffb485Schristos }
6238dffb485Schristos
6248dffb485Schristos va_end (arg_ptr);
6258dffb485Schristos
6268dffb485Schristos return result;
6278dffb485Schristos }
6288dffb485Schristos
6298dffb485Schristos #endif
630