aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorPaul Eggert2013-01-31 22:30:51 -0800
committerPaul Eggert2013-01-31 22:30:51 -0800
commit8654f9d7d6d7c3ee97232a34a40250dcbc57af8e (patch)
treeff488081b57dabdfe7e85c11a34e8ac522b5bdb4 /lib
parent44b12dd6994a6214b9d6f73539b441080611369b (diff)
downloademacs-8654f9d7d6d7c3ee97232a34a40250dcbc57af8e.tar.gz
emacs-8654f9d7d6d7c3ee97232a34a40250dcbc57af8e.zip
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)
8DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src 8DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src
9 9
10include gnulib.mk 10include gnulib.mk
11
12libgnu_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 @@
24 24
25#include <errno.h> 25#include <errno.h>
26#include <limits.h> 26#include <limits.h>
27#include <stdlib.h>
28#include <string.h> 27#include <string.h>
29#include <unistd.h>
30 28
31/* Define this independently so that stdint.h is not a prerequisite. */ 29/* Define this independently so that stdint.h is not a prerequisite. */
32#ifndef SIZE_MAX 30#ifndef SIZE_MAX
@@ -39,20 +37,6 @@
39 37
40#include "allocator.h" 38#include "allocator.h"
41 39
42/* Get the symbolic link value of FILENAME and put it into BUFFER, with
43 size BUFFER_SIZE. This function acts like readlink but has
44 readlinkat's signature. */
45ssize_t
46careadlinkatcwd (int fd, char const *filename, char *buffer,
47 size_t buffer_size)
48{
49 /* FD must be AT_FDCWD here, otherwise the caller is using this
50 function in contexts for which it was not meant for. */
51 if (fd != AT_FDCWD)
52 abort ();
53 return readlink (filename, buffer, buffer_size);
54}
55
56/* Assuming the current directory is FD, get the symbolic link value 40/* Assuming the current directory is FD, get the symbolic link value
57 of FILENAME as a null-terminated string and put it into a buffer. 41 of FILENAME as a null-terminated string and put it into a buffer.
58 If FD is AT_FDCWD, FILENAME is interpreted relative to the current 42 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,
52 ssize_t (*preadlinkat) (int, char const *, 52 ssize_t (*preadlinkat) (int, char const *,
53 char *, size_t)); 53 char *, size_t));
54 54
55/* Suitable values for careadlinkat's FD and PREADLINKAT arguments, 55/* Suitable value for careadlinkat's FD argument,
56 when doing a plain readlink: 56 when doing a plain readlink:
57 Pass FD = AT_FDCWD and PREADLINKAT = careadlinkatcwd. */ 57 Pass FD = AT_FDCWD. */
58#if HAVE_READLINKAT 58#if HAVE_READLINKAT
59/* AT_FDCWD is declared in <fcntl.h>. */ 59/* AT_FDCWD is declared in <fcntl.h>. */
60#else 60#else
@@ -66,7 +66,5 @@ char *careadlinkat (int fd, char const *filename,
66# define AT_FDCWD (-3041965) 66# define AT_FDCWD (-3041965)
67# endif 67# endif
68#endif 68#endif
69ssize_t careadlinkatcwd (int fd, char const *filename,
70 char *buffer, size_t buffer_size);
71 69
72#endif /* _GL_CAREADLINKAT_H */ 70#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 @@
1/* A GNU-like <dirent.h>.
2 Copyright (C) 2006-2013 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17#ifndef _@GUARD_PREFIX@_DIRENT_H
18
19#if __GNUC__ >= 3
20@PRAGMA_SYSTEM_HEADER@
21#endif
22@PRAGMA_COLUMNS@
23
24/* The include_next requires a split double-inclusion guard. */
25#if @HAVE_DIRENT_H@
26# @INCLUDE_NEXT@ @NEXT_DIRENT_H@
27#endif
28
29#ifndef _@GUARD_PREFIX@_DIRENT_H
30#define _@GUARD_PREFIX@_DIRENT_H
31
32/* Get ino_t. Needed on some systems, including glibc 2.8. */
33#include <sys/types.h>
34
35#if !@HAVE_DIRENT_H@
36/* Define types DIR and 'struct dirent'. */
37# if !GNULIB_defined_struct_dirent
38struct dirent
39{
40 char d_type;
41 char d_name[1];
42};
43/* Possible values for 'd_type'. */
44# define DT_UNKNOWN 0
45# define DT_FIFO 1 /* FIFO */
46# define DT_CHR 2 /* character device */
47# define DT_DIR 4 /* directory */
48# define DT_BLK 6 /* block device */
49# define DT_REG 8 /* regular file */
50# define DT_LNK 10 /* symbolic link */
51# define DT_SOCK 12 /* socket */
52# define DT_WHT 14 /* whiteout */
53typedef struct gl_directory DIR;
54# define GNULIB_defined_struct_dirent 1
55# endif
56#endif
57
58/* The __attribute__ feature is available in gcc versions 2.5 and later.
59 The attribute __pure__ was added in gcc 2.96. */
60#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
61# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
62#else
63# define _GL_ATTRIBUTE_PURE /* empty */
64#endif
65
66/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */
67
68/* The definition of _GL_ARG_NONNULL is copied here. */
69
70/* The definition of _GL_WARN_ON_USE is copied here. */
71
72
73/* Declare overridden functions. */
74
75#if @GNULIB_OPENDIR@
76# if @REPLACE_OPENDIR@
77# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
78# undef opendir
79# define opendir rpl_opendir
80# endif
81_GL_FUNCDECL_RPL (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
82_GL_CXXALIAS_RPL (opendir, DIR *, (const char *dir_name));
83# else
84# if !@HAVE_OPENDIR@
85_GL_FUNCDECL_SYS (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
86# endif
87_GL_CXXALIAS_SYS (opendir, DIR *, (const char *dir_name));
88# endif
89_GL_CXXALIASWARN (opendir);
90#elif defined GNULIB_POSIXCHECK
91# undef opendir
92# if HAVE_RAW_DECL_OPENDIR
93_GL_WARN_ON_USE (opendir, "opendir is not portable - "
94 "use gnulib module opendir for portability");
95# endif
96#endif
97
98#if @GNULIB_READDIR@
99# if !@HAVE_READDIR@
100_GL_FUNCDECL_SYS (readdir, struct dirent *, (DIR *dirp) _GL_ARG_NONNULL ((1)));
101# endif
102_GL_CXXALIAS_SYS (readdir, struct dirent *, (DIR *dirp));
103_GL_CXXALIASWARN (readdir);
104#elif defined GNULIB_POSIXCHECK
105# undef readdir
106# if HAVE_RAW_DECL_READDIR
107_GL_WARN_ON_USE (readdir, "readdir is not portable - "
108 "use gnulib module readdir for portability");
109# endif
110#endif
111
112#if @GNULIB_REWINDDIR@
113# if !@HAVE_REWINDDIR@
114_GL_FUNCDECL_SYS (rewinddir, void, (DIR *dirp) _GL_ARG_NONNULL ((1)));
115# endif
116_GL_CXXALIAS_SYS (rewinddir, void, (DIR *dirp));
117_GL_CXXALIASWARN (rewinddir);
118#elif defined GNULIB_POSIXCHECK
119# undef rewinddir
120# if HAVE_RAW_DECL_REWINDDIR
121_GL_WARN_ON_USE (rewinddir, "rewinddir is not portable - "
122 "use gnulib module rewinddir for portability");
123# endif
124#endif
125
126#if @GNULIB_CLOSEDIR@
127# if @REPLACE_CLOSEDIR@
128# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
129# undef closedir
130# define closedir rpl_closedir
131# endif
132_GL_FUNCDECL_RPL (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
133_GL_CXXALIAS_RPL (closedir, int, (DIR *dirp));
134# else
135# if !@HAVE_CLOSEDIR@
136_GL_FUNCDECL_SYS (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
137# endif
138_GL_CXXALIAS_SYS (closedir, int, (DIR *dirp));
139# endif
140_GL_CXXALIASWARN (closedir);
141#elif defined GNULIB_POSIXCHECK
142# undef closedir
143# if HAVE_RAW_DECL_CLOSEDIR
144_GL_WARN_ON_USE (closedir, "closedir is not portable - "
145 "use gnulib module closedir for portability");
146# endif
147#endif
148
149#if @GNULIB_DIRFD@
150/* Return the file descriptor associated with the given directory stream,
151 or -1 if none exists. */
152# if @REPLACE_DIRFD@
153# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
154# undef dirfd
155# define dirfd rpl_dirfd
156# endif
157_GL_FUNCDECL_RPL (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
158_GL_CXXALIAS_RPL (dirfd, int, (DIR *));
159# else
160# if defined __cplusplus && defined GNULIB_NAMESPACE && defined dirfd
161 /* dirfd is defined as a macro and not as a function.
162 Turn it into a function and get rid of the macro. */
163static inline int (dirfd) (DIR *dp) { return dirfd (dp); }
164# undef dirfd
165# endif
166# if !(@HAVE_DECL_DIRFD@ || defined dirfd)
167_GL_FUNCDECL_SYS (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
168# endif
169_GL_CXXALIAS_SYS (dirfd, int, (DIR *));
170# endif
171_GL_CXXALIASWARN (dirfd);
172#elif defined GNULIB_POSIXCHECK
173# undef dirfd
174# if HAVE_RAW_DECL_DIRFD
175_GL_WARN_ON_USE (dirfd, "dirfd is unportable - "
176 "use gnulib module dirfd for portability");
177# endif
178#endif
179
180#if @GNULIB_FDOPENDIR@
181/* Open a directory stream visiting the given directory file
182 descriptor. Return NULL and set errno if fd is not visiting a
183 directory. On success, this function consumes fd (it will be
184 implicitly closed either by this function or by a subsequent
185 closedir). */
186# if @REPLACE_FDOPENDIR@
187# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
188# undef fdopendir
189# define fdopendir rpl_fdopendir
190# endif
191_GL_FUNCDECL_RPL (fdopendir, DIR *, (int fd));
192_GL_CXXALIAS_RPL (fdopendir, DIR *, (int fd));
193# else
194# if !@HAVE_FDOPENDIR@ || !@HAVE_DECL_FDOPENDIR@
195_GL_FUNCDECL_SYS (fdopendir, DIR *, (int fd));
196# endif
197_GL_CXXALIAS_SYS (fdopendir, DIR *, (int fd));
198# endif
199_GL_CXXALIASWARN (fdopendir);
200#elif defined GNULIB_POSIXCHECK
201# undef fdopendir
202# if HAVE_RAW_DECL_FDOPENDIR
203_GL_WARN_ON_USE (fdopendir, "fdopendir is unportable - "
204 "use gnulib module fdopendir for portability");
205# endif
206#endif
207
208#if @GNULIB_SCANDIR@
209/* Scan the directory DIR, calling FILTER on each directory entry.
210 Entries for which FILTER returns nonzero are individually malloc'd,
211 sorted using qsort with CMP, and collected in a malloc'd array in
212 *NAMELIST. Returns the number of entries selected, or -1 on error. */
213# if !@HAVE_SCANDIR@
214_GL_FUNCDECL_SYS (scandir, int,
215 (const char *dir, struct dirent ***namelist,
216 int (*filter) (const struct dirent *),
217 int (*cmp) (const struct dirent **, const struct dirent **))
218 _GL_ARG_NONNULL ((1, 2, 4)));
219# endif
220/* Need to cast, because on glibc systems, the fourth parameter is
221 int (*cmp) (const void *, const void *). */
222_GL_CXXALIAS_SYS_CAST (scandir, int,
223 (const char *dir, struct dirent ***namelist,
224 int (*filter) (const struct dirent *),
225 int (*cmp) (const struct dirent **, const struct dirent **)));
226_GL_CXXALIASWARN (scandir);
227#elif defined GNULIB_POSIXCHECK
228# undef scandir
229# if HAVE_RAW_DECL_SCANDIR
230_GL_WARN_ON_USE (scandir, "scandir is unportable - "
231 "use gnulib module scandir for portability");
232# endif
233#endif
234
235#if @GNULIB_ALPHASORT@
236/* Compare two 'struct dirent' entries alphabetically. */
237# if !@HAVE_ALPHASORT@
238_GL_FUNCDECL_SYS (alphasort, int,
239 (const struct dirent **, const struct dirent **)
240 _GL_ATTRIBUTE_PURE
241 _GL_ARG_NONNULL ((1, 2)));
242# endif
243/* Need to cast, because on glibc systems, the parameters are
244 (const void *, const void *). */
245_GL_CXXALIAS_SYS_CAST (alphasort, int,
246 (const struct dirent **, const struct dirent **));
247_GL_CXXALIASWARN (alphasort);
248#elif defined GNULIB_POSIXCHECK
249# undef alphasort
250# if HAVE_RAW_DECL_ALPHASORT
251_GL_WARN_ON_USE (alphasort, "alphasort is unportable - "
252 "use gnulib module alphasort for portability");
253# endif
254#endif
255
256
257#endif /* _@GUARD_PREFIX@_DIRENT_H */
258#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 @@
1/* provide a replacement fdopendir function
2 Copyright (C) 2004-2013 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17/* written by Jim Meyering */
18
19#include <config.h>
20
21#include <dirent.h>
22
23#include <stdlib.h>
24#include <unistd.h>
25
26#if !HAVE_FDOPENDIR
27
28# include "openat.h"
29# include "openat-priv.h"
30# include "save-cwd.h"
31
32# if GNULIB_DIRENT_SAFER
33# include "dirent--.h"
34# endif
35
36# ifndef REPLACE_FCHDIR
37# define REPLACE_FCHDIR 0
38# endif
39
40static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
41static DIR *fd_clone_opendir (int, struct saved_cwd const *);
42
43/* Replacement for POSIX fdopendir.
44
45 First, try to simulate it via opendir ("/proc/self/fd/..."). Failing
46 that, simulate it by using fchdir metadata, or by doing
47 save_cwd/fchdir/opendir(".")/restore_cwd.
48 If either the save_cwd or the restore_cwd fails (relatively unlikely),
49 then give a diagnostic and exit nonzero.
50
51 If successful, the resulting stream is based on FD in
52 implementations where streams are based on file descriptors and in
53 applications where no other thread or signal handler allocates or
54 frees file descriptors. In other cases, consult dirfd on the result
55 to find out whether FD is still being used.
56
57 Otherwise, this function works just like POSIX fdopendir.
58
59 W A R N I N G:
60
61 Unlike other fd-related functions, this one places constraints on FD.
62 If this function returns successfully, FD is under control of the
63 dirent.h system, and the caller should not close or modify the state of
64 FD other than by the dirent.h functions. */
65DIR *
66fdopendir (int fd)
67{
68 DIR *dir = fdopendir_with_dup (fd, -1, NULL);
69
70 if (! REPLACE_FCHDIR && ! dir)
71 {
72 int saved_errno = errno;
73 if (EXPECTED_ERRNO (saved_errno))
74 {
75 struct saved_cwd cwd;
76 if (save_cwd (&cwd) != 0)
77 openat_save_fail (errno);
78 dir = fdopendir_with_dup (fd, -1, &cwd);
79 saved_errno = errno;
80 free_cwd (&cwd);
81 errno = saved_errno;
82 }
83 }
84
85 return dir;
86}
87
88/* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
89 to be a dup of FD which is less than FD - 1 and which will be
90 closed by the caller and not otherwise used by the caller. This
91 function makes sure that FD is closed and all file descriptors less
92 than FD are open, and then calls fd_clone_opendir on a dup of FD.
93 That way, barring race conditions, fd_clone_opendir returns a
94 stream whose file descriptor is FD.
95
96 If REPLACE_CHDIR or CWD is null, use opendir ("/proc/self/fd/...",
97 falling back on fchdir metadata. Otherwise, CWD is a saved version
98 of the working directory; use fchdir/opendir(".")/restore_cwd(CWD). */
99static DIR *
100fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd)
101{
102 int dupfd = dup (fd);
103 if (dupfd < 0 && errno == EMFILE)
104 dupfd = older_dupfd;
105 if (dupfd < 0)
106 return NULL;
107 else
108 {
109 DIR *dir;
110 int saved_errno;
111 if (dupfd < fd - 1 && dupfd != older_dupfd)
112 {
113 dir = fdopendir_with_dup (fd, dupfd, cwd);
114 saved_errno = errno;
115 }
116 else
117 {
118 close (fd);
119 dir = fd_clone_opendir (dupfd, cwd);
120 saved_errno = errno;
121 if (! dir)
122 {
123 int fd1 = dup (dupfd);
124 if (fd1 != fd)
125 openat_save_fail (fd1 < 0 ? errno : EBADF);
126 }
127 }
128
129 if (dupfd != older_dupfd)
130 close (dupfd);
131 errno = saved_errno;
132 return dir;
133 }
134}
135
136/* Like fdopendir, except the result controls a clone of FD. It is
137 the caller's responsibility both to close FD and (if the result is
138 not null) to closedir the result. */
139static DIR *
140fd_clone_opendir (int fd, struct saved_cwd const *cwd)
141{
142 if (REPLACE_FCHDIR || ! cwd)
143 {
144 DIR *dir = NULL;
145 int saved_errno = EOPNOTSUPP;
146 char buf[OPENAT_BUFFER_SIZE];
147 char *proc_file = openat_proc_name (buf, fd, ".");
148 if (proc_file)
149 {
150 dir = opendir (proc_file);
151 saved_errno = errno;
152 if (proc_file != buf)
153 free (proc_file);
154 }
155# if REPLACE_FCHDIR
156 if (! dir && EXPECTED_ERRNO (saved_errno))
157 {
158 char const *name = _gl_directory_name (fd);
159 return (name ? opendir (name) : NULL);
160 }
161# endif
162 errno = saved_errno;
163 return dir;
164 }
165 else
166 {
167 if (fchdir (fd) != 0)
168 return NULL;
169 else
170 {
171 DIR *dir = opendir (".");
172 int saved_errno = errno;
173 if (restore_cwd (cwd) != 0)
174 openat_restore_fail (errno);
175 errno = saved_errno;
176 return dir;
177 }
178 }
179}
180
181#else /* HAVE_FDOPENDIR */
182
183# include <errno.h>
184# include <sys/stat.h>
185
186# undef fdopendir
187
188/* Like fdopendir, but work around GNU/Hurd bug by validating FD. */
189
190DIR *
191rpl_fdopendir (int fd)
192{
193 struct stat st;
194 if (fstat (fd, &st))
195 return NULL;
196 if (!S_ISDIR (st.st_mode))
197 {
198 errno = ENOTDIR;
199 return NULL;
200 }
201 return fdopendir (fd);
202}
203
204#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 @@
1/* Work around an fstatat bug on Solaris 9.
2
3 Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18/* Written by Paul Eggert and Jim Meyering. */
19
20/* If the user's config.h happens to include <sys/stat.h>, let it include only
21 the system's <sys/stat.h> here, so that orig_fstatat doesn't recurse to
22 rpl_fstatat. */
23#define __need_system_sys_stat_h
24#include <config.h>
25
26/* Get the original definition of fstatat. It might be defined as a macro. */
27#include <sys/types.h>
28#include <sys/stat.h>
29#undef __need_system_sys_stat_h
30
31#if HAVE_FSTATAT
32static int
33orig_fstatat (int fd, char const *filename, struct stat *buf, int flags)
34{
35 return fstatat (fd, filename, buf, flags);
36}
37#endif
38
39/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
40 eliminates this include because of the preliminary #include <sys/stat.h>
41 above. */
42#include "sys/stat.h"
43
44#include <errno.h>
45#include <fcntl.h>
46#include <string.h>
47
48#if HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG
49
50# ifndef LSTAT_FOLLOWS_SLASHED_SYMLINK
51# define LSTAT_FOLLOWS_SLASHED_SYMLINK 0
52# endif
53
54/* fstatat should always follow symbolic links that end in /, but on
55 Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.
56 Likewise, trailing slash on a non-directory should be an error.
57 These are the same problems that lstat.c and stat.c address, so
58 solve it in a similar way.
59
60 AIX 7.1 fstatat (AT_FDCWD, ..., 0) always fails, which is a bug.
61 Work around this bug if FSTATAT_AT_FDCWD_0_BROKEN is nonzero. */
62
63int
64rpl_fstatat (int fd, char const *file, struct stat *st, int flag)
65{
66 int result = orig_fstatat (fd, file, st, flag);
67 size_t len;
68
69 if (LSTAT_FOLLOWS_SLASHED_SYMLINK || result != 0)
70 return result;
71 len = strlen (file);
72 if (flag & AT_SYMLINK_NOFOLLOW)
73 {
74 /* Fix lstat behavior. */
75 if (file[len - 1] != '/' || S_ISDIR (st->st_mode))
76 return 0;
77 if (!S_ISLNK (st->st_mode))
78 {
79 errno = ENOTDIR;
80 return -1;
81 }
82 result = orig_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
83 }
84 /* Fix stat behavior. */
85 if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/')
86 {
87 errno = ENOTDIR;
88 return -1;
89 }
90 return result;
91}
92
93#else /* ! (HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG) */
94
95/* On mingw, the gnulib <sys/stat.h> defines 'stat' as a function-like
96 macro; but using it in AT_FUNC_F2 causes compilation failure
97 because the preprocessor sees a use of a macro that requires two
98 arguments but is only given one. Hence, we need an inline
99 forwarder to get past the preprocessor. */
100static int
101stat_func (char const *name, struct stat *st)
102{
103 return stat (name, st);
104}
105
106/* Likewise, if there is no native 'lstat', then the gnulib
107 <sys/stat.h> defined it as stat, which also needs adjustment. */
108# if !HAVE_LSTAT
109# undef lstat
110# define lstat stat_func
111# endif
112
113/* Replacement for Solaris' function by the same name.
114 <http://www.google.com/search?q=fstatat+site:docs.sun.com>
115 First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
116 Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
117 If either the save_cwd or the restore_cwd fails (relatively unlikely),
118 then give a diagnostic and exit nonzero.
119 Otherwise, this function works just like Solaris' fstatat. */
120
121# define AT_FUNC_NAME fstatat
122# define AT_FUNC_F1 lstat
123# define AT_FUNC_F2 stat_func
124# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
125# define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
126# define AT_FUNC_POST_FILE_ARGS , st
127# include "at-func.c"
128# undef AT_FUNC_NAME
129# undef AT_FUNC_F1
130# undef AT_FUNC_F2
131# undef AT_FUNC_USE_F1_COND
132# undef AT_FUNC_POST_FILE_PARAM_DECLS
133# undef AT_FUNC_POST_FILE_ARGS
134
135#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 @@
21# the same distribution terms as the rest of that program. 21# the same distribution terms as the rest of that program.
22# 22#
23# Generated by gnulib-tool. 23# Generated by gnulib-tool.
24# 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 24# 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
25 25
26 26
27MOSTLYCLEANFILES += core *.stackdump 27MOSTLYCLEANFILES += core *.stackdump
@@ -64,6 +64,17 @@ EXTRA_DIST += allocator.h
64 64
65## end gnulib module allocator 65## end gnulib module allocator
66 66
67## begin gnulib module at-internal
68
69if gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b
70
71endif
72EXTRA_DIST += openat-priv.h openat-proc.c
73
74EXTRA_libgnu_a_SOURCES += openat-proc.c
75
76## end gnulib module at-internal
77
67## begin gnulib module c-ctype 78## begin gnulib module c-ctype
68 79
69libgnu_a_SOURCES += c-ctype.h c-ctype.c 80libgnu_a_SOURCES += c-ctype.h c-ctype.c
@@ -124,6 +135,54 @@ EXTRA_DIST += sha512.h
124 135
125## end gnulib module crypto/sha512 136## end gnulib module crypto/sha512
126 137
138## begin gnulib module dirent
139
140BUILT_SOURCES += dirent.h
141
142# We need the following in order to create <dirent.h> when the system
143# doesn't have one that works with the given compiler.
144dirent.h: dirent.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
145 $(AM_V_GEN)rm -f $@-t $@ && \
146 { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
147 sed -e 's|@''GUARD_PREFIX''@|GL|g' \
148 -e 's|@''HAVE_DIRENT_H''@|$(HAVE_DIRENT_H)|g' \
149 -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
150 -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
151 -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
152 -e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \
153 -e 's/@''GNULIB_OPENDIR''@/$(GNULIB_OPENDIR)/g' \
154 -e 's/@''GNULIB_READDIR''@/$(GNULIB_READDIR)/g' \
155 -e 's/@''GNULIB_REWINDDIR''@/$(GNULIB_REWINDDIR)/g' \
156 -e 's/@''GNULIB_CLOSEDIR''@/$(GNULIB_CLOSEDIR)/g' \
157 -e 's/@''GNULIB_DIRFD''@/$(GNULIB_DIRFD)/g' \
158 -e 's/@''GNULIB_FDOPENDIR''@/$(GNULIB_FDOPENDIR)/g' \
159 -e 's/@''GNULIB_SCANDIR''@/$(GNULIB_SCANDIR)/g' \
160 -e 's/@''GNULIB_ALPHASORT''@/$(GNULIB_ALPHASORT)/g' \
161 -e 's/@''HAVE_OPENDIR''@/$(HAVE_OPENDIR)/g' \
162 -e 's/@''HAVE_READDIR''@/$(HAVE_READDIR)/g' \
163 -e 's/@''HAVE_REWINDDIR''@/$(HAVE_REWINDDIR)/g' \
164 -e 's/@''HAVE_CLOSEDIR''@/$(HAVE_CLOSEDIR)/g' \
165 -e 's|@''HAVE_DECL_DIRFD''@|$(HAVE_DECL_DIRFD)|g' \
166 -e 's|@''HAVE_DECL_FDOPENDIR''@|$(HAVE_DECL_FDOPENDIR)|g' \
167 -e 's|@''HAVE_FDOPENDIR''@|$(HAVE_FDOPENDIR)|g' \
168 -e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \
169 -e 's|@''HAVE_ALPHASORT''@|$(HAVE_ALPHASORT)|g' \
170 -e 's|@''REPLACE_OPENDIR''@|$(REPLACE_OPENDIR)|g' \
171 -e 's|@''REPLACE_CLOSEDIR''@|$(REPLACE_CLOSEDIR)|g' \
172 -e 's|@''REPLACE_DIRFD''@|$(REPLACE_DIRFD)|g' \
173 -e 's|@''REPLACE_FDOPENDIR''@|$(REPLACE_FDOPENDIR)|g' \
174 -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
175 -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
176 -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
177 < $(srcdir)/dirent.in.h; \
178 } > $@-t && \
179 mv $@-t $@
180MOSTLYCLEANFILES += dirent.h dirent.h-t
181
182EXTRA_DIST += dirent.in.h
183
184## end gnulib module dirent
185
127## begin gnulib module dosname 186## begin gnulib module dosname
128 187
129if gl_GNULIB_ENABLED_dosname 188if gl_GNULIB_ENABLED_dosname
@@ -238,6 +297,15 @@ EXTRA_DIST += fcntl.in.h
238 297
239## end gnulib module fcntl-h 298## end gnulib module fcntl-h
240 299
300## begin gnulib module fdopendir
301
302
303EXTRA_DIST += fdopendir.c
304
305EXTRA_libgnu_a_SOURCES += fdopendir.c
306
307## end gnulib module fdopendir
308
241## begin gnulib module filemode 309## begin gnulib module filemode
242 310
243libgnu_a_SOURCES += filemode.c 311libgnu_a_SOURCES += filemode.c
@@ -255,6 +323,15 @@ EXTRA_libgnu_a_SOURCES += fpending.c
255 323
256## end gnulib module fpending 324## end gnulib module fpending
257 325
326## begin gnulib module fstatat
327
328
329EXTRA_DIST += at-func.c fstatat.c
330
331EXTRA_libgnu_a_SOURCES += at-func.c fstatat.c
332
333## end gnulib module fstatat
334
258## begin gnulib module getgroups 335## begin gnulib module getgroups
259 336
260if gl_GNULIB_ENABLED_getgroups 337if gl_GNULIB_ENABLED_getgroups
@@ -412,6 +489,15 @@ EXTRA_libgnu_a_SOURCES += mktime.c
412 489
413## end gnulib module mktime 490## end gnulib module mktime
414 491
492## begin gnulib module openat-h
493
494if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7
495
496endif
497EXTRA_DIST += openat.h
498
499## end gnulib module openat-h
500
415## begin gnulib module pathmax 501## begin gnulib module pathmax
416 502
417if gl_GNULIB_ENABLED_pathmax 503if gl_GNULIB_ENABLED_pathmax
@@ -457,6 +543,15 @@ EXTRA_libgnu_a_SOURCES += readlink.c
457 543
458## end gnulib module readlink 544## end gnulib module readlink
459 545
546## begin gnulib module readlinkat
547
548
549EXTRA_DIST += at-func.c readlinkat.c
550
551EXTRA_libgnu_a_SOURCES += at-func.c readlinkat.c
552
553## end gnulib module readlinkat
554
460## begin gnulib module root-uid 555## begin gnulib module root-uid
461 556
462if gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c 557if 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 @@
1/* Respond to a save- or restore-cwd failure.
2 This should never happen with Emacs. */
3#include <config.h>
4#include "openat.h"
5void openat_save_fail (int errnum) { abort (); }
6void 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 @@
1/* Internals for openat-like functions.
2
3 Copyright (C) 2005-2006, 2009-2013 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18/* written by Jim Meyering */
19
20#ifndef _GL_HEADER_OPENAT_PRIV
21#define _GL_HEADER_OPENAT_PRIV
22
23#include <errno.h>
24#include <limits.h>
25#include <stdlib.h>
26
27/* Maximum number of bytes that it is safe to allocate as a single
28 array on the stack, and that is known as a compile-time constant.
29 The assumption is that we'll touch the array very quickly, or a
30 temporary very near the array, provoking an out-of-memory trap. On
31 some operating systems, there is only one guard page for the stack,
32 and a page size can be as small as 4096 bytes. Subtract 64 in the
33 hope that this will let the compiler touch a nearby temporary and
34 provoke a trap. */
35#define SAFER_ALLOCA_MAX (4096 - 64)
36
37#define SAFER_ALLOCA(m) ((m) < SAFER_ALLOCA_MAX ? (m) : SAFER_ALLOCA_MAX)
38
39#if defined PATH_MAX
40# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (PATH_MAX)
41#elif defined _XOPEN_PATH_MAX
42# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (_XOPEN_PATH_MAX)
43#else
44# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (1024)
45#endif
46
47char *openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file);
48
49/* Trying to access a BUILD_PROC_NAME file will fail on systems without
50 /proc support, and even on systems *with* ProcFS support. Return
51 nonzero if the failure may be legitimate, e.g., because /proc is not
52 readable, or the particular .../fd/N directory is not present. */
53#define EXPECTED_ERRNO(Errno) \
54 ((Errno) == ENOTDIR || (Errno) == ENOENT \
55 || (Errno) == EPERM || (Errno) == EACCES \
56 || (Errno) == ENOSYS /* Solaris 8 */ \
57 || (Errno) == EOPNOTSUPP /* FreeBSD */)
58
59/* Wrapper function shared among linkat and renameat. */
60int at_func2 (int fd1, char const *file1,
61 int fd2, char const *file2,
62 int (*func) (char const *file1, char const *file2));
63
64#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 @@
1/* Create /proc/self/fd-related names for subfiles of open directories.
2
3 Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18/* Written by Paul Eggert. */
19
20#include <config.h>
21
22#include "openat-priv.h"
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <fcntl.h>
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32
33#include "intprops.h"
34
35/* The results of open() in this file are not used with fchdir,
36 and we do not leak fds to any single-threaded code that could use stdio,
37 therefore save some unnecessary work in fchdir.c.
38 FIXME - if the kernel ever adds support for multi-thread safety for
39 avoiding standard fds, then we should use open_safer. */
40#undef open
41#undef close
42
43#define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/%s"
44
45#define PROC_SELF_FD_NAME_SIZE_BOUND(len) \
46 (sizeof PROC_SELF_FD_FORMAT - sizeof "%d%s" \
47 + INT_STRLEN_BOUND (int) + (len) + 1)
48
49
50/* Set BUF to the expansion of PROC_SELF_FD_FORMAT, using FD and FILE
51 respectively for %d and %s. If successful, return BUF if the
52 result fits in BUF, dynamically allocated memory otherwise. But
53 return NULL if /proc is not reliable, either because the operating
54 system support is lacking or because memory is low. */
55char *
56openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file)
57{
58 static int proc_status = 0;
59
60 /* Make sure the caller gets ENOENT when appropriate. */
61 if (!*file)
62 {
63 buf[0] = '\0';
64 return buf;
65 }
66
67 if (! proc_status)
68 {
69 /* Set PROC_STATUS to a positive value if /proc/self/fd is
70 reliable, and a negative value otherwise. Solaris 10
71 /proc/self/fd mishandles "..", and any file name might expand
72 to ".." after symbolic link expansion, so avoid /proc/self/fd
73 if it mishandles "..". Solaris 10 has openat, but this
74 problem is exhibited on code that built on Solaris 8 and
75 running on Solaris 10. */
76
77 int proc_self_fd = open ("/proc/self/fd",
78 O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
79 if (proc_self_fd < 0)
80 proc_status = -1;
81 else
82 {
83 /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
84 number of a file descriptor open on /proc/self/fd. On Linux,
85 that name resolves to /proc/self/fd, which was opened above.
86 However, on Solaris, it may resolve to /proc/self/fd/fd, which
87 cannot exist, since all names in /proc/self/fd are numeric. */
88 char dotdot_buf[PROC_SELF_FD_NAME_SIZE_BOUND (sizeof "../fd" - 1)];
89 sprintf (dotdot_buf, PROC_SELF_FD_FORMAT, proc_self_fd, "../fd");
90 proc_status = access (dotdot_buf, F_OK) ? -1 : 1;
91 close (proc_self_fd);
92 }
93 }
94
95 if (proc_status < 0)
96 return NULL;
97 else
98 {
99 size_t bufsize = PROC_SELF_FD_NAME_SIZE_BOUND (strlen (file));
100 char *result = buf;
101 if (OPENAT_BUFFER_SIZE < bufsize)
102 {
103 result = malloc (bufsize);
104 if (! result)
105 return NULL;
106 }
107 sprintf (result, PROC_SELF_FD_FORMAT, fd, file);
108 return result;
109 }
110}
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 @@
1/* provide a replacement openat function
2 Copyright (C) 2004-2006, 2008-2013 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17/* written by Jim Meyering */
18
19#ifndef _GL_HEADER_OPENAT
20#define _GL_HEADER_OPENAT
21
22#include <fcntl.h>
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <stdbool.h>
28
29_GL_INLINE_HEADER_BEGIN
30
31#if !HAVE_OPENAT
32
33int openat_permissive (int fd, char const *file, int flags, mode_t mode,
34 int *cwd_errno);
35bool openat_needs_fchdir (void);
36
37#else
38
39# define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \
40 openat (Fd, File, Flags, Mode)
41# define openat_needs_fchdir() false
42
43#endif
44
45_Noreturn void openat_restore_fail (int);
46_Noreturn void openat_save_fail (int);
47
48/* Using these function names makes application code
49 slightly more readable than it would be with
50 fchownat (..., 0) or fchownat (..., AT_SYMLINK_NOFOLLOW). */
51
52#if GNULIB_FCHOWNAT
53
54# ifndef FCHOWNAT_INLINE
55# define FCHOWNAT_INLINE _GL_INLINE
56# endif
57
58FCHOWNAT_INLINE int
59chownat (int fd, char const *file, uid_t owner, gid_t group)
60{
61 return fchownat (fd, file, owner, group, 0);
62}
63
64FCHOWNAT_INLINE int
65lchownat (int fd, char const *file, uid_t owner, gid_t group)
66{
67 return fchownat (fd, file, owner, group, AT_SYMLINK_NOFOLLOW);
68}
69
70#endif
71
72#if GNULIB_FCHMODAT
73
74# ifndef FCHMODAT_INLINE
75# define FCHMODAT_INLINE _GL_INLINE
76# endif
77
78FCHMODAT_INLINE int
79chmodat (int fd, char const *file, mode_t mode)
80{
81 return fchmodat (fd, file, mode, 0);
82}
83
84FCHMODAT_INLINE int
85lchmodat (int fd, char const *file, mode_t mode)
86{
87 return fchmodat (fd, file, mode, AT_SYMLINK_NOFOLLOW);
88}
89
90#endif
91
92#if GNULIB_STATAT
93
94# ifndef STATAT_INLINE
95# define STATAT_INLINE _GL_INLINE
96# endif
97
98STATAT_INLINE int
99statat (int fd, char const *name, struct stat *st)
100{
101 return fstatat (fd, name, st, 0);
102}
103
104STATAT_INLINE int
105lstatat (int fd, char const *name, struct stat *st)
106{
107 return fstatat (fd, name, st, AT_SYMLINK_NOFOLLOW);
108}
109
110#endif
111
112/* For now, there are no wrappers named laccessat or leuidaccessat,
113 since gnulib doesn't support faccessat(,AT_SYMLINK_NOFOLLOW) and
114 since access rights on symlinks are of limited utility. Likewise,
115 wrappers are not provided for accessat or euidaccessat, so as to
116 avoid dragging in -lgen on some platforms. */
117
118_GL_INLINE_HEADER_END
119
120#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 @@
1/* Read a symlink relative to an open directory.
2 Copyright (C) 2009-2013 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17/* written by Eric Blake */
18
19#include <config.h>
20
21#include <unistd.h>
22
23/* Gnulib provides a readlink stub for mingw; use it for distinction
24 between EINVAL and ENOENT, rather than always failing with ENOSYS. */
25
26/* POSIX 2008 says that unlike readlink, readlinkat returns 0 for
27 success instead of the buffer length. But this would render
28 readlinkat worthless since readlink does not guarantee a
29 NUL-terminated buffer. Assume this was a bug in POSIX. */
30
31/* Read the contents of symlink FILE into buffer BUF of size LEN, in the
32 directory open on descriptor FD. If possible, do it without changing
33 the working directory. Otherwise, resort to using save_cwd/fchdir,
34 then readlink/restore_cwd. If either the save_cwd or the restore_cwd
35 fails, then give a diagnostic and exit nonzero. */
36
37#define AT_FUNC_NAME readlinkat
38#define AT_FUNC_F1 readlink
39#define AT_FUNC_POST_FILE_PARAM_DECLS , char *buf, size_t len
40#define AT_FUNC_POST_FILE_ARGS , buf, len
41#define AT_FUNC_RESULT ssize_t
42#include "at-func.c"
43#undef AT_FUNC_NAME
44#undef AT_FUNC_F1
45#undef AT_FUNC_POST_FILE_PARAM_DECLS
46#undef AT_FUNC_POST_FILE_ARGS
47#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 @@
1#include <config.h>
2#define SAVE_CWD_INLINE _GL_EXTERN_INLINE
3#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 @@
1/* Do not save and restore the current working directory.
2
3 Copyright 2013 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18/* Gnulib needs to save and restore the current working directory to
19 fully emulate functions like fstatat. But Emacs doesn't care what
20 the current working directory is; it always uses absolute file
21 names. This module replaces the Gnulib module by omitting the code
22 that Emacs does not need. */
23
24#ifndef SAVE_CWD_H
25#define SAVE_CWD_H 1
26
27_GL_INLINE_HEADER_BEGIN
28#ifndef SAVE_CWD_INLINE
29# define SAVE_CWD_INLINE _GL_INLINE
30#endif
31
32struct saved_cwd { int desc; };
33
34SAVE_CWD_INLINE int
35save_cwd (struct saved_cwd *cwd)
36{
37 cwd->desc = -1;
38 return 0;
39}
40
41SAVE_CWD_INLINE int restore_cwd (struct saved_cwd const *cwd) { return 0; }
42SAVE_CWD_INLINE void free_cwd (struct saved_cwd *cwd) { }
43
44_GL_INLINE_HEADER_END
45
46#endif