changeset 12127:bda9467a9d66

fdopendir: fix GNU/Hurd bug fdopendir(open("file",O_RDONLY)) mistakenly succeeded, with subsequent readdir() failing with ENOTDIR. * m4/fdopendir.m4 (gl_FUNC_FDOPENDIR): Check for Hurd bug in allowing non-directory fds. * lib/fdopendir.c (rpl_fdopendir): Work around it. * m4/dirent_h.m4 (gl_DIRENT_H_DEFAULTS): New witness. * modules/dirent (Makefile.am): Substitute it. * lib/dirent.in.h (fdopendir): Declare replacement. * doc/posix-functions/fdopendir.texi (fdopendir): Document this. * tests/test-fdopendir.c (main): Test something other than /dev/null, since on Hurd that behaves like a directory. Signed-off-by: Eric Blake <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Tue, 06 Oct 2009 06:58:08 -0600
parents 6489be573ae5
children a92e67f6c6f1
files ChangeLog doc/posix-functions/fdopendir.texi lib/dirent.in.h lib/fdopendir.c m4/dirent_h.m4 m4/fdopendir.m4 modules/dirent tests/test-fdopendir.c
diffstat 8 files changed, 90 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2009-10-06  Eric Blake  <ebb9@byu.net>
 
+	fdopendir: fix GNU/Hurd bug
+	* m4/fdopendir.m4 (gl_FUNC_FDOPENDIR): Check for Hurd bug in
+	allowing non-directory fds.
+	* lib/fdopendir.c (rpl_fdopendir): Work around it.
+	* m4/dirent_h.m4 (gl_DIRENT_H_DEFAULTS): New witness.
+	* modules/dirent (Makefile.am): Substitute it.
+	* lib/dirent.in.h (fdopendir): Declare replacement.
+	* doc/posix-functions/fdopendir.texi (fdopendir): Document this.
+	* tests/test-fdopendir.c (main): Test something other than
+	/dev/null, since on Hurd that behaves like a directory.
+
 	test-symlink: port to GNU/Hurd
 	* tests/test-symlink.h (test_symlink): Relax expected errno.
 
--- a/doc/posix-functions/fdopendir.texi
+++ b/doc/posix-functions/fdopendir.texi
@@ -16,6 +16,10 @@
 is not multithread-safe.  Also, the replacement does not guarantee
 that @samp{dirfd(fdopendir(n))==n} (dirfd might fail, or return a
 different file descriptor than n).
+@item
+This function does not reject non-directory file descriptors on some
+platforms:
+GNU/Hurd.
 @end itemize
 
 Portability problems not fixed by Gnulib:
--- a/lib/dirent.in.h
+++ b/lib/dirent.in.h
@@ -55,7 +55,11 @@
 #endif
 
 #if @GNULIB_FDOPENDIR@
-# if !@HAVE_FDOPENDIR@
+# if @REPLACE_FDOPENDIR@
+#  undef fdopendir
+#  define fdopendir rpl_fdopendir
+# endif
+# if !@HAVE_FDOPENDIR@ || @REPLACE_FDOPENDIR@
 /* Open a directory stream visiting the given directory file
    descriptor.  Return NULL and set errno if fd is not visiting a
    directory.  On success, this function consumes fd (it will be
--- a/lib/fdopendir.c
+++ b/lib/fdopendir.c
@@ -23,13 +23,15 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-#include "openat.h"
-#include "openat-priv.h"
-#include "save-cwd.h"
+#if !HAVE_FDOPENDIR
 
-#if GNULIB_DIRENT_SAFER
-# include "dirent--.h"
-#endif
+# include "openat.h"
+# 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>
@@ -70,12 +72,12 @@
      save_cwd/restore_cwd.  */
   if (! dir && EXPECTED_ERRNO (saved_errno))
     {
-#if REPLACE_FCHDIR
+# if REPLACE_FCHDIR
       const char *name = _gl_directory_name (fd);
       if (name)
         dir = opendir (name);
       saved_errno = errno;
-#else /* !REPLACE_FCHDIR */
+# else /* !REPLACE_FCHDIR */
       struct saved_cwd saved_cwd;
       if (save_cwd (&saved_cwd) != 0)
 	openat_save_fail (errno);
@@ -95,7 +97,7 @@
 	}
 
       free_cwd (&saved_cwd);
-#endif /* !REPLACE_FCHDIR */
+# endif /* !REPLACE_FCHDIR */
     }
 
   if (dir)
@@ -105,3 +107,28 @@
   errno = saved_errno;
   return dir;
 }
+
+#else /* HAVE_FDOPENDIR */
+
+# include <errno.h>
+# include <sys/stat.h>
+
+# undef fdopendir
+
+/* Like fdopendir, but work around GNU/Hurd bug by validating FD.  */
+
+DIR *
+rpl_fdopendir (int fd)
+{
+  struct stat st;
+  if (fstat (fd, &st))
+    return NULL;
+  if (!S_ISDIR (st.st_mode))
+    {
+      errno = ENOTDIR;
+      return NULL;
+    }
+  return fdopendir (fd);
+}
+
+#endif /* HAVE_FDOPENDIR */
--- a/m4/dirent_h.m4
+++ b/m4/dirent_h.m4
@@ -1,4 +1,4 @@
-# dirent_h.m4 serial 5
+# dirent_h.m4 serial 6
 dnl Copyright (C) 2008-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,
