Blame gnulib-tests/test-dup-safer.c

Packit 9e4112
/* Test that dup_safer leaves standard fds alone.
Packit 9e4112
   Copyright (C) 2009-2018 Free Software Foundation, Inc.
Packit 9e4112
Packit 9e4112
   This program is free software: you can redistribute it and/or modify
Packit 9e4112
   it under the terms of the GNU General Public License as published by
Packit 9e4112
   the Free Software Foundation; either version 3 of the License, or
Packit 9e4112
   (at your option) any later version.
Packit 9e4112
Packit 9e4112
   This program is distributed in the hope that it will be useful,
Packit 9e4112
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 9e4112
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 9e4112
   GNU General Public License for more details.
Packit 9e4112
Packit 9e4112
   You should have received a copy of the GNU General Public License
Packit 9e4112
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 9e4112
Packit 9e4112
/* Written by Eric Blake <ebb9@byu.net>, 2009.  */
Packit 9e4112
Packit 9e4112
#include <config.h>
Packit 9e4112
Packit 9e4112
#include "unistd--.h"
Packit 9e4112
Packit 9e4112
#include <fcntl.h>
Packit 9e4112
#include <errno.h>
Packit 9e4112
#include <stdbool.h>
Packit 9e4112
#include <stdio.h>
Packit 9e4112
#include <unistd.h>
Packit 9e4112
Packit 9e4112
#include "binary-io.h"
Packit 9e4112
#include "cloexec.h"
Packit 9e4112
Packit 9e4112
#if defined _WIN32 && ! defined __CYGWIN__
Packit 9e4112
/* Get declarations of the native Windows API functions.  */
Packit 9e4112
# define WIN32_LEAN_AND_MEAN
Packit 9e4112
# include <windows.h>
Packit 9e4112
/* Get _get_osfhandle.  */
Packit 9e4112
# if GNULIB_MSVC_NOTHROW
Packit 9e4112
#  include "msvc-nothrow.h"
Packit 9e4112
# else
Packit 9e4112
#  include <io.h>
Packit 9e4112
# endif
Packit 9e4112
#endif
Packit 9e4112
Packit 9e4112
#if !O_BINARY
Packit 9e4112
# define setmode(f,m) zero ()
Packit 9e4112
static int zero (void) { return 0; }
Packit 9e4112
#endif
Packit 9e4112
Packit 9e4112
/* This test intentionally closes stderr.  So, we arrange to have fd 10
Packit 9e4112
   (outside the range of interesting fd's during the test) set up to
Packit 9e4112
   duplicate the original stderr.  */
Packit 9e4112
Packit 9e4112
#define BACKUP_STDERR_FILENO 10
Packit 9e4112
#define ASSERT_STREAM myerr
Packit 9e4112
#include "macros.h"
Packit 9e4112
Packit 9e4112
static FILE *myerr;
Packit 9e4112
Packit 9e4112
/* Return true if FD is open.  */
Packit 9e4112
static bool
Packit 9e4112
is_open (int fd)
Packit 9e4112
{
Packit 9e4112
#if defined _WIN32 && ! defined __CYGWIN__
Packit 9e4112
  /* On native Windows, the initial state of unassigned standard file
Packit 9e4112
     descriptors is that they are open but point to an
Packit 9e4112
     INVALID_HANDLE_VALUE, and there is no fcntl.  */
Packit 9e4112
  return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
Packit 9e4112
#else
Packit 9e4112
# ifndef F_GETFL
Packit 9e4112
#  error Please port fcntl to your platform
Packit 9e4112
# endif
Packit 9e4112
  return 0 <= fcntl (fd, F_GETFL);
Packit 9e4112
#endif
Packit 9e4112
}
Packit 9e4112
Packit 9e4112
/* Return true if FD is open and inheritable across exec/spawn.  */
Packit 9e4112
static bool
Packit 9e4112
is_inheritable (int fd)
Packit 9e4112
{
Packit 9e4112
#if defined _WIN32 && ! defined __CYGWIN__
Packit 9e4112
  /* On native Windows, the initial state of unassigned standard file
Packit 9e4112
     descriptors is that they are open but point to an
Packit 9e4112
     INVALID_HANDLE_VALUE, and there is no fcntl.  */
Packit 9e4112
  HANDLE h = (HANDLE) _get_osfhandle (fd);
Packit 9e4112
  DWORD flags;
Packit 9e4112
  if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0)
Packit 9e4112
    return 0;
Packit 9e4112
  return (flags & HANDLE_FLAG_INHERIT) != 0;
Packit 9e4112
#else
Packit 9e4112
# ifndef F_GETFD
Packit 9e4112
#  error Please port fcntl to your platform
Packit 9e4112
# endif
Packit 9e4112
  int i = fcntl (fd, F_GETFD);
