Coverage Report

Created: 2023-03-26 07:33

/src/wget/lib/fcntl.c
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