From dfbec8be94ca116ce40c04f88302329505dbb745 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 6 Oct 2009 06:58:08 -0600 Subject: [PATCH] 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 --- ChangeLog | 11 ++++++++++ doc/posix-functions/fdopendir.texi | 4 ++++ lib/dirent.in.h | 6 ++++- lib/fdopendir.c | 45 ++++++++++++++++++++++++++++++-------- m4/dirent_h.m4 | 25 +++++++++++---------- m4/fdopendir.m4 | 19 +++++++++++++++- modules/dirent | 1 + tests/test-fdopendir.c | 3 ++- 8 files changed, 90 insertions(+), 24 deletions(-) diff --git a/ChangeLog b/ChangeLog index ef050d76f..7ab913532 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2009-10-06 Eric Blake + 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. diff --git a/doc/posix-functions/fdopendir.texi b/doc/posix-functions/fdopendir.texi index 20db12cfe..fae7bb751 100644 --- a/doc/posix-functions/fdopendir.texi +++ b/doc/posix-functions/fdopendir.texi @@ -16,6 +16,10 @@ But the replacement function is not safe to be used in libraries and 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: diff --git a/lib/dirent.in.h b/lib/dirent.in.h index 6da77d9ff..2885c561f 100644 --- a/lib/dirent.in.h +++ b/lib/dirent.in.h @@ -55,7 +55,11 @@ extern int dirfd (DIR const *dir); #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 diff --git a/lib/fdopendir.c b/lib/fdopendir.c index 14dc111c8..c36430663 100644 --- a/lib/fdopendir.c +++ b/lib/fdopendir.c @@ -23,13 +23,15 @@ #include #include -#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. @@ -70,12 +72,12 @@ fdopendir (int fd) 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 @@ fdopendir (int fd) } free_cwd (&saved_cwd); -#endif /* !REPLACE_FCHDIR */ +# endif /* !REPLACE_FCHDIR */ } if (dir) @@ -105,3 +107,28 @@ fdopendir (int fd) errno = saved_errno; return dir; } + +#else /* HAVE_FDOPENDIR */ + +# include +# include + +# 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 */ diff --git a/m4/dirent_h.m4 b/m4/dirent_h.m4 index 16d8a0221..a9964e231 100644 --- 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_MODULE_INDICATOR], 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]) ]) diff --git a/m4/fdopendir.m4 b/m4/fdopendir.m4 index 09670bb43..0ffb7fbbe 100644 --- 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_DEFUN([gl_FUNC_FDOPENDIR], 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 +#include +]], [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 ]) diff --git a/modules/dirent b/modules/dirent index f729bcdbb..d21cfafbd 100644 --- a/modules/dirent +++ b/modules/dirent @@ -33,6 +33,7 @@ dirent.h: dirent.in.h -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; \ diff --git a/tests/test-fdopendir.c b/tests/test-fdopendir.c index 003a27972..330544c5d 100644 --- a/tests/test-fdopendir.c +++ b/tests/test-fdopendir.c @@ -45,12 +45,13 @@ main () 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; -- 2.11.0