From d1aa3e40286ed2b0ad1045b326a37167ac5d8847 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 31 Aug 2009 20:37:59 -0600 Subject: [PATCH] fdopendir: split into its own module * lib/openat.c (fdopendir): Move... * lib/fdopendir.c: ...into new file. * modules/fdopendir: New module. * m4/fdopendir.m4 (gl_FUNC_FDOPENDIR): New file. * modules/openat (Depends-on): Add fdopendir. * m4/openat.m4 (gl_FUNC_OPENAT): No longer need to check for fdopendir here. * modules/savedir (Depends-on): Only need fdopendir, not full openat. * lib/savedir.c (include): Use , not "openat.h". * lib/openat.h (fdopendir): Drop prototype. * lib/dirent.in.h (fdopendir): Provide prototype. * m4/dirent_h.m4 (gl_DIRENT_H_DEFAULTS): Add replacements. * modules/dirent (Makefile.am): Substitute them. * MODULES.html.sh (File system functions): Mention it. * doc/posix-functions/fdopendir.texi (fdopendir): Likewise. * modules/fdopendir-tests: New file. * tests/test-fdopendir.c: Likewise. Signed-off-by: Eric Blake --- ChangeLog | 20 ++++++++ MODULES.html.sh | 1 + doc/posix-functions/fdopendir.texi | 7 ++- lib/dirent.in.h | 17 +++++++ lib/fdopendir.c | 95 ++++++++++++++++++++++++++++++++++++++ lib/openat.c | 68 --------------------------- lib/openat.h | 6 +-- lib/savedir.c | 3 +- m4/dirent_h.m4 | 10 ++-- m4/fdopendir.m4 | 21 +++++++++ m4/openat.m4 | 3 +- modules/dirent | 2 + modules/fdopendir | 30 ++++++++++++ modules/fdopendir-tests | 13 ++++++ modules/openat | 1 + modules/savedir | 2 +- tests/test-fdopendir.c | 76 ++++++++++++++++++++++++++++++ 17 files changed, 291 insertions(+), 84 deletions(-) create mode 100644 lib/fdopendir.c create mode 100644 m4/fdopendir.m4 create mode 100644 modules/fdopendir create mode 100644 modules/fdopendir-tests create mode 100644 tests/test-fdopendir.c diff --git a/ChangeLog b/ChangeLog index 3205ba8d7..0cd6e2096 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,25 @@ 2009-09-02 Eric Blake + fdopendir: split into its own module + * lib/openat.c (fdopendir): Move... + * lib/fdopendir.c: ...into new file. + * modules/fdopendir: New module. + * m4/fdopendir.m4 (gl_FUNC_FDOPENDIR): New file. + * modules/openat (Depends-on): Add fdopendir. + * m4/openat.m4 (gl_FUNC_OPENAT): No longer need to check for + fdopendir here. + * modules/savedir (Depends-on): Only need fdopendir, not full + openat. + * lib/savedir.c (include): Use , not "openat.h". + * lib/openat.h (fdopendir): Drop prototype. + * lib/dirent.in.h (fdopendir): Provide prototype. + * m4/dirent_h.m4 (gl_DIRENT_H_DEFAULTS): Add replacements. + * modules/dirent (Makefile.am): Substitute them. + * MODULES.html.sh (File system functions): Mention it. + * doc/posix-functions/fdopendir.texi (fdopendir): Likewise. + * modules/fdopendir-tests: New file. + * tests/test-fdopendir.c: Likewise. + fchdir: use more consistent macro convention * lib/fcntl.in.h (_gl_register_fd): Move declaration to unistd. * lib/sys_stat.in.h (rpl_fstat): Declare via make-time diff --git a/MODULES.html.sh b/MODULES.html.sh index 3c54cec98..027a0bca1 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2451,6 +2451,7 @@ func_all_modules () func_module dirfd func_module double-slash-root func_module euidaccess + func_module fdopendir func_module file-type func_module fileblocks func_module filemode diff --git a/doc/posix-functions/fdopendir.texi b/doc/posix-functions/fdopendir.texi index 1d9827c53..20db12cfe 100644 --- a/doc/posix-functions/fdopendir.texi +++ b/doc/posix-functions/fdopendir.texi @@ -4,7 +4,7 @@ POSIX specification: @url{http://www.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html} -Gnulib module: openat +Gnulib module: fdopendir Portability problems fixed by Gnulib: @itemize @@ -12,7 +12,10 @@ Portability problems fixed by Gnulib: This function is missing on some platforms: glibc 2.3.6, MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Cygwin 1.5.x, mingw, Interix 3.5, BeOS. -But the replacement function is not safe to be used in libraries and is not multithread-safe. +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). @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/dirent.in.h b/lib/dirent.in.h index 15f0245b0..893076567 100644 --- a/lib/dirent.in.h +++ b/lib/dirent.in.h @@ -58,6 +58,23 @@ extern int dirfd (DIR const *dir); dirfd (d)) #endif +#if @GNULIB_FDOPENDIR@ +# if !@HAVE_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 + implicitly closed either by this function or by a subsequent + closedir). */ +extern DIR *fdopendir (int fd); +# endif +#elif defined GNULIB_POSIXCHECK +# undef fdopendir +# define fdopendir(f) \ + (GL_LINK_WARNING ("fdopendir is unportable - " \ + "use gnulib module fdopendir for portability"), \ + fdopendir (f)) +#endif + #if @GNULIB_SCANDIR@ /* Scan the directory DIR, calling FILTER on each directory entry. Entries for which FILTER returns nonzero are individually malloc'd, diff --git a/lib/fdopendir.c b/lib/fdopendir.c new file mode 100644 index 000000000..70951562f --- /dev/null +++ b/lib/fdopendir.c @@ -0,0 +1,95 @@ +/* provide a replacement fdopendir function + Copyright (C) 2004-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 . */ + +/* written by Jim Meyering */ + +#include + +#include + +#include +#include + +#include "openat.h" +#include "openat-priv.h" +#include "save-cwd.h" + +/* Replacement for Solaris' function by the same name. + + First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing + that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd. + If either the save_cwd or the restore_cwd fails (relatively unlikely), + then give a diagnostic and exit nonzero. + Otherwise, this function works just like Solaris' fdopendir. + + W A R N I N G: + Unlike other fd-related functions, this one effectively consumes + its FD parameter. The caller should not close or otherwise + manipulate FD if this function returns successfully. Also, this + implementation does not guarantee that dirfd(fdopendir(n))==n; + the open directory stream may use a clone of FD, or have no + associated fd at all. */ +DIR * +fdopendir (int fd) +{ + struct saved_cwd saved_cwd; + int saved_errno; + DIR *dir; + + char buf[OPENAT_BUFFER_SIZE]; + char *proc_file = openat_proc_name (buf, fd, "."); + if (proc_file) + { + dir = opendir (proc_file); + saved_errno = errno; + } + else + { + dir = NULL; + saved_errno = EOPNOTSUPP; + } + + /* If the syscall fails with an expected errno value, resort to + save_cwd/restore_cwd. */ + if (! dir && EXPECTED_ERRNO (saved_errno)) + { + if (save_cwd (&saved_cwd) != 0) + openat_save_fail (errno); + + if (fchdir (fd) != 0) + { + dir = NULL; + saved_errno = errno; + } + else + { + dir = opendir ("."); + saved_errno = errno; + + if (restore_cwd (&saved_cwd) != 0) + openat_restore_fail (errno); + } + + free_cwd (&saved_cwd); + } + + if (dir) + close (fd); + if (proc_file != buf) + free (proc_file); + errno = saved_errno; + return dir; +} diff --git a/lib/openat.c b/lib/openat.c index 77a85bffe..7a68cba60 100644 --- a/lib/openat.c +++ b/lib/openat.c @@ -152,74 +152,6 @@ openat_needs_fchdir (void) return needs_fchdir; } -#if !HAVE_FDOPENDIR - -/* Replacement for Solaris' function by the same name. - - First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing - that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd. - If either the save_cwd or the restore_cwd fails (relatively unlikely), - then give a diagnostic and exit nonzero. - Otherwise, this function works just like Solaris' fdopendir. - - W A R N I N G: - Unlike the other fd-related functions here, this one - effectively consumes its FD parameter. The caller should not - close or otherwise manipulate FD if this function returns successfully. */ -DIR * -fdopendir (int fd) -{ - struct saved_cwd saved_cwd; - int saved_errno; - DIR *dir; - - char buf[OPENAT_BUFFER_SIZE]; - char *proc_file = openat_proc_name (buf, fd, "."); - if (proc_file) - { - dir = opendir (proc_file); - saved_errno = errno; - } - else - { - dir = NULL; - saved_errno = EOPNOTSUPP; - } - - /* If the syscall fails with an expected errno value, resort to - save_cwd/restore_cwd. */ - if (! dir && EXPECTED_ERRNO (saved_errno)) - { - if (save_cwd (&saved_cwd) != 0) - openat_save_fail (errno); - - if (fchdir (fd) != 0) - { - dir = NULL; - saved_errno = errno; - } - else - { - dir = opendir ("."); - saved_errno = errno; - - if (restore_cwd (&saved_cwd) != 0) - openat_restore_fail (errno); - } - - free_cwd (&saved_cwd); - } - - if (dir) - close (fd); - if (proc_file != buf) - free (proc_file); - errno = saved_errno; - return dir; -} - -#endif - /* Replacement for Solaris' function by the same name. First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE"). diff --git a/lib/openat.h b/lib/openat.h index 68c7df0b7..4072c94ed 100644 --- a/lib/openat.h +++ b/lib/openat.h @@ -1,5 +1,5 @@ /* provide a replacement openat function - Copyright (C) 2004, 2005, 2006, 2008 Free Software Foundation, Inc. + Copyright (C) 2004-2006, 2008-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 @@ -66,10 +66,6 @@ int openat (int fd, char const *file, int flags, /* mode_t mode */ ...); int openat_permissive (int fd, char const *file, int flags, mode_t mode, int *cwd_errno); -# if ! HAVE_FDOPENDIR -# define fdopendir __OPENAT_ID (fdopendir) -# endif -DIR *fdopendir (int fd); # define fstatat __OPENAT_ID (fstatat) int fstatat (int fd, char const *file, struct stat *st, int flag); # define unlinkat __OPENAT_ID (unlinkat) diff --git a/lib/savedir.c b/lib/savedir.c index b83741433..8400145ad 100644 --- a/lib/savedir.c +++ b/lib/savedir.c @@ -1,7 +1,7 @@ /* savedir.c -- save the list of files in a directory in a string Copyright (C) 1990, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, - 2006 Free Software Foundation, Inc. + 2006, 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 @@ -35,7 +35,6 @@ #include #include -#include "openat.h" #include "xalloc.h" #ifndef NAME_SIZE_DEFAULT diff --git a/m4/dirent_h.m4 b/m4/dirent_h.m4 index e507e3de8..06fffefc1 100644 --- a/m4/dirent_h.m4 +++ b/m4/dirent_h.m4 @@ -33,11 +33,13 @@ 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]) dnl Assume proper GNU behavior unless another module says otherwise. - HAVE_DECL_DIRFD=1; AC_SUBST([HAVE_DECL_DIRFD]) - HAVE_SCANDIR=1; AC_SUBST([HAVE_SCANDIR]) - HAVE_ALPHASORT=1; AC_SUBST([HAVE_ALPHASORT]) - 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]) + DIRENT_H=''; AC_SUBST([DIRENT_H]) ]) diff --git a/m4/fdopendir.m4 b/m4/fdopendir.m4 new file mode 100644 index 000000000..09670bb43 --- /dev/null +++ b/m4/fdopendir.m4 @@ -0,0 +1,21 @@ +# serial 1 +# See if we need to provide fdopendir. + +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. + +# Written by Eric Blake. + +AC_DEFUN([gl_FUNC_FDOPENDIR], +[ + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_FUNCS_ONCE([fdopendir]) + if test $ac_cv_func_fdopendir = no; then + AC_LIBOBJ([openat-proc]) + AC_LIBOBJ([fdopendir]) + gl_REPLACE_DIRENT_H + HAVE_FDOPENDIR=0 + fi +]) diff --git a/m4/openat.m4 b/m4/openat.m4 index daa6a539e..c8403a15f 100644 --- a/m4/openat.m4 +++ b/m4/openat.m4 @@ -1,4 +1,4 @@ -# serial 18 +# serial 19 # See if we need to use our replacement for Solaris' openat et al functions. dnl Copyright (C) 2004-2009 Free Software Foundation, Inc. @@ -13,7 +13,6 @@ AC_DEFUN([gl_FUNC_OPENAT], AC_LIBOBJ([openat-proc]) AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) AC_CHECK_FUNCS_ONCE([lchmod]) - AC_CHECK_FUNCS_ONCE([fdopendir]) AC_REPLACE_FUNCS([fchmodat mkdirat openat]) AC_REQUIRE([AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK]) case $ac_cv_func_openat+$ac_cv_func_lstat_dereferences_slashed_symlink in diff --git a/modules/dirent b/modules/dirent index 8df7c354a..3eb7411f1 100644 --- a/modules/dirent +++ b/modules/dirent @@ -25,9 +25,11 @@ dirent.h: dirent.in.h -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ -e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \ -e 's|@''GNULIB_DIRFD''@|$(GNULIB_DIRFD)|g' \ + -e 's|@''GNULIB_FDOPENDIR''@|$(GNULIB_FDOPENDIR)|g' \ -e 's|@''GNULIB_SCANDIR''@|$(GNULIB_SCANDIR)|g' \ -e 's|@''GNULIB_ALPHASORT''@|$(GNULIB_ALPHASORT)|g' \ -e 's|@''HAVE_DECL_DIRFD''@|$(HAVE_DECL_DIRFD)|g' \ + -e 's|@''HAVE_FDOPENDIR''@|$(HAVE_FDOPENDIR)|g' \ -e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \ -e 's|@''HAVE_ALPHASORT''@|$(HAVE_ALPHASORT)|g' \ -e 's|@''REPLACE_FCHDIR''@|$(REPLACE_FCHDIR)|g' \ diff --git a/modules/fdopendir b/modules/fdopendir new file mode 100644 index 000000000..7a90aa367 --- /dev/null +++ b/modules/fdopendir @@ -0,0 +1,30 @@ +Description: +Open a directory stream from a file descriptor. + +Files: +lib/fdopendir.c +lib/openat-priv.h +lib/openat-proc.c +m4/fdopendir.m4 + +Depends-on: +extensions +dirent +fchdir +openat-die +save-cwd + +configure.ac: +gl_FUNC_FDOPENDIR +gl_DIRENT_MODULE_INDICATOR([fdopendir]) + +Makefile.am: + +Include: + + +License: +GPL + +Maintainer: +Jim Meyering, Eric Blake diff --git a/modules/fdopendir-tests b/modules/fdopendir-tests new file mode 100644 index 000000000..9df5e293a --- /dev/null +++ b/modules/fdopendir-tests @@ -0,0 +1,13 @@ +Files: +tests/test-fdopendir.c + +Depends-on: +open +progname + +configure.ac: + +Makefile.am: +TESTS += test-fdopendir +check_PROGRAMS += test-fdopendir +test_fdopendir_LDADD = $(LDADD) @LIBINTL@ diff --git a/modules/openat b/modules/openat index 561687d9d..5c326a07d 100644 --- a/modules/openat +++ b/modules/openat @@ -18,6 +18,7 @@ Depends-on: dirname extensions fchdir +fdopendir gettext-h intprops lchown diff --git a/modules/savedir b/modules/savedir index e781af76b..4171b802c 100644 --- a/modules/savedir +++ b/modules/savedir @@ -7,7 +7,7 @@ lib/savedir.c m4/savedir.m4 Depends-on: -openat +fdopendir xalloc configure.ac: diff --git a/tests/test-fdopendir.c b/tests/test-fdopendir.c new file mode 100644 index 000000000..003a27972 --- /dev/null +++ b/tests/test-fdopendir.c @@ -0,0 +1,76 @@ +/* Test opening a directory stream from a file descriptor. + 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 . */ + +/* Written by Eric Blake , 2009. */ + +#include + +#include + +#include +#include +#include +#include +#include + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +int +main () +{ + DIR *d; + int fd; + + /* A non-directory cannot be turned into a directory stream. */ + fd = open ("/dev/null", O_RDONLY); + ASSERT (0 <= fd); + errno = 0; + ASSERT (fdopendir (fd) == NULL); + ASSERT (errno == ENOTDIR); + ASSERT (close (fd) == 0); + + /* A bad fd cannot be turned into a stream. */ + errno = 0; + ASSERT (fdopendir (-1) == NULL); + ASSERT (errno == EBADF); + + /* This should work. */ + fd = open (".", O_RDONLY); + ASSERT (0 <= fd); + d = fdopendir (fd); + /* We know that fd is now out of our reach, but it is not specified + whether it is closed now or at the closedir. We also can't + guarantee whether dirfd returns fd, some other descriptor, or + -1. */ + ASSERT (d); + ASSERT (closedir (d) == 0); + /* Now we can guarantee that fd must be closed. */ + errno = 0; + ASSERT (dup2 (fd, fd) == -1); + ASSERT (errno == EBADF); + + return 0; +} -- 2.11.0