(root)/
gettext-0.22.4/
gettext-tools/
gnulib-tests/
test-fcntl.c
       1  /* Test of fcntl(2).
       2     Copyright (C) 2009-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Eric Blake <ebb9@byu.net>, 2009.  */
      18  
      19  #include <config.h>
      20  
      21  /* Specification.  */
      22  #include <fcntl.h>
      23  
      24  #include "signature.h"
      25  SIGNATURE_CHECK (fcntl, int, (int, int, ...));
      26  
      27  /* Helpers.  */
      28  #include <errno.h>
      29  #include <stdarg.h>
      30  #include <unistd.h>
      31  
      32  #if defined _WIN32 && ! defined __CYGWIN__
      33  /* Get declarations of the native Windows API functions.  */
      34  # define WIN32_LEAN_AND_MEAN
      35  # include <windows.h>
      36  /* Get _get_osfhandle.  */
      37  # if GNULIB_MSVC_NOTHROW
      38  #  include "msvc-nothrow.h"
      39  # else
      40  #  include <io.h>
      41  # endif
      42  #endif
      43  
      44  #include "binary-io.h"
      45  #include "macros.h"
      46  
      47  /* Tell GCC not to warn about the specific edge cases tested here.  */
      48  #if __GNUC__ >= 13
      49  # pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
      50  # pragma GCC diagnostic ignored "-Wanalyzer-va-arg-type-mismatch"
      51  #endif
      52  
      53  #if !O_BINARY
      54  # define set_binary_mode my_set_binary_mode
      55  static int
      56  set_binary_mode (_GL_UNUSED int fd, _GL_UNUSED int mode)
      57  {
      58    return 0;
      59  }
      60  #endif
      61  
      62  /* Return true if FD is open.  */
      63  static bool
      64  is_open (int fd)
      65  {
      66  #if defined _WIN32 && ! defined __CYGWIN__
      67    /* On native Windows, the initial state of unassigned standard file
      68       descriptors is that they are open but point to an
      69       INVALID_HANDLE_VALUE, and there is no fcntl.  */
      70    return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
      71  #else
      72  # ifndef F_GETFL
      73  #  error Please port fcntl to your platform
      74  # endif
      75    return 0 <= fcntl (fd, F_GETFL);
      76  #endif
      77  }
      78  
      79  /* Return true if FD is open and inheritable across exec/spawn.  */
      80  static bool
      81  is_inheritable (int fd)
      82  {
      83  #if defined _WIN32 && ! defined __CYGWIN__
      84    /* On native Windows, the initial state of unassigned standard file
      85       descriptors is that they are open but point to an
      86       INVALID_HANDLE_VALUE, and there is no fcntl.  */
      87    HANDLE h = (HANDLE) _get_osfhandle (fd);
      88    DWORD flags;
      89    if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0)
      90      return false;
      91    return (flags & HANDLE_FLAG_INHERIT) != 0;
      92  #else
      93  # ifndef F_GETFD
      94  #  error Please port fcntl to your platform
      95  # endif
      96    int i = fcntl (fd, F_GETFD);
      97    return 0 <= i && (i & FD_CLOEXEC) == 0;
      98  #endif
      99  }
     100  
     101  /* Return non-zero if FD is open in the given MODE, which is either
     102     O_TEXT or O_BINARY.  */
     103  static bool
     104  is_mode (int fd, int mode)
     105  {
     106    int value = set_binary_mode (fd, O_BINARY);
     107    set_binary_mode (fd, value);
     108    return mode == value;
     109  }
     110  
     111  /* Since native fcntl can have more supported operations than our
     112     replacement is aware of, and since various operations assign
     113     different types to the vararg argument, a wrapper around fcntl must
     114     be able to pass a vararg of unknown type on through to the original
     115     fcntl.  Make sure that this works properly: func1 behaves like the
     116     original fcntl interpreting the vararg as an int or a pointer to a
     117     struct, and func2 behaves like rpl_fcntl that doesn't know what
     118     type to forward.  */
     119  struct dummy_struct
     120  {
     121    long filler;
     122    int value;
     123  };
     124  static int
     125  func1 (int a, ...)
     126  {
     127    va_list arg;
     128    int i;
     129    va_start (arg, a);
     130    if (a < 4)
     131      i = va_arg (arg, int);
     132    else
     133      {
     134        struct dummy_struct *s = va_arg (arg, struct dummy_struct *);
     135        i = s->value;
     136      }
     137    va_end (arg);
     138    return i;
     139  }
     140  static int
     141  func2 (int a, ...)
     142  {
     143    va_list arg;
     144    void *p;
     145    va_start (arg, a);
     146    p = va_arg (arg, void *);
     147    va_end (arg);
     148    return func1 (a, p);
     149  }
     150  
     151  /* Ensure that all supported fcntl actions are distinct, and
     152     usable in preprocessor expressions.  */
     153  static void
     154  check_flags (void)
     155  {
     156    switch (0)
     157      {
     158      case F_DUPFD:
     159  #if F_DUPFD
     160  #endif
     161  
     162      case F_DUPFD_CLOEXEC:
     163  #if F_DUPFD_CLOEXEC
     164  #endif
     165  
     166      case F_GETFD:
     167  #if F_GETFD
     168  #endif
     169  
     170  #ifdef F_SETFD
     171      case F_SETFD:
     172  # if F_SETFD
     173  # endif
     174  #endif
     175  
     176  #ifdef F_GETFL
     177      case F_GETFL:
     178  # if F_GETFL
     179  # endif
     180  #endif
     181  
     182  #ifdef F_SETFL
     183      case F_SETFL:
     184  # if F_SETFL
     185  # endif
     186  #endif
     187  
     188  #ifdef F_GETOWN
     189      case F_GETOWN:
     190  # if F_GETOWN
     191  # endif
     192  #endif
     193  
     194  #ifdef F_SETOWN
     195      case F_SETOWN:
     196  # if F_SETOWN
     197  # endif
     198  #endif
     199  
     200  #ifdef F_GETLK
     201      case F_GETLK:
     202  # if F_GETLK
     203  # endif
     204  #endif
     205  
     206  #ifdef F_SETLK
     207      case F_SETLK:
     208  # if F_SETLK
     209  # endif
     210  #endif
     211  
     212  #ifdef F_SETLKW
     213      case F_SETLKW:
     214  # if F_SETLKW
     215  # endif
     216  #endif
     217  
     218      default:
     219        ;
     220      }
     221  }
     222  
     223  int
     224  main (int argc, _GL_UNUSED char *argv[])
     225  {
     226    if (argc > 1)
     227      /* child process */
     228      return (is_open (10) ? 42 : 0);
     229  
     230    const char *file = "test-fcntl.tmp";
     231    int fd;
     232    int bad_fd = getdtablesize ();
     233  
     234    /* Sanity check that rpl_fcntl is likely to work.  */
     235    ASSERT (func2 (1, 2) == 2);
     236    ASSERT (func2 (2, -2) == -2);
     237    ASSERT (func2 (3, 0x80000000) == 0x80000000);
     238    {
     239      struct dummy_struct s = { 0L, 4 };
     240      ASSERT (func2 (4, &s) == 4);
     241    }
     242    check_flags ();
     243  
     244    /* Assume std descriptors were provided by invoker, and ignore fds
     245       that might have been inherited.  */
     246    fd = creat (file, 0600);
     247    ASSERT (STDERR_FILENO < fd);
     248    close (fd + 1);
     249    close (fd + 2);
     250  
     251    /* For F_DUPFD*, the source must be valid.  */
     252    errno = 0;
     253    ASSERT (fcntl (-1, F_DUPFD, 0) == -1);
     254    ASSERT (errno == EBADF);
     255    errno = 0;
     256    ASSERT (fcntl (fd + 1, F_DUPFD, 0) == -1);
     257    ASSERT (errno == EBADF);
     258    errno = 0;
     259    ASSERT (fcntl (bad_fd, F_DUPFD, 0) == -1);
     260    ASSERT (errno == EBADF);
     261    errno = 0;
     262    ASSERT (fcntl (-1, F_DUPFD_CLOEXEC, 0) == -1);
     263    ASSERT (errno == EBADF);
     264    errno = 0;
     265    ASSERT (fcntl (fd + 1, F_DUPFD_CLOEXEC, 0) == -1);
     266    ASSERT (errno == EBADF);
     267    errno = 0;
     268    ASSERT (fcntl (bad_fd, F_DUPFD_CLOEXEC, 0) == -1);
     269    ASSERT (errno == EBADF);
     270  
     271    /* For F_DUPFD*, the destination must be valid.  */
     272    errno = 0;
     273    ASSERT (fcntl (fd, F_DUPFD, -1) == -1);
     274    ASSERT (errno == EINVAL);
     275    errno = 0;
     276    ASSERT (fcntl (fd, F_DUPFD, bad_fd) == -1);
     277    ASSERT (errno == EINVAL);
     278    errno = 0;
     279    ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, -1) == -1);
     280    ASSERT (errno == EINVAL);
     281    errno = 0;
     282    ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, bad_fd) == -1);
     283    ASSERT (errno == EINVAL
     284            || errno == EMFILE /* WSL */);
     285  
     286    /* For F_DUPFD*, check for correct inheritance, as well as
     287       preservation of text vs. binary.  */
     288    set_binary_mode (fd, O_BINARY);
     289    ASSERT (is_open (fd));
     290    ASSERT (!is_open (fd + 1));
     291    ASSERT (!is_open (fd + 2));
     292    ASSERT (is_inheritable (fd));
     293    ASSERT (is_mode (fd, O_BINARY));
     294  
     295    ASSERT (fcntl (fd, F_DUPFD, fd) == fd + 1);
     296    ASSERT (is_open (fd));
     297    ASSERT (is_open (fd + 1));
     298    ASSERT (!is_open (fd + 2));
     299    ASSERT (is_inheritable (fd + 1));
     300    ASSERT (is_mode (fd, O_BINARY));
     301    ASSERT (is_mode (fd + 1, O_BINARY));
     302    ASSERT (close (fd + 1) == 0);
     303  
     304    ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, fd + 2) == fd + 2);
     305    ASSERT (is_open (fd));
     306    ASSERT (!is_open (fd + 1));
     307    ASSERT (is_open (fd + 2));
     308    ASSERT (is_inheritable (fd));
     309    ASSERT (!is_inheritable (fd + 2));
     310    ASSERT (is_mode (fd, O_BINARY));
     311    ASSERT (is_mode (fd + 2, O_BINARY));
     312    ASSERT (close (fd) == 0);
     313  
     314    set_binary_mode (fd + 2, O_TEXT);
     315    ASSERT (fcntl (fd + 2, F_DUPFD, fd + 1) == fd + 1);
     316    ASSERT (!is_open (fd));
     317    ASSERT (is_open (fd + 1));
     318    ASSERT (is_open (fd + 2));
     319    ASSERT (is_inheritable (fd + 1));
     320    ASSERT (!is_inheritable (fd + 2));
     321    ASSERT (is_mode (fd + 1, O_TEXT));
     322    ASSERT (is_mode (fd + 2, O_TEXT));
     323    ASSERT (close (fd + 1) == 0);
     324  
     325    ASSERT (fcntl (fd + 2, F_DUPFD_CLOEXEC, 0) == fd);
     326    ASSERT (is_open (fd));
     327    ASSERT (!is_open (fd + 1));
     328    ASSERT (is_open (fd + 2));
     329    ASSERT (!is_inheritable (fd));
     330    ASSERT (!is_inheritable (fd + 2));
     331    ASSERT (is_mode (fd, O_TEXT));
     332    ASSERT (is_mode (fd + 2, O_TEXT));
     333    ASSERT (close (fd + 2) == 0);
     334  
     335    /* Test F_GETFD on invalid file descriptors.  */
     336    errno = 0;
     337    ASSERT (fcntl (-1, F_GETFD) == -1);
     338    ASSERT (errno == EBADF);
     339    errno = 0;
     340    ASSERT (fcntl (fd + 1, F_GETFD) == -1);
     341    ASSERT (errno == EBADF);
     342    errno = 0;
     343    ASSERT (fcntl (bad_fd, F_GETFD) == -1);
     344    ASSERT (errno == EBADF);
     345  
     346    /* Test F_GETFD, the FD_CLOEXEC bit.  */
     347    {
     348      int result = fcntl (fd, F_GETFD);
     349      ASSERT (0 <= result);
     350      ASSERT ((result & FD_CLOEXEC) == FD_CLOEXEC);
     351      ASSERT (dup (fd) == fd + 1);
     352      result = fcntl (fd + 1, F_GETFD);
     353      ASSERT (0 <= result);
     354      ASSERT ((result & FD_CLOEXEC) == 0);
     355      ASSERT (close (fd + 1) == 0);
     356    }
     357  
     358  #ifdef F_SETFD
     359    /* Test F_SETFD on invalid file descriptors.  */
     360    errno = 0;
     361    ASSERT (fcntl (-1, F_SETFD, 0) == -1);
     362    ASSERT (errno == EBADF);
     363    errno = 0;
     364    ASSERT (fcntl (fd + 1, F_SETFD, 0) == -1);
     365    ASSERT (errno == EBADF);
     366    errno = 0;
     367    ASSERT (fcntl (bad_fd, F_SETFD, 0) == -1);
     368    ASSERT (errno == EBADF);
     369  #endif
     370  
     371  #ifdef F_GETFL
     372    /* Test F_GETFL on invalid file descriptors.  */
     373    errno = 0;
     374    ASSERT (fcntl (-1, F_GETFL) == -1);
     375    ASSERT (errno == EBADF);
     376    errno = 0;
     377    ASSERT (fcntl (fd + 1, F_GETFL) == -1);
     378    ASSERT (errno == EBADF);
     379    errno = 0;
     380    ASSERT (fcntl (bad_fd, F_GETFL) == -1);
     381    ASSERT (errno == EBADF);
     382  #endif
     383  
     384  #ifdef F_SETFL
     385    /* Test F_SETFL on invalid file descriptors.  */
     386    errno = 0;
     387    ASSERT (fcntl (-1, F_SETFL, 0) == -1);
     388    ASSERT (errno == EBADF);
     389    errno = 0;
     390    ASSERT (fcntl (fd + 1, F_SETFL, 0) == -1);
     391    ASSERT (errno == EBADF);
     392    errno = 0;
     393    ASSERT (fcntl (bad_fd, F_SETFL, 0) == -1);
     394    ASSERT (errno == EBADF);
     395  #endif
     396  
     397  #ifdef F_GETOWN
     398    /* Test F_GETOWN on invalid file descriptors.  */
     399    errno = 0;
     400    ASSERT (fcntl (-1, F_GETOWN) == -1);
     401    ASSERT (errno == EBADF);
     402    errno = 0;
     403    ASSERT (fcntl (fd + 1, F_GETOWN) == -1);
     404    ASSERT (errno == EBADF);
     405    errno = 0;
     406    ASSERT (fcntl (bad_fd, F_GETOWN) == -1);
     407    ASSERT (errno == EBADF);
     408  #endif
     409  
     410  #ifdef F_SETOWN
     411    /* Test F_SETFL on invalid file descriptors.  */
     412    errno = 0;
     413    ASSERT (fcntl (-1, F_SETOWN, 0) == -1);
     414    ASSERT (errno == EBADF);
     415    errno = 0;
     416    ASSERT (fcntl (fd + 1, F_SETOWN, 0) == -1);
     417    ASSERT (errno == EBADF);
     418    errno = 0;
     419    ASSERT (fcntl (bad_fd, F_SETOWN, 0) == -1);
     420    ASSERT (errno == EBADF);
     421  #endif
     422  
     423    /* Cleanup.  */
     424    ASSERT (close (fd) == 0);
     425    ASSERT (unlink (file) == 0);
     426  
     427    /* Close file descriptors that may have been inherited from the parent
     428       process and that would cause failures below.
     429       Such file descriptors have been seen:
     430         - with GNU make, when invoked as 'make -j N' with j > 1,
     431         - in some versions of the KDE desktop environment,
     432         - on NetBSD,
     433         - in MacPorts with the "trace mode" enabled.
     434     */
     435    (void) close (10);
     436  
     437    /* Test whether F_DUPFD_CLOEXEC is effective.  */
     438    ASSERT (fcntl (1, F_DUPFD_CLOEXEC, 10) >= 0);
     439  #if defined _WIN32 && !defined __CYGWIN__
     440    return _execl ("./test-fcntl", "./test-fcntl", "child", NULL);
     441  #else
     442    return execl ("./test-fcntl", "./test-fcntl", "child", NULL);
     443  #endif
     444  }