@@ -32,16 +32,17 @@
 AC_DEFUN([gl_DIRENT_H_DEFAULTS],
 [
   AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl for REPLACE_FCHDIR
-  GNULIB_DIRFD=0;     AC_SUBST([GNULIB_DIRFD])
-  GNULIB_FDOPENDIR=0; AC_SUBST([GNULIB_FDOPENDIR])
-  GNULIB_SCANDIR=0;   AC_SUBST([GNULIB_SCANDIR])
-  GNULIB_ALPHASORT=0; AC_SUBST([GNULIB_ALPHASORT])
+  GNULIB_DIRFD=0;       AC_SUBST([GNULIB_DIRFD])
+  GNULIB_FDOPENDIR=0;   AC_SUBST([GNULIB_FDOPENDIR])
+  GNULIB_SCANDIR=0;     AC_SUBST([GNULIB_SCANDIR])
+  GNULIB_ALPHASORT=0;   AC_SUBST([GNULIB_ALPHASORT])
   dnl Assume proper GNU behavior unless another module says otherwise.
-  HAVE_DECL_DIRFD=1;  AC_SUBST([HAVE_DECL_DIRFD])
-  HAVE_FDOPENDIR=1;   AC_SUBST([HAVE_FDOPENDIR])
-  HAVE_SCANDIR=1;     AC_SUBST([HAVE_SCANDIR])
-  HAVE_ALPHASORT=1;   AC_SUBST([HAVE_ALPHASORT])
-  REPLACE_CLOSEDIR=0; AC_SUBST([REPLACE_CLOSEDIR])
-  REPLACE_OPENDIR=0;  AC_SUBST([REPLACE_OPENDIR])
-  DIRENT_H='';        AC_SUBST([DIRENT_H])
+  HAVE_DECL_DIRFD=1;    AC_SUBST([HAVE_DECL_DIRFD])
+  HAVE_FDOPENDIR=1;     AC_SUBST([HAVE_FDOPENDIR])
+  HAVE_SCANDIR=1;       AC_SUBST([HAVE_SCANDIR])
+  HAVE_ALPHASORT=1;     AC_SUBST([HAVE_ALPHASORT])
+  REPLACE_CLOSEDIR=0;   AC_SUBST([REPLACE_CLOSEDIR])
+  REPLACE_FDOPENDIR=0;  AC_SUBST([REPLACE_FDOPENDIR])
+  REPLACE_OPENDIR=0;    AC_SUBST([REPLACE_OPENDIR])
+  DIRENT_H='';          AC_SUBST([DIRENT_H])
 ])
--- a/m4/fdopendir.m4
+++ b/m4/fdopendir.m4
@@ -1,4 +1,4 @@
-# serial 1
+# serial 2
 # See if we need to provide fdopendir.
 
 dnl Copyright (C) 2009 Free Software Foundation, Inc.
@@ -17,5 +17,22 @@
     AC_LIBOBJ([fdopendir])
     gl_REPLACE_DIRENT_H
     HAVE_FDOPENDIR=0
+  else
+    AC_CACHE_CHECK([whether fdopendir works],
+      [gl_cv_func_fdopendir_works],
+      [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <dirent.h>
+#include <fcntl.h>
+]], [int fd = open ("conftest.h", O_RDONLY);
+     if (fd < 0) return 2;
+     return !!fdopendir (fd);])],
+	 [gl_cv_func_fdopendir_works=yes],
+	 [gl_cv_func_fdopendir_works=no],
+	 [gl_cv_func_fdopendir_works="guessing no"])])
+    if test "$gl_cv_func_fdopendir_works" != yes; then
+      REPLACE_FDOPENDIR=1
+      gl_REPLACE_DIRENT_H
+      AC_LIBOBJ([fdopendir])
+    fi
   fi
 ])
--- a/modules/dirent
+++ b/modules/dirent
@@ -33,6 +33,7 @@
 	      -e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \
 	      -e 's|@''HAVE_ALPHASORT''@|$(HAVE_ALPHASORT)|g' \
 	      -e 's|@''REPLACE_CLOSEDIR''@|$(REPLACE_CLOSEDIR)|g' \
+	      -e 's|@''REPLACE_FDOPENDIR''@|$(REPLACE_FDOPENDIR)|g' \
 	      -e 's|@''REPLACE_OPENDIR''@|$(REPLACE_OPENDIR)|g' \
 	      -e '/definition of GL_LINK_WARNING/r $(LINK_WARNING_H)' \
 	      < $(srcdir)/dirent.in.h; \
--- a/tests/test-fdopendir.c
+++ b/tests/test-fdopendir.c
@@ -45,12 +45,13 @@
   int fd;
 
   /* A non-directory cannot be turned into a directory stream.  */
-  fd = open ("/dev/null", O_RDONLY);
+  fd = open ("test-fdopendir.tmp", O_RDONLY | O_CREAT, 0600);
   ASSERT (0 <= fd);
   errno = 0;
   ASSERT (fdopendir (fd) == NULL);
   ASSERT (errno == ENOTDIR);
   ASSERT (close (fd) == 0);
+  ASSERT (unlink ("test-fdopendir.tmp") == 0);
 
   /* A bad fd cannot be turned into a stream.  */
   errno = 0;