unistd-safer: add unit test
authorEric Blake <ebb9@byu.net>
Mon, 7 Dec 2009 17:17:07 +0000 (10:17 -0700)
committerEric Blake <ebb9@byu.net>
Tue, 8 Dec 2009 03:56:47 +0000 (20:56 -0700)
Add more unit tests.  Meanwhile, fix compilation error on mingw when
testing unistd-safer and fchdir together; and avoid gcc warning on
platforms without setmode.

* modules/unistd-safer-tests: New file.
* tests/test-dup-safer.c: Likewise.
* tests/test-cloexec.c (setmode): Avoid compiler warning.
* tests/test-dup2.c (setmode): Likewise.
* lib/cloexec.c (dup_cloexec): Fix mingw compile error.

Signed-off-by: Eric Blake <ebb9@byu.net>
ChangeLog
lib/cloexec.c
modules/unistd-safer-tests [new file with mode: 0644]
tests/test-cloexec.c
tests/test-dup-safer.c [new file with mode: 0644]
tests/test-dup2.c

index 3cf1bdb..73e6faa 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2009-12-07  Eric Blake  <ebb9@byu.net>
 
+       unistd-safer: add unit test
+       * modules/unistd-safer-tests: New file.
+       * tests/test-dup-safer.c: Likewise.
+       * tests/test-cloexec.c (setmode): Avoid compiler warning.
+       * tests/test-dup2.c (setmode): Likewise.
+       * lib/cloexec.c (dup_cloexec): Fix mingw compile error.
+
        cloexec: preserve text vs. binary across dup_cloexec
        * lib/cloexec.c (dup_cloexec) [W32]: Query and use translation
        mode.
index 57e0be5..18985cb 100644 (file)
@@ -128,7 +128,7 @@ dup_cloexec (int fd)
 
 #  if REPLACE_FCHDIR
   if (0 <= nfd)
-    result = _gl_register_dup (fd, nfd);
+    nfd = _gl_register_dup (fd, nfd);
 #  endif
   return nfd;
 
diff --git a/modules/unistd-safer-tests b/modules/unistd-safer-tests
new file mode 100644 (file)
index 0000000..17b17a0
--- /dev/null
@@ -0,0 +1,13 @@
+Files:
+tests/test-dup-safer.c
+
+Depends-on:
+binary-io
+cloexec
+stdbool
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-dup-safer
+check_PROGRAMS += test-dup-safer
index a29d1be..1f43014 100644 (file)
@@ -69,7 +69,8 @@ is_inheritable (int fd)
 }
 
 #if !O_BINARY
