(root)/
sed-4.9/
gnulib-tests/
test-dup2.c
       1  /* Test duplicating file descriptors.
       2     Copyright (C) 2009-2022 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  #include <unistd.h>
      22  
      23  #include "signature.h"
      24  SIGNATURE_CHECK (dup2, int, (int, int));
      25  
      26  #include <errno.h>
      27  #include <fcntl.h>
      28  
      29  #if HAVE_SYS_RESOURCE_H
      30  # include <sys/resource.h>
      31  #endif
      32  
      33  #include "binary-io.h"
      34  
      35  #if GNULIB_TEST_CLOEXEC
      36  # include "cloexec.h"
      37  #endif
      38  
      39  #if defined _WIN32 && ! defined __CYGWIN__
      40  /* Get declarations of the native Windows API functions.  */
      41  # define WIN32_LEAN_AND_MEAN
      42  # include <windows.h>
      43  /* Get _get_osfhandle.  */
      44  # if GNULIB_MSVC_NOTHROW
      45  #  include "msvc-nothrow.h"
      46  # else
      47  #  include <io.h>
      48  # endif
      49  #endif
      50  
      51  #include "macros.h"
      52  
      53  /* Return non-zero if FD is open.  */
      54  static int
      55  is_open (int fd)
      56  {
      57  #if defined _WIN32 && ! defined __CYGWIN__
      58    /* On native Windows, the initial state of unassigned standard file
      59       descriptors is that they are open but point to an
      60       INVALID_HANDLE_VALUE, and there is no fcntl.  */
      61    return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
      62  #else
      63  # ifndef F_GETFL
      64  #  error Please port fcntl to your platform
      65  # endif
      66    return 0 <= fcntl (fd, F_GETFL);
      67  #endif
      68  }
      69  
      70  #if GNULIB_TEST_CLOEXEC
      71  /* Return non-zero if FD is open and inheritable across exec/spawn.  */
      72  static int
      73  is_inheritable (int fd)
      74  {
      75  # if defined _WIN32 && ! defined __CYGWIN__
      76    /* On native Windows, the initial state of unassigned standard file
      77       descriptors is that they are open but point to an
      78       INVALID_HANDLE_VALUE, and there is no fcntl.  */
      79    HANDLE h = (HANDLE) _get_osfhandle (fd);
      80    DWORD flags;
      81    if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0)
      82      return 0;
      83    return (flags & HANDLE_FLAG_INHERIT) != 0;
      84  # else
      85  #  ifndef F_GETFD
      86  #   error Please port fcntl to your platform
      87  #  endif
      88    int i = fcntl (fd, F_GETFD);
      89    return 0 <= i && (i & FD_CLOEXEC) == 0;
      90  # endif
      91  }
      92  #endif /* GNULIB_TEST_CLOEXEC */
      93  
      94  #if !O_BINARY
      95  # define set_binary_mode(f,m) zero ()
      96  static int zero (void) { return 0; }
      97  #endif
      98  
      99  /* Return non-zero if FD is open in the given MODE, which is either
     100     O_TEXT or O_BINARY.  */
     101  static int
     102  is_mode (int fd, int mode)
     103  {
     104    int value = set_binary_mode (fd, O_BINARY);
     105    set_binary_mode (fd, value);
     106    return mode == value;
     107  }
     108  
     109  int
     110  main (void)
     111  {
     112    const char *file = "test-dup2.tmp";
     113    char buffer[1];
     114    int bad_fd = getdtablesize ();
     115    int fd = open (file, O_CREAT | O_TRUNC | O_RDWR, 0600);
     116  
     117    /* Assume std descriptors were provided by invoker.  */
     118    ASSERT (STDERR_FILENO < fd);
     119    ASSERT (is_open (fd));
     120    /* Ignore any other fd's leaked into this process.  */
     121    close (fd + 1);
     122    close (fd + 2);
     123    ASSERT (!is_open (fd + 1));
     124    ASSERT (!is_open (fd + 2));
     125  
     126    /* Assigning to self must be a no-op.  */
     127    ASSERT (dup2 (fd, fd) == fd);
     128    ASSERT (is_open (fd));
     129  
     130    /* The source must be valid.  */
     131    errno = 0;
     132    ASSERT (dup2 (-1, fd) == -1);
     133    ASSERT (errno == EBADF);
     134    close (99);
     135    errno = 0;
     136    ASSERT (dup2 (99, fd) == -1);
     137    ASSERT (errno == EBADF);
     138    errno = 0;
     139    ASSERT (dup2 (AT_FDCWD, fd) == -1);
     140    ASSERT (errno == EBADF);
     141    ASSERT (is_open (fd));
     142  
     143    /* If the source is not open, then the destination is unaffected.  */
     144    errno = 0;
     145    ASSERT (dup2 (fd + 1, fd + 1) == -1);
     146    ASSERT (errno == EBADF);
     147    ASSERT (!is_open (fd + 1));
     148    errno = 0;
     149    ASSERT (dup2 (fd + 1, fd) == -1);
     150    ASSERT (errno == EBADF);
     151    ASSERT (is_open (fd));
     152  
     153    /* The destination must be valid.  */
     154    errno = 0;
     155    ASSERT (dup2 (fd, -2) == -1);
     156    ASSERT (errno == EBADF);
     157    if (bad_fd > 256)
     158      {
     159        ASSERT (dup2 (fd, 255) == 255);
     160        ASSERT (dup2 (fd, 256) == 256);
     161        ASSERT (close (255) == 0);
     162        ASSERT (close (256) == 0);
     163      }
     164    ASSERT (dup2 (fd, bad_fd - 1) == bad_fd - 1);
     165    ASSERT (close (bad_fd - 1) == 0);
     166    errno = 0;
     167    ASSERT (dup2 (fd, bad_fd) == -1);
     168    ASSERT (errno == EBADF);
     169  
     170    /* Using dup2 can skip fds.  */
     171    ASSERT (dup2 (fd, fd + 2) == fd + 2);
     172    ASSERT (is_open (fd));
     173    ASSERT (!is_open (fd + 1));
     174    ASSERT (is_open (fd + 2));
     175  
     176    /* Verify that dup2 closes the previous occupant of a fd.  */
     177    ASSERT (open ("/dev/null", O_WRONLY, 0600) == fd + 1);
     178    ASSERT (dup2 (fd + 1, fd) == fd);
     179    ASSERT (close (fd + 1) == 0);
     180    ASSERT (write (fd, "1", 1) == 1);
     181    ASSERT (dup2 (fd + 2, fd) == fd);
     182    ASSERT (lseek (fd, 0, SEEK_END) == 0);
     183    ASSERT (write (fd + 2, "2", 1) == 1);
     184    ASSERT (lseek (fd, 0, SEEK_SET) == 0);
     185    ASSERT (read (fd, buffer, 1) == 1);
     186    ASSERT (*buffer == '2');
     187  
     188  #if GNULIB_TEST_CLOEXEC
     189    /* Any new fd created by dup2 must not be cloexec.  */
     190    ASSERT (close (fd + 2) == 0);
     191    ASSERT (dup_cloexec (fd) == fd + 1);
     192    ASSERT (!is_inheritable (fd + 1));
     193    ASSERT (dup2 (fd + 1, fd + 1) == fd + 1);
     194    ASSERT (!is_inheritable (fd + 1));
     195    ASSERT (dup2 (fd + 1, fd + 2) == fd + 2);
     196    ASSERT (!is_inheritable (fd + 1));
     197    ASSERT (is_inheritable (fd + 2));
     198    errno = 0;
     199    ASSERT (dup2 (fd + 1, -1) == -1);
     200    ASSERT (errno == EBADF);
     201    ASSERT (!is_inheritable (fd + 1));
     202  #endif
     203  
     204    /* On systems that distinguish between text and binary mode, dup2
     205       reuses the mode of the source.  */
     206    set_binary_mode (fd, O_BINARY);
     207    ASSERT (is_mode (fd, O_BINARY));
     208    ASSERT (dup2 (fd, fd + 1) == fd + 1);
     209    ASSERT (is_mode (fd + 1, O_BINARY));
     210    set_binary_mode (fd, O_TEXT);
     211    ASSERT (is_mode (fd, O_TEXT));
     212    ASSERT (dup2 (fd, fd + 1) == fd + 1);
     213    ASSERT (is_mode (fd + 1, O_TEXT));
     214  
     215    /* Clean up.  */
     216    ASSERT (close (fd + 2) == 0);
     217    ASSERT (close (fd + 1) == 0);
     218    ASSERT (close (fd) == 0);
     219    ASSERT (unlink (file) == 0);
     220  
     221    return 0;
     222  }