dirent-safer: new module
authorEric Blake <ebb9@byu.net>
Tue, 1 Sep 2009 13:41:28 +0000 (07:41 -0600)
committerEric Blake <ebb9@byu.net>
Thu, 3 Sep 2009 01:14:53 +0000 (19:14 -0600)
* modules/dirent-safer: New file.
* lib/dirent--.h: Likewise.
* lib/dirent-safer.h: Likewise.
* lib/opendir-safer.c: Likewise.
* m4/dirent-safer.m4: Likewise.
* MODULES.html.sh (Enhancements for POSIX:2008): Mention it.
* modules/dirent-safer-tests: New test.
* tests/test-dirent-safer.c: New file.
* lib/fdopendir.c (includes): Ensure fdopendir is also safe.

Signed-off-by: Eric Blake <ebb9@byu.net>
ChangeLog
MODULES.html.sh
lib/dirent--.h [new file with mode: 0644]
lib/dirent-safer.h [new file with mode: 0644]
lib/fdopendir.c
lib/opendir-safer.c [new file with mode: 0644]
m4/dirent-safer.m4 [new file with mode: 0644]
modules/dirent-safer [new file with mode: 0644]
modules/dirent-safer-tests [new file with mode: 0644]
tests/test-dirent-safer.c [new file with mode: 0644]

index cf8b115..c30ac8e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2009-09-02  Eric Blake  <ebb9@byu.net>
 
+       dirent-safer: new module
+       * modules/dirent-safer: New file.
+       * lib/dirent--.h: Likewise.
+       * lib/dirent-safer.h: Likewise.
+       * lib/opendir-safer.c: Likewise.
+       * m4/dirent-safer.m4: Likewise.
+       * MODULES.html.sh (Enhancements for POSIX:2008): Mention it.
+       * modules/dirent-safer-tests: New test.
+       * tests/test-dirent-safer.c: New file.
+       * lib/fdopendir.c (includes): Ensure fdopendir is also safe.
+
        fdopendir: optimize on mingw
        * lib/unistd.in.h (_gl_directory_name): New prototype.
        * lib/fchdir.c (_gl_directory_name): Implement it.
index 027a0bc..bb99642 100755 (executable)
@@ -2394,6 +2394,7 @@ func_all_modules ()
 
   func_begin_table
   func_module chdir-long
+  func_module dirent-safer
   func_module dirname
   func_module getopt
   func_module iconv_open-utf
diff --git a/lib/dirent--.h b/lib/dirent--.h
new file mode 100644 (file)
index 0000000..088aafd
--- /dev/null
@@ -0,0 +1,23 @@
+/* Like dirent.h, but redefine some names to avoid glitches.
+
+   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.  */
+
+#include "dirent-safer.h"
+
+#undef opendir
+#define opendir opendir_safer
diff --git a/lib/dirent-safer.h b/lib/dirent-safer.h
new file mode 100644 (file)
index 0000000..0debe26
--- /dev/null
@@ -0,0 +1,22 @@
+/* Invoke dirent-like functions, but avoid some glitches.
+
+   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.  */
+
+#include <dirent.h>
+
+DIR *opendir_safer (const char *name);
index 3bc13ac..14dc111 100644 (file)
 #include "openat-priv.h"
 #include "save-cwd.h"
 