-# define setmode(f,m) 0
+# define setmode(f,m) zero ()
+static int zero (void) { return 0; }
 #endif
 
 /* Return non-zero if FD is open in the given MODE, which is either
diff --git a/tests/test-dup-safer.c b/tests/test-dup-safer.c
new file mode 100644 (file)
index 0000000..24cc9e5
--- /dev/null
@@ -0,0 +1,186 @@
+/* Test that dup_safer leaves standard fds alone.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Eric Blake <ebb9@byu.net>, 2009.  */
+
+#include <config.h>
+
+#include "unistd--.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "binary-io.h"
+#include "cloexec.h"
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Get declarations of the Win32 API functions.  */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+#if !O_BINARY
+# define setmode(f,m) zero ()
+static int zero (void) { return 0; }
+#endif
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+
+/* This test intentionally closes stderr.  So, we arrange to have fd 10
+   (outside the range of interesting fd's during the test) set up to
+   duplicate the original stderr.  */
+
+#define BACKUP_STDERR_FILENO 10
+static FILE *myerr;
+
+#define ASSERT(expr) \
+  do                                                                         \
+    {                                                                        \
+      if (!(expr))                                                           \
+        {                                                                    \
+          fprintf (myerr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
+          fflush (myerr);                                                    \
+          abort ();                                                          \
+        }                                                                    \
+    }                                                                        \
+  while (0)
+
+/* Return true if FD is open.  */
+static bool
+is_open (int fd)
+{
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  /* On Win32, the initial state of unassigned standard file
+     descriptors is that they are open but point to an
+     INVALID_HANDLE_VALUE, and there is no fcntl.  */
+  return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
+#else
+# ifndef F_GETFL
+#  error Please port fcntl to your platform
+# endif
+  return 0 <= fcntl (fd, F_GETFL);
+#endif
+}
+
+/* Return true if FD is open and inheritable across exec/spawn.  */
+static bool
+is_inheritable (int fd)
+{
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  /* On Win32, the initial state of unassigned standard file
+     descriptors is that they are open but point to an
+     INVALID_HANDLE_VALUE, and there is no fcntl.  */
+  HANDLE h = (HANDLE) _get_osfhandle (fd);
+  DWORD flags;
+  if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0)
+    return 0;
+  return (flags & HANDLE_FLAG_INHERIT) != 0;
+#else
+# ifndef F_GETFD
+#  error Please port fcntl to your platform
+# endif
+  int i = fcntl (fd, F_GETFD);
+  return 0 <= i && (i & FD_CLOEXEC) == 0;
+#endif
+}
+
+/* Return true if FD is open in the given MODE, which is either
+   O_TEXT or O_BINARY.  */
+static bool
+is_mode (int fd, int mode)
+{
+  int value = setmode (fd, O_BINARY);
+  setmode (fd, value);
+  return mode == value;
+}
+
+#define witness "test-dup-safer.txt"
+
+int
+main (void)
+{
+  int i;
+  int fd;
+
+  /* We close fd 2 later, so save it in fd 10.  */
+  if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO
+      || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL)
+    return 2;
+
+  /* Create file for later checks.  */
+  fd = creat (witness, 0600);
+  ASSERT (STDERR_FILENO < fd);
+
+  /* Four iterations, with progressively more standard descriptors
+     closed.  */
+  for (i = -1; i <= STDERR_FILENO; i++)
+    {
+      if (0 <= i)
+        ASSERT (close (i) == 0);
+
+      /* Detect errors.  */
+      errno = 0;
+      ASSERT (dup (-1) == -1);
+      ASSERT (errno == EBADF);
+      errno = 0;
+      ASSERT (dup (10000000) == -1);
+      ASSERT (errno == EBADF);
+      close (fd + 1);
+      errno = 0;
+      ASSERT (dup (fd + 1) == -1);
+      ASSERT (errno == EBADF);
+
+      /* Preserve text vs. binary.  */
+      setmode (fd, O_BINARY);
+      ASSERT (dup (fd) == fd + 1);
+      ASSERT (is_open (fd + 1));
+      ASSERT (is_inheritable (fd + 1));
+      ASSERT (is_mode (fd + 1, O_BINARY));
+
+      ASSERT (close (fd + 1) == 0);
+      setmode (fd, O_TEXT);
+      ASSERT (dup (fd) == fd + 1);
+      ASSERT (is_open (fd + 1));
+      ASSERT (is_inheritable (fd + 1));
+      ASSERT (is_mode (fd + 1, O_TEXT));
+
+      /* Create cloexec copy.  */
+      ASSERT (close (fd + 1) == 0);
+      ASSERT (fd_safer_flag (dup_cloexec (fd), O_CLOEXEC) == fd + 1);
+      ASSERT (set_cloexec_flag (fd + 1, true) == 0);
+      ASSERT (is_open (fd + 1));
+      ASSERT (!is_inheritable (fd + 1));
+      ASSERT (close (fd) == 0);
+
+      /* dup always creates inheritable copies.  Also, check that
+         earliest slot past std fds is used.  */
+      ASSERT (dup (fd + 1) == fd);
+      ASSERT (is_open (fd));
+      ASSERT (is_inheritable (fd));
+      ASSERT (close (fd + 1) == 0);
+    }
+
+  /* Cleanup.  */
+  ASSERT (close (fd) == 0);
+  ASSERT (unlink (witness) == 0);
+
+  return 0;
+}
index e6d3c62..b22d1e6 100644 (file)
@@ -86,7 +86,8 @@ is_inheritable (int fd)
 }
 
 #if !O_BINARY
-# define setmode(f,m) 0
+# define setmode(f,m) zero ()
+static int zero (void) { return 0; }
 #endif
 
 /* Return non-zero if FD is open in the given MODE, which is either