summaryrefslogtreecommitdiff
path: root/lib
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 /lib
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
Diffstat (limited to 'lib')
-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
14 files changed, 1093 insertions, 21 deletions
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