Line | Count | Source (jump to first uncovered line) |
1 | | /* Provide file descriptor control. |
2 | | |
3 | | Copyright (C) 2009-2023 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 | | # define INCL_DOS |
33 | | # include <os2.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 | | 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; |
63 | | int result; |
64 | | BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE; |
65 | | int mode; |
66 | | |
67 | | if (newfd < 0 || getdtablesize () <= newfd) |
68 | | { |
69 | | errno = EINVAL; |
70 | | return -1; |
71 | | } |
72 | | if (old_handle == INVALID_HANDLE_VALUE |
73 | | || (mode = _setmode (oldfd, O_BINARY)) == -1) |
74 | | { |
75 | | /* oldfd is not open, or is an unassigned standard file |
76 | | descriptor. */ |
77 | | errno = EBADF; |
78 | | return -1; |
79 | | } |
80 | | _setmode (oldfd, mode); |
81 | | flags |= mode; |
82 | | |
83 | | for (;;) |
84 | | { |
85 | | HANDLE new_handle; |
86 | | int duplicated_fd; |
87 | | unsigned int index; |
88 | | |
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 */ |
96 | | { |
97 | | switch (GetLastError ()) |
98 | | { |
99 | | case ERROR_TOO_MANY_OPEN_FILES: |
100 | | errno = EMFILE; |
101 | | break; |
102 | | case ERROR_INVALID_HANDLE: |
103 | | case ERROR_INVALID_TARGET_HANDLE: |
104 | | case ERROR_DIRECT_ACCESS_HANDLE: |
105 | | errno = EBADF; |
106 | | break; |
107 | | case ERROR_INVALID_PARAMETER: |
108 | | case ERROR_INVALID_FUNCTION: |
109 | | case ERROR_INVALID_ACCESS: |
110 | | errno = EINVAL; |
111 | | break; |
112 | | default: |
113 | | errno = EACCES; |
114 | | break; |
115 | | } |
116 | | result = -1; |
117 | | break; |
118 | | } |
119 | | duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags); |
120 | | if (duplicated_fd < 0) |
121 | | { |
122 | | CloseHandle (new_handle); |
123 | | result = -1; |
124 | | break; |
125 | | } |
126 | | if (newfd <= duplicated_fd) |
127 | | { |
128 | | result = duplicated_fd; |
129 | | break; |
130 | | } |
131 | | |
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) |
135 | | { |
136 | | if (sizeof fds_to_close <= index) |
137 | | /* Need to increase OPEN_MAX_MAX. */ |
138 | | abort (); |
139 | | memset (fds_to_close + fds_to_close_bound, '\0', |
140 | | index + 1 - fds_to_close_bound); |
141 | | fds_to_close_bound = index + 1; |
142 | | } |
143 | | fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT); |
144 | | } |
145 | | |
146 | | /* Close the previous fds that turned out to be too small. */ |
147 | | { |
148 | | int saved_errno = errno; |
149 | | unsigned int duplicated_fd; |
150 | | |
151 | | for (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 | 0 | { |
208 | 0 | va_list arg; |
209 | 0 | int result = -1; |
210 | 0 | va_start (arg, action); |
211 | 0 | switch (action) |
212 | 0 | { |
213 | 0 | case F_DUPFD: |
214 | 0 | { |
215 | 0 | int target = va_arg (arg, int); |
216 | 0 | result = rpl_fcntl_DUPFD (fd, target); |
217 | 0 | break; |
218 | 0 | } |
219 | | |
220 | 0 | case F_DUPFD_CLOEXEC: |
221 | 0 | { |
222 | 0 | int target = va_arg (arg, int); |
223 | 0 | result = rpl_fcntl_DUPFD_CLOEXEC (fd, target); |
224 | 0 | break; |
225 | 0 | } |
226 | | |
227 | | #if !HAVE_FCNTL |
228 | | case F_GETFD: |
229 | | { |
230 | | # if defined _WIN32 && ! defined __CYGWIN__ |
231 | | HANDLE handle = (HANDLE) _get_osfhandle (fd); |
232 | | DWORD flags; |
233 | | if (handle == INVALID_HANDLE_VALUE |
234 | | || GetHandleInformation (handle, &flags) == 0) |
235 | | errno = EBADF; |
236 | | else |
237 | | result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC; |
238 | | # else /* !W32 */ |
239 | | /* Use dup2 to reject invalid file descriptors. No way to |
240 | | access this information, so punt. */ |
241 | | if (0 <= dup2 (fd, fd)) |
242 | | result = 0; |
243 | | # endif /* !W32 */ |
244 | | break; |
245 | | } /* F_GETFD */ |
246 | | #endif /* !HAVE_FCNTL */ |
247 | | |
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. */ |
255 | | |
256 | 0 | default: |
257 | 0 | { |
258 | 0 | #if HAVE_FCNTL |
259 | 0 | switch (action) |
260 | 0 | { |
261 | | #ifdef F_BARRIERFSYNC /* macOS */ |
262 | | case F_BARRIERFSYNC: |
263 | | #endif |
264 | | #ifdef F_CHKCLEAN /* macOS */ |
265 | | case F_CHKCLEAN: |
266 | | #endif |
267 | | #ifdef F_CLOSEM /* NetBSD, HP-UX */ |
268 | | case F_CLOSEM: |
269 | | #endif |
270 | | #ifdef F_FLUSH_DATA /* macOS */ |
271 | | case F_FLUSH_DATA: |
272 | | #endif |
273 | | #ifdef F_FREEZE_FS /* macOS */ |
274 | | case F_FREEZE_FS: |
275 | | #endif |
276 | | #ifdef F_FULLFSYNC /* macOS */ |
277 | | case F_FULLFSYNC: |
278 | | #endif |
279 | | #ifdef F_GETCONFINED /* macOS */ |
280 | | case F_GETCONFINED: |
281 | | #endif |
282 | | #ifdef F_GETDEFAULTPROTLEVEL /* macOS */ |
283 | | case F_GETDEFAULTPROTLEVEL: |
284 | | #endif |
285 | 0 | #ifdef F_GETFD /* POSIX */ |
286 | 0 | case F_GETFD: |
287 | 0 | #endif |
288 | 0 | #ifdef F_GETFL /* POSIX */ |
289 | 0 | case F_GETFL: |
290 | 0 | #endif |
291 | 0 | #ifdef F_GETLEASE /* Linux */ |
292 | 0 | case F_GETLEASE: |
293 | 0 | #endif |
294 | | #ifdef F_GETNOSIGPIPE /* macOS */ |
295 | | case F_GETNOSIGPIPE: |
296 | | #endif |
297 | 0 | #ifdef F_GETOWN /* POSIX */ |
298 | 0 | case F_GETOWN: |
299 | 0 | #endif |
300 | 0 | #ifdef F_GETPIPE_SZ /* Linux */ |
301 | 0 | case F_GETPIPE_SZ: |
302 | 0 | #endif |
303 | | #ifdef F_GETPROTECTIONCLASS /* macOS */ |
304 | | case F_GETPROTECTIONCLASS: |
305 | | #endif |
306 | | #ifdef F_GETPROTECTIONLEVEL /* macOS */ |
307 | | case F_GETPROTECTIONLEVEL: |
308 | | #endif |
309 | 0 | #ifdef F_GET_SEALS /* Linux */ |
310 | 0 | case F_GET_SEALS: |
311 | 0 | #endif |
312 | 0 | #ifdef F_GETSIG /* Linux */ |
313 | 0 | case F_GETSIG: |
314 | 0 | #endif |
315 | | #ifdef F_MAXFD /* NetBSD */ |
316 | | case F_MAXFD: |
317 | | #endif |
318 | | #ifdef F_RECYCLE /* macOS */ |
319 | | case F_RECYCLE: |
320 | | #endif |
321 | | #ifdef F_SETFIFOENH /* HP-UX */ |
322 | | case F_SETFIFOENH: |
323 | | #endif |
324 | | #ifdef F_THAW_FS /* macOS */ |
325 | | case F_THAW_FS: |
326 | | #endif |
327 | | /* These actions take no argument. */ |
328 | 0 | result = fcntl (fd, action); |
329 | 0 | break; |
330 | | |
331 | 0 | #ifdef F_ADD_SEALS /* Linux */ |
332 | 0 | case F_ADD_SEALS: |
333 | 0 | #endif |
334 | | #ifdef F_BADFD /* Solaris */ |
335 | | case F_BADFD: |
336 | | #endif |
337 | | #ifdef F_CHECK_OPENEVT /* macOS */ |
338 | | case F_CHECK_OPENEVT: |
339 | | #endif |
340 | | #ifdef F_DUP2FD /* FreeBSD, AIX, Solaris */ |
341 | | case F_DUP2FD: |
342 | | #endif |
343 | | #ifdef F_DUP2FD_CLOEXEC /* FreeBSD, Solaris */ |
344 | | case F_DUP2FD_CLOEXEC: |
345 | | #endif |
346 | | #ifdef F_DUP2FD_CLOFORK /* Solaris */ |
347 | | case F_DUP2FD_CLOFORK: |
348 | | #endif |
349 | 0 | #ifdef F_DUPFD /* POSIX */ |
350 | 0 | case F_DUPFD: |
351 | 0 | #endif |
352 | 0 | #ifdef F_DUPFD_CLOEXEC /* POSIX */ |
353 | 0 | case F_DUPFD_CLOEXEC: |
354 | 0 | #endif |
355 | | #ifdef F_DUPFD_CLOFORK /* Solaris */ |
356 | | case F_DUPFD_CLOFORK: |
357 | | #endif |
358 | | #ifdef F_GETXFL /* Solaris */ |
359 | | case F_GETXFL: |
360 | | #endif |
361 | | #ifdef F_GLOBAL_NOCACHE /* macOS */ |
362 | | case F_GLOBAL_NOCACHE: |
363 | | #endif |
364 | | #ifdef F_MAKECOMPRESSED /* macOS */ |
365 | | case F_MAKECOMPRESSED: |
366 | | #endif |
367 | | #ifdef F_MOVEDATAEXTENTS /* macOS */ |
368 | | case F_MOVEDATAEXTENTS: |
369 | | #endif |
370 | | #ifdef F_NOCACHE /* macOS */ |
371 | | case F_NOCACHE: |
372 | | #endif |
373 | | #ifdef F_NODIRECT /* macOS */ |
374 | | case F_NODIRECT: |
375 | | #endif |
376 | 0 | #ifdef F_NOTIFY /* Linux */ |
377 | 0 | case F_NOTIFY: |
378 | 0 | #endif |
379 | | #ifdef F_OPLKACK /* IRIX */ |
380 | | case F_OPLKACK: |
381 | | #endif |
382 | | #ifdef F_OPLKREG /* IRIX */ |
383 | | case F_OPLKREG: |
384 | | #endif |
385 | | #ifdef F_RDAHEAD /* macOS */ |
386 | | case F_RDAHEAD: |
387 | | #endif |
388 | | #ifdef F_SETBACKINGSTORE /* macOS */ |
389 | | case F_SETBACKINGSTORE: |
390 | | #endif |
391 | | #ifdef F_SETCONFINED /* macOS */ |
392 | | case F_SETCONFINED: |
393 | | #endif |
394 | 0 | #ifdef F_SETFD /* POSIX */ |
395 | 0 | case F_SETFD: |
396 | 0 | #endif |
397 | 0 | #ifdef F_SETFL /* POSIX */ |
398 | 0 | case F_SETFL: |
399 | 0 | #endif |
400 | 0 | #ifdef F_SETLEASE /* Linux */ |
401 | 0 | case F_SETLEASE: |
402 | 0 | #endif |
403 | | #ifdef F_SETNOSIGPIPE /* macOS */ |
404 | | case F_SETNOSIGPIPE: |
405 | | #endif |
406 | 0 | #ifdef F_SETOWN /* POSIX */ |
407 | 0 | case F_SETOWN: |
408 | 0 | #endif |
409 | 0 | #ifdef F_SETPIPE_SZ /* Linux */ |
410 | 0 | case F_SETPIPE_SZ: |
411 | 0 | #endif |
412 | | #ifdef F_SETPROTECTIONCLASS /* macOS */ |
413 | | case F_SETPROTECTIONCLASS: |
414 | | #endif |
415 | 0 | #ifdef F_SETSIG /* Linux */ |
416 | 0 | case F_SETSIG: |
417 | 0 | #endif |
418 | | #ifdef F_SINGLE_WRITER /* macOS */ |
419 | | case F_SINGLE_WRITER: |
420 | | #endif |
421 | | /* These actions take an 'int' argument. */ |
422 | 0 | { |
423 | 0 | int x = va_arg (arg, int); |
424 | 0 | result = fcntl (fd, action, x); |
425 | 0 | } |
426 | 0 | break; |
427 | | |
428 | 0 | default: |
429 | | /* Other actions take a pointer argument. */ |
430 | 0 | { |
431 | 0 | void *p = va_arg (arg, void *); |
432 | 0 | result = fcntl (fd, action, p); |
433 | 0 | } |
434 | 0 | break; |
435 | 0 | } |
436 | | #else |
437 | | errno = EINVAL; |
438 | | #endif |
439 | 0 | break; |
440 | 0 | } |
441 | 0 | } |
442 | 0 | va_end (arg); |
443 | 0 | return result; |
444 | 0 | } |
445 | | |
446 | | static int |
447 | | rpl_fcntl_DUPFD (int fd, int target) |
448 | 0 | { |
449 | 0 | int result; |
450 | | #if !HAVE_FCNTL |
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) |
455 | | { |
456 | | result = -1; |
457 | | errno = EINVAL; |
458 | | } |
459 | | else |
460 | | { |
461 | | /* Haiku alpha 2 loses fd flags on original. */ |
462 | | int flags = fcntl (fd, F_GETFD); |
463 | | if (flags < 0) |
464 | | result = -1; |
465 | | else |
466 | | { |
467 | | result = fcntl (fd, F_DUPFD, target); |
468 | | if (0 <= result && fcntl (fd, F_SETFD, flags) == -1) |
469 | | { |
470 | | int saved_errno = errno; |
471 | | close (result); |
472 | | result = -1; |
473 | | errno = saved_errno; |
474 | | } |
475 | | # if REPLACE_FCHDIR |
476 | | if (0 <= result) |
477 | | result = _gl_register_dup (fd, result); |
478 | | # endif |
479 | | } |
480 | | } |
481 | | #else |
482 | 0 | result = fcntl (fd, F_DUPFD, target); |
483 | 0 | #endif |
484 | 0 | return result; |
485 | 0 | } |
486 | | |
487 | | static int |
488 | | rpl_fcntl_DUPFD_CLOEXEC (int fd, int target) |
489 | 0 | { |
490 | 0 | int result; |
491 | | #if !HAVE_FCNTL |
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 |
501 | | # else |
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 | 0 | static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0; |
510 | 0 | if (0 <= have_dupfd_cloexec) |
511 | 0 | { |
512 | 0 | result = fcntl (fd, F_DUPFD_CLOEXEC, target); |
513 | 0 | if (0 <= result || errno != EINVAL) |
514 | 0 | { |
515 | 0 | have_dupfd_cloexec = 1; |
516 | | # if REPLACE_FCHDIR |
517 | | if (0 <= result) |
518 | | result = _gl_register_dup (fd, result); |
519 | | # endif |
520 | 0 | } |
521 | 0 | else |
522 | 0 | { |
523 | 0 | result = rpl_fcntl_DUPFD (fd, target); |
524 | 0 | if (result >= 0) |
525 | 0 | have_dupfd_cloexec = -1; |
526 | 0 | } |
527 | 0 | } |
528 | 0 | else |
529 | 0 | # endif |
530 | 0 | result = rpl_fcntl_DUPFD (fd, target); |
531 | 0 | if (0 <= result && have_dupfd_cloexec == -1) |
532 | 0 | { |
533 | 0 | int flags = fcntl (result, F_GETFD); |
534 | 0 | if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1) |
535 | 0 | { |
536 | 0 | int saved_errno = errno; |
537 | 0 | close (result); |
538 | 0 | errno = saved_errno; |
539 | 0 | result = -1; |
540 | 0 | } |
541 | 0 | } |
542 | 0 | #endif /* HAVE_FCNTL */ |
543 | 0 | return result; |
544 | 0 | } |
545 | | |
546 | | #undef fcntl |
547 | | |
548 | | #ifdef __KLIBC__ |
549 | | |
550 | | static int |
551 | | klibc_fcntl (int fd, int action, /* arg */...) |
552 | | { |
553 | | va_list arg_ptr; |
554 | | int arg; |
555 | | struct stat sbuf; |
556 | | int result; |
557 | | |
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)) |
564 | | { |
565 | | ULONG ulMode; |
566 | | |
567 | | switch (action) |
568 | | { |
569 | | case F_DUPFD: |
570 | | /* Find available fd */ |
571 | | while (fcntl (arg, F_GETFL) != -1 || errno != EBADF) |
572 | | arg++; |
573 | | |
574 | | result = dup2 (fd, arg); |
575 | | break; |
576 | | |
577 | | /* Using underlying APIs is right ? */ |
578 | | case F_GETFD: |
579 | | if (DosQueryFHState (fd, &ulMode)) |
580 | | break; |
581 | | |
582 | | result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0; |
583 | | break; |
584 | | |
585 | | case F_SETFD: |
586 | | if (arg & ~FD_CLOEXEC) |
587 | | break; |
588 | | |
589 | | if (DosQueryFHState (fd, &ulMode)) |
590 | | break; |
591 | | |
592 | | if (arg & FD_CLOEXEC) |
593 | | ulMode |= OPEN_FLAGS_NOINHERIT; |
594 | | else |
595 | | ulMode &= ~OPEN_FLAGS_NOINHERIT; |
596 | | |
597 | | /* Filter supported flags. */ |
598 | | ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR |
599 | | | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT); |
600 | | |
601 | | if (DosSetFHState (fd, ulMode)) |
602 | | break; |
603 | | |
604 | | result = 0; |
605 | | break; |
606 | | |
607 | | case F_GETFL: |
608 | | result = 0; |
609 | | break; |
610 | | |
611 | | case F_SETFL: |
612 | | if (arg != 0) |
613 | | break; |
614 | | |
615 | | result = 0; |
616 | | break; |
617 | | |
618 | | default: |
619 | | errno = EINVAL; |
620 | | break; |
621 | | } |
622 | | } |
623 | | |
624 | | va_end (arg_ptr); |
625 | | |
626 | | return result; |
627 | | } |
628 | | |
629 | | #endif |