diff options
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | admin/ChangeLog | 7 | ||||
-rwxr-xr-x | admin/merge-gnulib | 8 | ||||
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/careadlinkat.c | 16 | ||||
-rw-r--r-- | lib/careadlinkat.h | 6 | ||||
-rw-r--r-- | lib/dirent.in.h | 258 | ||||
-rw-r--r-- | lib/fdopendir.c | 204 | ||||
-rw-r--r-- | lib/fstatat.c | 135 | ||||
-rw-r--r-- | lib/gnulib.mk | 97 | ||||
-rw-r--r-- | lib/openat-die.c | 6 | ||||
-rw-r--r-- | lib/openat-priv.h | 64 | ||||
-rw-r--r-- | lib/openat-proc.c | 110 | ||||
-rw-r--r-- | lib/openat.h | 120 | ||||
-rw-r--r-- | lib/readlinkat.c | 47 | ||||
-rw-r--r-- | lib/save-cwd.c | 3 | ||||
-rw-r--r-- | lib/save-cwd.h | 46 | ||||
-rw-r--r-- | m4/dirent_h.m4 | 64 | ||||
-rw-r--r-- | m4/fdopendir.m4 | 61 | ||||
-rw-r--r-- | m4/fstatat.m4 | 60 | ||||
-rw-r--r-- | m4/gnulib-comp.m4 | 78 | ||||
-rw-r--r-- | m4/readlinkat.m4 | 19 | ||||
-rw-r--r-- | nt/ChangeLog | 6 | ||||
-rw-r--r-- | nt/inc/sys/stat.h | 3 | ||||
-rw-r--r-- | nt/inc/unistd.h | 1 | ||||
-rw-r--r-- | src/ChangeLog | 31 | ||||
-rw-r--r-- | src/conf_post.h | 4 | ||||
-rw-r--r-- | src/dired.c | 120 | ||||
-rw-r--r-- | src/fileio.c | 40 | ||||
-rw-r--r-- | src/filelock.c | 18 | ||||
-rw-r--r-- | src/lisp.h | 3 | ||||
-rw-r--r-- | src/sysdep.c | 18 | ||||
-rw-r--r-- | src/w32.c | 60 |
33 files changed, 1595 insertions, 140 deletions
diff --git a/ChangeLog b/ChangeLog index ec25d5f7a7b..c510360e30f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2013-02-01 Paul Eggert <eggert@cs.ucla.edu> + + Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539). + On my host, this speeds up directory-files-and-attributes by a + factor of 3, when applied to Emacs's src directory. + These functions are standardized by POSIX and are common these + days; fall back on a (slower) gnulib implementation if the host + is too old to supply them. + * .bzrignore: Add lib/dirent.h. + * lib/Makefile.am (libgnu_a_SOURCES): Add openat-die.c, save-cwd.c. + * lib/careadlinkat.c, lib/careadlinkat.h: Merge from gnulib, + incorporating: 2013-01-29 careadlinkat: do not provide careadlinkatcwd. + * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. + * lib/dirent.in.h, lib/fdopendir.c, lib/fstatat.c, lib/openat-priv.h: + * lib/openat-proc.c, lib/openat.h, m4/dirent_h.m4, m4/fdopendir.m4: + * m4/fstatat.m4: New files, from gnulib. + * lib/openat-die.c, lib/save-cwd.c, lib/save-cwd.h: New files. + These last three are specific to Emacs and are not copied from gnulib. + They are simpler than the gnulib versions and are tuned for Emacs. + 2013-02-01 Glenn Morris <rgm@gnu.org> * make-dist: Only README files exist in lisp/ now, not README*. diff --git a/admin/ChangeLog b/admin/ChangeLog index 5da0bf0c67d..76ac11446a3 100644 --- a/admin/ChangeLog +++ b/admin/ChangeLog @@ -1,3 +1,10 @@ +2013-02-01 Paul Eggert <eggert@cs.ucla.edu> + + Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539). + * merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat. + (GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h. + Avoid dup, open, opendir. + 2013-01-15 Dmitry Antipov <dmantipov@yandex.ru> * coccinelle/xsave.cocci: Semantic patch to adjust users of diff --git a/admin/merge-gnulib b/admin/merge-gnulib index f3509d98b85..e90e2e23b29 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -29,9 +29,9 @@ GNULIB_MODULES=' alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat - fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday + fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat - manywarnings mktime pselect pthread_sigmask putenv readlink + manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens @@ -39,10 +39,10 @@ GNULIB_MODULES=' ' GNULIB_TOOL_FLAGS=' - --avoid=at-internal + --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow - --avoid=openat-die --avoid=openat-h + --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib diff --git a/lib/Makefile.am b/lib/Makefile.am index 28fdafd4b45..a341609e895 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -8,3 +8,5 @@ AM_CFLAGS = $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS) DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src include gnulib.mk + +libgnu_a_SOURCES += openat-die.c save-cwd.c diff --git a/lib/careadlinkat.c b/lib/careadlinkat.c index 1a759be7caf..d242ffaac7d 100644 --- a/lib/careadlinkat.c +++ b/lib/careadlinkat.c @@ -24,9 +24,7 @@ #include <errno.h> #include <limits.h> -#include <stdlib.h> #include <string.h> -#include <unistd.h> /* Define this independently so that stdint.h is not a prerequisite. */ #ifndef SIZE_MAX @@ -39,20 +37,6 @@ #include "allocator.h" -/* Get the symbolic link value of FILENAME and put it into BUFFER, with - size BUFFER_SIZE. This function acts like readlink but has - readlinkat's signature. */ -ssize_t -careadlinkatcwd (int fd, char const *filename, char *buffer, - size_t buffer_size) -{ - /* FD must be AT_FDCWD here, otherwise the caller is using this - function in contexts for which it was not meant for. */ - if (fd != AT_FDCWD) - abort (); - return readlink (filename, buffer, buffer_size); -} - /* Assuming the current directory is FD, get the symbolic link value of FILENAME as a null-terminated string and put it into a buffer. If FD is AT_FDCWD, FILENAME is interpreted relative to the current diff --git a/lib/careadlinkat.h b/lib/careadlinkat.h index 5cdb813fedd..965573bef3a 100644 --- a/lib/careadlinkat.h +++ b/lib/careadlinkat.h @@ -52,9 +52,9 @@ char *careadlinkat (int fd, char const *filename, ssize_t (*preadlinkat) (int, char const *, char *, size_t)); -/* Suitable values for careadlinkat's FD and PREADLINKAT arguments, +/* Suitable value for careadlinkat's FD argument, when doing a plain readlink: - Pass FD = AT_FDCWD and PREADLINKAT = careadlinkatcwd. */ + Pass FD = AT_FDCWD. */ #if HAVE_READLINKAT /* AT_FDCWD is declared in <fcntl.h>. */ #else @@ -66,7 +66,5 @@ char *careadlinkat (int fd, char const *filename, # define AT_FDCWD (-3041965) # endif #endif -ssize_t careadlinkatcwd (int fd, char const *filename, - char *buffer, size_t buffer_size); #endif /* _GL_CAREADLINKAT_H */ diff --git a/lib/dirent.in.h b/lib/dirent.in.h new file mode 100644 index 00000000000..fad3797b8ba --- /dev/null +++ b/lib/dirent.in.h @@ -0,0 +1,258 @@ +/* A GNU-like <dirent.h>. + Copyright (C) 2006-2013 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/>. */ + +#ifndef _@GUARD_PREFIX@_DIRENT_H + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif +@PRAGMA_COLUMNS@ + +/* The include_next requires a split double-inclusion guard. */ +#if @HAVE_DIRENT_H@ +# @INCLUDE_NEXT@ @NEXT_DIRENT_H@ +#endif + +#ifndef _@GUARD_PREFIX@_DIRENT_H +#define _@GUARD_PREFIX@_DIRENT_H + +/* Get ino_t. Needed on some systems, including glibc 2.8. */ +#include <sys/types.h> + +#if !@HAVE_DIRENT_H@ +/* Define types DIR and 'struct dirent'. */ +# if !GNULIB_defined_struct_dirent +struct dirent +{ + char d_type; + char d_name[1]; +}; +/* Possible values for 'd_type'. */ +# define DT_UNKNOWN 0 +# define DT_FIFO 1 /* FIFO */ +# define DT_CHR 2 /* character device */ +# define DT_DIR 4 /* directory */ +# define DT_BLK 6 /* block device */ +# define DT_REG 8 /* regular file */ +# define DT_LNK 10 /* symbolic link */ +# define DT_SOCK 12 /* socket */ +# define DT_WHT 14 /* whiteout */ +typedef struct gl_directory DIR; +# define GNULIB_defined_struct_dirent 1 +# endif +#endif + +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The attribute __pure__ was added in gcc 2.96. */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) +# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__)) +#else +# define _GL_ATTRIBUTE_PURE /* empty */ +#endif + +/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */ + +/* The definition of _GL_ARG_NONNULL is copied here. */ + +/* The definition of _GL_WARN_ON_USE is copied here. */ + + +/* Declare overridden functions. */ + +#if @GNULIB_OPENDIR@ +# if @REPLACE_OPENDIR@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef opendir +# define opendir rpl_opendir +# endif +_GL_FUNCDECL_RPL (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1))); +_GL_CXXALIAS_RPL (opendir, DIR *, (const char *dir_name)); +# else +# if !@HAVE_OPENDIR@ +_GL_FUNCDECL_SYS (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1))); +# endif +_GL_CXXALIAS_SYS (opendir, DIR *, (const char *dir_name)); +# endif +_GL_CXXALIASWARN (opendir); +#elif defined GNULIB_POSIXCHECK +# undef opendir +# if HAVE_RAW_DECL_OPENDIR +_GL_WARN_ON_USE (opendir, "opendir is not portable - " + "use gnulib module opendir for portability"); +# endif +#endif + +#if @GNULIB_READDIR@ +# if !@HAVE_READDIR@ +_GL_FUNCDECL_SYS (readdir, struct dirent *, (DIR *dirp) _GL_ARG_NONNULL ((1))); +# endif +_GL_CXXALIAS_SYS (readdir, struct dirent *, (DIR *dirp)); +_GL_CXXALIASWARN (readdir); +#elif defined GNULIB_POSIXCHECK +# undef readdir +# if HAVE_RAW_DECL_READDIR +_GL_WARN_ON_USE (readdir, "readdir is not portable - " + "use gnulib module readdir for portability"); +# endif +#endif + +#if @GNULIB_REWINDDIR@ +# if !@HAVE_REWINDDIR@ +_GL_FUNCDECL_SYS (rewinddir, void, (DIR *dirp) _GL_ARG_NONNULL ((1))); +# endif +_GL_CXXALIAS_SYS (rewinddir, void, (DIR *dirp)); +_GL_CXXALIASWARN (rewinddir); +#elif defined GNULIB_POSIXCHECK +# undef rewinddir +# if HAVE_RAW_DECL_REWINDDIR +_GL_WARN_ON_USE (rewinddir, "rewinddir is not portable - " + "use gnulib module rewinddir for portability"); +# endif +#endif + +#if @GNULIB_CLOSEDIR@ +# if @REPLACE_CLOSEDIR@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef closedir +# define closedir rpl_closedir +# endif +_GL_FUNCDECL_RPL (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1))); +_GL_CXXALIAS_RPL (closedir, int, (DIR *dirp)); +# else +# if !@HAVE_CLOSEDIR@ +_GL_FUNCDECL_SYS (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1))); +# endif +_GL_CXXALIAS_SYS (closedir, int, (DIR *dirp)); +# endif +_GL_CXXALIASWARN (closedir); +#elif defined GNULIB_POSIXCHECK +# undef closedir +# if HAVE_RAW_DECL_CLOSEDIR +_GL_WARN_ON_USE (closedir, "closedir is not portable - " + "use gnulib module closedir for portability"); +# endif +#endif + +#if @GNULIB_DIRFD@ +/* Return the file descriptor associated with the given directory stream, + or -1 if none exists. */ +# if @REPLACE_DIRFD@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef dirfd +# define dirfd rpl_dirfd +# endif +_GL_FUNCDECL_RPL (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1))); +_GL_CXXALIAS_RPL (dirfd, int, (DIR *)); +# else +# if defined __cplusplus && defined GNULIB_NAMESPACE && defined dirfd + /* dirfd is defined as a macro and not as a function. + Turn it into a function and get rid of the macro. */ +static inline int (dirfd) (DIR *dp) { return dirfd (dp); } +# undef dirfd +# endif +# if !(@HAVE_DECL_DIRFD@ || defined dirfd) +_GL_FUNCDECL_SYS (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1))); +# endif +_GL_CXXALIAS_SYS (dirfd, int, (DIR *)); +# endif +_GL_CXXALIASWARN (dirfd); +#elif defined GNULIB_POSIXCHECK +# undef dirfd +# if HAVE_RAW_DECL_DIRFD +_GL_WARN_ON_USE (dirfd, "dirfd is unportable - " + "use gnulib module dirfd for portability"); +# endif +#endif + +#if @GNULIB_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). */ +# if @REPLACE_FDOPENDIR@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef fdopendir +# define fdopendir rpl_fdopendir +# endif +_GL_FUNCDECL_RPL (fdopendir, DIR *, (int fd)); +_GL_CXXALIAS_RPL (fdopendir, DIR *, (int fd)); +# else +# if !@HAVE_FDOPENDIR@ || !@HAVE_DECL_FDOPENDIR@ +_GL_FUNCDECL_SYS (fdopendir, DIR *, (int fd)); +# endif +_GL_CXXALIAS_SYS (fdopendir, DIR *, (int fd)); +# endif +_GL_CXXALIASWARN (fdopendir); +#elif defined GNULIB_POSIXCHECK +# undef fdopendir +# if HAVE_RAW_DECL_FDOPENDIR +_GL_WARN_ON_USE (fdopendir, "fdopendir is unportable - " + "use gnulib module fdopendir for portability"); +# endif +#endif + +#if @GNULIB_SCANDIR@ +/* Scan the directory DIR, calling FILTER on each directory entry. + Entries for which FILTER returns nonzero are individually malloc'd, + sorted using qsort with CMP, and collected in a malloc'd array in + *NAMELIST. Returns the number of entries selected, or -1 on error. */ +# if !@HAVE_SCANDIR@ +_GL_FUNCDECL_SYS (scandir, int, + (const char *dir, struct dirent ***namelist, + int (*filter) (const struct dirent *), + int (*cmp) (const struct dirent **, const struct dirent **)) + _GL_ARG_NONNULL ((1, 2, 4))); +# endif +/* Need to cast, because on glibc systems, the fourth parameter is + int (*cmp) (const void *, const void *). */ +_GL_CXXALIAS_SYS_CAST (scandir, int, + (const char *dir, struct dirent ***namelist, + int (*filter) (const struct dirent *), + int (*cmp) (const struct dirent **, const struct dirent **))); +_GL_CXXALIASWARN (scandir); +#elif defined GNULIB_POSIXCHECK +# undef scandir +# if HAVE_RAW_DECL_SCANDIR +_GL_WARN_ON_USE (scandir, "scandir is unportable - " + "use gnulib module scandir for portability"); +# endif +#endif + +#if @GNULIB_ALPHASORT@ +/* Compare two 'struct dirent' entries alphabetically. */ +# if !@HAVE_ALPHASORT@ +_GL_FUNCDECL_SYS (alphasort, int, + (const struct dirent **, const struct dirent **) + _GL_ATTRIBUTE_PURE + _GL_ARG_NONNULL ((1, 2))); +# endif +/* Need to cast, because on glibc systems, the parameters are + (const void *, const void *). */ +_GL_CXXALIAS_SYS_CAST (alphasort, int, + (const struct dirent **, const struct dirent **)); +_GL_CXXALIASWARN (alphasort); +#elif defined GNULIB_POSIXCHECK +# undef alphasort +# if HAVE_RAW_DECL_ALPHASORT +_GL_WARN_ON_USE (alphasort, "alphasort is unportable - " + "use gnulib module alphasort for portability"); +# endif +#endif + + +#endif /* _@GUARD_PREFIX@_DIRENT_H */ +#endif /* _@GUARD_PREFIX@_DIRENT_H */ diff --git a/lib/fdopendir.c b/lib/fdopendir.c new file mode 100644 index 00000000000..63e06b92ae8 --- /dev/null +++ b/lib/fdopendir.c @@ -0,0 +1,204 @@ +/* provide a replacement fdopendir function + Copyright (C) 2004-2013 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 Jim Meyering */ + +#include <config.h> + +#include <dirent.h> + +#include <stdlib.h> +#include <unistd.h> + +#if !HAVE_FDOPENDIR + +# include "openat.h" +# include "openat-priv.h" +# include "save-cwd.h" + +# if GNULIB_DIRENT_SAFER +# include "dirent--.h" +# endif + +# ifndef REPLACE_FCHDIR +# define REPLACE_FCHDIR 0 +# endif + +static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *); +static DIR *fd_clone_opendir (int, struct saved_cwd const *); + +/* Replacement for POSIX fdopendir. + + First, try to simulate it via opendir ("/proc/self/fd/..."). Failing + that, simulate it by using fchdir metadata, or 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. + + If successful, the resulting stream is based on FD in + implementations where streams are based on file descriptors and in + applications where no other thread or signal handler allocates or + frees file descriptors. In other cases, consult dirfd on the result + to find out whether FD is still being used. + + Otherwise, this function works just like POSIX fdopendir. + + W A R N I N G: + + Unlike other fd-related functions, this one places constraints on FD. + If this function returns successfully, FD is under control of the + dirent.h system, and the caller should not close or modify the state of + FD other than by the dirent.h functions. */ +DIR * +fdopendir (int fd) +{ + DIR *dir = fdopendir_with_dup (fd, -1, NULL); + + if (! REPLACE_FCHDIR && ! dir) + { + int saved_errno = errno; + if (EXPECTED_ERRNO (saved_errno)) + { + struct saved_cwd cwd; + if (save_cwd (&cwd) != 0) + openat_save_fail (errno); + dir = fdopendir_with_dup (fd, -1, &cwd); + saved_errno = errno; + free_cwd (&cwd); + errno = saved_errno; + } + } + + return dir; +} + +/* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known + to be a dup of FD which is less than FD - 1 and which will be + closed by the caller and not otherwise used by the caller. This + function makes sure that FD is closed and all file descriptors less + than FD are open, and then calls fd_clone_opendir on a dup of FD. + That way, barring race conditions, fd_clone_opendir returns a + stream whose file descriptor is FD. + + If REPLACE_CHDIR or CWD is null, use opendir ("/proc/self/fd/...", + falling back on fchdir metadata. Otherwise, CWD is a saved version + of the working directory; use fchdir/opendir(".")/restore_cwd(CWD). */ +static DIR * +fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd) +{ + int dupfd = dup (fd); + if (dupfd < 0 && errno == EMFILE) + dupfd = older_dupfd; + if (dupfd < 0) + return NULL; + else + { + DIR *dir; + int saved_errno; + if (dupfd < fd - 1 && dupfd != older_dupfd) + { + dir = fdopendir_with_dup (fd, dupfd, cwd); + saved_errno = errno; + } + else + { + close (fd); + dir = fd_clone_opendir (dupfd, cwd); + saved_errno = errno; + if (! dir) + { + int fd1 = dup (dupfd); + if (fd1 != fd) + openat_save_fail (fd1 < 0 ? errno : EBADF); + } + } + + if (dupfd != older_dupfd) + close (dupfd); + errno = saved_errno; + return dir; + } +} + +/* Like fdopendir, except the result controls a clone of FD. It is + the caller's responsibility both to close FD and (if the result is + not null) to closedir the result. */ +static DIR * +fd_clone_opendir (int fd, struct saved_cwd const *cwd) +{ + if (REPLACE_FCHDIR || ! cwd) + { + DIR *dir = NULL; + int saved_errno = EOPNOTSUPP; + char buf[OPENAT_BUFFER_SIZE]; + char *proc_file = openat_proc_name (buf, fd, "."); + if (proc_file) + { + dir = opendir (proc_file); + saved_errno = errno; + if (proc_file != buf) + free (proc_file); + } +# if REPLACE_FCHDIR + if (! dir && EXPECTED_ERRNO (saved_errno)) + { + char const *name = _gl_directory_name (fd); + return (name ? opendir (name) : NULL); + } +# endif + errno = saved_errno; + return dir; + } + else + { + if (fchdir (fd) != 0) + return NULL; + else + { + DIR *dir = opendir ("."); + int saved_errno = errno; + if (restore_cwd (cwd) != 0) + openat_restore_fail (errno); + 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 */ diff --git a/lib/fstatat.c b/lib/fstatat.c new file mode 100644 index 00000000000..845c171fb45 --- /dev/null +++ b/lib/fstatat.c @@ -0,0 +1,135 @@ +/* Work around an fstatat bug on Solaris 9. + + Copyright (C) 2006, 2009-2013 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 Paul Eggert and Jim Meyering. */ + +/* If the user's config.h happens to include <sys/stat.h>, let it include only + the system's <sys/stat.h> here, so that orig_fstatat doesn't recurse to + rpl_fstatat. */ +#define __need_system_sys_stat_h +#include <config.h> + +/* Get the original definition of fstatat. It might be defined as a macro. */ +#include <sys/types.h> +#include <sys/stat.h> +#undef __need_system_sys_stat_h + +#if HAVE_FSTATAT +static int +orig_fstatat (int fd, char const *filename, struct stat *buf, int flags) +{ + return fstatat (fd, filename, buf, flags); +} +#endif + +/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc + eliminates this include because of the preliminary #include <sys/stat.h> + above. */ +#include "sys/stat.h" + +#include <errno.h> +#include <fcntl.h> +#include <string.h> + +#if HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG + +# ifndef LSTAT_FOLLOWS_SLASHED_SYMLINK +# define LSTAT_FOLLOWS_SLASHED_SYMLINK 0 +# endif + +/* fstatat should always follow symbolic links that end in /, but on + Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified. + Likewise, trailing slash on a non-directory should be an error. + These are the same problems that lstat.c and stat.c address, so + solve it in a similar way. + + AIX 7.1 fstatat (AT_FDCWD, ..., 0) always fails, which is a bug. + Work around this bug if FSTATAT_AT_FDCWD_0_BROKEN is nonzero. */ + +int +rpl_fstatat (int fd, char const *file, struct stat *st, int flag) +{ + int result = orig_fstatat (fd, file, st, flag); + size_t len; + + if (LSTAT_FOLLOWS_SLASHED_SYMLINK || result != 0) + return result; + len = strlen (file); + if (flag & AT_SYMLINK_NOFOLLOW) + { + /* Fix lstat behavior. */ + if (file[len - 1] != '/' || S_ISDIR (st->st_mode)) + return 0; + if (!S_ISLNK (st->st_mode)) + { + errno = ENOTDIR; + return -1; + } + result = orig_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW); + } + /* Fix stat behavior. */ + if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/') + { + errno = ENOTDIR; + return -1; + } + return result; +} + +#else /* ! (HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG) */ + +/* On mingw, the gnulib <sys/stat.h> defines 'stat' as a function-like + macro; but using it in AT_FUNC_F2 causes compilation failure + because the preprocessor sees a use of a macro that requires two + arguments but is only given one. Hence, we need an inline + forwarder to get past the preprocessor. */ +static int +stat_func (char const *name, struct stat *st) +{ + return stat (name, st); +} + +/* Likewise, if there is no native 'lstat', then the gnulib + <sys/stat.h> defined it as stat, which also needs adjustment. */ +# if !HAVE_LSTAT +# undef lstat +# define lstat stat_func +# endif + +/* Replacement for Solaris' function by the same name. + <http://www.google.com/search?q=fstatat+site:docs.sun.com> + First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE"). + Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/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' fstatat. */ + +# define AT_FUNC_NAME fstatat +# define AT_FUNC_F1 lstat +# define AT_FUNC_F2 stat_func +# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW +# define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag +# define AT_FUNC_POST_FILE_ARGS , st +# include "at-func.c" +# undef AT_FUNC_NAME +# undef AT_FUNC_F1 +# undef AT_FUNC_F2 +# undef AT_FUNC_USE_F1_COND +# undef AT_FUNC_POST_FILE_PARAM_DECLS +# undef AT_FUNC_POST_FILE_ARGS + +#endif /* !HAVE_FSTATAT */ diff --git a/lib/gnulib.mk b/lib/gnulib.mk index 2347d84448d..89317fd2088 100644 --- a/lib/gnulib.mk +++ b/lib/gnulib.mk @@ -21,7 +21,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=at-internal --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=openat-h --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings +# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings MOSTLYCLEANFILES += core *.stackdump @@ -64,6 +64,17 @@ EXTRA_DIST += allocator.h ## end gnulib module allocator +## begin gnulib module at-internal + +if gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b + +endif +EXTRA_DIST += openat-priv.h openat-proc.c + +EXTRA_libgnu_a_SOURCES += openat-proc.c + +## end gnulib module at-internal + ## begin gnulib module c-ctype libgnu_a_SOURCES += c-ctype.h c-ctype.c @@ -124,6 +135,54 @@ EXTRA_DIST += sha512.h ## end gnulib module crypto/sha512 +## begin gnulib module dirent + +BUILT_SOURCES += dirent.h + +# We need the following in order to create <dirent.h> when the system +# doesn't have one that works with the given compiler. +dirent.h: dirent.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H) + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's|@''HAVE_DIRENT_H''@|$(HAVE_DIRENT_H)|g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \ + -e 's/@''GNULIB_OPENDIR''@/$(GNULIB_OPENDIR)/g' \ + -e 's/@''GNULIB_READDIR''@/$(GNULIB_READDIR)/g' \ + -e 's/@''GNULIB_REWINDDIR''@/$(GNULIB_REWINDDIR)/g' \ + -e 's/@''GNULIB_CLOSEDIR''@/$(GNULIB_CLOSEDIR)/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_OPENDIR''@/$(HAVE_OPENDIR)/g' \ + -e 's/@''HAVE_READDIR''@/$(HAVE_READDIR)/g' \ + -e 's/@''HAVE_REWINDDIR''@/$(HAVE_REWINDDIR)/g' \ + -e 's/@''HAVE_CLOSEDIR''@/$(HAVE_CLOSEDIR)/g' \ + -e 's|@''HAVE_DECL_DIRFD''@|$(HAVE_DECL_DIRFD)|g' \ + -e 's|@''HAVE_DECL_FDOPENDIR''@|$(HAVE_DECL_FDOPENDIR)|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_OPENDIR''@|$(REPLACE_OPENDIR)|g' \ + -e 's|@''REPLACE_CLOSEDIR''@|$(REPLACE_CLOSEDIR)|g' \ + -e 's|@''REPLACE_DIRFD''@|$(REPLACE_DIRFD)|g' \ + -e 's|@''REPLACE_FDOPENDIR''@|$(REPLACE_FDOPENDIR)|g' \ + -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ + -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ + -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ + < $(srcdir)/dirent.in.h; \ + } > $@-t && \ + mv $@-t $@ +MOSTLYCLEANFILES += dirent.h dirent.h-t + +EXTRA_DIST += dirent.in.h + +## end gnulib module dirent + ## begin gnulib module dosname if gl_GNULIB_ENABLED_dosname @@ -238,6 +297,15 @@ EXTRA_DIST += fcntl.in.h ## end gnulib module fcntl-h +## begin gnulib module fdopendir + + +EXTRA_DIST += fdopendir.c + +EXTRA_libgnu_a_SOURCES += fdopendir.c + +## end gnulib module fdopendir + ## begin gnulib module filemode libgnu_a_SOURCES += filemode.c @@ -255,6 +323,15 @@ EXTRA_libgnu_a_SOURCES += fpending.c ## end gnulib module fpending +## begin gnulib module fstatat + + +EXTRA_DIST += at-func.c fstatat.c + +EXTRA_libgnu_a_SOURCES += at-func.c fstatat.c + +## end gnulib module fstatat + ## begin gnulib module getgroups if gl_GNULIB_ENABLED_getgroups @@ -412,6 +489,15 @@ EXTRA_libgnu_a_SOURCES += mktime.c ## end gnulib module mktime +## begin gnulib module openat-h + +if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7 + +endif +EXTRA_DIST += openat.h + +## end gnulib module openat-h + ## begin gnulib module pathmax if gl_GNULIB_ENABLED_pathmax @@ -457,6 +543,15 @@ EXTRA_libgnu_a_SOURCES += readlink.c ## end gnulib module readlink +## begin gnulib module readlinkat + + +EXTRA_DIST += at-func.c readlinkat.c + +EXTRA_libgnu_a_SOURCES += at-func.c readlinkat.c + +## end gnulib module readlinkat + ## begin gnulib module root-uid if gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c diff --git a/lib/openat-die.c b/lib/openat-die.c new file mode 100644 index 00000000000..f09123ea785 --- /dev/null +++ b/lib/openat-die.c @@ -0,0 +1,6 @@ +/* Respond to a save- or restore-cwd failure. + This should never happen with Emacs. */ +#include <config.h> +#include "openat.h" +void openat_save_fail (int errnum) { abort (); } +void openat_restore_fail (int errnum) { abort (); } diff --git a/lib/openat-priv.h b/lib/openat-priv.h new file mode 100644 index 00000000000..829cf7d0855 --- /dev/null +++ b/lib/openat-priv.h @@ -0,0 +1,64 @@ +/* Internals for openat-like functions. + + Copyright (C) 2005-2006, 2009-2013 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 Jim Meyering */ + +#ifndef _GL_HEADER_OPENAT_PRIV +#define _GL_HEADER_OPENAT_PRIV + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> + +/* Maximum number of bytes that it is safe to allocate as a single + array on the stack, and that is known as a compile-time constant. + The assumption is that we'll touch the array very quickly, or a + temporary very near the array, provoking an out-of-memory trap. On + some operating systems, there is only one guard page for the stack, + and a page size can be as small as 4096 bytes. Subtract 64 in the + hope that this will let the compiler touch a nearby temporary and + provoke a trap. */ +#define SAFER_ALLOCA_MAX (4096 - 64) + +#define SAFER_ALLOCA(m) ((m) < SAFER_ALLOCA_MAX ? (m) : SAFER_ALLOCA_MAX) + +#if defined PATH_MAX +# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (PATH_MAX) +#elif defined _XOPEN_PATH_MAX +# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (_XOPEN_PATH_MAX) +#else +# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (1024) +#endif + +char *openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file); + +/* Trying to access a BUILD_PROC_NAME file will fail on systems without + /proc support, and even on systems *with* ProcFS support. Return + nonzero if the failure may be legitimate, e.g., because /proc is not + readable, or the particular .../fd/N directory is not present. */ +#define EXPECTED_ERRNO(Errno) \ + ((Errno) == ENOTDIR || (Errno) == ENOENT \ + || (Errno) == EPERM || (Errno) == EACCES \ + || (Errno) == ENOSYS /* Solaris 8 */ \ + || (Errno) == EOPNOTSUPP /* FreeBSD */) + +/* Wrapper function shared among linkat and renameat. */ +int at_func2 (int fd1, char const *file1, + int fd2, char const *file2, + int (*func) (char const *file1, char const *file2)); + +#endif /* _GL_HEADER_OPENAT_PRIV */ diff --git a/lib/openat-proc.c b/lib/openat-proc.c new file mode 100644 index 00000000000..d7a68e26d0b --- /dev/null +++ b/lib/openat-proc.c @@ -0,0 +1,110 @@ +/* Create /proc/self/fd-related names for subfiles of open directories. + + Copyright (C) 2006, 2009-2013 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 Paul Eggert. */ + +#include <config.h> + +#include "openat-priv.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "intprops.h" + +/* The results of open() in this file are not used with fchdir, + and we do not leak fds to any single-threaded code that could use stdio, + therefore save some unnecessary work in fchdir.c. + FIXME - if the kernel ever adds support for multi-thread safety for + avoiding standard fds, then we should use open_safer. */ +#undef open +#undef close + +#define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/%s" + +#define PROC_SELF_FD_NAME_SIZE_BOUND(len) \ + (sizeof PROC_SELF_FD_FORMAT - sizeof "%d%s" \ + + INT_STRLEN_BOUND (int) + (len) + 1) + + +/* Set BUF to the expansion of PROC_SELF_FD_FORMAT, using FD and FILE + respectively for %d and %s. If successful, return BUF if the + result fits in BUF, dynamically allocated memory otherwise. But + return NULL if /proc is not reliable, either because the operating + system support is lacking or because memory is low. */ +char * +openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file) +{ + static int proc_status = 0; + + /* Make sure the caller gets ENOENT when appropriate. */ + if (!*file) + { + buf[0] = '\0'; + return buf; + } + + if (! proc_status) + { + /* Set PROC_STATUS to a positive value if /proc/self/fd is + reliable, and a negative value otherwise. Solaris 10 + /proc/self/fd mishandles "..", and any file name might expand + to ".." after symbolic link expansion, so avoid /proc/self/fd + if it mishandles "..". Solaris 10 has openat, but this + problem is exhibited on code that built on Solaris 8 and + running on Solaris 10. */ + + int proc_self_fd = open ("/proc/self/fd", + O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK); + if (proc_self_fd < 0) + proc_status = -1; + else + { + /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the + number of a file descriptor open on /proc/self/fd. On Linux, + that name resolves to /proc/self/fd, which was opened above. + However, on Solaris, it may resolve to /proc/self/fd/fd, which + cannot exist, since all names in /proc/self/fd are numeric. */ + char dotdot_buf[PROC_SELF_FD_NAME_SIZE_BOUND (sizeof "../fd" - 1)]; + sprintf (dotdot_buf, PROC_SELF_FD_FORMAT, proc_self_fd, "../fd"); + proc_status = access (dotdot_buf, F_OK) ? -1 : 1; + close (proc_self_fd); + } + } + + if (proc_status < 0) + return NULL; + else + { + size_t bufsize = PROC_SELF_FD_NAME_SIZE_BOUND (strlen (file)); + char *result = buf; + if (OPENAT_BUFFER_SIZE < bufsize) + { + result = malloc (bufsize); + if (! result) + return NULL; + } + sprintf (result, PROC_SELF_FD_FORMAT, fd, file); + return result; + } +} diff --git a/lib/openat.h b/lib/openat.h new file mode 100644 index 00000000000..eb90990da1d --- /dev/null +++ b/lib/openat.h @@ -0,0 +1,120 @@ +/* provide a replacement openat function + Copyright (C) 2004-2006, 2008-2013 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 Jim Meyering */ + +#ifndef _GL_HEADER_OPENAT +#define _GL_HEADER_OPENAT + +#include <fcntl.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdbool.h> + +_GL_INLINE_HEADER_BEGIN + +#if !HAVE_OPENAT + +int openat_permissive (int fd, char const *file, int flags, mode_t mode, + int *cwd_errno); +bool openat_needs_fchdir (void); + +#else + +# define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \ + openat (Fd, File, Flags, Mode) +# define openat_needs_fchdir() false + +#endif + +_Noreturn void openat_restore_fail (int); +_Noreturn void openat_save_fail (int); + +/* Using these function names makes application code + slightly more readable than it would be with + fchownat (..., 0) or fchownat (..., AT_SYMLINK_NOFOLLOW). */ + +#if GNULIB_FCHOWNAT + +# ifndef FCHOWNAT_INLINE +# define FCHOWNAT_INLINE _GL_INLINE +# endif + +FCHOWNAT_INLINE int +chownat (int fd, char const *file, uid_t owner, gid_t group) +{ + return fchownat (fd, file, owner, group, 0); +} + +FCHOWNAT_INLINE int +lchownat (int fd, char const *file, uid_t owner, gid_t group) +{ + return fchownat (fd, file, owner, group, AT_SYMLINK_NOFOLLOW); +} + +#endif + +#if GNULIB_FCHMODAT + +# ifndef FCHMODAT_INLINE +# define FCHMODAT_INLINE _GL_INLINE +# endif + +FCHMODAT_INLINE int +chmodat (int fd, char const *file, mode_t mode) +{ + return fchmodat (fd, file, mode, 0); +} + +FCHMODAT_INLINE int +lchmodat (int fd, char const *file, mode_t mode) +{ + return fchmodat (fd, file, mode, AT_SYMLINK_NOFOLLOW); +} + +#endif + +#if GNULIB_STATAT + +# ifndef STATAT_INLINE +# define STATAT_INLINE _GL_INLINE +# endif + +STATAT_INLINE int +statat (int fd, char const *name, struct stat *st) +{ + return fstatat (fd, name, st, 0); +} + +STATAT_INLINE int +lstatat (int fd, char const *name, struct stat *st) +{ + return fstatat (fd, name, st, AT_SYMLINK_NOFOLLOW); +} + +#endif + +/* For now, there are no wrappers named laccessat or leuidaccessat, + since gnulib doesn't support faccessat(,AT_SYMLINK_NOFOLLOW) and + since access rights on symlinks are of limited utility. Likewise, + wrappers are not provided for accessat or euidaccessat, so as to + avoid dragging in -lgen on some platforms. */ + +_GL_INLINE_HEADER_END + +#endif /* _GL_HEADER_OPENAT */ diff --git a/lib/readlinkat.c b/lib/readlinkat.c new file mode 100644 index 00000000000..504e6ebbc49 --- /dev/null +++ b/lib/readlinkat.c @@ -0,0 +1,47 @@ +/* Read a symlink relative to an open directory. + Copyright (C) 2009-2013 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 <unistd.h> + +/* Gnulib provides a readlink stub for mingw; use it for distinction + between EINVAL and ENOENT, rather than always failing with ENOSYS. */ + +/* POSIX 2008 says that unlike readlink, readlinkat returns 0 for + success instead of the buffer length. But this would render + readlinkat worthless since readlink does not guarantee a + NUL-terminated buffer. Assume this was a bug in POSIX. */ + +/* Read the contents of symlink FILE into buffer BUF of size LEN, in the + directory open on descriptor FD. If possible, do it without changing + the working directory. Otherwise, resort to using save_cwd/fchdir, + then readlink/restore_cwd. If either the save_cwd or the restore_cwd + fails, then give a diagnostic and exit nonzero. */ + +#define AT_FUNC_NAME readlinkat +#define AT_FUNC_F1 readlink +#define AT_FUNC_POST_FILE_PARAM_DECLS , char *buf, size_t len +#define AT_FUNC_POST_FILE_ARGS , buf, len +#define AT_FUNC_RESULT ssize_t +#include "at-func.c" +#undef AT_FUNC_NAME +#undef AT_FUNC_F1 +#undef AT_FUNC_POST_FILE_PARAM_DECLS +#undef AT_FUNC_POST_FILE_ARGS +#undef AT_FUNC_RESULT diff --git a/lib/save-cwd.c b/lib/save-cwd.c new file mode 100644 index 00000000000..b8dae34ca02 --- /dev/null +++ b/lib/save-cwd.c @@ -0,0 +1,3 @@ +#include <config.h> +#define SAVE_CWD_INLINE _GL_EXTERN_INLINE +#include "save-cwd.h" diff --git a/lib/save-cwd.h b/lib/save-cwd.h new file mode 100644 index 00000000000..bd0cd8d5707 --- /dev/null +++ b/lib/save-cwd.h @@ -0,0 +1,46 @@ +/* Do not save and restore the current working directory. + + Copyright 2013 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/>. */ + +/* Gnulib needs to save and restore the current working directory to + fully emulate functions like fstatat. But Emacs doesn't care what + the current working directory is; it always uses absolute file + names. This module replaces the Gnulib module by omitting the code + that Emacs does not need. */ + +#ifndef SAVE_CWD_H +#define SAVE_CWD_H 1 + +_GL_INLINE_HEADER_BEGIN +#ifndef SAVE_CWD_INLINE +# define SAVE_CWD_INLINE _GL_INLINE +#endif + +struct saved_cwd { int desc; }; + +SAVE_CWD_INLINE int +save_cwd (struct saved_cwd *cwd) +{ + cwd->desc = -1; + return 0; +} + +SAVE_CWD_INLINE int restore_cwd (struct saved_cwd const *cwd) { return 0; } +SAVE_CWD_INLINE void free_cwd (struct saved_cwd *cwd) { } + +_GL_INLINE_HEADER_END + +#endif diff --git a/m4/dirent_h.m4 b/m4/dirent_h.m4 new file mode 100644 index 00000000000..54c16634314 --- /dev/null +++ b/m4/dirent_h.m4 @@ -0,0 +1,64 @@ +# dirent_h.m4 serial 16 +dnl Copyright (C) 2008-2013 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. + +dnl Written by Bruno Haible. + +AC_DEFUN([gl_DIRENT_H], +[ + dnl Use AC_REQUIRE here, so that the default behavior below is expanded + dnl once only, before all statements that occur in other macros. + AC_REQUIRE([gl_DIRENT_H_DEFAULTS]) + + dnl <dirent.h> is always overridden, because of GNULIB_POSIXCHECK. + gl_CHECK_NEXT_HEADERS([dirent.h]) + if test $ac_cv_header_dirent_h = yes; then + HAVE_DIRENT_H=1 + else + HAVE_DIRENT_H=0 + fi + AC_SUBST([HAVE_DIRENT_H]) + + dnl Check for declarations of anything we want to poison if the + dnl corresponding gnulib module is not in use. + gl_WARN_ON_USE_PREPARE([[#include <dirent.h> + ]], [alphasort closedir dirfd fdopendir opendir readdir rewinddir scandir]) +]) + +AC_DEFUN([gl_DIRENT_MODULE_INDICATOR], +[ + dnl Use AC_REQUIRE here, so that the default settings are expanded once only. + AC_REQUIRE([gl_DIRENT_H_DEFAULTS]) + gl_MODULE_INDICATOR_SET_VARIABLE([$1]) + dnl Define it also as a C macro, for the benefit of the unit tests. + gl_MODULE_INDICATOR_FOR_TESTS([$1]) +]) + +AC_DEFUN([gl_DIRENT_H_DEFAULTS], +[ + AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl for REPLACE_FCHDIR + GNULIB_OPENDIR=0; AC_SUBST([GNULIB_OPENDIR]) + GNULIB_READDIR=0; AC_SUBST([GNULIB_READDIR]) + GNULIB_REWINDDIR=0; AC_SUBST([GNULIB_REWINDDIR]) + GNULIB_CLOSEDIR=0; AC_SUBST([GNULIB_CLOSEDIR]) + 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_OPENDIR=1; AC_SUBST([HAVE_OPENDIR]) + HAVE_READDIR=1; AC_SUBST([HAVE_READDIR]) + HAVE_REWINDDIR=1; AC_SUBST([HAVE_REWINDDIR]) + HAVE_CLOSEDIR=1; AC_SUBST([HAVE_CLOSEDIR]) + HAVE_DECL_DIRFD=1; AC_SUBST([HAVE_DECL_DIRFD]) + HAVE_DECL_FDOPENDIR=1;AC_SUBST([HAVE_DECL_FDOPENDIR]) + HAVE_FDOPENDIR=1; AC_SUBST([HAVE_FDOPENDIR]) + HAVE_SCANDIR=1; AC_SUBST([HAVE_SCANDIR]) + HAVE_ALPHASORT=1; AC_SUBST([HAVE_ALPHASORT]) + REPLACE_OPENDIR=0; AC_SUBST([REPLACE_OPENDIR]) + REPLACE_CLOSEDIR=0; AC_SUBST([REPLACE_CLOSEDIR]) + REPLACE_DIRFD=0; AC_SUBST([REPLACE_DIRFD]) + REPLACE_FDOPENDIR=0; AC_SUBST([REPLACE_FDOPENDIR]) +]) diff --git a/m4/fdopendir.m4 b/m4/fdopendir.m4 new file mode 100644 index 00000000000..b7be78324dc --- /dev/null +++ b/m4/fdopendir.m4 @@ -0,0 +1,61 @@ +# serial 10 +# See if we need to provide fdopendir. + +dnl Copyright (C) 2009-2013 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_DIRENT_H_DEFAULTS]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + + dnl FreeBSD 7.3 has the function, but failed to declare it. + AC_CHECK_DECLS([fdopendir], [], [HAVE_DECL_FDOPENDIR=0], [[ +#include <dirent.h> + ]]) + AC_CHECK_FUNCS_ONCE([fdopendir]) + if test $ac_cv_func_fdopendir = no; then + 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> +#include <unistd.h> +#if !HAVE_DECL_FDOPENDIR +extern +# ifdef __cplusplus +"C" +# endif +DIR *fdopendir (int); +#endif +]], [int result = 0; + int fd = open ("conftest.c", O_RDONLY); + if (fd < 0) result |= 1; + if (fdopendir (fd)) result |= 2; + if (close (fd)) result |= 4; + return result;])], + [gl_cv_func_fdopendir_works=yes], + [gl_cv_func_fdopendir_works=no], + [case "$host_os" in + # Guess yes on glibc systems. + *-gnu*) gl_cv_func_fdopendir_works="guessing yes" ;; + # If we don't know, assume the worst. + *) gl_cv_func_fdopendir_works="guessing no" ;; + esac + ])]) + case "$gl_cv_func_fdopendir_works" in + *yes) ;; + *) + REPLACE_FDOPENDIR=1 + ;; + esac + fi +]) diff --git a/m4/fstatat.m4 b/m4/fstatat.m4 new file mode 100644 index 00000000000..adbc7e57f51 --- /dev/null +++ b/m4/fstatat.m4 @@ -0,0 +1,60 @@ +# fstatat.m4 serial 3 +dnl Copyright (C) 2004-2013 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 Jim Meyering. + +# If we have the fstatat function, and it has the bug (in AIX 7.1) +# that it does not fill in st_size correctly, use the replacement function. +AC_DEFUN([gl_FUNC_FSTATAT], +[ + AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS]) + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CHECK_FUNCS_ONCE([fstatat]) + + if test $ac_cv_func_fstatat = no; then + HAVE_FSTATAT=0 + else + dnl Test for an AIX 7.1 bug; see + dnl <http://lists.gnu.org/archive/html/bug-tar/2011-09/msg00015.html>. + AC_CACHE_CHECK([whether fstatat (..., 0) works], + [gl_cv_func_fstatat_zero_flag], + [AC_RUN_IFELSE( + [AC_LANG_SOURCE( + [[ + #include <fcntl.h> + #include <sys/stat.h> + int + main (void) + { + struct stat a; + return fstatat (AT_FDCWD, ".", &a, 0) != 0; + } + ]])], + [gl_cv_func_fstatat_zero_flag=yes], + [gl_cv_func_fstatat_zero_flag=no], + [case "$host_os" in + aix*) gl_cv_func_fstatat_zero_flag="guessing no";; + *) gl_cv_func_fstatat_zero_flag="guessing yes";; + esac + ]) + ]) + + case $gl_cv_func_fstatat_zero_flag+$gl_cv_func_lstat_dereferences_slashed_symlink in + *yes+*yes) ;; + *) REPLACE_FSTATAT=1 + case $gl_cv_func_fstatat_zero_flag in + *yes) + AC_DEFINE([HAVE_WORKING_FSTATAT_ZERO_FLAG], [1], + [Define to 1 if fstatat (..., 0) works. + For example, it does not work in AIX 7.1.]) + ;; + esac + ;; + esac + fi +]) diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index b7109c5f87f..8098a52e501 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -40,6 +40,7 @@ AC_DEFUN([gl_EARLY], AC_REQUIRE([gl_PROG_AR_RANLIB]) # Code from module alloca-opt: # Code from module allocator: + # Code from module at-internal: # Code from module c-ctype: # Code from module c-strcase: # Code from module careadlinkat: @@ -49,6 +50,7 @@ AC_DEFUN([gl_EARLY], # Code from module crypto/sha1: # Code from module crypto/sha256: # Code from module crypto/sha512: + # Code from module dirent: # Code from module dosname: # Code from module dtoastr: # Code from module dtotimespec: @@ -61,8 +63,10 @@ AC_DEFUN([gl_EARLY], # Code from module extern-inline: # Code from module faccessat: # Code from module fcntl-h: + # Code from module fdopendir: # Code from module filemode: # Code from module fpending: + # Code from module fstatat: # Code from module getgroups: # Code from module getloadavg: # Code from module getopt-gnu: @@ -82,11 +86,13 @@ AC_DEFUN([gl_EARLY], # Code from module mktime: # Code from module multiarch: # Code from module nocrash: + # Code from module openat-h: # Code from module pathmax: # Code from module pselect: # Code from module pthread_sigmask: # Code from module putenv: # Code from module readlink: + # Code from module readlinkat: # Code from module root-uid: # Code from module sig2str: # Code from module signal-h: @@ -159,6 +165,7 @@ AC_DEFUN([gl_INIT], gl_SHA1 gl_SHA256 gl_SHA512 + gl_DIRENT_H AC_REQUIRE([gl_C99_STRTOLD]) gl_FUNC_DUP2 if test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1; then @@ -178,12 +185,23 @@ AC_DEFUN([gl_INIT], gl_MODULE_INDICATOR([faccessat]) gl_UNISTD_MODULE_INDICATOR([faccessat]) gl_FCNTL_H + gl_FUNC_FDOPENDIR + if test $HAVE_FDOPENDIR = 0 || test $REPLACE_FDOPENDIR = 1; then + AC_LIBOBJ([fdopendir]) + fi + gl_DIRENT_MODULE_INDICATOR([fdopendir]) + gl_MODULE_INDICATOR([fdopendir]) gl_FILEMODE gl_FUNC_FPENDING if test $ac_cv_func___fpending = no; then AC_LIBOBJ([fpending]) gl_PREREQ_FPENDING fi + gl_FUNC_FSTATAT + if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then + AC_LIBOBJ([fstatat]) + fi + gl_SYS_STAT_MODULE_INDICATOR([fstatat]) gl_GETLOADAVG if test $HAVE_GETLOADAVG = 0; then AC_LIBOBJ([getloadavg]) @@ -253,6 +271,11 @@ AC_DEFUN([gl_INIT], gl_PREREQ_READLINK fi gl_UNISTD_MODULE_INDICATOR([readlink]) + gl_FUNC_READLINKAT + if test $HAVE_READLINKAT = 0; then + AC_LIBOBJ([readlinkat]) + fi + gl_UNISTD_MODULE_INDICATOR([readlinkat]) gl_FUNC_SIG2STR if test $ac_cv_func_sig2str = no; then AC_LIBOBJ([sig2str]) @@ -311,11 +334,13 @@ AC_DEFUN([gl_INIT], fi gl_STDLIB_MODULE_INDICATOR([unsetenv]) gl_UTIMENS + gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false gl_gnulib_enabled_dosname=false gl_gnulib_enabled_euidaccess=false gl_gnulib_enabled_getgroups=false gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false + gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false gl_gnulib_enabled_pathmax=false gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false gl_gnulib_enabled_stat=false @@ -323,6 +348,13 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_strtoull=false gl_gnulib_enabled_verify=false gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false + func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () + { + if ! $gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b; then + AC_LIBOBJ([openat-proc]) + gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=true + fi + } func_gl_gnulib_m4code_dosname () { if ! $gl_gnulib_enabled_dosname; then @@ -385,6 +417,12 @@ AC_DEFUN([gl_INIT], fi fi } + func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 () + { + if ! $gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7; then + gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=true + fi + } func_gl_gnulib_m4code_pathmax () { if ! $gl_gnulib_enabled_pathmax; then @@ -456,11 +494,29 @@ AC_DEFUN([gl_INIT], fi } if test $HAVE_FACCESSAT = 0; then + func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b + fi + if test $HAVE_FACCESSAT = 0; then func_gl_gnulib_m4code_dosname fi if test $HAVE_FACCESSAT = 0; then func_gl_gnulib_m4code_euidaccess fi + if test $HAVE_FACCESSAT = 0; then + func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 + fi + if test $HAVE_FDOPENDIR = 0; then + func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b + fi + if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then + func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b + fi + if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then + func_gl_gnulib_m4code_dosname + fi + if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then + func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 + fi if test $REPLACE_GETOPT = 1; then func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 fi @@ -473,6 +529,15 @@ AC_DEFUN([gl_INIT], if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then func_gl_gnulib_m4code_stat fi + if test $HAVE_READLINKAT = 0; then + func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b + fi + if test $HAVE_READLINKAT = 0; then + func_gl_gnulib_m4code_dosname + fi + if test $HAVE_READLINKAT = 0; then + func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 + fi if { test $HAVE_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then func_gl_gnulib_m4code_strtoll fi @@ -486,11 +551,13 @@ AC_DEFUN([gl_INIT], func_gl_gnulib_m4code_verify fi m4_pattern_allow([^gl_GNULIB_ENABLED_]) + AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b]) AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname]) AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess]) AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups]) AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36]) AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1]) + AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7]) AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax]) AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat]) @@ -656,6 +723,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/careadlinkat.h lib/close-stream.c lib/close-stream.h + lib/dirent.in.h lib/dosname.h lib/dtoastr.c lib/dtotimespec.c @@ -665,10 +733,12 @@ AC_DEFUN([gl_FILE_LIST], [ lib/execinfo.in.h lib/faccessat.c lib/fcntl.in.h + lib/fdopendir.c lib/filemode.c lib/filemode.h lib/fpending.c lib/fpending.h + lib/fstatat.c lib/ftoastr.c lib/ftoastr.h lib/getgroups.c @@ -689,11 +759,15 @@ AC_DEFUN([gl_FILE_LIST], [ lib/md5.h lib/mktime-internal.h lib/mktime.c + lib/openat-priv.h + lib/openat-proc.c + lib/openat.h lib/pathmax.h lib/pselect.c lib/pthread_sigmask.c lib/putenv.c lib/readlink.c + lib/readlinkat.c lib/root-uid.h lib/sha1.c lib/sha1.h @@ -746,6 +820,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/c-strtod.m4 m4/clock_time.m4 m4/close-stream.m4 + m4/dirent_h.m4 m4/dup2.m4 m4/environ.m4 m4/euidaccess.m4 @@ -755,8 +830,10 @@ AC_DEFUN([gl_FILE_LIST], [ m4/faccessat.m4 m4/fcntl-o.m4 m4/fcntl_h.m4 + m4/fdopendir.m4 m4/filemode.m4 m4/fpending.m4 + m4/fstatat.m4 m4/getgroups.m4 m4/getloadavg.m4 m4/getopt.m4 @@ -780,6 +857,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/pthread_sigmask.m4 m4/putenv.m4 m4/readlink.m4 + m4/readlinkat.m4 m4/setenv.m4 m4/sha1.m4 m4/sha256.m4 diff --git a/m4/readlinkat.m4 b/m4/readlinkat.m4 new file mode 100644 index 00000000000..b2ff40dcb02 --- /dev/null +++ b/m4/readlinkat.m4 @@ -0,0 +1,19 @@ +# serial 3 +# See if we need to provide readlinkat replacement. + +dnl Copyright (C) 2009-2013 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_READLINKAT], +[ + AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_FUNCS_ONCE([readlinkat]) + if test $ac_cv_func_readlinkat = no; then + HAVE_READLINKAT=0 + fi +]) diff --git a/nt/ChangeLog b/nt/ChangeLog index 8b8628db1e2..b2a481354bc 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog @@ -1,3 +1,9 @@ +2013-02-01 Paul Eggert <eggert@cs.ucla.edu> + + Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539). + * inc/sys/stat.h (fstatat): + * inc/unistd.h (readlinkat): New decls. + 2013-01-28 Eli Zaretskii <eliz@gnu.org> * inc/dirent.h (opendir): Update prototype. diff --git a/nt/inc/sys/stat.h b/nt/inc/sys/stat.h index 45689d24776..c356283c04b 100644 --- a/nt/inc/sys/stat.h +++ b/nt/inc/sys/stat.h @@ -110,6 +110,7 @@ _CRTIMP int __cdecl __MINGW_NOTHROW fstat (int, struct stat*); _CRTIMP int __cdecl __MINGW_NOTHROW chmod (const char*, int); _CRTIMP int __cdecl __MINGW_NOTHROW stat (const char*, struct stat*); _CRTIMP int __cdecl __MINGW_NOTHROW lstat (const char*, struct stat*); +_CRTIMP int __cdecl __MINGW_NOTHROW fstatat (int, char const *, + struct stat *, int); #endif /* INC_SYS_STAT_H_ */ - diff --git a/nt/inc/unistd.h b/nt/inc/unistd.h index 9c8a64d5ed2..3fd9289d83d 100644 --- a/nt/inc/unistd.h +++ b/nt/inc/unistd.h @@ -12,6 +12,7 @@ and whose prototypes are usually found in unistd.h on POSIX platforms. */ extern ssize_t readlink (const char *, char *, size_t); +extern ssize_t readlinkat (int, const char *, char *, size_t); extern int symlink (char const *, char const *); extern int setpgid (pid_t, pid_t); extern pid_t getpgrp (void); diff --git a/src/ChangeLog b/src/ChangeLog index 5c13e35306d..2156d7f19c9 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,34 @@ +2013-02-01 Paul Eggert <eggert@cs.ucla.edu> + + Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539). + * conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove. + * dired.c: Include <fcntl.h>. + (open_directory): New function, which uses open and fdopendir + rather than opendir. DOS_NT platforms still use opendir, though. + (directory_files_internal, file_name_completion): Use it. + (file_attributes): New function, with most of the old Ffile_attributes. + (directory_files_internal, Ffile_attributes): Use it. + (file_attributes, file_name_completion_stat): First arg is now fd, + not dir name. All uses changed. Use fstatat rather than lstat + + stat. + (file_attributes): Use emacs_readlinkat rather than Ffile_symlink_p. + * fileio.c: Include <allocator.h>, <careadlinkat.h>. + (emacs_readlinkat): New function, with much of the old + Ffile_symlink_p, but with an fd argument for speed. + It uses readlinkat rather than careadlinkatcwd, so that it + need not assume the working directory. + (Ffile_symlink_p): Use it. + * filelock.c (current_lock_owner): Use emacs_readlinkat + rather than emacs_readlink. + * lisp.h (emacs_readlinkat): New decl. + (READLINK_BUFSIZE, emacs_readlink): Remove. + * sysdep.c: Do not include <allocator.h>, <careadlinkat.h>. + (emacs_norealloc_allocator, emacs_readlink): Remove. + This stuff is moved to fileio.c. + * w32.c (fstatat, readlinkat): New functions. + (careadlinkat): Don't check that fd == AT_FDCWD. + (careadlinkatcwd): Remove; no longer needed. + 2013-01-31 Glenn Morris <rgm@gnu.org> * fileio.c (choose_write_coding_system): Make it callable from Lisp. diff --git a/src/conf_post.h b/src/conf_post.h index cd1e35bea7a..6c9747a436c 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -182,10 +182,6 @@ extern void _DebPrint (const char *fmt, ...); #endif #endif -/* Tell gnulib to omit support for openat-related functions having a - first argument other than AT_FDCWD. */ -#define GNULIB_SUPPORT_ONLY_AT_FDCWD - #include <string.h> #include <stdlib.h> diff --git a/src/dired.c b/src/dired.c index a4c8621e9c0..ed0571fe9fe 100644 --- a/src/dired.c +++ b/src/dired.c @@ -30,6 +30,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <grp.h> #include <errno.h> +#include <fcntl.h> #include <unistd.h> #include <dirent.h> @@ -54,6 +55,7 @@ static Lisp_Object Qfile_attributes; static Lisp_Object Qfile_attributes_lessp; static ptrdiff_t scmp (const char *, const char *, ptrdiff_t); +static Lisp_Object file_attributes (int, char const *, Lisp_Object); /* Return the number of bytes in DP's name. */ static ptrdiff_t @@ -66,6 +68,44 @@ dirent_namelen (struct dirent *dp) #endif } +static DIR * +open_directory (char const *name, int *fdp) +{ + DIR *d; + int fd, opendir_errno; + + block_input (); + +#ifdef DOS_NT + /* Directories cannot be opened. The emulation assumes that any + file descriptor other than AT_FDCWD corresponds to the most + recently opened directory. This hack is good enough for Emacs. */ + fd = 0; + d = opendir (name); + opendir_errno = errno; +#else + fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0); + if (fd < 0) + { + opendir_errno = errno; + d = 0; + } + else + { + d = fdopendir (fd); + opendir_errno = errno; + if (! d) + close (fd); + } +#endif + + unblock_input (); + + *fdp = fd; + errno = opendir_errno; + return d; +} + #ifdef WINDOWSNT Lisp_Object directory_files_internal_w32_unwind (Lisp_Object arg) @@ -96,6 +136,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, Lisp_Object id_format) { DIR *d; + int fd; ptrdiff_t directory_nbytes; Lisp_Object list, dirfilename, encoded_directory; struct re_pattern_buffer *bufp = NULL; @@ -142,9 +183,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, /* Now *bufp is the compiled form of MATCH; don't call anything which might compile a new regexp until we're done with the loop! */ - block_input (); - d = opendir (SSDATA (dirfilename)); - unblock_input (); + d = open_directory (SSDATA (dirfilename), &fd); if (d == NULL) report_file_error ("Opening directory", Fcons (directory, Qnil)); @@ -259,20 +298,9 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, if (attrs) { - /* Construct an expanded filename for the directory entry. - Use the decoded names for input to Ffile_attributes. */ - Lisp_Object decoded_fullname, fileattrs; - struct gcpro gcpro1, gcpro2; - - decoded_fullname = fileattrs = Qnil; - GCPRO2 (decoded_fullname, fileattrs); - - /* Both Fexpand_file_name and Ffile_attributes can GC. */ - decoded_fullname = Fexpand_file_name (name, directory); - fileattrs = Ffile_attributes (decoded_fullname, id_format); - + Lisp_Object fileattrs + = file_attributes (fd, dp->d_name, id_format); list = Fcons (Fcons (finalname, fileattrs), list); - UNGCPRO; } else list = Fcons (finalname, list); @@ -413,8 +441,7 @@ These are all file names in directory DIRECTORY which begin with FILE. */) return file_name_completion (file, directory, 1, Qnil); } -static int file_name_completion_stat (Lisp_Object dirname, struct dirent *dp, - struct stat *st_addr); +static int file_name_completion_stat (int, struct dirent *, struct stat *); static Lisp_Object Qdefault_directory; static Lisp_Object @@ -422,6 +449,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, Lisp_Object predicate) { DIR *d; + int fd; ptrdiff_t bestmatchsize = 0; int matchcount = 0; /* If ALL_FLAG is 1, BESTMATCH is the list of all matches, decoded. @@ -458,9 +486,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, encoded_dir = ENCODE_FILE (dirname); - block_input (); - d = opendir (SSDATA (Fdirectory_file_name (encoded_dir))); - unblock_input (); + d = open_directory (SSDATA (Fdirectory_file_name (encoded_dir)), &fd); if (!d) report_file_error ("Opening directory", Fcons (dirname, Qnil)); @@ -495,7 +521,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, SCHARS (encoded_file))) continue; - if (file_name_completion_stat (encoded_dir, dp, &st) < 0) + if (file_name_completion_stat (fd, dp, &st) < 0) continue; directoryp = S_ISDIR (st.st_mode) != 0; @@ -772,14 +798,9 @@ scmp (const char *s1, const char *s2, ptrdiff_t len) } static int -file_name_completion_stat (Lisp_Object dirname, struct dirent *dp, - struct stat *st_addr) +file_name_completion_stat (int fd, struct dirent *dp, struct stat *st_addr) { - ptrdiff_t len = dirent_namelen (dp); - ptrdiff_t pos = SCHARS (dirname); int value; - USE_SAFE_ALLOCA; - char *fullname = SAFE_ALLOCA (len + pos + 2); #ifdef MSDOS /* Some fields of struct stat are *very* expensive to compute on MS-DOS, @@ -792,23 +813,15 @@ file_name_completion_stat (Lisp_Object dirname, struct dirent *dp, _djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; #endif /* MSDOS */ - memcpy (fullname, SDATA (dirname), pos); - if (!IS_DIRECTORY_SEP (fullname[pos - 1])) - fullname[pos++] = DIRECTORY_SEP; - - memcpy (fullname + pos, dp->d_name, len); - fullname[pos + len] = 0; - /* We want to return success if a link points to a nonexistent file, but we want to return the status for what the link points to, in case it is a directory. */ - value = lstat (fullname, st_addr); + value = fstatat (fd, dp->d_name, st_addr, AT_SYMLINK_NOFOLLOW); if (value == 0 && S_ISLNK (st_addr->st_mode)) - stat (fullname, st_addr); + fstatat (fd, dp->d_name, st_addr, 0); #ifdef MSDOS _djstat_flags = save_djstat_flags; #endif /* MSDOS */ - SAFE_FREE (); return value; } @@ -886,18 +899,8 @@ On some FAT-based filesystems, only the date of last access is recorded, so last access time will always be midnight of that day. */) (Lisp_Object filename, Lisp_Object id_format) { - Lisp_Object values[12]; Lisp_Object encoded; - struct stat s; - int lstat_result; - - /* An array to hold the mode string generated by filemodestring, - including its terminating space and null byte. */ - char modes[sizeof "-rwxr-xr-x "]; - Lisp_Object handler; - struct gcpro gcpro1; - char *uname = NULL, *gname = NULL; filename = Fexpand_file_name (filename, Qnil); @@ -913,9 +916,22 @@ so last access time will always be midnight of that day. */) return call3 (handler, Qfile_attributes, filename, id_format); } - GCPRO1 (filename); encoded = ENCODE_FILE (filename); - UNGCPRO; + return file_attributes (AT_FDCWD, SSDATA (encoded), id_format); +} + +static Lisp_Object +file_attributes (int fd, char const *name, Lisp_Object id_format) +{ + Lisp_Object values[12]; + struct stat s; + int lstat_result; + + /* An array to hold the mode string generated by filemodestring, + including its terminating space and null byte. */ + char modes[sizeof "-rwxr-xr-x "]; + + char *uname = NULL, *gname = NULL; #ifdef WINDOWSNT /* We usually don't request accurate owner and group info, because @@ -925,7 +941,7 @@ so last access time will always be midnight of that day. */) w32_stat_get_owner_group = 1; #endif - lstat_result = lstat (SSDATA (encoded), &s); + lstat_result = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW); #ifdef WINDOWSNT w32_stat_get_owner_group = 0; @@ -934,7 +950,7 @@ so last access time will always be midnight of that day. */) if (lstat_result < 0) return Qnil; - values[0] = (S_ISLNK (s.st_mode) ? Ffile_symlink_p (filename) + values[0] = (S_ISLNK (s.st_mode) ? emacs_readlinkat (fd, name) : S_ISDIR (s.st_mode) ? Qt : Qnil); values[1] = make_number (s.st_nlink); diff --git a/src/fileio.c b/src/fileio.c index cd4bd4fa86e..a8218fcd797 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -82,6 +82,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #endif #include "systime.h" +#include <allocator.h> +#include <careadlinkat.h> #include <stat-time.h> #ifdef HPUX @@ -2759,6 +2761,29 @@ If there is no error, returns nil. */) return Qnil; } +/* Relative to directory FD, return the symbolic link value of FILENAME. + On failure, return nil. */ +Lisp_Object +emacs_readlinkat (int fd, char const *filename) +{ + static struct allocator const emacs_norealloc_allocator = + { xmalloc, NULL, xfree, memory_full }; + Lisp_Object val; + char readlink_buf[1024]; + char *buf = careadlinkat (fd, filename, readlink_buf, sizeof readlink_buf, + &emacs_norealloc_allocator, readlinkat); + if (!buf) + return Qnil; + + val = build_string (buf); + if (buf[0] == '/' && strchr (buf, ':')) + val = concat2 (build_string ("/:"), val); + if (buf != readlink_buf) + xfree (buf); + val = DECODE_FILE (val); + return val; +} + DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0, doc: /* Return non-nil if file FILENAME is the name of a symbolic link. The value is the link target, as a string. @@ -2769,9 +2794,6 @@ points to a nonexistent file. */) (Lisp_Object filename) { Lisp_Object handler; - char *buf; - Lisp_Object val; - char readlink_buf[READLINK_BUFSIZE]; CHECK_STRING (filename); filename = Fexpand_file_name (filename, Qnil); @@ -2784,17 +2806,7 @@ points to a nonexistent file. */) filename = ENCODE_FILE (filename); - buf = emacs_readlink (SSDATA (filename), readlink_buf); - if (! buf) - return Qnil; - - val = build_string (buf); - if (buf[0] == '/' && strchr (buf, ':')) - val = concat2 (build_string ("/:"), val); - if (buf != readlink_buf) - xfree (buf); - val = DECODE_FILE (val); - return val; + return emacs_readlinkat (AT_FDCWD, SSDATA (filename)); } DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0, diff --git a/src/filelock.c b/src/filelock.c index f21240f8340..228fe98e8c7 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -390,12 +390,14 @@ current_lock_owner (lock_info_type *owner, char *lfname) lock_info_type local_owner; intmax_t n; char *at, *dot, *colon; - char readlink_buf[READLINK_BUFSIZE]; - char *lfinfo = emacs_readlink (lfname, readlink_buf); + Lisp_Object lfinfo_object = emacs_readlinkat (AT_FDCWD, lfname); + char *lfinfo; + struct gcpro gcpro1; /* If nonexistent lock file, all is well; otherwise, got strange error. */ - if (!lfinfo) + if (NILP (lfinfo_object)) return errno == ENOENT ? 0 : -1; + lfinfo = SSDATA (lfinfo_object); /* Even if the caller doesn't want the owner info, we still have to read it to determine return value. */ @@ -407,12 +409,9 @@ current_lock_owner (lock_info_type *owner, char *lfname) at = strrchr (lfinfo, '@'); dot = strrchr (lfinfo, '.'); if (!at || !dot) - { - if (lfinfo != readlink_buf) - xfree (lfinfo); - return -1; - } + return -1; len = at - lfinfo; + GCPRO1 (lfinfo_object); owner->user = xmalloc (len + 1); memcpy (owner->user, lfinfo, len); owner->user[len] = 0; @@ -445,8 +444,7 @@ current_lock_owner (lock_info_type *owner, char *lfname) owner->host[len] = 0; /* We're done looking at the link info. */ - if (lfinfo != readlink_buf) - xfree (lfinfo); + UNGCPRO; /* On current host? */ if (STRINGP (Fsystem_name ()) diff --git a/src/lisp.h b/src/lisp.h index 2e9088f1834..251b5e069ec 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3294,6 +3294,7 @@ extern Lisp_Object close_file_unwind (Lisp_Object); extern Lisp_Object restore_point_unwind (Lisp_Object); extern _Noreturn void report_file_error (const char *, Lisp_Object); extern bool internal_delete_file (Lisp_Object); +extern Lisp_Object emacs_readlinkat (int, const char *); extern bool file_directory_p (const char *); extern bool file_accessible_directory_p (const char *); extern void init_fileio (void); @@ -3566,8 +3567,6 @@ extern int emacs_open (const char *, int, int); extern int emacs_close (int); extern ptrdiff_t emacs_read (int, char *, ptrdiff_t); extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t); -enum { READLINK_BUFSIZE = 1024 }; -extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]); extern void unlock_all_files (void); extern void lock_file (Lisp_Object); diff --git a/src/sysdep.c b/src/sysdep.c index 0e9a6826005..57ca8265a65 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -30,9 +30,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <limits.h> #include <unistd.h> -#include <allocator.h> #include <c-ctype.h> -#include <careadlinkat.h> #include <ignore-value.h> #include <utimens.h> @@ -2247,22 +2245,6 @@ emacs_write (int fildes, const char *buf, ptrdiff_t nbyte) return (bytes_written); } - -static struct allocator const emacs_norealloc_allocator = - { xmalloc, NULL, xfree, memory_full }; - -/* Get the symbolic link value of FILENAME. Return a pointer to a - NUL-terminated string. If readlink fails, return NULL and set - errno. If the value fits in INITIAL_BUF, return INITIAL_BUF. - Otherwise, allocate memory and return a pointer to that memory. If - memory allocation fails, diagnose and fail without returning. If - successful, store the length of the symbolic link into *LINKLEN. */ -char * -emacs_readlink (char const *filename, char initial_buf[READLINK_BUFSIZE]) -{ - return careadlinkat (AT_FDCWD, filename, initial_buf, READLINK_BUFSIZE, - &emacs_norealloc_allocator, careadlinkatcwd); -} /* Return a struct timeval that is roughly equivalent to T. Use the least timeval not less than T. diff --git a/src/w32.c b/src/w32.c index d0af53889e7..64f8a0335ac 100644 --- a/src/w32.c +++ b/src/w32.c @@ -4274,6 +4274,30 @@ lstat (const char * path, struct stat * buf) return stat_worker (path, buf, 0); } +int +fstatat (int fd, char const *name, struct stat *st, int flags) +{ + /* Rely on a hack: an open directory is modeled as file descriptor 0. + This is good enough for the current usage in Emacs, but is fragile. + + FIXME: Add proper support for fdopendir, fstatatat, readlinkat. + Gnulib does this and can serve as a model. */ + char fullname[MAX_PATH]; + + if (fd != AT_FDCWD) + { + if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name) + < 0) + { + errno = ENAMETOOLONG; + return -1; + } + name = fullname; + } + + return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW)); +} + /* Provide fstat and utime as well as stat for consistent handling of file timestamps. */ int @@ -4816,6 +4840,28 @@ readlink (const char *name, char *buf, size_t buf_size) return retval; } +ssize_t +readlinkat (int fd, char const *name, char *buffer, + size_t buffer_size) +{ + /* Rely on a hack: an open directory is modeled as file descriptor 0, + as in fstatat. FIXME: Add proper support for readlinkat. */ + char fullname[MAX_PATH]; + + if (fd != AT_FDCWD) + { + if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name) + < 0) + { + errno = ENAMETOOLONG; + return -1; + } + name = fullname; + } + + return readlink (name, buffer, buffer_size); +} + /* If FILE is a symlink, return its target (stored in a static buffer); otherwise return FILE. @@ -5168,12 +5214,6 @@ careadlinkat (int fd, char const *filename, char linkname[MAX_PATH]; ssize_t link_size; - if (fd != AT_FDCWD) - { - errno = EINVAL; - return NULL; - } - link_size = preadlinkat (fd, filename, linkname, sizeof(linkname)); if (link_size > 0) @@ -5191,14 +5231,6 @@ careadlinkat (int fd, char const *filename, return NULL; } -ssize_t -careadlinkatcwd (int fd, char const *filename, char *buffer, - size_t buffer_size) -{ - (void) fd; - return readlink (filename, buffer, buffer_size); -} - /* Support for browsing other processes and their attributes. See process.c for the Lisp bindings. */ |