+#if GNULIB_DIRENT_SAFER
+# include "dirent--.h"
+#endif
+
 /* Replacement for Solaris' function by the same name.
    <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
    First, try to simulate it via opendir ("/proc/self/fd/FD").  Failing
diff --git a/lib/opendir-safer.c b/lib/opendir-safer.c
new file mode 100644 (file)
index 0000000..8c399d0
--- /dev/null
@@ -0,0 +1,68 @@
+/* Invoke opendir, but avoid some glitches.
+
+   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.  */
+
+#include <config.h>
+
+#include "dirent-safer.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include "unistd-safer.h"
+
+/* Like opendir, but do not clobber stdin, stdout, or stderr.  */
+
+DIR *
+opendir_safer (char const *name)
+{
+  DIR *dp = opendir (name);
+
+  if (dp)
+    {
+      int fd = dirfd (dp);
+
+      if (0 <= fd && fd <= STDERR_FILENO)
+        {
+          /* If fdopendir is native (as on Linux), then it is safe to
+             assume dirfd(fdopendir(n))==n.  If we are using the
+             gnulib module fdopendir, then this guarantee is not met,
+             but fdopendir recursively calls opendir_safer up to 3
+             times to at least get a safe fd.  If fdopendir is not
+             present but dirfd is accurate (as on cygwin 1.5.x), then
+             we recurse up to 3 times ourselves.  Finally, if dirfd
+             always fails (as on mingw), then we are already safe.  */
+          DIR *newdp;
+          int e;
+#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR
+          int f = dup_safer (fd);
+          newdp = fdopendir (f);
+          e = errno;
+          if (! newdp)
+            close (f);
+#else /* !FDOPENDIR */
+          newdp = opendir_safer (name);
+          e = errno;
+#endif
+          closedir (dp);
+          errno = e;
+          dp = newdp;
+        }
+    }
+
+  return dp;
+}
diff --git a/m4/dirent-safer.m4 b/m4/dirent-safer.m4
new file mode 100644 (file)
index 0000000..6e8e6c4
--- /dev/null
@@ -0,0 +1,11 @@
+#serial 1
+dnl Copyright (C) 2009 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_DIRENT_SAFER],
+[
+  AC_CHECK_FUNCS_ONCE([fdopendir])
+  AC_LIBOBJ([opendir-safer])
+])
diff --git a/modules/dirent-safer b/modules/dirent-safer
new file mode 100644 (file)
index 0000000..47db18e
--- /dev/null
@@ -0,0 +1,28 @@
+Description:
+Directory functions that avoid clobbering STD{IN,OUT,ERR}_FILENO.
+
+Files:
+lib/dirent--.h
+lib/dirent-safer.h
+lib/opendir-safer.c
+m4/dirent-safer.m4
+
+Depends-on:
+dirent
+dirfd
+unistd-safer
+
+configure.ac:
+gl_DIRENT_SAFER
+gl_MODULE_INDICATOR([dirent-safer])
+
+Makefile.am:
+
+Include:
+"dirent-safer.h"
+
+License:
+GPL
+
+Maintainer:
+Eric Blake
diff --git a/modules/dirent-safer-tests b/modules/dirent-safer-tests
new file mode 100644 (file)
index 0000000..da87778
--- /dev/null
@@ -0,0 +1,11 @@
+Files:
+tests/test-dirent-safer.c
+
+Depends-on:
+dup2
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-dirent-safer
+check_PROGRAMS += test-dirent-safer
diff --git a/tests/test-dirent-safer.c b/tests/test-dirent-safer.c
new file mode 100644 (file)
index 0000000..aeb8342
--- /dev/null
@@ -0,0 +1,106 @@
+/* Test that directory streams leave 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 "dirent--.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "unistd-safer.h"
+
+/* 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)
+
+int
+main ()
+{
+  int i;
+  DIR *dp;
+  /* The dirent-safer module works without the use of fdopendir (which
+     would also pull in fchdir and openat); but if those modules were
+     also used, we ensure that they are safe.  In particular, the
+     gnulib version of fdopendir is unable to guarantee that
+     dirfd(fdopendir(fd))==fd, but we can at least guarantee that if
+     they are not equal, the fd returned by dirfd is safe.  */
+#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR
+  int dfd;
+#endif
+
+  /* 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;
+
+#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR
+  dfd = open (".", O_RDONLY);
+  ASSERT (STDERR_FILENO < dfd);
+#endif
+
+  /* Four iterations, with progressively more standard descriptors
+     closed.  */
+  for (i = -1; i <= STDERR_FILENO; i++)
+    {
+      if (0 <= i)
+        ASSERT (close (i) == 0);
+      dp = opendir (".");
+      ASSERT (dp);
+      ASSERT (dirfd (dp) == -1 || STDERR_FILENO < dirfd (dp));
+      ASSERT (closedir (dp) == 0);
+
+#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR
+      {
+        int fd = dup_safer (dfd);
+        ASSERT (STDERR_FILENO < fd);
+        dp = fdopendir (fd);
+        ASSERT (dp);
+        ASSERT (dirfd (dp) == -1 || STDERR_FILENO < dirfd (dp));
+        ASSERT (closedir (dp) == 0);
+        errno = 0;
+        ASSERT (close (fd) == -1);
+        ASSERT (errno == EBADF);
+      }
+#endif
+    }
+
+#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR
+  ASSERT (close (dfd) == 0);
+#endif
+
+  return 0;
+}