summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2013-01-31 22:30:51 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2013-01-31 22:30:51 -0800
commit8654f9d7d6d7c3ee97232a34a40250dcbc57af8e (patch)
treeff488081b57dabdfe7e85c11a34e8ac522b5bdb4
parent44b12dd6994a6214b9d6f73539b441080611369b (diff)
downloademacs-8654f9d7d6d7c3ee97232a34a40250dcbc57af8e.tar.gz
Use fdopendir, fstatat and readlinkat, for efficiency.
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. * admin/merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat. (GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h. Avoid dup, open, opendir. * nt/inc/sys/stat.h (fstatat): * nt/inc/unistd.h (readlinkat): New decls. * src/conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove. * src/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. * src/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. * src/filelock.c (current_lock_owner): Use emacs_readlinkat rather than emacs_readlink. * src/lisp.h (emacs_readlinkat): New decl. (READLINK_BUFSIZE, emacs_readlink): Remove. * src/sysdep.c: Do not include <allocator.h>, <careadlinkat.h>. (emacs_norealloc_allocator, emacs_readlink): Remove. This stuff is moved to fileio.c. * src/w32.c (fstatat, readlinkat): New functions. (careadlinkat): Don't check that fd == AT_FDCWD. (careadlinkatcwd): Remove; no longer needed. Fixes: debbugs:13539
-rw-r--r--ChangeLog20
-rw-r--r--admin/ChangeLog7
-rwxr-xr-xadmin/merge-gnulib8
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/careadlinkat.c16
-rw-r--r--lib/careadlinkat.h6
-rw-r--r--lib/dirent.in.h258
-rw-r--r--lib/fdopendir.c204
-rw-r--r--lib/fstatat.c135
-rw-r--r--lib/gnulib.mk97
-rw-r--r--lib/openat-die.c6
-rw-r--r--lib/openat-priv.h64
-rw-r--r--lib/openat-proc.c110
-rw-r--r--lib/openat.h120
-rw-r--r--lib/readlinkat.c47
-rw-r--r--lib/save-cwd.c3
-rw-r--r--lib/save-cwd.h46
-rw-r--r--m4/dirent_h.m464
-rw-r--r--m4/fdopendir.m461
-rw-r--r--m4/fstatat.m460
-rw-r--r--m4/gnulib-comp.m478
-rw-r--r--m4/readlinkat.m419
-rw-r--r--nt/ChangeLog6
-rw-r--r--nt/inc/sys/stat.h3
-rw-r--r--nt/inc/unistd.h1
-rw-r--r--src/ChangeLog31
-rw-r--r--src/conf_post.h4
-rw-r--r--src/dired.c120
-rw-r--r--src/fileio.c40
-rw-r--r--src/filelock.c18
-rw-r--r--src/lisp.h3
-rw-r--r--src/sysdep.c18
-rw-r--r--src/w32.c60
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. */