fcntl.c revision 1.1.1.1 1 1.1 christos /* Provide file descriptor control.
2 1.1 christos
3 1.1 christos Copyright (C) 2009-2020 Free Software Foundation, Inc.
4 1.1 christos
5 1.1 christos This program is free software: you can redistribute it and/or modify
6 1.1 christos it under the terms of the GNU General Public License as published by
7 1.1 christos the Free Software Foundation; either version 3 of the License, or
8 1.1 christos (at your option) any later version.
9 1.1 christos
10 1.1 christos This program 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 christos GNU General Public License for more details.
14 1.1 christos
15 1.1 christos You should have received a copy of the GNU 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 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 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 christos # if defined __HAIKU__
495 1.1 christos /* On Haiku, the system fcntl (fd, F_DUPFD_CLOEXEC, target) sets
496 1.1 christos the FD_CLOEXEC flag on fd, not on target. Therefore avoid the
497 1.1 christos system fcntl in this case. */
498 1.1 christos # define have_dupfd_cloexec -1
499 1.1 christos # else
500 1.1 christos /* Try the system call first, if the headers claim it exists
501 1.1 christos (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
502 1.1 christos may be running with a glibc that has the macro but with an
503 1.1 christos older kernel that does not support it. Cache the
504 1.1 christos information on whether the system call really works, but
505 1.1 christos avoid caching failure if the corresponding F_DUPFD fails
506 1.1 christos for any reason. 0 = unknown, 1 = yes, -1 = no. */
507 1.1 christos static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0;
508 1.1 christos if (0 <= have_dupfd_cloexec)
509 1.1 christos {
510 1.1 christos result = fcntl (fd, F_DUPFD_CLOEXEC, target);
511 1.1 christos if (0 <= result || errno != EINVAL)
512 1.1 christos {
513 1.1 christos have_dupfd_cloexec = 1;
514 1.1 christos # if REPLACE_FCHDIR
515 1.1 christos if (0 <= result)
516 1.1 christos result = _gl_register_dup (fd, result);
517 1.1 christos # endif
518 1.1 christos }
519 1.1 christos else
520 1.1 christos {
521 1.1 christos result = rpl_fcntl_DUPFD (fd, target);
522 1.1 christos if (result >= 0)
523 1.1 christos have_dupfd_cloexec = -1;
524 1.1 christos }
525 1.1 christos }
526 1.1 christos else
527 1.1 christos # endif
528 1.1 christos result = rpl_fcntl_DUPFD (fd, target);
529 1.1 christos if (0 <= result && have_dupfd_cloexec == -1)
530 1.1 christos {
531 1.1 christos int flags = fcntl (result, F_GETFD);
532 1.1 christos if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
533 1.1 christos {
534 1.1 christos int saved_errno = errno;
535 1.1 christos close (result);
536 1.1 christos errno = saved_errno;
537 1.1 christos result = -1;
538 1.1 christos }
539 1.1 christos }
540 1.1 christos #endif /* HAVE_FCNTL */
541 1.1 christos return result;
542 1.1 christos }
543 1.1 christos
544 1.1 christos #undef fcntl
545 1.1 christos
546 1.1 christos #ifdef __KLIBC__
547 1.1 christos
548 1.1 christos static int
549 1.1 christos klibc_fcntl (int fd, int action, /* arg */...)
550 1.1 christos {
551 1.1 christos va_list arg_ptr;
552 1.1 christos int arg;
553 1.1 christos struct stat sbuf;
554 1.1 christos int result;
555 1.1 christos
556 1.1 christos va_start (arg_ptr, action);
557 1.1 christos arg = va_arg (arg_ptr, int);
558 1.1 christos result = fcntl (fd, action, arg);
559 1.1 christos /* EPERM for F_DUPFD, ENOTSUP for others */
560 1.1 christos if (result == -1 && (errno == EPERM || errno == ENOTSUP)
561 1.1 christos && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode))
562 1.1 christos {
563 1.1 christos ULONG ulMode;
564 1.1 christos
565 1.1 christos switch (action)
566 1.1 christos {
567 1.1 christos case F_DUPFD:
568 1.1 christos /* Find available fd */
569 1.1 christos while (fcntl (arg, F_GETFL) != -1 || errno != EBADF)
570 1.1 christos arg++;
571 1.1 christos
572 1.1 christos result = dup2 (fd, arg);
573 1.1 christos break;
574 1.1 christos
575 1.1 christos /* Using underlying APIs is right ? */
576 1.1 christos case F_GETFD:
577 1.1 christos if (DosQueryFHState (fd, &ulMode))
578 1.1 christos break;
579 1.1 christos
580 1.1 christos result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0;
581 1.1 christos break;
582 1.1 christos
583 1.1 christos case F_SETFD:
584 1.1 christos if (arg & ~FD_CLOEXEC)
585 1.1 christos break;
586 1.1 christos
587 1.1 christos if (DosQueryFHState (fd, &ulMode))
588 1.1 christos break;
589 1.1 christos
590 1.1 christos if (arg & FD_CLOEXEC)
591 1.1 christos ulMode |= OPEN_FLAGS_NOINHERIT;
592 1.1 christos else
593 1.1 christos ulMode &= ~OPEN_FLAGS_NOINHERIT;
594 1.1 christos
595 1.1 christos /* Filter supported flags. */
596 1.1 christos ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR
597 1.1 christos | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT);
598 1.1 christos
599 1.1 christos if (DosSetFHState (fd, ulMode))
600 1.1 christos break;
601 1.1 christos
602 1.1 christos result = 0;
603 1.1 christos break;
604 1.1 christos
605 1.1 christos case F_GETFL:
606 1.1 christos result = 0;
607 1.1 christos break;
608 1.1 christos
609 1.1 christos case F_SETFL:
610 1.1 christos if (arg != 0)
611 1.1 christos break;
612 1.1 christos
613 1.1 christos result = 0;
614 1.1 christos break;
615 1.1 christos
616 1.1 christos default:
617 1.1 christos errno = EINVAL;
618 1.1 christos break;
619 1.1 christos }
620 1.1 christos }
621 1.1 christos
622 1.1 christos va_end (arg_ptr);
623 1.1 christos
624 1.1 christos return result;
625 1.1 christos }
626 1.1 christos
627 1.1 christos #endif
628