aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2013-01-31 22:30:51 -0800
committerPaul Eggert2013-01-31 22:30:51 -0800
commit8654f9d7d6d7c3ee97232a34a40250dcbc57af8e (patch)
treeff488081b57dabdfe7e85c11a34e8ac522b5bdb4
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
-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 @@
12013-02-01 Paul Eggert <eggert@cs.ucla.edu>
2
3 Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
4 On my host, this speeds up directory-files-and-attributes by a
5 factor of 3, when applied to Emacs's src directory.
6 These functions are standardized by POSIX and are common these
7 days; fall back on a (slower) gnulib implementation if the host
8 is too old to supply them.
9 * .bzrignore: Add lib/dirent.h.
10 * lib/Makefile.am (libgnu_a_SOURCES): Add openat-die.c, save-cwd.c.
11 * lib/careadlinkat.c, lib/careadlinkat.h: Merge from gnulib,
12 incorporating: 2013-01-29 careadlinkat: do not provide careadlinkatcwd.
13 * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
14 * lib/dirent.in.h, lib/fdopendir.c, lib/fstatat.c, lib/openat-priv.h:
15 * lib/openat-proc.c, lib/openat.h, m4/dirent_h.m4, m4/fdopendir.m4:
16 * m4/fstatat.m4: New files, from gnulib.
17 * lib/openat-die.c, lib/save-cwd.c, lib/save-cwd.h: New files.
18 These last three are specific to Emacs and are not copied from gnulib.
19 They are simpler than the gnulib versions and are tuned for Emacs.
20
12013-02-01 Glenn Morris <rgm@gnu.org> 212013-02-01 Glenn Morris <rgm@gnu.org>
2 22
3 * make-dist: Only README files exist in lisp/ now, not README*. 23 * 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 @@
12013-02-01 Paul Eggert <eggert@cs.ucla.edu>
2
3 Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
4 * merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat.
5 (GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h.
6 Avoid dup, open, opendir.
7
12013-01-15 Dmitry Antipov <dmantipov@yandex.ru> 82013-01-15 Dmitry Antipov <dmantipov@yandex.ru>
2 9
3 * coccinelle/xsave.cocci: Semantic patch to adjust users of 10 * 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='
29 alloca-opt c-ctype c-strcase 29 alloca-opt c-ctype c-strcase
30 careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 30 careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
31 dtoastr dtotimespec dup2 environ execinfo faccessat 31 dtoastr dtotimespec dup2 environ execinfo faccessat
32 fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday 32 fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday
33 ignore-value intprops largefile lstat 33 ignore-value intprops largefile lstat
34 manywarnings mktime pselect pthread_sigmask putenv readlink 34 manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat
35 sig2str socklen stat-time stdalign stdarg stdbool stdio 35 sig2str socklen stat-time stdalign stdarg stdbool stdio
36 strftime strtoimax strtoumax symlink sys_stat 36 strftime strtoimax strtoumax symlink sys_stat
37 sys_time time timer-time timespec-add timespec-sub unsetenv utimens 37 sys_time time timer-time timespec-add timespec-sub unsetenv utimens
@@ -39,10 +39,10 @@ GNULIB_MODULES='
39' 39'
40 40
41GNULIB_TOOL_FLAGS=' 41GNULIB_TOOL_FLAGS='
42 --avoid=at-internal 42 --avoid=dup
43 --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat 43 --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat
44 --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow 44 --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
45 --avoid=openat-die --avoid=openat-h 45 --avoid=open --avoid=openat-die --avoid=opendir
46 --avoid=raise 46 --avoid=raise
47 --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types 47 --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types
48 --avoid=threadlib 48 --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)
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
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 @@
1# dirent_h.m4 serial 16
2dnl Copyright (C) 2008-2013 Free Software Foundation, Inc.
3dnl This file is free software; the Free Software Foundation
4dnl gives unlimited permission to copy and/or distribute it,
5dnl with or without modifications, as long as this notice is preserved.
6
7dnl Written by Bruno Haible.
8
9AC_DEFUN([gl_DIRENT_H],
10[
11 dnl Use AC_REQUIRE here, so that the default behavior below is expanded
12 dnl once only, before all statements that occur in other macros.
13 AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
14
15 dnl <dirent.h> is always overridden, because of GNULIB_POSIXCHECK.
16 gl_CHECK_NEXT_HEADERS([dirent.h])
17 if test $ac_cv_header_dirent_h = yes; then
18 HAVE_DIRENT_H=1
19 else
20 HAVE_DIRENT_H=0
21 fi
22 AC_SUBST([HAVE_DIRENT_H])
23
24 dnl Check for declarations of anything we want to poison if the
25 dnl corresponding gnulib module is not in use.
26 gl_WARN_ON_USE_PREPARE([[#include <dirent.h>
27 ]], [alphasort closedir dirfd fdopendir opendir readdir rewinddir scandir])
28])
29
30AC_DEFUN([gl_DIRENT_MODULE_INDICATOR],
31[
32 dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
33 AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
34 gl_MODULE_INDICATOR_SET_VARIABLE([$1])
35 dnl Define it also as a C macro, for the benefit of the unit tests.
36 gl_MODULE_INDICATOR_FOR_TESTS([$1])
37])
38
39AC_DEFUN([gl_DIRENT_H_DEFAULTS],
40[
41 AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl for REPLACE_FCHDIR
42 GNULIB_OPENDIR=0; AC_SUBST([GNULIB_OPENDIR])
43 GNULIB_READDIR=0; AC_SUBST([GNULIB_READDIR])
44 GNULIB_REWINDDIR=0; AC_SUBST([GNULIB_REWINDDIR])
45 GNULIB_CLOSEDIR=0; AC_SUBST([GNULIB_CLOSEDIR])
46 GNULIB_DIRFD=0; AC_SUBST([GNULIB_DIRFD])
47 GNULIB_FDOPENDIR=0; AC_SUBST([GNULIB_FDOPENDIR])
48 GNULIB_SCANDIR=0; AC_SUBST([GNULIB_SCANDIR])
49 GNULIB_ALPHASORT=0; AC_SUBST([GNULIB_ALPHASORT])
50 dnl Assume proper GNU behavior unless another module says otherwise.
51 HAVE_OPENDIR=1; AC_SUBST([HAVE_OPENDIR])
52 HAVE_READDIR=1; AC_SUBST([HAVE_READDIR])
53 HAVE_REWINDDIR=1; AC_SUBST([HAVE_REWINDDIR])
54 HAVE_CLOSEDIR=1; AC_SUBST([HAVE_CLOSEDIR])
55 HAVE_DECL_DIRFD=1; AC_SUBST([HAVE_DECL_DIRFD])
56 HAVE_DECL_FDOPENDIR=1;AC_SUBST([HAVE_DECL_FDOPENDIR])
57 HAVE_FDOPENDIR=1; AC_SUBST([HAVE_FDOPENDIR])
58 HAVE_SCANDIR=1; AC_SUBST([HAVE_SCANDIR])
59 HAVE_ALPHASORT=1; AC_SUBST([HAVE_ALPHASORT])
60 REPLACE_OPENDIR=0; AC_SUBST([REPLACE_OPENDIR])
61 REPLACE_CLOSEDIR=0; AC_SUBST([REPLACE_CLOSEDIR])
62 REPLACE_DIRFD=0; AC_SUBST([REPLACE_DIRFD])
63 REPLACE_FDOPENDIR=0; AC_SUBST([REPLACE_FDOPENDIR])
64])
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 @@
1# serial 10
2# See if we need to provide fdopendir.
3
4dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
5dnl This file is free software; the Free Software Foundation
6dnl gives unlimited permission to copy and/or distribute it,
7dnl with or without modifications, as long as this notice is preserved.
8
9# Written by Eric Blake.
10
11AC_DEFUN([gl_FUNC_FDOPENDIR],
12[
13 AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
14 AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
15
16 AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
17
18 dnl FreeBSD 7.3 has the function, but failed to declare it.
19 AC_CHECK_DECLS([fdopendir], [], [HAVE_DECL_FDOPENDIR=0], [[
20#include <dirent.h>
21 ]])
22 AC_CHECK_FUNCS_ONCE([fdopendir])
23 if test $ac_cv_func_fdopendir = no; then
24 HAVE_FDOPENDIR=0
25 else
26 AC_CACHE_CHECK([whether fdopendir works],
27 [gl_cv_func_fdopendir_works],
28 [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
29#include <dirent.h>
30#include <fcntl.h>
31#include <unistd.h>
32#if !HAVE_DECL_FDOPENDIR
33extern
34# ifdef __cplusplus
35"C"
36# endif
37DIR *fdopendir (int);
38#endif
39]], [int result = 0;
40 int fd = open ("conftest.c", O_RDONLY);
41 if (fd < 0) result |= 1;
42 if (fdopendir (fd)) result |= 2;
43 if (close (fd)) result |= 4;
44 return result;])],
45 [gl_cv_func_fdopendir_works=yes],
46 [gl_cv_func_fdopendir_works=no],
47 [case "$host_os" in
48 # Guess yes on glibc systems.
49 *-gnu*) gl_cv_func_fdopendir_works="guessing yes" ;;
50 # If we don't know, assume the worst.
51 *) gl_cv_func_fdopendir_works="guessing no" ;;
52 esac
53 ])])
54 case "$gl_cv_func_fdopendir_works" in
55 *yes) ;;
56 *)
57 REPLACE_FDOPENDIR=1
58 ;;
59 esac
60 fi
61])
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 @@
1# fstatat.m4 serial 3
2dnl Copyright (C) 2004-2013 Free Software Foundation, Inc.
3dnl This file is free software; the Free Software Foundation
4dnl gives unlimited permission to copy and/or distribute it,
5dnl with or without modifications, as long as this notice is preserved.
6
7# Written by Jim Meyering.
8
9# If we have the fstatat function, and it has the bug (in AIX 7.1)
10# that it does not fill in st_size correctly, use the replacement function.
11AC_DEFUN([gl_FUNC_FSTATAT],
12[
13 AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
14 AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
15 AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK])
16 AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
17 AC_CHECK_FUNCS_ONCE([fstatat])
18
19 if test $ac_cv_func_fstatat = no; then
20 HAVE_FSTATAT=0
21 else
22 dnl Test for an AIX 7.1 bug; see
23 dnl <http://lists.gnu.org/archive/html/bug-tar/2011-09/msg00015.html>.
24 AC_CACHE_CHECK([whether fstatat (..., 0) works],
25 [gl_cv_func_fstatat_zero_flag],
26 [AC_RUN_IFELSE(
27 [AC_LANG_SOURCE(
28 [[
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 int
32 main (void)
33 {
34 struct stat a;
35 return fstatat (AT_FDCWD, ".", &a, 0) != 0;
36 }
37 ]])],
38 [gl_cv_func_fstatat_zero_flag=yes],
39 [gl_cv_func_fstatat_zero_flag=no],
40 [case "$host_os" in
41 aix*) gl_cv_func_fstatat_zero_flag="guessing no";;
42 *) gl_cv_func_fstatat_zero_flag="guessing yes";;
43 esac
44 ])
45 ])
46
47 case $gl_cv_func_fstatat_zero_flag+$gl_cv_func_lstat_dereferences_slashed_symlink in
48 *yes+*yes) ;;
49 *) REPLACE_FSTATAT=1
50 case $gl_cv_func_fstatat_zero_flag in
51 *yes)
52 AC_DEFINE([HAVE_WORKING_FSTATAT_ZERO_FLAG], [1],
53 [Define to 1 if fstatat (..., 0) works.
54 For example, it does not work in AIX 7.1.])
55 ;;
56 esac
57 ;;
58 esac
59 fi
60])
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],
40 AC_REQUIRE([gl_PROG_AR_RANLIB]) 40 AC_REQUIRE([gl_PROG_AR_RANLIB])
41 # Code from module alloca-opt: 41 # Code from module alloca-opt:
42 # Code from module allocator: 42 # Code from module allocator:
43 # Code from module at-internal:
43 # Code from module c-ctype: 44 # Code from module c-ctype:
44 # Code from module c-strcase: 45 # Code from module c-strcase:
45 # Code from module careadlinkat: 46 # Code from module careadlinkat:
@@ -49,6 +50,7 @@ AC_DEFUN([gl_EARLY],
49 # Code from module crypto/sha1: 50 # Code from module crypto/sha1:
50 # Code from module crypto/sha256: 51 # Code from module crypto/sha256:
51 # Code from module crypto/sha512: 52 # Code from module crypto/sha512:
53 # Code from module dirent:
52 # Code from module dosname: 54 # Code from module dosname:
53 # Code from module dtoastr: 55 # Code from module dtoastr:
54 # Code from module dtotimespec: 56 # Code from module dtotimespec:
@@ -61,8 +63,10 @@ AC_DEFUN([gl_EARLY],
61 # Code from module extern-inline: 63 # Code from module extern-inline:
62 # Code from module faccessat: 64 # Code from module faccessat:
63 # Code from module fcntl-h: 65 # Code from module fcntl-h:
66 # Code from module fdopendir:
64 # Code from module filemode: 67 # Code from module filemode:
65 # Code from module fpending: 68 # Code from module fpending:
69 # Code from module fstatat:
66 # Code from module getgroups: 70 # Code from module getgroups:
67 # Code from module getloadavg: 71 # Code from module getloadavg:
68 # Code from module getopt-gnu: 72 # Code from module getopt-gnu:
@@ -82,11 +86,13 @@ AC_DEFUN([gl_EARLY],
82 # Code from module mktime: 86 # Code from module mktime:
83 # Code from module multiarch: 87 # Code from module multiarch:
84 # Code from module nocrash: 88 # Code from module nocrash:
89 # Code from module openat-h:
85 # Code from module pathmax: 90 # Code from module pathmax:
86 # Code from module pselect: 91 # Code from module pselect:
87 # Code from module pthread_sigmask: 92 # Code from module pthread_sigmask:
88 # Code from module putenv: 93 # Code from module putenv:
89 # Code from module readlink: 94 # Code from module readlink:
95 # Code from module readlinkat:
90 # Code from module root-uid: 96 # Code from module root-uid:
91 # Code from module sig2str: 97 # Code from module sig2str:
92 # Code from module signal-h: 98 # Code from module signal-h:
@@ -159,6 +165,7 @@ AC_DEFUN([gl_INIT],
159 gl_SHA1 165 gl_SHA1
160 gl_SHA256 166 gl_SHA256
161 gl_SHA512 167 gl_SHA512
168 gl_DIRENT_H
162 AC_REQUIRE([gl_C99_STRTOLD]) 169 AC_REQUIRE([gl_C99_STRTOLD])
163 gl_FUNC_DUP2 170 gl_FUNC_DUP2
164 if test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1; then 171 if test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1; then
@@ -178,12 +185,23 @@ AC_DEFUN([gl_INIT],
178 gl_MODULE_INDICATOR([faccessat]) 185 gl_MODULE_INDICATOR([faccessat])
179 gl_UNISTD_MODULE_INDICATOR([faccessat]) 186 gl_UNISTD_MODULE_INDICATOR([faccessat])
180 gl_FCNTL_H 187 gl_FCNTL_H
188 gl_FUNC_FDOPENDIR
189 if test $HAVE_FDOPENDIR = 0 || test $REPLACE_FDOPENDIR = 1; then
190 AC_LIBOBJ([fdopendir])
191 fi
192 gl_DIRENT_MODULE_INDICATOR([fdopendir])
193 gl_MODULE_INDICATOR([fdopendir])
181 gl_FILEMODE 194 gl_FILEMODE
182 gl_FUNC_FPENDING 195 gl_FUNC_FPENDING
183 if test $ac_cv_func___fpending = no; then 196 if test $ac_cv_func___fpending = no; then
184 AC_LIBOBJ([fpending]) 197 AC_LIBOBJ([fpending])
185 gl_PREREQ_FPENDING 198 gl_PREREQ_FPENDING
186 fi 199 fi
200 gl_FUNC_FSTATAT
201 if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
202 AC_LIBOBJ([fstatat])
203 fi
204 gl_SYS_STAT_MODULE_INDICATOR([fstatat])
187 gl_GETLOADAVG 205 gl_GETLOADAVG
188 if test $HAVE_GETLOADAVG = 0; then 206 if test $HAVE_GETLOADAVG = 0; then
189 AC_LIBOBJ([getloadavg]) 207 AC_LIBOBJ([getloadavg])
@@ -253,6 +271,11 @@ AC_DEFUN([gl_INIT],
253 gl_PREREQ_READLINK 271 gl_PREREQ_READLINK
254 fi 272 fi
255 gl_UNISTD_MODULE_INDICATOR([readlink]) 273 gl_UNISTD_MODULE_INDICATOR([readlink])
274 gl_FUNC_READLINKAT
275 if test $HAVE_READLINKAT = 0; then
276 AC_LIBOBJ([readlinkat])
277 fi
278 gl_UNISTD_MODULE_INDICATOR([readlinkat])
256 gl_FUNC_SIG2STR 279 gl_FUNC_SIG2STR
257 if test $ac_cv_func_sig2str = no; then 280 if test $ac_cv_func_sig2str = no; then
258 AC_LIBOBJ([sig2str]) 281 AC_LIBOBJ([sig2str])
@@ -311,11 +334,13 @@ AC_DEFUN([gl_INIT],
311 fi 334 fi
312 gl_STDLIB_MODULE_INDICATOR([unsetenv]) 335 gl_STDLIB_MODULE_INDICATOR([unsetenv])
313 gl_UTIMENS 336 gl_UTIMENS
337 gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false
314 gl_gnulib_enabled_dosname=false 338 gl_gnulib_enabled_dosname=false
315 gl_gnulib_enabled_euidaccess=false 339 gl_gnulib_enabled_euidaccess=false
316 gl_gnulib_enabled_getgroups=false 340 gl_gnulib_enabled_getgroups=false
317 gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false 341 gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
318 gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false 342 gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
343 gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false
319 gl_gnulib_enabled_pathmax=false 344 gl_gnulib_enabled_pathmax=false
320 gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false 345 gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
321 gl_gnulib_enabled_stat=false 346 gl_gnulib_enabled_stat=false
@@ -323,6 +348,13 @@ AC_DEFUN([gl_INIT],
323 gl_gnulib_enabled_strtoull=false 348 gl_gnulib_enabled_strtoull=false
324 gl_gnulib_enabled_verify=false 349 gl_gnulib_enabled_verify=false
325 gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false 350 gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
351 func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b ()
352 {
353 if ! $gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b; then
354 AC_LIBOBJ([openat-proc])
355 gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=true
356 fi
357 }
326 func_gl_gnulib_m4code_dosname () 358 func_gl_gnulib_m4code_dosname ()
327 { 359 {
328 if ! $gl_gnulib_enabled_dosname; then 360 if ! $gl_gnulib_enabled_dosname; then
@@ -385,6 +417,12 @@ AC_DEFUN([gl_INIT],
385 fi 417 fi
386 fi 418 fi
387 } 419 }
420 func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 ()
421 {
422 if ! $gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7; then
423 gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=true
424 fi
425 }
388 func_gl_gnulib_m4code_pathmax () 426 func_gl_gnulib_m4code_pathmax ()
389 { 427 {
390 if ! $gl_gnulib_enabled_pathmax; then 428 if ! $gl_gnulib_enabled_pathmax; then
@@ -456,11 +494,29 @@ AC_DEFUN([gl_INIT],
456 fi 494 fi
457 } 495 }
458 if test $HAVE_FACCESSAT = 0; then 496 if test $HAVE_FACCESSAT = 0; then
497 func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
498 fi
499 if test $HAVE_FACCESSAT = 0; then
459 func_gl_gnulib_m4code_dosname 500 func_gl_gnulib_m4code_dosname
460 fi 501 fi
461 if test $HAVE_FACCESSAT = 0; then 502 if test $HAVE_FACCESSAT = 0; then
462 func_gl_gnulib_m4code_euidaccess 503 func_gl_gnulib_m4code_euidaccess
463 fi 504 fi
505 if test $HAVE_FACCESSAT = 0; then
506 func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
507 fi
508 if test $HAVE_FDOPENDIR = 0; then
509 func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
510 fi
511 if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
512 func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
513 fi
514 if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
515 func_gl_gnulib_m4code_dosname
516 fi
517 if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
518 func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
519 fi
464 if test $REPLACE_GETOPT = 1; then 520 if test $REPLACE_GETOPT = 1; then
465 func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 521 func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
466 fi 522 fi
@@ -473,6 +529,15 @@ AC_DEFUN([gl_INIT],
473 if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then 529 if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then
474 func_gl_gnulib_m4code_stat 530 func_gl_gnulib_m4code_stat
475 fi 531 fi
532 if test $HAVE_READLINKAT = 0; then
533 func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
534 fi
535 if test $HAVE_READLINKAT = 0; then
536 func_gl_gnulib_m4code_dosname
537 fi
538 if test $HAVE_READLINKAT = 0; then
539 func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
540 fi
476 if { test $HAVE_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then 541 if { test $HAVE_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then
477 func_gl_gnulib_m4code_strtoll 542 func_gl_gnulib_m4code_strtoll
478 fi 543 fi
@@ -486,11 +551,13 @@ AC_DEFUN([gl_INIT],
486 func_gl_gnulib_m4code_verify 551 func_gl_gnulib_m4code_verify
487 fi 552 fi
488 m4_pattern_allow([^gl_GNULIB_ENABLED_]) 553 m4_pattern_allow([^gl_GNULIB_ENABLED_])
554 AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b])
489 AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname]) 555 AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
490 AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess]) 556 AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess])
491 AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups]) 557 AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
492 AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36]) 558 AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
493 AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1]) 559 AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
560 AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7])
494 AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax]) 561 AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
495 AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) 562 AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
496 AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat]) 563 AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
@@ -656,6 +723,7 @@ AC_DEFUN([gl_FILE_LIST], [
656 lib/careadlinkat.h 723 lib/careadlinkat.h
657 lib/close-stream.c 724 lib/close-stream.c
658 lib/close-stream.h 725 lib/close-stream.h
726 lib/dirent.in.h
659 lib/dosname.h 727 lib/dosname.h
660 lib/dtoastr.c 728 lib/dtoastr.c
661 lib/dtotimespec.c 729 lib/dtotimespec.c
@@ -665,10 +733,12 @@ AC_DEFUN([gl_FILE_LIST], [
665 lib/execinfo.in.h 733 lib/execinfo.in.h
666 lib/faccessat.c 734 lib/faccessat.c
667 lib/fcntl.in.h 735 lib/fcntl.in.h
736 lib/fdopendir.c
668 lib/filemode.c 737 lib/filemode.c
669 lib/filemode.h 738 lib/filemode.h
670 lib/fpending.c 739 lib/fpending.c
671 lib/fpending.h 740 lib/fpending.h
741 lib/fstatat.c
672 lib/ftoastr.c 742 lib/ftoastr.c
673 lib/ftoastr.h 743 lib/ftoastr.h
674 lib/getgroups.c 744 lib/getgroups.c
@@ -689,11 +759,15 @@ AC_DEFUN([gl_FILE_LIST], [
689 lib/md5.h 759 lib/md5.h
690 lib/mktime-internal.h 760 lib/mktime-internal.h
691 lib/mktime.c 761 lib/mktime.c
762 lib/openat-priv.h
763 lib/openat-proc.c
764 lib/openat.h
692 lib/pathmax.h 765 lib/pathmax.h
693 lib/pselect.c 766 lib/pselect.c
694 lib/pthread_sigmask.c 767 lib/pthread_sigmask.c
695 lib/putenv.c 768 lib/putenv.c
696 lib/readlink.c 769 lib/readlink.c
770 lib/readlinkat.c
697 lib/root-uid.h 771 lib/root-uid.h
698 lib/sha1.c 772 lib/sha1.c
699 lib/sha1.h 773 lib/sha1.h
@@ -746,6 +820,7 @@ AC_DEFUN([gl_FILE_LIST], [
746 m4/c-strtod.m4 820 m4/c-strtod.m4
747 m4/clock_time.m4 821 m4/clock_time.m4
748 m4/close-stream.m4 822 m4/close-stream.m4
823 m4/dirent_h.m4
749 m4/dup2.m4 824 m4/dup2.m4
750 m4/environ.m4 825 m4/environ.m4
751 m4/euidaccess.m4 826 m4/euidaccess.m4
@@ -755,8 +830,10 @@ AC_DEFUN([gl_FILE_LIST], [
755 m4/faccessat.m4 830 m4/faccessat.m4
756 m4/fcntl-o.m4 831 m4/fcntl-o.m4
757 m4/fcntl_h.m4 832 m4/fcntl_h.m4
833 m4/fdopendir.m4
758 m4/filemode.m4 834 m4/filemode.m4
759 m4/fpending.m4 835 m4/fpending.m4
836 m4/fstatat.m4
760 m4/getgroups.m4 837 m4/getgroups.m4
761 m4/getloadavg.m4 838 m4/getloadavg.m4
762 m4/getopt.m4 839 m4/getopt.m4
@@ -780,6 +857,7 @@ AC_DEFUN([gl_FILE_LIST], [
780 m4/pthread_sigmask.m4 857 m4/pthread_sigmask.m4
781 m4/putenv.m4 858 m4/putenv.m4
782 m4/readlink.m4 859 m4/readlink.m4
860 m4/readlinkat.m4
783 m4/setenv.m4 861 m4/setenv.m4
784 m4/sha1.m4 862 m4/sha1.m4
785 m4/sha256.m4 863 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 @@
1# serial 3
2# See if we need to provide readlinkat replacement.
3
4dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
5dnl This file is free software; the Free Software Foundation
6dnl gives unlimited permission to copy and/or distribute it,
7dnl with or without modifications, as long as this notice is preserved.
8
9# Written by Eric Blake.
10
11AC_DEFUN([gl_FUNC_READLINKAT],
12[
13 AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
14 AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
15 AC_CHECK_FUNCS_ONCE([readlinkat])
16 if test $ac_cv_func_readlinkat = no; then
17 HAVE_READLINKAT=0
18 fi
19])
diff --git a/nt/ChangeLog b/nt/ChangeLog
index 8b8628db1e2..b2a481354bc 100644
--- a/nt/ChangeLog
+++ b/nt/ChangeLog
@@ -1,3 +1,9 @@
12013-02-01 Paul Eggert <eggert@cs.ucla.edu>
2
3 Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
4 * inc/sys/stat.h (fstatat):
5 * inc/unistd.h (readlinkat): New decls.
6
12013-01-28 Eli Zaretskii <eliz@gnu.org> 72013-01-28 Eli Zaretskii <eliz@gnu.org>
2 8
3 * inc/dirent.h (opendir): Update prototype. 9 * 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*);
110_CRTIMP int __cdecl __MINGW_NOTHROW chmod (const char*, int); 110_CRTIMP int __cdecl __MINGW_NOTHROW chmod (const char*, int);
111_CRTIMP int __cdecl __MINGW_NOTHROW stat (const char*, struct stat*); 111_CRTIMP int __cdecl __MINGW_NOTHROW stat (const char*, struct stat*);
112_CRTIMP int __cdecl __MINGW_NOTHROW lstat (const char*, struct stat*); 112_CRTIMP int __cdecl __MINGW_NOTHROW lstat (const char*, struct stat*);
113_CRTIMP int __cdecl __MINGW_NOTHROW fstatat (int, char const *,
114 struct stat *, int);
113 115
114#endif /* INC_SYS_STAT_H_ */ 116#endif /* INC_SYS_STAT_H_ */
115
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 @@
12 and whose prototypes are usually found in unistd.h on POSIX 12 and whose prototypes are usually found in unistd.h on POSIX
13 platforms. */ 13 platforms. */
14extern ssize_t readlink (const char *, char *, size_t); 14extern ssize_t readlink (const char *, char *, size_t);
15extern ssize_t readlinkat (int, const char *, char *, size_t);
15extern int symlink (char const *, char const *); 16extern int symlink (char const *, char const *);
16extern int setpgid (pid_t, pid_t); 17extern int setpgid (pid_t, pid_t);
17extern pid_t getpgrp (void); 18extern 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 @@
12013-02-01 Paul Eggert <eggert@cs.ucla.edu>
2
3 Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
4 * conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove.
5 * dired.c: Include <fcntl.h>.
6 (open_directory): New function, which uses open and fdopendir
7 rather than opendir. DOS_NT platforms still use opendir, though.
8 (directory_files_internal, file_name_completion): Use it.
9 (file_attributes): New function, with most of the old Ffile_attributes.
10 (directory_files_internal, Ffile_attributes): Use it.
11 (file_attributes, file_name_completion_stat): First arg is now fd,
12 not dir name. All uses changed. Use fstatat rather than lstat +
13 stat.
14 (file_attributes): Use emacs_readlinkat rather than Ffile_symlink_p.
15 * fileio.c: Include <allocator.h>, <careadlinkat.h>.
16 (emacs_readlinkat): New function, with much of the old
17 Ffile_symlink_p, but with an fd argument for speed.
18 It uses readlinkat rather than careadlinkatcwd, so that it
19 need not assume the working directory.
20 (Ffile_symlink_p): Use it.
21 * filelock.c (current_lock_owner): Use emacs_readlinkat
22 rather than emacs_readlink.
23 * lisp.h (emacs_readlinkat): New decl.
24 (READLINK_BUFSIZE, emacs_readlink): Remove.
25 * sysdep.c: Do not include <allocator.h>, <careadlinkat.h>.
26 (emacs_norealloc_allocator, emacs_readlink): Remove.
27 This stuff is moved to fileio.c.
28 * w32.c (fstatat, readlinkat): New functions.
29 (careadlinkat): Don't check that fd == AT_FDCWD.
30 (careadlinkatcwd): Remove; no longer needed.
31
12013-01-31 Glenn Morris <rgm@gnu.org> 322013-01-31 Glenn Morris <rgm@gnu.org>
2 33
3 * fileio.c (choose_write_coding_system): Make it callable from Lisp. 34 * 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, ...);
182#endif 182#endif
183#endif 183#endif
184 184
185/* Tell gnulib to omit support for openat-related functions having a
186 first argument other than AT_FDCWD. */
187#define GNULIB_SUPPORT_ONLY_AT_FDCWD
188
189#include <string.h> 185#include <string.h>
190#include <stdlib.h> 186#include <stdlib.h>
191 187
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/>. */
30#include <grp.h> 30#include <grp.h>
31 31
32#include <errno.h> 32#include <errno.h>
33#include <fcntl.h>
33#include <unistd.h> 34#include <unistd.h>
34 35
35#include <dirent.h> 36#include <dirent.h>
@@ -54,6 +55,7 @@ static Lisp_Object Qfile_attributes;
54static Lisp_Object Qfile_attributes_lessp; 55static Lisp_Object Qfile_attributes_lessp;
55 56
56static ptrdiff_t scmp (const char *, const char *, ptrdiff_t); 57static ptrdiff_t scmp (const char *, const char *, ptrdiff_t);
58static Lisp_Object file_attributes (int, char const *, Lisp_Object);
57 59
58/* Return the number of bytes in DP's name. */ 60/* Return the number of bytes in DP's name. */
59static ptrdiff_t 61static ptrdiff_t
@@ -66,6 +68,44 @@ dirent_namelen (struct dirent *dp)
66#endif 68#endif
67} 69}
68 70
71static DIR *
72open_directory (char const *name, int *fdp)
73{
74 DIR *d;
75 int fd, opendir_errno;
76
77 block_input ();
78
79#ifdef DOS_NT
80 /* Directories cannot be opened. The emulation assumes that any
81 file descriptor other than AT_FDCWD corresponds to the most
82 recently opened directory. This hack is good enough for Emacs. */
83 fd = 0;
84 d = opendir (name);
85 opendir_errno = errno;
86#else
87 fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
88 if (fd < 0)
89 {
90 opendir_errno = errno;
91 d = 0;
92 }
93 else
94 {
95 d = fdopendir (fd);
96 opendir_errno = errno;
97 if (! d)
98 close (fd);
99 }
100#endif
101
102 unblock_input ();
103
104 *fdp = fd;
105 errno = opendir_errno;
106 return d;
107}
108
69#ifdef WINDOWSNT 109#ifdef WINDOWSNT
70Lisp_Object 110Lisp_Object
71directory_files_internal_w32_unwind (Lisp_Object arg) 111directory_files_internal_w32_unwind (Lisp_Object arg)
@@ -96,6 +136,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
96 Lisp_Object id_format) 136 Lisp_Object id_format)
97{ 137{
98 DIR *d; 138 DIR *d;
139 int fd;
99 ptrdiff_t directory_nbytes; 140 ptrdiff_t directory_nbytes;
100 Lisp_Object list, dirfilename, encoded_directory; 141 Lisp_Object list, dirfilename, encoded_directory;
101 struct re_pattern_buffer *bufp = NULL; 142 struct re_pattern_buffer *bufp = NULL;
@@ -142,9 +183,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
142 /* Now *bufp is the compiled form of MATCH; don't call anything 183 /* Now *bufp is the compiled form of MATCH; don't call anything
143 which might compile a new regexp until we're done with the loop! */ 184 which might compile a new regexp until we're done with the loop! */
144 185
145 block_input (); 186 d = open_directory (SSDATA (dirfilename), &fd);
146 d = opendir (SSDATA (dirfilename));
147 unblock_input ();
148 if (d == NULL) 187 if (d == NULL)
149 report_file_error ("Opening directory", Fcons (directory, Qnil)); 188 report_file_error ("Opening directory", Fcons (directory, Qnil));
150 189
@@ -259,20 +298,9 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
259 298
260 if (attrs) 299 if (attrs)
261 { 300 {
262 /* Construct an expanded filename for the directory entry. 301 Lisp_Object fileattrs
263 Use the decoded names for input to Ffile_attributes. */ 302 = file_attributes (fd, dp->d_name, id_format);
264 Lisp_Object decoded_fullname, fileattrs;
265 struct gcpro gcpro1, gcpro2;
266
267 decoded_fullname = fileattrs = Qnil;
268 GCPRO2 (decoded_fullname, fileattrs);
269
270 /* Both Fexpand_file_name and Ffile_attributes can GC. */
271 decoded_fullname = Fexpand_file_name (name, directory);
272 fileattrs = Ffile_attributes (decoded_fullname, id_format);
273
274 list = Fcons (Fcons (finalname, fileattrs), list); 303 list = Fcons (Fcons (finalname, fileattrs), list);
275 UNGCPRO;
276 } 304 }
277 else 305 else
278 list = Fcons (finalname, list); 306 list = Fcons (finalname, list);
@@ -413,8 +441,7 @@ These are all file names in directory DIRECTORY which begin with FILE. */)
413 return file_name_completion (file, directory, 1, Qnil); 441 return file_name_completion (file, directory, 1, Qnil);
414} 442}
415 443
416static int file_name_completion_stat (Lisp_Object dirname, struct dirent *dp, 444static int file_name_completion_stat (int, struct dirent *, struct stat *);
417 struct stat *st_addr);
418static Lisp_Object Qdefault_directory; 445static Lisp_Object Qdefault_directory;
419 446
420static Lisp_Object 447static Lisp_Object
@@ -422,6 +449,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
422 Lisp_Object predicate) 449 Lisp_Object predicate)
423{ 450{
424 DIR *d; 451 DIR *d;
452 int fd;
425 ptrdiff_t bestmatchsize = 0; 453 ptrdiff_t bestmatchsize = 0;
426 int matchcount = 0; 454 int matchcount = 0;
427 /* If ALL_FLAG is 1, BESTMATCH is the list of all matches, decoded. 455 /* 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,
458 486
459 encoded_dir = ENCODE_FILE (dirname); 487 encoded_dir = ENCODE_FILE (dirname);
460 488
461 block_input (); 489 d = open_directory (SSDATA (Fdirectory_file_name (encoded_dir)), &fd);
462 d = opendir (SSDATA (Fdirectory_file_name (encoded_dir)));
463 unblock_input ();
464 if (!d) 490 if (!d)
465 report_file_error ("Opening directory", Fcons (dirname, Qnil)); 491 report_file_error ("Opening directory", Fcons (dirname, Qnil));
466 492
@@ -495,7 +521,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
495 SCHARS (encoded_file))) 521 SCHARS (encoded_file)))
496 continue; 522 continue;
497 523
498 if (file_name_completion_stat (encoded_dir, dp, &st) < 0) 524 if (file_name_completion_stat (fd, dp, &st) < 0)
499 continue; 525 continue;
500 526
501 directoryp = S_ISDIR (st.st_mode) != 0; 527 directoryp = S_ISDIR (st.st_mode) != 0;
@@ -772,14 +798,9 @@ scmp (const char *s1, const char *s2, ptrdiff_t len)
772} 798}
773 799
774static int 800static int
775file_name_completion_stat (Lisp_Object dirname, struct dirent *dp, 801file_name_completion_stat (int fd, struct dirent *dp, struct stat *st_addr)
776 struct stat *st_addr)
777{ 802{
778 ptrdiff_t len = dirent_namelen (dp);
779 ptrdiff_t pos = SCHARS (dirname);
780 int value; 803 int value;
781 USE_SAFE_ALLOCA;
782 char *fullname = SAFE_ALLOCA (len + pos + 2);
783 804
784#ifdef MSDOS 805#ifdef MSDOS
785 /* Some fields of struct stat are *very* expensive to compute on MS-DOS, 806 /* 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,
792 _djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; 813 _djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
793#endif /* MSDOS */ 814#endif /* MSDOS */
794 815
795 memcpy (fullname, SDATA (dirname), pos);
796 if (!IS_DIRECTORY_SEP (fullname[pos - 1]))
797 fullname[pos++] = DIRECTORY_SEP;
798
799 memcpy (fullname + pos, dp->d_name, len);
800 fullname[pos + len] = 0;
801
802 /* We want to return success if a link points to a nonexistent file, 816 /* We want to return success if a link points to a nonexistent file,
803 but we want to return the status for what the link points to, 817 but we want to return the status for what the link points to,
804 in case it is a directory. */ 818 in case it is a directory. */
805 value = lstat (fullname, st_addr); 819 value = fstatat (fd, dp->d_name, st_addr, AT_SYMLINK_NOFOLLOW);
806 if (value == 0 && S_ISLNK (st_addr->st_mode)) 820 if (value == 0 && S_ISLNK (st_addr->st_mode))
807 stat (fullname, st_addr); 821 fstatat (fd, dp->d_name, st_addr, 0);
808#ifdef MSDOS 822#ifdef MSDOS
809 _djstat_flags = save_djstat_flags; 823 _djstat_flags = save_djstat_flags;
810#endif /* MSDOS */ 824#endif /* MSDOS */
811 SAFE_FREE ();
812 return value; 825 return value;
813} 826}
814 827
@@ -886,18 +899,8 @@ On some FAT-based filesystems, only the date of last access is recorded,
886so last access time will always be midnight of that day. */) 899so last access time will always be midnight of that day. */)
887 (Lisp_Object filename, Lisp_Object id_format) 900 (Lisp_Object filename, Lisp_Object id_format)
888{ 901{
889 Lisp_Object values[12];
890 Lisp_Object encoded; 902 Lisp_Object encoded;
891 struct stat s;
892 int lstat_result;
893
894 /* An array to hold the mode string generated by filemodestring,
895 including its terminating space and null byte. */
896 char modes[sizeof "-rwxr-xr-x "];
897
898 Lisp_Object handler; 903 Lisp_Object handler;
899 struct gcpro gcpro1;
900 char *uname = NULL, *gname = NULL;
901 904
902 filename = Fexpand_file_name (filename, Qnil); 905 filename = Fexpand_file_name (filename, Qnil);
903 906
@@ -913,9 +916,22 @@ so last access time will always be midnight of that day. */)
913 return call3 (handler, Qfile_attributes, filename, id_format); 916 return call3 (handler, Qfile_attributes, filename, id_format);
914 } 917 }
915 918
916 GCPRO1 (filename);
917 encoded = ENCODE_FILE (filename); 919 encoded = ENCODE_FILE (filename);
918 UNGCPRO; 920 return file_attributes (AT_FDCWD, SSDATA (encoded), id_format);
921}
922
923static Lisp_Object
924file_attributes (int fd, char const *name, Lisp_Object id_format)
925{
926 Lisp_Object values[12];
927 struct stat s;
928 int lstat_result;
929
930 /* An array to hold the mode string generated by filemodestring,
931 including its terminating space and null byte. */
932 char modes[sizeof "-rwxr-xr-x "];
933
934 char *uname = NULL, *gname = NULL;
919 935
920#ifdef WINDOWSNT 936#ifdef WINDOWSNT
921 /* We usually don't request accurate owner and group info, because 937 /* 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. */)
925 w32_stat_get_owner_group = 1; 941 w32_stat_get_owner_group = 1;
926#endif 942#endif
927 943
928 lstat_result = lstat (SSDATA (encoded), &s); 944 lstat_result = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW);
929 945
930#ifdef WINDOWSNT 946#ifdef WINDOWSNT
931 w32_stat_get_owner_group = 0; 947 w32_stat_get_owner_group = 0;
@@ -934,7 +950,7 @@ so last access time will always be midnight of that day. */)
934 if (lstat_result < 0) 950 if (lstat_result < 0)
935 return Qnil; 951 return Qnil;
936 952
937 values[0] = (S_ISLNK (s.st_mode) ? Ffile_symlink_p (filename) 953 values[0] = (S_ISLNK (s.st_mode) ? emacs_readlinkat (fd, name)
938 : S_ISDIR (s.st_mode) ? Qt : Qnil); 954 : S_ISDIR (s.st_mode) ? Qt : Qnil);
939 values[1] = make_number (s.st_nlink); 955 values[1] = make_number (s.st_nlink);
940 956
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/>. */
82#endif 82#endif
83 83
84#include "systime.h" 84#include "systime.h"
85#include <allocator.h>
86#include <careadlinkat.h>
85#include <stat-time.h> 87#include <stat-time.h>
86 88
87#ifdef HPUX 89#ifdef HPUX
@@ -2759,6 +2761,29 @@ If there is no error, returns nil. */)
2759 return Qnil; 2761 return Qnil;
2760} 2762}
2761 2763
2764/* Relative to directory FD, return the symbolic link value of FILENAME.
2765 On failure, return nil. */
2766Lisp_Object
2767emacs_readlinkat (int fd, char const *filename)
2768{
2769 static struct allocator const emacs_norealloc_allocator =
2770 { xmalloc, NULL, xfree, memory_full };
2771 Lisp_Object val;
2772 char readlink_buf[1024];
2773 char *buf = careadlinkat (fd, filename, readlink_buf, sizeof readlink_buf,
2774 &emacs_norealloc_allocator, readlinkat);
2775 if (!buf)
2776 return Qnil;
2777
2778 val = build_string (buf);
2779 if (buf[0] == '/' && strchr (buf, ':'))
2780 val = concat2 (build_string ("/:"), val);
2781 if (buf != readlink_buf)
2782 xfree (buf);
2783 val = DECODE_FILE (val);
2784 return val;
2785}
2786
2762DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0, 2787DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
2763 doc: /* Return non-nil if file FILENAME is the name of a symbolic link. 2788 doc: /* Return non-nil if file FILENAME is the name of a symbolic link.
2764The value is the link target, as a string. 2789The value is the link target, as a string.
@@ -2769,9 +2794,6 @@ points to a nonexistent file. */)
2769 (Lisp_Object filename) 2794 (Lisp_Object filename)
2770{ 2795{
2771 Lisp_Object handler; 2796 Lisp_Object handler;
2772 char *buf;
2773 Lisp_Object val;
2774 char readlink_buf[READLINK_BUFSIZE];
2775 2797
2776 CHECK_STRING (filename); 2798 CHECK_STRING (filename);
2777 filename = Fexpand_file_name (filename, Qnil); 2799 filename = Fexpand_file_name (filename, Qnil);
@@ -2784,17 +2806,7 @@ points to a nonexistent file. */)
2784 2806
2785 filename = ENCODE_FILE (filename); 2807 filename = ENCODE_FILE (filename);
2786 2808
2787 buf = emacs_readlink (SSDATA (filename), readlink_buf); 2809 return emacs_readlinkat (AT_FDCWD, SSDATA (filename));
2788 if (! buf)
2789 return Qnil;
2790
2791 val = build_string (buf);
2792 if (buf[0] == '/' && strchr (buf, ':'))
2793 val = concat2 (build_string ("/:"), val);
2794 if (buf != readlink_buf)
2795 xfree (buf);
2796 val = DECODE_FILE (val);
2797 return val;
2798} 2810}
2799 2811
2800DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0, 2812DEFUN ("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)
390 lock_info_type local_owner; 390 lock_info_type local_owner;
391 intmax_t n; 391 intmax_t n;
392 char *at, *dot, *colon; 392 char *at, *dot, *colon;
393 char readlink_buf[READLINK_BUFSIZE]; 393 Lisp_Object lfinfo_object = emacs_readlinkat (AT_FDCWD, lfname);
394 char *lfinfo = emacs_readlink (lfname, readlink_buf); 394 char *lfinfo;
395 struct gcpro gcpro1;
395 396
396 /* If nonexistent lock file, all is well; otherwise, got strange error. */ 397 /* If nonexistent lock file, all is well; otherwise, got strange error. */
397 if (!lfinfo) 398 if (NILP (lfinfo_object))
398 return errno == ENOENT ? 0 : -1; 399 return errno == ENOENT ? 0 : -1;
400 lfinfo = SSDATA (lfinfo_object);
399 401
400 /* Even if the caller doesn't want the owner info, we still have to 402 /* Even if the caller doesn't want the owner info, we still have to
401 read it to determine return value. */ 403 read it to determine return value. */
@@ -407,12 +409,9 @@ current_lock_owner (lock_info_type *owner, char *lfname)
407 at = strrchr (lfinfo, '@'); 409 at = strrchr (lfinfo, '@');
408 dot = strrchr (lfinfo, '.'); 410 dot = strrchr (lfinfo, '.');
409 if (!at || !dot) 411 if (!at || !dot)
410 { 412 return -1;
411 if (lfinfo != readlink_buf)
412 xfree (lfinfo);
413 return -1;
414 }
415 len = at - lfinfo; 413 len = at - lfinfo;
414 GCPRO1 (lfinfo_object);
416 owner->user = xmalloc (len + 1); 415 owner->user = xmalloc (len + 1);
417 memcpy (owner->user, lfinfo, len); 416 memcpy (owner->user, lfinfo, len);
418 owner->user[len] = 0; 417 owner->user[len] = 0;
@@ -445,8 +444,7 @@ current_lock_owner (lock_info_type *owner, char *lfname)
445 owner->host[len] = 0; 444 owner->host[len] = 0;
446 445
447 /* We're done looking at the link info. */ 446 /* We're done looking at the link info. */
448 if (lfinfo != readlink_buf) 447 UNGCPRO;
449 xfree (lfinfo);
450 448
451 /* On current host? */ 449 /* On current host? */
452 if (STRINGP (Fsystem_name ()) 450 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);
3294extern Lisp_Object restore_point_unwind (Lisp_Object); 3294extern Lisp_Object restore_point_unwind (Lisp_Object);
3295extern _Noreturn void report_file_error (const char *, Lisp_Object); 3295extern _Noreturn void report_file_error (const char *, Lisp_Object);
3296extern bool internal_delete_file (Lisp_Object); 3296extern bool internal_delete_file (Lisp_Object);
3297extern Lisp_Object emacs_readlinkat (int, const char *);
3297extern bool file_directory_p (const char *); 3298extern bool file_directory_p (const char *);
3298extern bool file_accessible_directory_p (const char *); 3299extern bool file_accessible_directory_p (const char *);
3299extern void init_fileio (void); 3300extern void init_fileio (void);
@@ -3566,8 +3567,6 @@ extern int emacs_open (const char *, int, int);
3566extern int emacs_close (int); 3567extern int emacs_close (int);
3567extern ptrdiff_t emacs_read (int, char *, ptrdiff_t); 3568extern ptrdiff_t emacs_read (int, char *, ptrdiff_t);
3568extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t); 3569extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t);
3569enum { READLINK_BUFSIZE = 1024 };
3570extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]);
3571 3570
3572extern void unlock_all_files (void); 3571extern void unlock_all_files (void);
3573extern void lock_file (Lisp_Object); 3572extern 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/>. */
30#include <limits.h> 30#include <limits.h>
31#include <unistd.h> 31#include <unistd.h>
32 32
33#include <allocator.h>
34#include <c-ctype.h> 33#include <c-ctype.h>
35#include <careadlinkat.h>
36#include <ignore-value.h> 34#include <ignore-value.h>
37#include <utimens.h> 35#include <utimens.h>
38 36
@@ -2247,22 +2245,6 @@ emacs_write (int fildes, const char *buf, ptrdiff_t nbyte)
2247 2245
2248 return (bytes_written); 2246 return (bytes_written);
2249} 2247}
2250
2251static struct allocator const emacs_norealloc_allocator =
2252 { xmalloc, NULL, xfree, memory_full };
2253
2254/* Get the symbolic link value of FILENAME. Return a pointer to a
2255 NUL-terminated string. If readlink fails, return NULL and set
2256 errno. If the value fits in INITIAL_BUF, return INITIAL_BUF.
2257 Otherwise, allocate memory and return a pointer to that memory. If
2258 memory allocation fails, diagnose and fail without returning. If
2259 successful, store the length of the symbolic link into *LINKLEN. */
2260char *
2261emacs_readlink (char const *filename, char initial_buf[READLINK_BUFSIZE])
2262{
2263 return careadlinkat (AT_FDCWD, filename, initial_buf, READLINK_BUFSIZE,
2264 &emacs_norealloc_allocator, careadlinkatcwd);
2265}
2266 2248
2267/* Return a struct timeval that is roughly equivalent to T. 2249/* Return a struct timeval that is roughly equivalent to T.
2268 Use the least timeval not less than T. 2250 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)
4274 return stat_worker (path, buf, 0); 4274 return stat_worker (path, buf, 0);
4275} 4275}
4276 4276
4277int
4278fstatat (int fd, char const *name, struct stat *st, int flags)
4279{
4280 /* Rely on a hack: an open directory is modeled as file descriptor 0.
4281 This is good enough for the current usage in Emacs, but is fragile.
4282
4283 FIXME: Add proper support for fdopendir, fstatatat, readlinkat.
4284 Gnulib does this and can serve as a model. */
4285 char fullname[MAX_PATH];
4286
4287 if (fd != AT_FDCWD)
4288 {
4289 if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
4290 < 0)
4291 {
4292 errno = ENAMETOOLONG;
4293 return -1;
4294 }
4295 name = fullname;
4296 }
4297
4298 return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW));
4299}
4300
4277/* Provide fstat and utime as well as stat for consistent handling of 4301/* Provide fstat and utime as well as stat for consistent handling of
4278 file timestamps. */ 4302 file timestamps. */
4279int 4303int
@@ -4816,6 +4840,28 @@ readlink (const char *name, char *buf, size_t buf_size)
4816 return retval; 4840 return retval;
4817} 4841}
4818 4842
4843ssize_t
4844readlinkat (int fd, char const *name, char *buffer,
4845 size_t buffer_size)
4846{
4847 /* Rely on a hack: an open directory is modeled as file descriptor 0,
4848 as in fstatat. FIXME: Add proper support for readlinkat. */
4849 char fullname[MAX_PATH];
4850
4851 if (fd != AT_FDCWD)
4852 {
4853 if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
4854 < 0)
4855 {
4856 errno = ENAMETOOLONG;
4857 return -1;
4858 }
4859 name = fullname;
4860 }
4861
4862 return readlink (name, buffer, buffer_size);
4863}
4864
4819/* If FILE is a symlink, return its target (stored in a static 4865/* If FILE is a symlink, return its target (stored in a static
4820 buffer); otherwise return FILE. 4866 buffer); otherwise return FILE.
4821 4867
@@ -5168,12 +5214,6 @@ careadlinkat (int fd, char const *filename,
5168 char linkname[MAX_PATH]; 5214 char linkname[MAX_PATH];
5169 ssize_t link_size; 5215 ssize_t link_size;
5170 5216
5171 if (fd != AT_FDCWD)
5172 {
5173 errno = EINVAL;
5174 return NULL;
5175 }
5176
5177 link_size = preadlinkat (fd, filename, linkname, sizeof(linkname)); 5217 link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
5178 5218
5179 if (link_size > 0) 5219 if (link_size > 0)
@@ -5191,14 +5231,6 @@ careadlinkat (int fd, char const *filename,
5191 return NULL; 5231 return NULL;
5192} 5232}
5193 5233
5194ssize_t
5195careadlinkatcwd (int fd, char const *filename, char *buffer,
5196 size_t buffer_size)
5197{
5198 (void) fd;
5199 return readlink (filename, buffer, buffer_size);
5200}
5201
5202 5234
5203/* Support for browsing other processes and their attributes. See 5235/* Support for browsing other processes and their attributes. See
5204 process.c for the Lisp bindings. */ 5236 process.c for the Lisp bindings. */