Packit 9e4112
  return 0 <= i && (i & FD_CLOEXEC) == 0;
Packit 9e4112
#endif
Packit 9e4112
}
Packit 9e4112
Packit 9e4112
/* Return true if FD is open in the given MODE, which is either
Packit 9e4112
   O_TEXT or O_BINARY.  */
Packit 9e4112
static bool
Packit 9e4112
is_mode (int fd, int mode)
Packit 9e4112
{
Packit 9e4112
  int value = setmode (fd, O_BINARY);
Packit 9e4112
  setmode (fd, value);
Packit 9e4112
  return mode == value;
Packit 9e4112
}
Packit 9e4112
Packit 9e4112
#define witness "test-dup-safer.txt"
Packit 9e4112
Packit 9e4112
int
Packit 9e4112
main (void)
Packit 9e4112
{
Packit 9e4112
  int i;
Packit 9e4112
  int fd;
Packit 9e4112
  int bad_fd = getdtablesize ();
Packit 9e4112
Packit 9e4112
  /* We close fd 2 later, so save it in fd 10.  */
Packit 9e4112
  if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO
Packit 9e4112
      || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL)
Packit 9e4112
    return 2;
Packit 9e4112
Packit 9e4112
  /* Create file for later checks.  */
Packit 9e4112
  fd = creat (witness, 0600);
Packit 9e4112
  ASSERT (STDERR_FILENO < fd);
Packit 9e4112
Packit 9e4112
  /* Four iterations, with progressively more standard descriptors
Packit 9e4112
     closed.  */
Packit 9e4112
  for (i = -1; i <= STDERR_FILENO; i++)
Packit 9e4112
    {
Packit 9e4112
      if (0 <= i)
Packit 9e4112
        ASSERT (close (i) == 0);
Packit 9e4112
Packit 9e4112
      /* Detect errors.  */
Packit 9e4112
      errno = 0;
Packit 9e4112
      ASSERT (dup (-1) == -1);
Packit 9e4112
      ASSERT (errno == EBADF);
Packit 9e4112
      errno = 0;
Packit 9e4112
      ASSERT (dup (bad_fd) == -1);
Packit 9e4112
      ASSERT (errno == EBADF);
Packit 9e4112
      close (fd + 1);
Packit 9e4112
      errno = 0;
Packit 9e4112
      ASSERT (dup (fd + 1) == -1);
Packit 9e4112
      ASSERT (errno == EBADF);
Packit 9e4112
Packit 9e4112
      /* Preserve text vs. binary.  */
Packit 9e4112
      setmode (fd, O_BINARY);
Packit 9e4112
      ASSERT (dup (fd) == fd + 1);
Packit 9e4112
      ASSERT (is_open (fd + 1));
Packit 9e4112
      ASSERT (is_inheritable (fd + 1));
Packit 9e4112
      ASSERT (is_mode (fd + 1, O_BINARY));
Packit 9e4112
Packit 9e4112
      ASSERT (close (fd + 1) == 0);
Packit 9e4112
      setmode (fd, O_TEXT);
Packit 9e4112
      ASSERT (dup (fd) == fd + 1);
Packit 9e4112
      ASSERT (is_open (fd + 1));
Packit 9e4112
      ASSERT (is_inheritable (fd + 1));
Packit 9e4112
      ASSERT (is_mode (fd + 1, O_TEXT));
Packit 9e4112
Packit 9e4112
      /* Create cloexec copy.  */
Packit 9e4112
      ASSERT (close (fd + 1) == 0);
Packit 9e4112
      ASSERT (fd_safer_flag (dup_cloexec (fd), O_CLOEXEC) == fd + 1);
Packit 9e4112
      ASSERT (set_cloexec_flag (fd + 1, true) == 0);
Packit 9e4112
      ASSERT (is_open (fd + 1));
Packit 9e4112
      ASSERT (!is_inheritable (fd + 1));
Packit 9e4112
      ASSERT (close (fd) == 0);
Packit 9e4112
Packit 9e4112
      /* dup always creates inheritable copies.  Also, check that
Packit 9e4112
         earliest slot past std fds is used.  */
Packit 9e4112
      ASSERT (dup (fd + 1) == fd);
Packit 9e4112
      ASSERT (is_open (fd));
Packit 9e4112
      ASSERT (is_inheritable (fd));
Packit 9e4112
      ASSERT (close (fd + 1) == 0);
Packit 9e4112
    }
Packit 9e4112
Packit 9e4112
  /* Cleanup.  */
Packit 9e4112
  ASSERT (close (fd) == 0);
Packit 9e4112
  ASSERT (unlink (witness) == 0);
Packit 9e4112
Packit 9e4112
  return 0;
Packit 9e4112
}