Line | Count | Source |
1 | | /* Provide file descriptor control. |
2 | | |
3 | | Copyright (C) 2009-2026 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 | | # include <emx/io.h> |
33 | | # include <InnoTekLIBC/backend.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 | | |
60 | | if (newfd < 0 || getdtablesize () <= newfd) |
61 | | { |
62 | | errno = EINVAL; |
63 | | return -1; |
64 | | } |
65 | | |
66 | | HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd); |
67 | | int mode; |
68 | | if (old_handle == INVALID_HANDLE_VALUE |
69 | | || (mode = _setmode (oldfd, O_BINARY)) == -1) |
70 | | { |
71 | | /* oldfd is not open, or is an unassigned standard file |
72 | | descriptor. */ |
73 | | errno = EBADF; |
74 | | return -1; |
75 | | } |
76 | | _setmode (oldfd, mode); |
77 | | flags |= mode; |
78 | | |
79 | | HANDLE curr_process = GetCurrentProcess (); |
80 | | BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE; |
81 | | unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT]; |
82 | | unsigned int fds_to_close_bound = 0; |
83 | | int result; |
84 | | for (;;) |
85 | | { |
86 | | HANDLE new_handle; |
87 | | int duplicated_fd; |
88 | | unsigned int index; |
89 | | |
90 | | if (!DuplicateHandle (curr_process, /* SourceProcessHandle */ |
91 | | old_handle, /* SourceHandle */ |
92 | | curr_process, /* TargetProcessHandle */ |
93 | | (PHANDLE) &new_handle, /* TargetHandle */ |
94 | | (DWORD) 0, /* DesiredAccess */ |
95 | | inherit, /* InheritHandle */ |
96 | | DUPLICATE_SAME_ACCESS)) /* Options */ |
97 | | { |
98 | | switch (GetLastError ()) |
99 | | { |
100 | | case ERROR_TOO_MANY_OPEN_FILES: |
101 | | errno = EMFILE; |
102 | | break; |
103 | | case ERROR_INVALID_HANDLE: |
104 | | case ERROR_INVALID_TARGET_HANDLE: |
105 | | case ERROR_DIRECT_ACCESS_HANDLE: |
106 | | errno = EBADF; |
107 | | break; |
108 | | case ERROR_INVALID_PARAMETER: |
109 | | case ERROR_INVALID_FUNCTION: |
110 | | case ERROR_INVALID_ACCESS: |
111 | | errno = EINVAL; |
112 | | break; |
113 | | default: |
114 | | errno = EACCES; |
115 | | break; |
116 | | } |
117 | | result = -1; |
118 | | break; |
119 | | } |
120 | | duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags); |
121 | | if (duplicated_fd < 0) |
122 | | { |
123 | | CloseHandle (new_handle); |
124 | | result = -1; |
125 | | break; |
126 | | } |
127 | | if (newfd <= duplicated_fd) |
128 | | { |
129 | | result = duplicated_fd; |
130 | | break; |
131 | | } |
132 | | |
133 | | /* Set the bit duplicated_fd in fds_to_close[]. */ |
134 | | index = (unsigned int) duplicated_fd / CHAR_BIT; |
135 | | if (fds_to_close_bound <= index) |
136 | | { |
137 | | if (sizeof fds_to_close <= index) |
138 | | /* Need to increase OPEN_MAX_MAX. */ |
139 | | abort (); |
140 | | memset (fds_to_close + fds_to_close_bound, '\0', |
141 | | index + 1 - fds_to_close_bound); |
142 | | fds_to_close_bound = index + 1; |
143 | | } |
144 | | fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT); |
145 | | } |
146 | | |
147 | | /* Close the previous fds that turned out to be too small. */ |
148 | | { |
149 | | int saved_errno = errno; |
150 | | |
151 | | for (unsigned int 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 | 7.77k | { |
208 | 7.77k | va_list arg; |
209 | 7.77k | va_start (arg, action); |
210 | | |
211 | 7.77k | int result = -1; |
212 | 7.77k | switch (action) |
213 | 7.77k | { |
214 | 0 | case F_DUPFD: |
215 | 0 | { |
216 | 0 | int target = va_arg (arg, int); |
217 | 0 | result = rpl_fcntl_DUPFD (fd, target); |
218 | 0 | break; |
219 | 0 | } |
220 | | |
221 | 0 | case F_DUPFD_CLOEXEC: |
222 | 0 | { |
223 | 0 | int target = va_arg (arg, int); |
224 | 0 | result = rpl_fcntl_DUPFD_CLOEXEC (fd, target); |
225 | 0 | break; |
226 | 0 | } |
227 | | |
228 | | #if !HAVE_FCNTL |
229 | | case F_GETFD: |
230 | | { |
231 | | # if defined _WIN32 && ! defined __CYGWIN__ |
232 | | HANDLE handle = (HANDLE) _get_osfhandle (fd); |
233 | | DWORD flags; |
234 | | if (handle == INVALID_HANDLE_VALUE |
235 | | || GetHandleInformation (handle, &flags) == 0) |
236 | | errno = EBADF; |
237 | | else |
238 | | result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC; |
239 | | # else /* !W32 */ |
240 | | /* Use dup2 to reject invalid file descriptors. No way to |
241 | | access this information, so punt. */ |
242 | | if (0 <= dup2 (fd, fd)) |
243 | | result = 0; |
244 | | # endif /* !W32 */ |
245 | | break; |
246 | | } /* F_GETFD */ |
247 | | #endif /* !HAVE_FCNTL */ |
248 | | |
249 | | /* Implementing F_SETFD on mingw is not trivial - there is no |
250 | | API for changing the O_NOINHERIT bit on an fd, and merely |
251 | | changing the HANDLE_FLAG_INHERIT bit on the underlying handle |
252 | | can lead to odd state. It may be possible by duplicating the |
253 | | handle, using _open_osfhandle with the right flags, then |
254 | | using dup2 to move the duplicate onto the original, but that |
255 | | is not supported for now. */ |
256 | | |
257 | 7.77k | default: |
258 | 7.77k | { |
259 | 7.77k | #if HAVE_FCNTL |
260 | 7.77k | switch (action) |
261 | 7.77k | { |
262 | | #ifdef F_BARRIERFSYNC /* macOS */ |
263 | | case F_BARRIERFSYNC: |
264 | | #endif |
265 | | #ifdef F_CHKCLEAN /* macOS */ |
266 | | case F_CHKCLEAN: |
267 | | #endif |
268 | | #ifdef F_CLOSEM /* NetBSD, HP-UX */ |
269 | | case F_CLOSEM: |
270 | | #endif |
271 | | #ifdef F_FLUSH_DATA /* macOS */ |
272 | | case F_FLUSH_DATA: |
273 | | #endif |
274 | | #ifdef F_FREEZE_FS /* macOS */ |
275 | | case F_FREEZE_FS: |
276 | | #endif |
277 | | #ifdef F_FULLFSYNC /* macOS */ |
278 | | case F_FULLFSYNC: |
279 | | #endif |
280 | | #ifdef F_GETCONFINED /* macOS */ |
281 | | case F_GETCONFINED: |
282 | | #endif |
283 | | #ifdef F_GETDEFAULTPROTLEVEL /* macOS */ |
284 | | case F_GETDEFAULTPROTLEVEL: |
285 | | #endif |
286 | 0 | #ifdef F_GETFD /* POSIX */ |
287 | 0 | case F_GETFD: |
288 | 0 | #endif |
289 | 0 | #ifdef F_GETFL /* POSIX */ |
290 | 3.88k | case F_GETFL: |
291 | 3.88k | #endif |
292 | 3.88k | #ifdef F_GETLEASE /* Linux */ |
293 | 3.88k | case F_GETLEASE: |
294 | 3.88k | #endif |
295 | | #ifdef F_GETNOSIGPIPE /* macOS */ |
296 | | case F_GETNOSIGPIPE: |
297 | | #endif |
298 | 3.88k | #ifdef F_GETOWN /* POSIX */ |
299 | 3.88k | case F_GETOWN: |
300 | 3.88k | #endif |
301 | 3.88k | #ifdef F_GETPIPE_SZ /* Linux */ |
302 | 3.88k | case F_GETPIPE_SZ: |
303 | 3.88k | #endif |
304 | | #ifdef F_GETPROTECTIONCLASS /* macOS */ |
305 | | case F_GETPROTECTIONCLASS: |
306 | | #endif |
307 | | #ifdef F_GETPROTECTIONLEVEL /* macOS */ |
308 | | case F_GETPROTECTIONLEVEL: |
309 | | #endif |
310 | 3.88k | #ifdef F_GET_SEALS /* Linux */ |
311 | 3.88k | case F_GET_SEALS: |
312 | 3.88k | #endif |
313 | 3.88k | #ifdef F_GETSIG /* Linux */ |
314 | 3.88k | case F_GETSIG: |
315 | 3.88k | #endif |
316 | | #ifdef F_MAXFD /* NetBSD */ |
317 | | case F_MAXFD: |
318 | | #endif |
319 | | #ifdef F_RECYCLE /* macOS */ |
320 | | case F_RECYCLE: |
321 | | #endif |
322 | | #ifdef F_SETFIFOENH /* HP-UX */ |
323 | | case F_SETFIFOENH: |
324 | | #endif |
325 | | #ifdef F_THAW_FS /* macOS */ |
326 | | case F_THAW_FS: |
327 | | #endif |
328 | | /* These actions take no argument. */ |
329 | 3.88k | result = fcntl (fd, action); |
330 | 3.88k | break; |
331 | | |
332 | 0 | #ifdef F_ADD_SEALS /* Linux */ |
333 | 0 | case F_ADD_SEALS: |
334 | 0 | #endif |
335 | | #ifdef F_BADFD /* Solaris */ |
336 | | case F_BADFD: |
337 | | #endif |
338 | | #ifdef F_CHECK_OPENEVT /* macOS */ |
339 | | case F_CHECK_OPENEVT: |
340 | | #endif |
341 | | #ifdef F_DUP2FD /* FreeBSD, AIX, Solaris */ |
342 | | case F_DUP2FD: |
343 | | #endif |
344 | | #ifdef F_DUP2FD_CLOEXEC /* FreeBSD, Solaris */ |
345 | | case F_DUP2FD_CLOEXEC: |
346 | | #endif |
347 | | #ifdef F_DUP2FD_CLOFORK /* Solaris */ |
348 | | case F_DUP2FD_CLOFORK: |
349 | | #endif |
350 | 0 | #ifdef F_DUPFD /* POSIX */ |
351 | 0 | case F_DUPFD: |
352 | 0 | #endif |
353 | 0 | #ifdef F_DUPFD_CLOEXEC /* POSIX */ |
354 | 0 | case F_DUPFD_CLOEXEC: |
355 | 0 | #endif |
356 | | #ifdef F_DUPFD_CLOFORK /* Solaris */ |
357 | | case F_DUPFD_CLOFORK: |
358 | | #endif |
359 | | #ifdef F_GETXFL /* Solaris */ |
360 | | case F_GETXFL: |
361 | | #endif |
362 | | #ifdef F_GLOBAL_NOCACHE /* macOS */ |
363 | | case F_GLOBAL_NOCACHE: |
364 | | #endif |
365 | | #ifdef F_MAKECOMPRESSED /* macOS */ |
366 | | case F_MAKECOMPRESSED: |
367 | | #endif |
368 | | #ifdef F_MOVEDATAEXTENTS /* macOS */ |
369 | | case F_MOVEDATAEXTENTS: |
370 | | #endif |
371 | | #ifdef F_NOCACHE /* macOS */ |
372 | | case F_NOCACHE: |
373 | | #endif |
374 | | #ifdef F_NODIRECT /* macOS */ |
375 | | case F_NODIRECT: |
376 | | #endif |
377 | 0 | #ifdef F_NOTIFY /* Linux */ |
378 | 0 | case F_NOTIFY: |
379 | 0 | #endif |
380 | | #ifdef F_RDAHEAD /* macOS */ |
381 | | case F_RDAHEAD: |
382 | | #endif |
383 | | #ifdef F_SETBACKINGSTORE /* macOS */ |
384 | | case F_SETBACKINGSTORE: |
385 | | #endif |
386 | | #ifdef F_SETCONFINED /* macOS */ |
387 | | case F_SETCONFINED: |
388 | | #endif |
389 | 0 | #ifdef F_SETFD /* POSIX */ |
390 | 0 | case F_SETFD: |
391 | 0 | #endif |
392 | 0 | #ifdef F_SETFL /* POSIX */ |
393 | 3.88k | case F_SETFL: |
394 | 3.88k | #endif |
395 | 3.88k | #ifdef F_SETLEASE /* Linux */ |
396 | 3.88k | case F_SETLEASE: |
397 | 3.88k | #endif |
398 | | #ifdef F_SETNOSIGPIPE /* macOS */ |
399 | | case F_SETNOSIGPIPE: |
400 | | #endif |
401 | 3.88k | #ifdef F_SETOWN /* POSIX */ |
402 | 3.88k | case F_SETOWN: |
403 | 3.88k | #endif |
404 | 3.88k | #ifdef F_SETPIPE_SZ /* Linux */ |
405 | 3.88k | case F_SETPIPE_SZ: |
406 | 3.88k | #endif |
407 | | #ifdef F_SETPROTECTIONCLASS /* macOS */ |
408 | | case F_SETPROTECTIONCLASS: |
409 | | #endif |
410 | 3.88k | #ifdef F_SETSIG /* Linux */ |
411 | 3.88k | case F_SETSIG: |
412 | 3.88k | #endif |
413 | | #ifdef F_SINGLE_WRITER /* macOS */ |
414 | | case F_SINGLE_WRITER: |
415 | | #endif |
416 | | /* These actions take an 'int' argument. */ |
417 | 3.88k | { |
418 | 3.88k | int x = va_arg (arg, int); |
419 | 3.88k | result = fcntl (fd, action, x); |
420 | 3.88k | } |
421 | 3.88k | break; |
422 | | |
423 | 0 | default: |
424 | | /* Other actions take a pointer argument. */ |
425 | 0 | { |
426 | 0 | void *p = va_arg (arg, void *); |
427 | 0 | result = fcntl (fd, action, p); |
428 | 0 | } |
429 | 0 | break; |
430 | 7.77k | } |
431 | | #else |
432 | | errno = EINVAL; |
433 | | #endif |
434 | 7.77k | break; |
435 | 7.77k | } |
436 | 7.77k | } |
437 | | |
438 | 7.77k | va_end (arg); |
439 | | |
440 | 7.77k | return result; |
441 | 7.77k | } |
442 | | |
443 | | static int |
444 | | rpl_fcntl_DUPFD (int fd, int target) |
445 | 0 | { |
446 | 0 | int result; |
447 | | #if !HAVE_FCNTL |
448 | | result = dupfd (fd, target, 0); |
449 | | #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR |
450 | | /* Detect invalid target; needed for cygwin 1.5.x. */ |
451 | | if (target < 0 || getdtablesize () <= target) |
452 | | { |
453 | | result = -1; |
454 | | errno = EINVAL; |
455 | | } |
456 | | else |
457 | | { |
458 | | /* Haiku alpha 2 loses fd flags on original. */ |
459 | | int flags = fcntl (fd, F_GETFD); |
460 | | if (flags < 0) |
461 | | result = -1; |
462 | | else |
463 | | { |
464 | | result = fcntl (fd, F_DUPFD, target); |
465 | | if (0 <= result && fcntl (fd, F_SETFD, flags) == -1) |
466 | | { |
467 | | int saved_errno = errno; |
468 | | close (result); |
469 | | result = -1; |
470 | | errno = saved_errno; |
471 | | } |
472 | | # if REPLACE_FCHDIR |
473 | | if (0 <= result) |
474 | | result = _gl_register_dup (fd, result); |
475 | | # endif |
476 | | } |
477 | | } |
478 | | #else |
479 | 0 | result = fcntl (fd, F_DUPFD, target); |
480 | 0 | #endif |
481 | 0 | return result; |
482 | 0 | } |
483 | | |
484 | | static int |
485 | | rpl_fcntl_DUPFD_CLOEXEC (int fd, int target) |
486 | 0 | { |
487 | 0 | int result; |
488 | | #if !HAVE_FCNTL |
489 | | result = dupfd (fd, target, O_CLOEXEC); |
490 | | #else /* HAVE_FCNTL */ |
491 | | # if defined __NetBSD__ || defined __HAIKU__ |
492 | | /* On NetBSD 9.0, the system fcntl (fd, F_DUPFD_CLOEXEC, target) |
493 | | has only the same effect as fcntl (fd, F_DUPFD, target). */ |
494 | | /* On Haiku, the system fcntl (fd, F_DUPFD_CLOEXEC, target) sets |
495 | | the FD_CLOEXEC flag on fd, not on target. Therefore avoid the |
496 | | system fcntl in this case. */ |
497 | | # define have_dupfd_cloexec -1 |
498 | | # else |
499 | | /* Try the system call first, if the headers claim it exists |
500 | | (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we |
501 | | may be running with a glibc that has the macro but with an |
502 | | older kernel that does not support it. Cache the |
503 | | information on whether the system call really works, but |
504 | | avoid caching failure if the corresponding F_DUPFD fails |
505 | | for any reason. 0 = unknown, 1 = yes, -1 = no. */ |
506 | 0 | static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0; |
507 | 0 | if (0 <= have_dupfd_cloexec) |
508 | 0 | { |
509 | 0 | result = fcntl (fd, F_DUPFD_CLOEXEC, target); |
510 | 0 | if (0 <= result || errno != EINVAL) |
511 | 0 | { |
512 | 0 | have_dupfd_cloexec = 1; |
513 | | # if REPLACE_FCHDIR |
514 | | if (0 <= result) |
515 | | result = _gl_register_dup (fd, result); |
516 | | # endif |
517 | 0 | } |
518 | 0 | else |
519 | 0 | { |
520 | 0 | result = rpl_fcntl_DUPFD (fd, target); |
521 | 0 | if (result >= 0) |
522 | 0 | have_dupfd_cloexec = -1; |
523 | 0 | } |
524 | 0 | } |
525 | 0 | else |
526 | 0 | # endif |
527 | 0 | result = rpl_fcntl_DUPFD (fd, target); |
528 | 0 | if (0 <= result && have_dupfd_cloexec == -1) |
529 | 0 | { |
530 | 0 | int flags = fcntl (result, F_GETFD); |
531 | 0 | if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1) |
532 | 0 | { |
533 | 0 | int saved_errno = errno; |
534 | 0 | close (result); |
535 | | errno = saved_errno; |
536 | 0 | result = -1; |
537 | 0 | } |
538 | 0 | } |
539 | 0 | #endif /* HAVE_FCNTL */ |
540 | 0 | return result; |
541 | 0 | } |
542 | | |
543 | | #undef fcntl |
544 | | |
545 | | #ifdef __KLIBC__ |
546 | | static int |
547 | | klibc_dupdirfd (int fd, int minfd) |
548 | | { |
549 | | int tempfd = open ("NUL", O_RDONLY); |
550 | | if (tempfd == -1) |
551 | | return -1; |
552 | | |
553 | | if (tempfd >= minfd) |
554 | | { |
555 | | close (tempfd); |
556 | | |
557 | | char path[_MAX_PATH]; |
558 | | if (__libc_Back_ioFHToPath (fd, path, sizeof (path))) |
559 | | return -1; |
560 | | |
561 | | int dupfd = open (path, O_RDONLY); |
562 | | if (dupfd == -1) |
563 | | return -1; |
564 | | |
565 | | if (dupfd >= minfd) |
566 | | return dupfd; |
567 | | |
568 | | /* Lower FD was closed by other threads. Fill again. */ |
569 | | tempfd = dupfd; |
570 | | } |
571 | | |
572 | | int dupfd = klibc_dupdirfd (fd, minfd); |
573 | | |
574 | | close (tempfd); |
575 | | |
576 | | return dupfd; |
577 | | } |
578 | | |
579 | | static int |
580 | | klibc_fcntl (int fd, int action, /* arg */...) |
581 | | { |
582 | | va_list arg_ptr; |
583 | | va_start (arg_ptr, action); |
584 | | |
585 | | int arg = va_arg (arg_ptr, int); |
586 | | int result = fcntl (fd, action, arg); |
587 | | /* EPERM for F_DUPFD, ENOTSUP for others */ |
588 | | if (result == -1 && (errno == EPERM || errno == ENOTSUP)) |
589 | | { |
590 | | struct stat sbuf; |
591 | | if (!fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode)) |
592 | | { |
593 | | switch (action) |
594 | | { |
595 | | case F_DUPFD: |
596 | | result = klibc_dupdirfd (fd, arg); |
597 | | break; |
598 | | |
599 | | case F_GETFD: |
600 | | { |
601 | | PLIBCFH pFH = __libc_FH (fd); |
602 | | if (!pFH) |
603 | | { |
604 | | errno = EBADF; |
605 | | break; |
606 | | } |
607 | | |
608 | | result = (pFH->fFlags & ((FD_CLOEXEC << __LIBC_FH_FDFLAGS_SHIFT ) |
609 | | | O_NOINHERIT)) ? FD_CLOEXEC : 0; |
610 | | } |
611 | | break; |
612 | | |
613 | | case F_SETFD: |
614 | | { |
615 | | if (arg & ~FD_CLOEXEC) |
616 | | break; |
617 | | |
618 | | PLIBCFH pFH = __libc_FH (fd); |
619 | | if (!pFH) |
620 | | { |
621 | | errno = EBADF; |
622 | | break; |
623 | | } |
624 | | |
625 | | unsigned fFlags = pFH->fFlags; |
626 | | if (arg & FD_CLOEXEC) |
627 | | fFlags |= (FD_CLOEXEC << __LIBC_FH_FDFLAGS_SHIFT) | O_NOINHERIT; |
628 | | else |
629 | | fFlags &= ~((FD_CLOEXEC << __LIBC_FH_FDFLAGS_SHIFT) | O_NOINHERIT); |
630 | | |
631 | | result = __libc_FHSetFlags (pFH, fd, fFlags); |
632 | | if (result < 0) |
633 | | { |
634 | | errno = -result; |
635 | | result = -1; |
636 | | } |
637 | | } |
638 | | break; |
639 | | |
640 | | case F_GETFL: |
641 | | result = 0; |
642 | | break; |
643 | | |
644 | | case F_SETFL: |
645 | | if (arg != 0) |
646 | | break; |
647 | | |
648 | | result = 0; |
649 | | break; |
650 | | |
651 | | default: |
652 | | errno = EINVAL; |
653 | | break; |
654 | | } |
655 | | } |
656 | | } |
657 | | |
658 | | va_end (arg_ptr); |
659 | | |
660 | | return result; |
661 | | } |
662 | | |
663 | | #endif |