Mercurial > hg > octave-nkf > gnulib-hg
changeset 11922:9750527ffbab
fchdir: port to mingw
* m4/fchdir.m4 (gl_FUNC_FCHDIR): Check for mingw bug.
* lib/open.c (open) [FCHDIR_REPLACEMENT]: If directories can't be
opened, then use a substitute.
* lib/sys_stat.in.h (fstat) [REPLACE_OPEN_DIRECTORY]: Declare
replacement.
* lib/fchdir.c (fstat) [REPLACE_OPEN_DIRECTORY]: Implement it.
(_gl_register_fd): No need to check stat if open already filters
all directories.
(fchdir): Fix error condition to match POSIX.
* modules/fchdir (Depends-on): Add sys_stat.
* doc/posix-functions/open.texi (open): Document the limitation.
* modules/fchdir-tests: New file.
* tests/test-fchdir.c: Likewise.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Mon, 31 Aug 2009 06:09:08 -0600 |
parents | c455cd9841a8 |
children | 61821cbbceb2 |
files | ChangeLog doc/posix-functions/open.texi lib/fchdir.c lib/open.c lib/sys_stat.in.h m4/fchdir.m4 modules/fchdir modules/fchdir-tests tests/test-fchdir.c |
diffstat | 9 files changed, 211 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,20 @@ 2009-08-31 Eric Blake <ebb9@byu.net> + fchdir: port to mingw + * m4/fchdir.m4 (gl_FUNC_FCHDIR): Check for mingw bug. + * lib/open.c (open) [FCHDIR_REPLACEMENT]: If directories can't be + opened, then use a substitute. + * lib/sys_stat.in.h (fstat) [REPLACE_OPEN_DIRECTORY]: Declare + replacement. + * lib/fchdir.c (fstat) [REPLACE_OPEN_DIRECTORY]: Implement it. + (_gl_register_fd): No need to check stat if open already filters + all directories. + (fchdir): Fix error condition to match POSIX. + * modules/fchdir (Depends-on): Add sys_stat. + * doc/posix-functions/open.texi (open): Document the limitation. + * modules/fchdir-tests: New file. + * tests/test-fchdir.c: Likewise. + canonicalize: allow cross-testing from cygwin to mingw * modules/canonicalize-tests (configure.ac): Define HAVE_SYMLINK. (Makefile.am): Pass it through TESTS_ENVIRONMENT.
--- a/doc/posix-functions/open.texi +++ b/doc/posix-functions/open.texi @@ -4,9 +4,9 @@ POSIX specification: @url{http://www.opengroup.org/onlinepubs/9699919799/functions/open.html} -Gnulib module: open +Gnulib module: open, fchdir -Portability problems fixed by Gnulib: +Portability problems fixed by the Gnulib module open: @itemize @item This function does not fail when the file name argument ends in a slash @@ -18,6 +18,13 @@ recognize the @file{/dev/null} filename. @end itemize +Portability problems fixed by the Gnulib module fchdir: +@itemize +@item +On Windows platforms (excluding Cygwin), this function fails to open a +read-only descriptor for directories. +@end itemize + Portability problems not fixed by Gnulib: @itemize @item
--- a/lib/fchdir.c +++ b/lib/fchdir.c @@ -30,8 +30,17 @@ #include "canonicalize.h" +#ifndef REPLACE_OPEN_DIRECTORY +# define REPLACE_OPEN_DIRECTORY 0 +#endif + /* This replacement assumes that a directory is not renamed while opened - through a file descriptor. */ + through a file descriptor. + + FIXME: On mingw, this would be possible to enforce if we were to + also open a HANDLE to each directory currently visited by a file + descriptor, since mingw refuses to rename any in-use file system + object. */ /* Array of file descriptors opened. If it points to a directory, it stores info about this directory; otherwise it stores an errno value of ENOTDIR. */ @@ -77,6 +86,8 @@ /* Hook into the gnulib replacements for open() and close() to keep track of the open file descriptors. */ +/* Close FD, cleaning up any fd to name mapping if fd was visiting a + directory. */ void _gl_unregister_fd (int fd) { @@ -89,6 +100,9 @@ } } +/* Mark FD as visiting FILENAME. FD must be positive, and refer to an + open file descriptor. If REPLACE_OPEN_DIRECTORY is non-zero, this + should only be called if FD is visiting a directory. */ void _gl_register_fd (int fd, const char *filename) { @@ -96,14 +110,28 @@ ensure_dirs_slot (fd); if (fd < dirs_allocated - && fstat (fd, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode)) + && (REPLACE_OPEN_DIRECTORY + || (fstat (fd, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode)))) { dirs[fd].name = canonicalize_file_name (filename); if (dirs[fd].name == NULL) - dirs[fd].saved_errno = errno; + dirs[fd].saved_errno = errno; } } +/* Return stat information about FD in STATBUF. Needed when + rpl_open() used a dummy file to work around an open() that can't + normally visit directories. */ +#if REPLACE_OPEN_DIRECTORY +int +rpl_fstat (int fd, struct stat *statbuf) +{ + if (0 <= fd && fd <= dirs_allocated && dirs[fd].name != NULL) + return stat (dirs[fd].name, statbuf); + return fstat (fd, statbuf); +} +#endif + /* Override opendir() and closedir(), to keep track of the open file descriptors. Needed because there is a function dirfd(). */ @@ -218,27 +246,16 @@ int fchdir (int fd) { - if (fd >= 0) + if (fd >= 0 && fd < dirs_allocated && dirs[fd].name != NULL) + return chdir (dirs[fd].name); + /* At this point, fd is either invalid, or open but not a directory. + If dup2 fails, errno is correctly EBADF. */ + if (0 <= fd) { - if (fd < dirs_allocated) - { - if (dirs[fd].name != NULL) - return chdir (dirs[fd].name); - else - { - errno = dirs[fd].saved_errno; - return -1; - } - } - else - { - errno = ENOMEM; - return -1; - } + if (dup2 (fd, fd) == fd) + errno = ENOTDIR; } else - { - errno = EBADF; - return -1; - } + errno = EBADF; + return -1; }
--- a/lib/open.c +++ b/lib/open.c @@ -39,6 +39,10 @@ #include <sys/types.h> #include <sys/stat.h> +#ifndef REPLACE_OPEN_DIRECTORY +# define REPLACE_OPEN_DIRECTORY 0 +#endif + int open (const char *filename, int flags, ...) { @@ -98,6 +102,29 @@ fd = orig_open (filename, flags, mode); +#ifdef FCHDIR_REPLACEMENT + /* Implementing fchdir and fdopendir requires the ability to open a + directory file descriptor. If open doesn't support that (as on + mingw), we use a dummy file that behaves the same as directories + on Linux (ie. always reports EOF on attempts to read()), and + override fstat() in fchdir.c to hide the fact that we have a + dummy. */ + if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES + && (mode & O_ACCMODE) == O_RDONLY) + { + struct stat statbuf; + if (stat (filename, &statbuf) == 0 && S_ISDIR (statbuf.st_mode)) + { + /* Maximum recursion depth of 1. */ + fd = open ("/dev/null", flags, mode); + if (0 <= fd) + _gl_register_fd (fd, filename); + } + else + errno = EACCES; + } +#endif + #if OPEN_TRAILING_SLASH_BUG /* If the filename ends in a slash and fd does not refer to a directory, then fail. @@ -129,7 +156,7 @@ #endif #ifdef FCHDIR_REPLACEMENT - if (fd >= 0) + if (!REPLACE_OPEN_DIRECTORY && 0 <= fd) _gl_register_fd (fd, filename); #endif
--- a/lib/sys_stat.in.h +++ b/lib/sys_stat.in.h @@ -302,6 +302,10 @@ lstat (p, b)) #endif +#if defined FCHDIR_REPLACEMENT && REPLACE_OPEN_DIRECTORY +# define fstat rpl_fstat +extern int fstat (int fd, struct stat *buf); +#endif #if @REPLACE_MKDIR@ # undef mkdir
--- a/m4/fchdir.m4 +++ b/m4/fchdir.m4 @@ -1,4 +1,4 @@ -# fchdir.m4 serial 7 +# fchdir.m4 serial 8 dnl Copyright (C) 2006-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, @@ -18,6 +18,17 @@ gl_REPLACE_OPEN gl_REPLACE_CLOSE gl_REPLACE_DIRENT_H + AC_CACHE_CHECK([whether open can visit directories], + [gl_cv_func_open_directory_works], + [AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h> +]], [return open(".", O_RDONLY);])], + [gl_cv_func_open_directory_works=yes], + [gl_cv_func_open_directory_works=no], + [gl_cv_func_open_directory_works="guessing no"])]) + if test "$gl_cv_func_open_directory_works" != yes; then + AC_DEFINE([REPLACE_OPEN_DIRECTORY], [1], [Define to 1 if open() should +work around the inability to open a directory.]) + fi fi ])
--- a/modules/fchdir +++ b/modules/fchdir @@ -15,6 +15,7 @@ include_next open strdup +sys_stat unistd configure.ac:
new file mode 100644 --- /dev/null +++ b/modules/fchdir-tests @@ -0,0 +1,11 @@ +Files: +tests/test-fchdir.c + +Depends-on: +getcwd + +configure.ac: + +Makefile.am: +TESTS += test-fchdir +check_PROGRAMS += test-fchdir
new file mode 100644 --- /dev/null +++ b/tests/test-fchdir.c @@ -0,0 +1,91 @@ +/* Test changing to a directory named by 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 <http://www.gnu.org/licenses/>. */ + +/* Written by Eric Blake <ebb9@byu.net>, 2009. */ + +#include <config.h> + +#include <unistd.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +int +main () +{ + char *cwd = getcwd (NULL, 0); + int fd = open (".", O_RDONLY); + int i; + + ASSERT (cwd); + ASSERT (0 <= fd); + + /* Check for failure cases. */ + { + int bad_fd = open ("/dev/null", O_RDONLY); + ASSERT (0 <= bad_fd); + errno = 0; + ASSERT (fchdir (bad_fd) == -1); + ASSERT (errno == ENOTDIR); + ASSERT (close (bad_fd) == 0); + errno = 0; + ASSERT (fchdir (-1) == -1); + ASSERT (errno == EBADF); + } + + /* Repeat test twice, once in '.' and once in '..'. */ + for (i = 0; i < 2; i++) + { + ASSERT (chdir (".." + 1 - i) == 0); + ASSERT (fchdir (fd) == 0); + { + size_t len = strlen (cwd) + 1; + char *new_dir = malloc (len); + ASSERT (new_dir); + ASSERT (getcwd (new_dir, len) == new_dir); + ASSERT (strcmp (cwd, new_dir) == 0); + free (new_dir); + } + + /* For second iteration, use a cloned fd, to ensure that dup + remembers whether an fd was associated with a directory. */ + if (!i) + { + int new_fd = dup (fd); + ASSERT (0 <= new_fd); + ASSERT (close (fd) == 0); + fd = new_fd; + } + } + + free (cwd); + return 0; +}