1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-01-02 11:21:42 +00:00

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
This commit is contained in:
Paul Eggert 2013-01-31 22:30:51 -08:00
parent 44b12dd699
commit 8654f9d7d6
33 changed files with 1595 additions and 140 deletions

View File

@ -1,3 +1,23 @@
2013-02-01 Paul Eggert <eggert@cs.ucla.edu>
Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
On my host, this speeds up directory-files-and-attributes by a
factor of 3, when applied to Emacs's src directory.
These functions are standardized by POSIX and are common these
days; fall back on a (slower) gnulib implementation if the host
is too old to supply them.
* .bzrignore: Add lib/dirent.h.
* lib/Makefile.am (libgnu_a_SOURCES): Add openat-die.c, save-cwd.c.
* lib/careadlinkat.c, lib/careadlinkat.h: Merge from gnulib,
incorporating: 2013-01-29 careadlinkat: do not provide careadlinkatcwd.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
* lib/dirent.in.h, lib/fdopendir.c, lib/fstatat.c, lib/openat-priv.h:
* lib/openat-proc.c, lib/openat.h, m4/dirent_h.m4, m4/fdopendir.m4:
* m4/fstatat.m4: New files, from gnulib.
* lib/openat-die.c, lib/save-cwd.c, lib/save-cwd.h: New files.
These last three are specific to Emacs and are not copied from gnulib.
They are simpler than the gnulib versions and are tuned for Emacs.
2013-02-01 Glenn Morris <rgm@gnu.org>
* make-dist: Only README files exist in lisp/ now, not README*.

View File

@ -1,3 +1,10 @@
2013-02-01 Paul Eggert <eggert@cs.ucla.edu>
Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
* merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat.
(GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h.
Avoid dup, open, opendir.
2013-01-15 Dmitry Antipov <dmantipov@yandex.ru>
* coccinelle/xsave.cocci: Semantic patch to adjust users of

View File

@ -29,9 +29,9 @@ GNULIB_MODULES='
alloca-opt c-ctype c-strcase
careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
dtoastr dtotimespec dup2 environ execinfo faccessat
fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday
fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday
ignore-value intprops largefile lstat
manywarnings mktime pselect pthread_sigmask putenv readlink
manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat
sig2str socklen stat-time stdalign stdarg stdbool stdio
strftime strtoimax strtoumax symlink sys_stat
sys_time time timer-time timespec-add timespec-sub unsetenv utimens
@ -39,10 +39,10 @@ GNULIB_MODULES='
'
GNULIB_TOOL_FLAGS='
--avoid=at-internal
--avoid=dup
--avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat
--avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
--avoid=openat-die --avoid=openat-h
--avoid=open --avoid=openat-die --avoid=opendir
--avoid=raise
--avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types
--avoid=threadlib

View File

@ -8,3 +8,5 @@ AM_CFLAGS = $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS)
DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src
include gnulib.mk
libgnu_a_SOURCES += openat-die.c save-cwd.c

View File

@ -24,9 +24,7 @@
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Define this independently so that stdint.h is not a prerequisite. */
#ifndef SIZE_MAX
@ -39,20 +37,6 @@
#include "allocator.h"
/* Get the symbolic link value of FILENAME and put it into BUFFER, with
size BUFFER_SIZE. This function acts like readlink but has
readlinkat's signature. */
ssize_t
careadlinkatcwd (int fd, char const *filename, char *buffer,
size_t buffer_size)
{
/* FD must be AT_FDCWD here, otherwise the caller is using this
function in contexts for which it was not meant for. */
if (fd != AT_FDCWD)
abort ();
return readlink (filename, buffer, buffer_size);
}
/* Assuming the current directory is FD, get the symbolic link value
of FILENAME as a null-terminated string and put it into a buffer.
If FD is AT_FDCWD, FILENAME is interpreted relative to the current

View File

@ -52,9 +52,9 @@ char *careadlinkat (int fd, char const *filename,
ssize_t (*preadlinkat) (int, char const *,
char *, size_t));
/* Suitable values for careadlinkat's FD and PREADLINKAT arguments,
/* Suitable value for careadlinkat's FD argument,
when doing a plain readlink:
Pass FD = AT_FDCWD and PREADLINKAT = careadlinkatcwd. */
Pass FD = AT_FDCWD. */
#if HAVE_READLINKAT
/* AT_FDCWD is declared in <fcntl.h>. */
#else
@ -66,7 +66,5 @@ char *careadlinkat (int fd, char const *filename,
# define AT_FDCWD (-3041965)
# endif
#endif
ssize_t careadlinkatcwd (int fd, char const *filename,
char *buffer, size_t buffer_size);
#endif /* _GL_CAREADLINKAT_H */

258
lib/dirent.in.h Normal file
View File

@ -0,0 +1,258 @@
/* A GNU-like <dirent.h>.
Copyright (C) 2006-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifndef _@GUARD_PREFIX@_DIRENT_H
#if __GNUC__ >= 3
@PRAGMA_SYSTEM_HEADER@
#endif
@PRAGMA_COLUMNS@
/* The include_next requires a split double-inclusion guard. */
#if @HAVE_DIRENT_H@
# @INCLUDE_NEXT@ @NEXT_DIRENT_H@
#endif
#ifndef _@GUARD_PREFIX@_DIRENT_H
#define _@GUARD_PREFIX@_DIRENT_H
/* Get ino_t. Needed on some systems, including glibc 2.8. */
#include <sys/types.h>
#if !@HAVE_DIRENT_H@
/* Define types DIR and 'struct dirent'. */
# if !GNULIB_defined_struct_dirent
struct dirent
{
char d_type;
char d_name[1];
};
/* Possible values for 'd_type'. */
# define DT_UNKNOWN 0
# define DT_FIFO 1 /* FIFO */
# define DT_CHR 2 /* character device */
# define DT_DIR 4 /* directory */
# define DT_BLK 6 /* block device */
# define DT_REG 8 /* regular file */
# define DT_LNK 10 /* symbolic link */
# define DT_SOCK 12 /* socket */
# define DT_WHT 14 /* whiteout */
typedef struct gl_directory DIR;
# define GNULIB_defined_struct_dirent 1
# endif
#endif
/* The __attribute__ feature is available in gcc versions 2.5 and later.
The attribute __pure__ was added in gcc 2.96. */
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
#else
# define _GL_ATTRIBUTE_PURE /* empty */
#endif
/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */
/* The definition of _GL_ARG_NONNULL is copied here. */
/* The definition of _GL_WARN_ON_USE is copied here. */
/* Declare overridden functions. */
#if @GNULIB_OPENDIR@
# if @REPLACE_OPENDIR@
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef opendir
# define opendir rpl_opendir
# endif
_GL_FUNCDECL_RPL (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
_GL_CXXALIAS_RPL (opendir, DIR *, (const char *dir_name));
# else
# if !@HAVE_OPENDIR@
_GL_FUNCDECL_SYS (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
# endif
_GL_CXXALIAS_SYS (opendir, DIR *, (const char *dir_name));
# endif
_GL_CXXALIASWARN (opendir);
#elif defined GNULIB_POSIXCHECK
# undef opendir
# if HAVE_RAW_DECL_OPENDIR
_GL_WARN_ON_USE (opendir, "opendir is not portable - "
"use gnulib module opendir for portability");
# endif
#endif
#if @GNULIB_READDIR@
# if !@HAVE_READDIR@
_GL_FUNCDECL_SYS (readdir, struct dirent *, (DIR *dirp) _GL_ARG_NONNULL ((1)));
# endif
_GL_CXXALIAS_SYS (readdir, struct dirent *, (DIR *dirp));
_GL_CXXALIASWARN (readdir);
#elif defined GNULIB_POSIXCHECK
# undef readdir
# if HAVE_RAW_DECL_READDIR
_GL_WARN_ON_USE (readdir, "readdir is not portable - "
"use gnulib module readdir for portability");
# endif
#endif
#if @GNULIB_REWINDDIR@
# if !@HAVE_REWINDDIR@
_GL_FUNCDECL_SYS (rewinddir, void, (DIR *dirp) _GL_ARG_NONNULL ((1)));
# endif
_GL_CXXALIAS_SYS (rewinddir, void, (DIR *dirp));
_GL_CXXALIASWARN (rewinddir);
#elif defined GNULIB_POSIXCHECK
# undef rewinddir
# if HAVE_RAW_DECL_REWINDDIR
_GL_WARN_ON_USE (rewinddir, "rewinddir is not portable - "
"use gnulib module rewinddir for portability");
# endif
#endif
#if @GNULIB_CLOSEDIR@
# if @REPLACE_CLOSEDIR@
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef closedir
# define closedir rpl_closedir
# endif
_GL_FUNCDECL_RPL (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
_GL_CXXALIAS_RPL (closedir, int, (DIR *dirp));
# else
# if !@HAVE_CLOSEDIR@
_GL_FUNCDECL_SYS (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
# endif
_GL_CXXALIAS_SYS (closedir, int, (DIR *dirp));
# endif
_GL_CXXALIASWARN (closedir);
#elif defined GNULIB_POSIXCHECK
# undef closedir
# if HAVE_RAW_DECL_CLOSEDIR
_GL_WARN_ON_USE (closedir, "closedir is not portable - "
"use gnulib module closedir for portability");
# endif
#endif
#if @GNULIB_DIRFD@
/* Return the file descriptor associated with the given directory stream,
or -1 if none exists. */
# if @REPLACE_DIRFD@
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef dirfd
# define dirfd rpl_dirfd
# endif
_GL_FUNCDECL_RPL (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
_GL_CXXALIAS_RPL (dirfd, int, (DIR *));
# else
# if defined __cplusplus && defined GNULIB_NAMESPACE && defined dirfd
/* dirfd is defined as a macro and not as a function.
Turn it into a function and get rid of the macro. */
static inline int (dirfd) (DIR *dp) { return dirfd (dp); }
# undef dirfd
# endif
# if !(@HAVE_DECL_DIRFD@ || defined dirfd)
_GL_FUNCDECL_SYS (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
# endif
_GL_CXXALIAS_SYS (dirfd, int, (DIR *));
# endif
_GL_CXXALIASWARN (dirfd);
#elif defined GNULIB_POSIXCHECK
# undef dirfd
# if HAVE_RAW_DECL_DIRFD
_GL_WARN_ON_USE (dirfd, "dirfd is unportable - "
"use gnulib module dirfd for portability");
# endif
#endif
#if @GNULIB_FDOPENDIR@
/* Open a directory stream visiting the given directory file
descriptor. Return NULL and set errno if fd is not visiting a
directory. On success, this function consumes fd (it will be
implicitly closed either by this function or by a subsequent
closedir). */
# if @REPLACE_FDOPENDIR@
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef fdopendir
# define fdopendir rpl_fdopendir
# endif
_GL_FUNCDECL_RPL (fdopendir, DIR *, (int fd));
_GL_CXXALIAS_RPL (fdopendir, DIR *, (int fd));
# else
# if !@HAVE_FDOPENDIR@ || !@HAVE_DECL_FDOPENDIR@
_GL_FUNCDECL_SYS (fdopendir, DIR *, (int fd));
# endif
_GL_CXXALIAS_SYS (fdopendir, DIR *, (int fd));
# endif
_GL_CXXALIASWARN (fdopendir);
#elif defined GNULIB_POSIXCHECK
# undef fdopendir
# if HAVE_RAW_DECL_FDOPENDIR
_GL_WARN_ON_USE (fdopendir, "fdopendir is unportable - "
"use gnulib module fdopendir for portability");
# endif
#endif
#if @GNULIB_SCANDIR@
/* Scan the directory DIR, calling FILTER on each directory entry.
Entries for which FILTER returns nonzero are individually malloc'd,
sorted using qsort with CMP, and collected in a malloc'd array in
*NAMELIST. Returns the number of entries selected, or -1 on error. */
# if !@HAVE_SCANDIR@
_GL_FUNCDECL_SYS (scandir, int,
(const char *dir, struct dirent ***namelist,
int (*filter) (const struct dirent *),
int (*cmp) (const struct dirent **, const struct dirent **))
_GL_ARG_NONNULL ((1, 2, 4)));
# endif
/* Need to cast, because on glibc systems, the fourth parameter is
int (*cmp) (const void *, const void *). */
_GL_CXXALIAS_SYS_CAST (scandir, int,
(const char *dir, struct dirent ***namelist,
int (*filter) (const struct dirent *),
int (*cmp) (const struct dirent **, const struct dirent **)));
_GL_CXXALIASWARN (scandir);
#elif defined GNULIB_POSIXCHECK
# undef scandir
# if HAVE_RAW_DECL_SCANDIR
_GL_WARN_ON_USE (scandir, "scandir is unportable - "
"use gnulib module scandir for portability");
# endif
#endif
#if @GNULIB_ALPHASORT@
/* Compare two 'struct dirent' entries alphabetically. */
# if !@HAVE_ALPHASORT@
_GL_FUNCDECL_SYS (alphasort, int,
(const struct dirent **, const struct dirent **)
_GL_ATTRIBUTE_PURE
_GL_ARG_NONNULL ((1, 2)));
# endif
/* Need to cast, because on glibc systems, the parameters are
(const void *, const void *). */
_GL_CXXALIAS_SYS_CAST (alphasort, int,
(const struct dirent **, const struct dirent **));
_GL_CXXALIASWARN (alphasort);
#elif defined GNULIB_POSIXCHECK
# undef alphasort
# if HAVE_RAW_DECL_ALPHASORT
_GL_WARN_ON_USE (alphasort, "alphasort is unportable - "
"use gnulib module alphasort for portability");
# endif
#endif
#endif /* _@GUARD_PREFIX@_DIRENT_H */
#endif /* _@GUARD_PREFIX@_DIRENT_H */

204
lib/fdopendir.c Normal file
View File

@ -0,0 +1,204 @@
/* provide a replacement fdopendir function
Copyright (C) 2004-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* written by Jim Meyering */
#include <config.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#if !HAVE_FDOPENDIR
# include "openat.h"
# include "openat-priv.h"
# include "save-cwd.h"
# if GNULIB_DIRENT_SAFER
# include "dirent--.h"
# endif
# ifndef REPLACE_FCHDIR
# define REPLACE_FCHDIR 0
# endif
static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
static DIR *fd_clone_opendir (int, struct saved_cwd const *);
/* Replacement for POSIX fdopendir.
First, try to simulate it via opendir ("/proc/self/fd/..."). Failing
that, simulate it by using fchdir metadata, or by doing
save_cwd/fchdir/opendir(".")/restore_cwd.
If either the save_cwd or the restore_cwd fails (relatively unlikely),
then give a diagnostic and exit nonzero.
If successful, the resulting stream is based on FD in
implementations where streams are based on file descriptors and in
applications where no other thread or signal handler allocates or
frees file descriptors. In other cases, consult dirfd on the result
to find out whether FD is still being used.
Otherwise, this function works just like POSIX fdopendir.
W A R N I N G:
Unlike other fd-related functions, this one places constraints on FD.
If this function returns successfully, FD is under control of the
dirent.h system, and the caller should not close or modify the state of
FD other than by the dirent.h functions. */
DIR *
fdopendir (int fd)
{
DIR *dir = fdopendir_with_dup (fd, -1, NULL);
if (! REPLACE_FCHDIR && ! dir)
{
int saved_errno = errno;
if (EXPECTED_ERRNO (saved_errno))
{
struct saved_cwd cwd;
if (save_cwd (&cwd) != 0)
openat_save_fail (errno);
dir = fdopendir_with_dup (fd, -1, &cwd);
saved_errno = errno;
free_cwd (&cwd);
errno = saved_errno;
}
}
return dir;
}
/* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
to be a dup of FD which is less than FD - 1 and which will be
closed by the caller and not otherwise used by the caller. This
function makes sure that FD is closed and all file descriptors less
than FD are open, and then calls fd_clone_opendir on a dup of FD.
That way, barring race conditions, fd_clone_opendir returns a
stream whose file descriptor is FD.
If REPLACE_CHDIR or CWD is null, use opendir ("/proc/self/fd/...",
falling back on fchdir metadata. Otherwise, CWD is a saved version
of the working directory; use fchdir/opendir(".")/restore_cwd(CWD). */
static DIR *
fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd)
{
int dupfd = dup (fd);
if (dupfd < 0 && errno == EMFILE)
dupfd = older_dupfd;
if (dupfd < 0)
return NULL;
else
{
DIR *dir;
int saved_errno;
if (dupfd < fd - 1 && dupfd != older_dupfd)
{
dir = fdopendir_with_dup (fd, dupfd, cwd);
saved_errno = errno;
}
else
{
close (fd);
dir = fd_clone_opendir (dupfd, cwd);
saved_errno = errno;
if (! dir)
{
int fd1 = dup (dupfd);
if (fd1 != fd)
openat_save_fail (fd1 < 0 ? errno : EBADF);
}
}
if (dupfd != older_dupfd)
close (dupfd);
errno = saved_errno;
return dir;
}
}
/* Like fdopendir, except the result controls a clone of FD. It is
the caller's responsibility both to close FD and (if the result is
not null) to closedir the result. */
static DIR *
fd_clone_opendir (int fd, struct saved_cwd const *cwd)
{
if (REPLACE_FCHDIR || ! cwd)
{
DIR *dir = NULL;
int saved_errno = EOPNOTSUPP;
char buf[OPENAT_BUFFER_SIZE];
char *proc_file = openat_proc_name (buf, fd, ".");
if (proc_file)
{
dir = opendir (proc_file);
saved_errno = errno;
if (proc_file != buf)
free (proc_file);
}
# if REPLACE_FCHDIR
if (! dir && EXPECTED_ERRNO (saved_errno))
{
char const *name = _gl_directory_name (fd);
return (name ? opendir (name) : NULL);
}
# endif
errno = saved_errno;
return dir;
}
else
{
if (fchdir (fd) != 0)
return NULL;
else
{
DIR *dir = opendir (".");
int saved_errno = errno;
if (restore_cwd (cwd) != 0)
openat_restore_fail (errno);
errno = saved_errno;
return dir;
}
}
}
#else /* HAVE_FDOPENDIR */
# include <errno.h>
# include <sys/stat.h>
# undef fdopendir
/* Like fdopendir, but work around GNU/Hurd bug by validating FD. */
DIR *
rpl_fdopendir (int fd)
{
struct stat st;
if (fstat (fd, &st))
return NULL;
if (!S_ISDIR (st.st_mode))
{
errno = ENOTDIR;
return NULL;
}
return fdopendir (fd);
}
#endif /* HAVE_FDOPENDIR */

135
lib/fstatat.c Normal file
View File

@ -0,0 +1,135 @@
/* Work around an fstatat bug on Solaris 9.
Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Eggert and Jim Meyering. */
/* If the user's config.h happens to include <sys/stat.h>, let it include only
the system's <sys/stat.h> here, so that orig_fstatat doesn't recurse to
rpl_fstatat. */
#define __need_system_sys_stat_h
#include <config.h>
/* Get the original definition of fstatat. It might be defined as a macro. */
#include <sys/types.h>
#include <sys/stat.h>
#undef __need_system_sys_stat_h
#if HAVE_FSTATAT
static int
orig_fstatat (int fd, char const *filename, struct stat *buf, int flags)
{
return fstatat (fd, filename, buf, flags);
}
#endif
/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
eliminates this include because of the preliminary #include <sys/stat.h>
above. */
#include "sys/stat.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#if HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG
# ifndef LSTAT_FOLLOWS_SLASHED_SYMLINK
# define LSTAT_FOLLOWS_SLASHED_SYMLINK 0
# endif
/* fstatat should always follow symbolic links that end in /, but on
Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.
Likewise, trailing slash on a non-directory should be an error.
These are the same problems that lstat.c and stat.c address, so
solve it in a similar way.
AIX 7.1 fstatat (AT_FDCWD, ..., 0) always fails, which is a bug.
Work around this bug if FSTATAT_AT_FDCWD_0_BROKEN is nonzero. */
int
rpl_fstatat (int fd, char const *file, struct stat *st, int flag)
{
int result = orig_fstatat (fd, file, st, flag);
size_t len;
if (LSTAT_FOLLOWS_SLASHED_SYMLINK || result != 0)
return result;
len = strlen (file);
if (flag & AT_SYMLINK_NOFOLLOW)
{
/* Fix lstat behavior. */
if (file[len - 1] != '/' || S_ISDIR (st->st_mode))
return 0;
if (!S_ISLNK (st->st_mode))
{
errno = ENOTDIR;
return -1;
}
result = orig_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
}
/* Fix stat behavior. */
if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/')
{
errno = ENOTDIR;
return -1;
}
return result;
}
#else /* ! (HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG) */
/* On mingw, the gnulib <sys/stat.h> defines 'stat' as a function-like
macro; but using it in AT_FUNC_F2 causes compilation failure
because the preprocessor sees a use of a macro that requires two
arguments but is only given one. Hence, we need an inline
forwarder to get past the preprocessor. */
static int
stat_func (char const *name, struct stat *st)
{
return stat (name, st);
}
/* Likewise, if there is no native 'lstat', then the gnulib
<sys/stat.h> defined it as stat, which also needs adjustment. */
# if !HAVE_LSTAT
# undef lstat
# define lstat stat_func
# endif
/* Replacement for Solaris' function by the same name.
<http://www.google.com/search?q=fstatat+site:docs.sun.com>
First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
If either the save_cwd or the restore_cwd fails (relatively unlikely),
then give a diagnostic and exit nonzero.
Otherwise, this function works just like Solaris' fstatat. */
# define AT_FUNC_NAME fstatat
# define AT_FUNC_F1 lstat
# define AT_FUNC_F2 stat_func
# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
# define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
# define AT_FUNC_POST_FILE_ARGS , st
# include "at-func.c"
# undef AT_FUNC_NAME
# undef AT_FUNC_F1
# undef AT_FUNC_F2
# undef AT_FUNC_USE_F1_COND
# undef AT_FUNC_POST_FILE_PARAM_DECLS
# undef AT_FUNC_POST_FILE_ARGS
#endif /* !HAVE_FSTATAT */

View File

@ -21,7 +21,7 @@
# the same distribution terms as the rest of that program.
#
# Generated by gnulib-tool.
# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=at-internal --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=openat-h --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
MOSTLYCLEANFILES += core *.stackdump
@ -64,6 +64,17 @@ EXTRA_DIST += allocator.h
## end gnulib module allocator
## begin gnulib module at-internal
if gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b
endif
EXTRA_DIST += openat-priv.h openat-proc.c
EXTRA_libgnu_a_SOURCES += openat-proc.c
## end gnulib module at-internal
## begin gnulib module c-ctype
libgnu_a_SOURCES += c-ctype.h c-ctype.c
@ -124,6 +135,54 @@ EXTRA_DIST += sha512.h
## end gnulib module crypto/sha512
## begin gnulib module dirent
BUILT_SOURCES += dirent.h
# We need the following in order to create <dirent.h> when the system
# doesn't have one that works with the given compiler.
dirent.h: dirent.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
$(AM_V_GEN)rm -f $@-t $@ && \
{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
sed -e 's|@''GUARD_PREFIX''@|GL|g' \
-e 's|@''HAVE_DIRENT_H''@|$(HAVE_DIRENT_H)|g' \
-e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
-e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
-e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
-e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \
-e 's/@''GNULIB_OPENDIR''@/$(GNULIB_OPENDIR)/g' \
-e 's/@''GNULIB_READDIR''@/$(GNULIB_READDIR)/g' \
-e 's/@''GNULIB_REWINDDIR''@/$(GNULIB_REWINDDIR)/g' \
-e 's/@''GNULIB_CLOSEDIR''@/$(GNULIB_CLOSEDIR)/g' \
-e 's/@''GNULIB_DIRFD''@/$(GNULIB_DIRFD)/g' \
-e 's/@''GNULIB_FDOPENDIR''@/$(GNULIB_FDOPENDIR)/g' \
-e 's/@''GNULIB_SCANDIR''@/$(GNULIB_SCANDIR)/g' \
-e 's/@''GNULIB_ALPHASORT''@/$(GNULIB_ALPHASORT)/g' \
-e 's/@''HAVE_OPENDIR''@/$(HAVE_OPENDIR)/g' \
-e 's/@''HAVE_READDIR''@/$(HAVE_READDIR)/g' \
-e 's/@''HAVE_REWINDDIR''@/$(HAVE_REWINDDIR)/g' \
-e 's/@''HAVE_CLOSEDIR''@/$(HAVE_CLOSEDIR)/g' \
-e 's|@''HAVE_DECL_DIRFD''@|$(HAVE_DECL_DIRFD)|g' \
-e 's|@''HAVE_DECL_FDOPENDIR''@|$(HAVE_DECL_FDOPENDIR)|g' \
-e 's|@''HAVE_FDOPENDIR''@|$(HAVE_FDOPENDIR)|g' \
-e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \
-e 's|@''HAVE_ALPHASORT''@|$(HAVE_ALPHASORT)|g' \
-e 's|@''REPLACE_OPENDIR''@|$(REPLACE_OPENDIR)|g' \
-e 's|@''REPLACE_CLOSEDIR''@|$(REPLACE_CLOSEDIR)|g' \
-e 's|@''REPLACE_DIRFD''@|$(REPLACE_DIRFD)|g' \
-e 's|@''REPLACE_FDOPENDIR''@|$(REPLACE_FDOPENDIR)|g' \
-e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
-e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
-e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
< $(srcdir)/dirent.in.h; \
} > $@-t && \
mv $@-t $@
MOSTLYCLEANFILES += dirent.h dirent.h-t
EXTRA_DIST += dirent.in.h
## end gnulib module dirent
## begin gnulib module dosname
if gl_GNULIB_ENABLED_dosname
@ -238,6 +297,15 @@ EXTRA_DIST += fcntl.in.h
## end gnulib module fcntl-h
## begin gnulib module fdopendir
EXTRA_DIST += fdopendir.c
EXTRA_libgnu_a_SOURCES += fdopendir.c
## end gnulib module fdopendir
## begin gnulib module filemode
libgnu_a_SOURCES += filemode.c
@ -255,6 +323,15 @@ EXTRA_libgnu_a_SOURCES += fpending.c
## end gnulib module fpending
## begin gnulib module fstatat
EXTRA_DIST += at-func.c fstatat.c
EXTRA_libgnu_a_SOURCES += at-func.c fstatat.c
## end gnulib module fstatat
## begin gnulib module getgroups
if gl_GNULIB_ENABLED_getgroups
@ -412,6 +489,15 @@ EXTRA_libgnu_a_SOURCES += mktime.c
## end gnulib module mktime
## begin gnulib module openat-h
if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7
endif
EXTRA_DIST += openat.h
## end gnulib module openat-h
## begin gnulib module pathmax
if gl_GNULIB_ENABLED_pathmax
@ -457,6 +543,15 @@ EXTRA_libgnu_a_SOURCES += readlink.c
## end gnulib module readlink
## begin gnulib module readlinkat
EXTRA_DIST += at-func.c readlinkat.c
EXTRA_libgnu_a_SOURCES += at-func.c readlinkat.c
## end gnulib module readlinkat
## begin gnulib module root-uid
if gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c

6
lib/openat-die.c Normal file
View File

@ -0,0 +1,6 @@
/* Respond to a save- or restore-cwd failure.
This should never happen with Emacs. */
#include <config.h>
#include "openat.h"
void openat_save_fail (int errnum) { abort (); }
void openat_restore_fail (int errnum) { abort (); }

64
lib/openat-priv.h Normal file
View File

@ -0,0 +1,64 @@
/* Internals for openat-like functions.
Copyright (C) 2005-2006, 2009-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* written by Jim Meyering */
#ifndef _GL_HEADER_OPENAT_PRIV
#define _GL_HEADER_OPENAT_PRIV
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
/* Maximum number of bytes that it is safe to allocate as a single
array on the stack, and that is known as a compile-time constant.
The assumption is that we'll touch the array very quickly, or a
temporary very near the array, provoking an out-of-memory trap. On
some operating systems, there is only one guard page for the stack,
and a page size can be as small as 4096 bytes. Subtract 64 in the
hope that this will let the compiler touch a nearby temporary and
provoke a trap. */
#define SAFER_ALLOCA_MAX (4096 - 64)
#define SAFER_ALLOCA(m) ((m) < SAFER_ALLOCA_MAX ? (m) : SAFER_ALLOCA_MAX)
#if defined PATH_MAX
# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (PATH_MAX)
#elif defined _XOPEN_PATH_MAX
# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (_XOPEN_PATH_MAX)
#else
# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (1024)
#endif
char *openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file);
/* Trying to access a BUILD_PROC_NAME file will fail on systems without
/proc support, and even on systems *with* ProcFS support. Return
nonzero if the failure may be legitimate, e.g., because /proc is not
readable, or the particular .../fd/N directory is not present. */
#define EXPECTED_ERRNO(Errno) \
((Errno) == ENOTDIR || (Errno) == ENOENT \
|| (Errno) == EPERM || (Errno) == EACCES \
|| (Errno) == ENOSYS /* Solaris 8 */ \
|| (Errno) == EOPNOTSUPP /* FreeBSD */)
/* Wrapper function shared among linkat and renameat. */
int at_func2 (int fd1, char const *file1,
int fd2, char const *file2,
int (*func) (char const *file1, char const *file2));
#endif /* _GL_HEADER_OPENAT_PRIV */

110
lib/openat-proc.c Normal file
View File

@ -0,0 +1,110 @@
/* Create /proc/self/fd-related names for subfiles of open directories.
Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Eggert. */
#include <config.h>
#include "openat-priv.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "intprops.h"
/* The results of open() in this file are not used with fchdir,
and we do not leak fds to any single-threaded code that could use stdio,
therefore save some unnecessary work in fchdir.c.
FIXME - if the kernel ever adds support for multi-thread safety for
avoiding standard fds, then we should use open_safer. */
#undef open
#undef close
#define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/%s"
#define PROC_SELF_FD_NAME_SIZE_BOUND(len) \
(sizeof PROC_SELF_FD_FORMAT - sizeof "%d%s" \
+ INT_STRLEN_BOUND (int) + (len) + 1)
/* Set BUF to the expansion of PROC_SELF_FD_FORMAT, using FD and FILE
respectively for %d and %s. If successful, return BUF if the
result fits in BUF, dynamically allocated memory otherwise. But
return NULL if /proc is not reliable, either because the operating
system support is lacking or because memory is low. */
char *
openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file)
{
static int proc_status = 0;
/* Make sure the caller gets ENOENT when appropriate. */
if (!*file)
{
buf[0] = '\0';
return buf;
}
if (! proc_status)
{
/* Set PROC_STATUS to a positive value if /proc/self/fd is
reliable, and a negative value otherwise. Solaris 10
/proc/self/fd mishandles "..", and any file name might expand
to ".." after symbolic link expansion, so avoid /proc/self/fd
if it mishandles "..". Solaris 10 has openat, but this
problem is exhibited on code that built on Solaris 8 and
running on Solaris 10. */
int proc_self_fd = open ("/proc/self/fd",
O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
if (proc_self_fd < 0)
proc_status = -1;
else
{
/* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
number of a file descriptor open on /proc/self/fd. On Linux,
that name resolves to /proc/self/fd, which was opened above.
However, on Solaris, it may resolve to /proc/self/fd/fd, which
cannot exist, since all names in /proc/self/fd are numeric. */
char dotdot_buf[PROC_SELF_FD_NAME_SIZE_BOUND (sizeof "../fd" - 1)];
sprintf (dotdot_buf, PROC_SELF_FD_FORMAT, proc_self_fd, "../fd");
proc_status = access (dotdot_buf, F_OK) ? -1 : 1;
close (proc_self_fd);
}
}
if (proc_status < 0)
return NULL;
else
{
size_t bufsize = PROC_SELF_FD_NAME_SIZE_BOUND (strlen (file));
char *result = buf;
if (OPENAT_BUFFER_SIZE < bufsize)
{
result = malloc (bufsize);
if (! result)
return NULL;
}
sprintf (result, PROC_SELF_FD_FORMAT, fd, file);
return result;
}
}

120
lib/openat.h Normal file
View File

@ -0,0 +1,120 @@
/* provide a replacement openat function
Copyright (C) 2004-2006, 2008-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* written by Jim Meyering */
#ifndef _GL_HEADER_OPENAT
#define _GL_HEADER_OPENAT
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdbool.h>
_GL_INLINE_HEADER_BEGIN
#if !HAVE_OPENAT
int openat_permissive (int fd, char const *file, int flags, mode_t mode,
int *cwd_errno);
bool openat_needs_fchdir (void);
#else
# define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \
openat (Fd, File, Flags, Mode)
# define openat_needs_fchdir() false
#endif
_Noreturn void openat_restore_fail (int);
_Noreturn void openat_save_fail (int);
/* Using these function names makes application code
slightly more readable than it would be with
fchownat (..., 0) or fchownat (..., AT_SYMLINK_NOFOLLOW). */
#if GNULIB_FCHOWNAT
# ifndef FCHOWNAT_INLINE
# define FCHOWNAT_INLINE _GL_INLINE
# endif
FCHOWNAT_INLINE int
chownat (int fd, char const *file, uid_t owner, gid_t group)
{
return fchownat (fd, file, owner, group, 0);
}
FCHOWNAT_INLINE int
lchownat (int fd, char const *file, uid_t owner, gid_t group)
{
return fchownat (fd, file, owner, group, AT_SYMLINK_NOFOLLOW);
}
#endif
#if GNULIB_FCHMODAT
# ifndef FCHMODAT_INLINE
# define FCHMODAT_INLINE _GL_INLINE
# endif
FCHMODAT_INLINE int
chmodat (int fd, char const *file, mode_t mode)
{
return fchmodat (fd, file, mode, 0);
}
FCHMODAT_INLINE int
lchmodat (int fd, char const *file, mode_t mode)
{
return fchmodat (fd, file, mode, AT_SYMLINK_NOFOLLOW);
}
#endif
#if GNULIB_STATAT
# ifndef STATAT_INLINE
# define STATAT_INLINE _GL_INLINE
# endif
STATAT_INLINE int
statat (int fd, char const *name, struct stat *st)
{
return fstatat (fd, name, st, 0);
}
STATAT_INLINE int
lstatat (int fd, char const *name, struct stat *st)
{
return fstatat (fd, name, st, AT_SYMLINK_NOFOLLOW);
}
#endif
/* For now, there are no wrappers named laccessat or leuidaccessat,
since gnulib doesn't support faccessat(,AT_SYMLINK_NOFOLLOW) and
since access rights on symlinks are of limited utility. Likewise,
wrappers are not provided for accessat or euidaccessat, so as to
avoid dragging in -lgen on some platforms. */
_GL_INLINE_HEADER_END
#endif /* _GL_HEADER_OPENAT */

47
lib/readlinkat.c Normal file
View File

@ -0,0 +1,47 @@
/* Read a symlink relative to an open directory.
Copyright (C) 2009-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* written by Eric Blake */
#include <config.h>
#include <unistd.h>
/* Gnulib provides a readlink stub for mingw; use it for distinction
between EINVAL and ENOENT, rather than always failing with ENOSYS. */
/* POSIX 2008 says that unlike readlink, readlinkat returns 0 for
success instead of the buffer length. But this would render
readlinkat worthless since readlink does not guarantee a
NUL-terminated buffer. Assume this was a bug in POSIX. */
/* Read the contents of symlink FILE into buffer BUF of size LEN, in the
directory open on descriptor FD. If possible, do it without changing
the working directory. Otherwise, resort to using save_cwd/fchdir,
then readlink/restore_cwd. If either the save_cwd or the restore_cwd
fails, then give a diagnostic and exit nonzero. */
#define AT_FUNC_NAME readlinkat
#define AT_FUNC_F1 readlink
#define AT_FUNC_POST_FILE_PARAM_DECLS , char *buf, size_t len
#define AT_FUNC_POST_FILE_ARGS , buf, len
#define AT_FUNC_RESULT ssize_t
#include "at-func.c"
#undef AT_FUNC_NAME
#undef AT_FUNC_F1
#undef AT_FUNC_POST_FILE_PARAM_DECLS
#undef AT_FUNC_POST_FILE_ARGS
#undef AT_FUNC_RESULT

3
lib/save-cwd.c Normal file
View File

@ -0,0 +1,3 @@
#include <config.h>
#define SAVE_CWD_INLINE _GL_EXTERN_INLINE
#include "save-cwd.h"

46
lib/save-cwd.h Normal file
View File

@ -0,0 +1,46 @@
/* Do not save and restore the current working directory.
Copyright 2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Gnulib needs to save and restore the current working directory to
fully emulate functions like fstatat. But Emacs doesn't care what
the current working directory is; it always uses absolute file
names. This module replaces the Gnulib module by omitting the code
that Emacs does not need. */
#ifndef SAVE_CWD_H
#define SAVE_CWD_H 1
_GL_INLINE_HEADER_BEGIN
#ifndef SAVE_CWD_INLINE
# define SAVE_CWD_INLINE _GL_INLINE
#endif
struct saved_cwd { int desc; };
SAVE_CWD_INLINE int
save_cwd (struct saved_cwd *cwd)
{
cwd->desc = -1;
return 0;
}
SAVE_CWD_INLINE int restore_cwd (struct saved_cwd const *cwd) { return 0; }
SAVE_CWD_INLINE void free_cwd (struct saved_cwd *cwd) { }
_GL_INLINE_HEADER_END
#endif

64
m4/dirent_h.m4 Normal file
View File

@ -0,0 +1,64 @@
# dirent_h.m4 serial 16
dnl Copyright (C) 2008-2013 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
dnl Written by Bruno Haible.
AC_DEFUN([gl_DIRENT_H],
[
dnl Use AC_REQUIRE here, so that the default behavior below is expanded
dnl once only, before all statements that occur in other macros.
AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
dnl <dirent.h> is always overridden, because of GNULIB_POSIXCHECK.
gl_CHECK_NEXT_HEADERS([dirent.h])
if test $ac_cv_header_dirent_h = yes; then
HAVE_DIRENT_H=1
else
HAVE_DIRENT_H=0
fi
AC_SUBST([HAVE_DIRENT_H])
dnl Check for declarations of anything we want to poison if the
dnl corresponding gnulib module is not in use.
gl_WARN_ON_USE_PREPARE([[#include <dirent.h>
]], [alphasort closedir dirfd fdopendir opendir readdir rewinddir scandir])
])
AC_DEFUN([gl_DIRENT_MODULE_INDICATOR],
[
dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
gl_MODULE_INDICATOR_SET_VARIABLE([$1])
dnl Define it also as a C macro, for the benefit of the unit tests.
gl_MODULE_INDICATOR_FOR_TESTS([$1])
])
AC_DEFUN([gl_DIRENT_H_DEFAULTS],
[
AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl for REPLACE_FCHDIR
GNULIB_OPENDIR=0; AC_SUBST([GNULIB_OPENDIR])
GNULIB_READDIR=0; AC_SUBST([GNULIB_READDIR])
GNULIB_REWINDDIR=0; AC_SUBST([GNULIB_REWINDDIR])
GNULIB_CLOSEDIR=0; AC_SUBST([GNULIB_CLOSEDIR])
GNULIB_DIRFD=0; AC_SUBST([GNULIB_DIRFD])
GNULIB_FDOPENDIR=0; AC_SUBST([GNULIB_FDOPENDIR])
GNULIB_SCANDIR=0; AC_SUBST([GNULIB_SCANDIR])
GNULIB_ALPHASORT=0; AC_SUBST([GNULIB_ALPHASORT])
dnl Assume proper GNU behavior unless another module says otherwise.
HAVE_OPENDIR=1; AC_SUBST([HAVE_OPENDIR])
HAVE_READDIR=1; AC_SUBST([HAVE_READDIR])
HAVE_REWINDDIR=1; AC_SUBST([HAVE_REWINDDIR])
HAVE_CLOSEDIR=1; AC_SUBST([HAVE_CLOSEDIR])
HAVE_DECL_DIRFD=1; AC_SUBST([HAVE_DECL_DIRFD])
HAVE_DECL_FDOPENDIR=1;AC_SUBST([HAVE_DECL_FDOPENDIR])
HAVE_FDOPENDIR=1; AC_SUBST([HAVE_FDOPENDIR])
HAVE_SCANDIR=1; AC_SUBST([HAVE_SCANDIR])
HAVE_ALPHASORT=1; AC_SUBST([HAVE_ALPHASORT])
REPLACE_OPENDIR=0; AC_SUBST([REPLACE_OPENDIR])
REPLACE_CLOSEDIR=0; AC_SUBST([REPLACE_CLOSEDIR])
REPLACE_DIRFD=0; AC_SUBST([REPLACE_DIRFD])
REPLACE_FDOPENDIR=0; AC_SUBST([REPLACE_FDOPENDIR])
])

61
m4/fdopendir.m4 Normal file
View File

@ -0,0 +1,61 @@
# serial 10
# See if we need to provide fdopendir.
dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
# Written by Eric Blake.
AC_DEFUN([gl_FUNC_FDOPENDIR],
[
AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
dnl FreeBSD 7.3 has the function, but failed to declare it.
AC_CHECK_DECLS([fdopendir], [], [HAVE_DECL_FDOPENDIR=0], [[
#include <dirent.h>
]])
AC_CHECK_FUNCS_ONCE([fdopendir])
if test $ac_cv_func_fdopendir = no; then
HAVE_FDOPENDIR=0
else
AC_CACHE_CHECK([whether fdopendir works],
[gl_cv_func_fdopendir_works],
[AC_RUN_IFELSE([AC_LANG_PROGRAM([[
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#if !HAVE_DECL_FDOPENDIR
extern
# ifdef __cplusplus
"C"
# endif
DIR *fdopendir (int);
#endif
]], [int result = 0;
int fd = open ("conftest.c", O_RDONLY);
if (fd < 0) result |= 1;
if (fdopendir (fd)) result |= 2;
if (close (fd)) result |= 4;
return result;])],
[gl_cv_func_fdopendir_works=yes],
[gl_cv_func_fdopendir_works=no],
[case "$host_os" in
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_fdopendir_works="guessing yes" ;;
# If we don't know, assume the worst.
*) gl_cv_func_fdopendir_works="guessing no" ;;
esac
])])
case "$gl_cv_func_fdopendir_works" in
*yes) ;;
*)
REPLACE_FDOPENDIR=1
;;
esac
fi
])

60
m4/fstatat.m4 Normal file
View File

@ -0,0 +1,60 @@
# fstatat.m4 serial 3
dnl Copyright (C) 2004-2013 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
# Written by Jim Meyering.
# If we have the fstatat function, and it has the bug (in AIX 7.1)
# that it does not fill in st_size correctly, use the replacement function.
AC_DEFUN([gl_FUNC_FSTATAT],
[
AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK])
AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
AC_CHECK_FUNCS_ONCE([fstatat])
if test $ac_cv_func_fstatat = no; then
HAVE_FSTATAT=0
else
dnl Test for an AIX 7.1 bug; see
dnl <http://lists.gnu.org/archive/html/bug-tar/2011-09/msg00015.html>.
AC_CACHE_CHECK([whether fstatat (..., 0) works],
[gl_cv_func_fstatat_zero_flag],
[AC_RUN_IFELSE(
[AC_LANG_SOURCE(
[[
#include <fcntl.h>
#include <sys/stat.h>
int
main (void)
{
struct stat a;
return fstatat (AT_FDCWD, ".", &a, 0) != 0;
}
]])],
[gl_cv_func_fstatat_zero_flag=yes],
[gl_cv_func_fstatat_zero_flag=no],
[case "$host_os" in
aix*) gl_cv_func_fstatat_zero_flag="guessing no";;
*) gl_cv_func_fstatat_zero_flag="guessing yes";;
esac
])
])
case $gl_cv_func_fstatat_zero_flag+$gl_cv_func_lstat_dereferences_slashed_symlink in
*yes+*yes) ;;
*) REPLACE_FSTATAT=1
case $gl_cv_func_fstatat_zero_flag in
*yes)
AC_DEFINE([HAVE_WORKING_FSTATAT_ZERO_FLAG], [1],
[Define to 1 if fstatat (..., 0) works.
For example, it does not work in AIX 7.1.])
;;
esac
;;
esac
fi
])

View File

@ -40,6 +40,7 @@ AC_DEFUN([gl_EARLY],
AC_REQUIRE([gl_PROG_AR_RANLIB])
# Code from module alloca-opt:
# Code from module allocator:
# Code from module at-internal:
# Code from module c-ctype:
# Code from module c-strcase:
# Code from module careadlinkat:
@ -49,6 +50,7 @@ AC_DEFUN([gl_EARLY],
# Code from module crypto/sha1:
# Code from module crypto/sha256:
# Code from module crypto/sha512:
# Code from module dirent:
# Code from module dosname:
# Code from module dtoastr:
# Code from module dtotimespec:
@ -61,8 +63,10 @@ AC_DEFUN([gl_EARLY],
# Code from module extern-inline:
# Code from module faccessat:
# Code from module fcntl-h:
# Code from module fdopendir:
# Code from module filemode:
# Code from module fpending:
# Code from module fstatat:
# Code from module getgroups:
# Code from module getloadavg:
# Code from module getopt-gnu:
@ -82,11 +86,13 @@ AC_DEFUN([gl_EARLY],
# Code from module mktime:
# Code from module multiarch:
# Code from module nocrash:
# Code from module openat-h:
# Code from module pathmax:
# Code from module pselect:
# Code from module pthread_sigmask:
# Code from module putenv:
# Code from module readlink:
# Code from module readlinkat:
# Code from module root-uid:
# Code from module sig2str:
# Code from module signal-h:
@ -159,6 +165,7 @@ AC_DEFUN([gl_INIT],
gl_SHA1
gl_SHA256
gl_SHA512
gl_DIRENT_H
AC_REQUIRE([gl_C99_STRTOLD])
gl_FUNC_DUP2
if test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1; then
@ -178,12 +185,23 @@ AC_DEFUN([gl_INIT],
gl_MODULE_INDICATOR([faccessat])
gl_UNISTD_MODULE_INDICATOR([faccessat])
gl_FCNTL_H
gl_FUNC_FDOPENDIR
if test $HAVE_FDOPENDIR = 0 || test $REPLACE_FDOPENDIR = 1; then
AC_LIBOBJ([fdopendir])
fi
gl_DIRENT_MODULE_INDICATOR([fdopendir])
gl_MODULE_INDICATOR([fdopendir])
gl_FILEMODE
gl_FUNC_FPENDING
if test $ac_cv_func___fpending = no; then
AC_LIBOBJ([fpending])
gl_PREREQ_FPENDING
fi
gl_FUNC_FSTATAT
if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
AC_LIBOBJ([fstatat])
fi
gl_SYS_STAT_MODULE_INDICATOR([fstatat])
gl_GETLOADAVG
if test $HAVE_GETLOADAVG = 0; then
AC_LIBOBJ([getloadavg])
@ -253,6 +271,11 @@ AC_DEFUN([gl_INIT],
gl_PREREQ_READLINK
fi
gl_UNISTD_MODULE_INDICATOR([readlink])
gl_FUNC_READLINKAT
if test $HAVE_READLINKAT = 0; then
AC_LIBOBJ([readlinkat])
fi
gl_UNISTD_MODULE_INDICATOR([readlinkat])
gl_FUNC_SIG2STR
if test $ac_cv_func_sig2str = no; then
AC_LIBOBJ([sig2str])
@ -311,11 +334,13 @@ AC_DEFUN([gl_INIT],
fi
gl_STDLIB_MODULE_INDICATOR([unsetenv])
gl_UTIMENS
gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false
gl_gnulib_enabled_dosname=false
gl_gnulib_enabled_euidaccess=false
gl_gnulib_enabled_getgroups=false
gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false
gl_gnulib_enabled_pathmax=false
gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
gl_gnulib_enabled_stat=false
@ -323,6 +348,13 @@ AC_DEFUN([gl_INIT],
gl_gnulib_enabled_strtoull=false
gl_gnulib_enabled_verify=false
gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b ()
{
if ! $gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b; then
AC_LIBOBJ([openat-proc])
gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=true
fi
}
func_gl_gnulib_m4code_dosname ()
{
if ! $gl_gnulib_enabled_dosname; then
@ -385,6 +417,12 @@ AC_DEFUN([gl_INIT],
fi
fi
}
func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 ()
{
if ! $gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7; then
gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=true
fi
}
func_gl_gnulib_m4code_pathmax ()
{
if ! $gl_gnulib_enabled_pathmax; then
@ -455,12 +493,30 @@ AC_DEFUN([gl_INIT],
gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true
fi
}
if test $HAVE_FACCESSAT = 0; then
func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
fi
if test $HAVE_FACCESSAT = 0; then
func_gl_gnulib_m4code_dosname
fi
if test $HAVE_FACCESSAT = 0; then
func_gl_gnulib_m4code_euidaccess
fi
if test $HAVE_FACCESSAT = 0; then
func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
fi
if test $HAVE_FDOPENDIR = 0; then
func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
fi
if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
fi
if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
func_gl_gnulib_m4code_dosname
fi
if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
fi
if test $REPLACE_GETOPT = 1; then
func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
fi
@ -473,6 +529,15 @@ AC_DEFUN([gl_INIT],
if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then
func_gl_gnulib_m4code_stat
fi
if test $HAVE_READLINKAT = 0; then
func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
fi
if test $HAVE_READLINKAT = 0; then
func_gl_gnulib_m4code_dosname
fi
if test $HAVE_READLINKAT = 0; then
func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
fi
if { test $HAVE_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then
func_gl_gnulib_m4code_strtoll
fi
@ -486,11 +551,13 @@ AC_DEFUN([gl_INIT],
func_gl_gnulib_m4code_verify
fi
m4_pattern_allow([^gl_GNULIB_ENABLED_])
AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b])
AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess])
AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7])
AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
@ -656,6 +723,7 @@ AC_DEFUN([gl_FILE_LIST], [
lib/careadlinkat.h
lib/close-stream.c
lib/close-stream.h
lib/dirent.in.h
lib/dosname.h
lib/dtoastr.c
lib/dtotimespec.c
@ -665,10 +733,12 @@ AC_DEFUN([gl_FILE_LIST], [
lib/execinfo.in.h
lib/faccessat.c
lib/fcntl.in.h
lib/fdopendir.c
lib/filemode.c
lib/filemode.h
lib/fpending.c
lib/fpending.h
lib/fstatat.c
lib/ftoastr.c
lib/ftoastr.h
lib/getgroups.c
@ -689,11 +759,15 @@ AC_DEFUN([gl_FILE_LIST], [
lib/md5.h
lib/mktime-internal.h
lib/mktime.c
lib/openat-priv.h
lib/openat-proc.c
lib/openat.h
lib/pathmax.h
lib/pselect.c
lib/pthread_sigmask.c
lib/putenv.c
lib/readlink.c
lib/readlinkat.c
lib/root-uid.h
lib/sha1.c
lib/sha1.h
@ -746,6 +820,7 @@ AC_DEFUN([gl_FILE_LIST], [
m4/c-strtod.m4
m4/clock_time.m4
m4/close-stream.m4
m4/dirent_h.m4
m4/dup2.m4
m4/environ.m4
m4/euidaccess.m4
@ -755,8 +830,10 @@ AC_DEFUN([gl_FILE_LIST], [
m4/faccessat.m4
m4/fcntl-o.m4
m4/fcntl_h.m4
m4/fdopendir.m4
m4/filemode.m4
m4/fpending.m4
m4/fstatat.m4
m4/getgroups.m4
m4/getloadavg.m4
m4/getopt.m4
@ -780,6 +857,7 @@ AC_DEFUN([gl_FILE_LIST], [
m4/pthread_sigmask.m4
m4/putenv.m4
m4/readlink.m4
m4/readlinkat.m4
m4/setenv.m4
m4/sha1.m4
m4/sha256.m4

19
m4/readlinkat.m4 Normal file
View File

@ -0,0 +1,19 @@
# serial 3
# See if we need to provide readlinkat replacement.
dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
# Written by Eric Blake.
AC_DEFUN([gl_FUNC_READLINKAT],
[
AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
AC_CHECK_FUNCS_ONCE([readlinkat])
if test $ac_cv_func_readlinkat = no; then
HAVE_READLINKAT=0
fi
])

View File

@ -1,3 +1,9 @@
2013-02-01 Paul Eggert <eggert@cs.ucla.edu>
Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
* inc/sys/stat.h (fstatat):
* inc/unistd.h (readlinkat): New decls.
2013-01-28 Eli Zaretskii <eliz@gnu.org>
* inc/dirent.h (opendir): Update prototype.

View File

@ -110,6 +110,7 @@ _CRTIMP int __cdecl __MINGW_NOTHROW fstat (int, struct stat*);
_CRTIMP int __cdecl __MINGW_NOTHROW chmod (const char*, int);
_CRTIMP int __cdecl __MINGW_NOTHROW stat (const char*, struct stat*);
_CRTIMP int __cdecl __MINGW_NOTHROW lstat (const char*, struct stat*);
_CRTIMP int __cdecl __MINGW_NOTHROW fstatat (int, char const *,
struct stat *, int);
#endif /* INC_SYS_STAT_H_ */

View File

@ -12,6 +12,7 @@
and whose prototypes are usually found in unistd.h on POSIX
platforms. */
extern ssize_t readlink (const char *, char *, size_t);
extern ssize_t readlinkat (int, const char *, char *, size_t);
extern int symlink (char const *, char const *);
extern int setpgid (pid_t, pid_t);
extern pid_t getpgrp (void);

View File

@ -1,3 +1,34 @@
2013-02-01 Paul Eggert <eggert@cs.ucla.edu>
Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
* conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove.
* dired.c: Include <fcntl.h>.
(open_directory): New function, which uses open and fdopendir
rather than opendir. DOS_NT platforms still use opendir, though.
(directory_files_internal, file_name_completion): Use it.
(file_attributes): New function, with most of the old Ffile_attributes.
(directory_files_internal, Ffile_attributes): Use it.
(file_attributes, file_name_completion_stat): First arg is now fd,
not dir name. All uses changed. Use fstatat rather than lstat +
stat.
(file_attributes): Use emacs_readlinkat rather than Ffile_symlink_p.
* fileio.c: Include <allocator.h>, <careadlinkat.h>.
(emacs_readlinkat): New function, with much of the old
Ffile_symlink_p, but with an fd argument for speed.
It uses readlinkat rather than careadlinkatcwd, so that it
need not assume the working directory.
(Ffile_symlink_p): Use it.
* filelock.c (current_lock_owner): Use emacs_readlinkat
rather than emacs_readlink.
* lisp.h (emacs_readlinkat): New decl.
(READLINK_BUFSIZE, emacs_readlink): Remove.
* sysdep.c: Do not include <allocator.h>, <careadlinkat.h>.
(emacs_norealloc_allocator, emacs_readlink): Remove.
This stuff is moved to fileio.c.
* w32.c (fstatat, readlinkat): New functions.
(careadlinkat): Don't check that fd == AT_FDCWD.
(careadlinkatcwd): Remove; no longer needed.
2013-01-31 Glenn Morris <rgm@gnu.org>
* fileio.c (choose_write_coding_system): Make it callable from Lisp.

View File

@ -182,10 +182,6 @@ extern void _DebPrint (const char *fmt, ...);
#endif
#endif
/* Tell gnulib to omit support for openat-related functions having a
first argument other than AT_FDCWD. */
#define GNULIB_SUPPORT_ONLY_AT_FDCWD
#include <string.h>
#include <stdlib.h>

View File

@ -30,6 +30,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <grp.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
@ -54,6 +55,7 @@ static Lisp_Object Qfile_attributes;
static Lisp_Object Qfile_attributes_lessp;
static ptrdiff_t scmp (const char *, const char *, ptrdiff_t);
static Lisp_Object file_attributes (int, char const *, Lisp_Object);
/* Return the number of bytes in DP's name. */
static ptrdiff_t
@ -66,6 +68,44 @@ dirent_namelen (struct dirent *dp)
#endif
}
static DIR *
open_directory (char const *name, int *fdp)
{
DIR *d;
int fd, opendir_errno;
block_input ();
#ifdef DOS_NT
/* Directories cannot be opened. The emulation assumes that any
file descriptor other than AT_FDCWD corresponds to the most
recently opened directory. This hack is good enough for Emacs. */
fd = 0;
d = opendir (name);
opendir_errno = errno;
#else
fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
if (fd < 0)
{
opendir_errno = errno;
d = 0;
}
else
{
d = fdopendir (fd);
opendir_errno = errno;
if (! d)
close (fd);
}
#endif
unblock_input ();
*fdp = fd;
errno = opendir_errno;
return d;
}
#ifdef WINDOWSNT
Lisp_Object
directory_files_internal_w32_unwind (Lisp_Object arg)
@ -96,6 +136,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
Lisp_Object id_format)
{
DIR *d;
int fd;
ptrdiff_t directory_nbytes;
Lisp_Object list, dirfilename, encoded_directory;
struct re_pattern_buffer *bufp = NULL;
@ -142,9 +183,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
/* Now *bufp is the compiled form of MATCH; don't call anything
which might compile a new regexp until we're done with the loop! */
block_input ();
d = opendir (SSDATA (dirfilename));
unblock_input ();
d = open_directory (SSDATA (dirfilename), &fd);
if (d == NULL)
report_file_error ("Opening directory", Fcons (directory, Qnil));
@ -259,20 +298,9 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
if (attrs)
{
/* Construct an expanded filename for the directory entry.
Use the decoded names for input to Ffile_attributes. */
Lisp_Object decoded_fullname, fileattrs;
struct gcpro gcpro1, gcpro2;
decoded_fullname = fileattrs = Qnil;
GCPRO2 (decoded_fullname, fileattrs);
/* Both Fexpand_file_name and Ffile_attributes can GC. */
decoded_fullname = Fexpand_file_name (name, directory);
fileattrs = Ffile_attributes (decoded_fullname, id_format);
Lisp_Object fileattrs
= file_attributes (fd, dp->d_name, id_format);
list = Fcons (Fcons (finalname, fileattrs), list);
UNGCPRO;
}
else
list = Fcons (finalname, list);
@ -413,8 +441,7 @@ These are all file names in directory DIRECTORY which begin with FILE. */)
return file_name_completion (file, directory, 1, Qnil);
}
static int file_name_completion_stat (Lisp_Object dirname, struct dirent *dp,
struct stat *st_addr);
static int file_name_completion_stat (int, struct dirent *, struct stat *);
static Lisp_Object Qdefault_directory;
static Lisp_Object
@ -422,6 +449,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
Lisp_Object predicate)
{
DIR *d;
int fd;
ptrdiff_t bestmatchsize = 0;
int matchcount = 0;
/* If ALL_FLAG is 1, BESTMATCH is the list of all matches, decoded.
@ -458,9 +486,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
encoded_dir = ENCODE_FILE (dirname);
block_input ();
d = opendir (SSDATA (Fdirectory_file_name (encoded_dir)));
unblock_input ();
d = open_directory (SSDATA (Fdirectory_file_name (encoded_dir)), &fd);
if (!d)
report_file_error ("Opening directory", Fcons (dirname, Qnil));
@ -495,7 +521,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
SCHARS (encoded_file)))
continue;
if (file_name_completion_stat (encoded_dir, dp, &st) < 0)
if (file_name_completion_stat (fd, dp, &st) < 0)
continue;
directoryp = S_ISDIR (st.st_mode) != 0;
@ -772,14 +798,9 @@ scmp (const char *s1, const char *s2, ptrdiff_t len)
}
static int
file_name_completion_stat (Lisp_Object dirname, struct dirent *dp,
struct stat *st_addr)
file_name_completion_stat (int fd, struct dirent *dp, struct stat *st_addr)
{
ptrdiff_t len = dirent_namelen (dp);
ptrdiff_t pos = SCHARS (dirname);
int value;
USE_SAFE_ALLOCA;
char *fullname = SAFE_ALLOCA (len + pos + 2);
#ifdef MSDOS
/* Some fields of struct stat are *very* expensive to compute on MS-DOS,
@ -792,23 +813,15 @@ file_name_completion_stat (Lisp_Object dirname, struct dirent *dp,
_djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
#endif /* MSDOS */
memcpy (fullname, SDATA (dirname), pos);
if (!IS_DIRECTORY_SEP (fullname[pos - 1]))
fullname[pos++] = DIRECTORY_SEP;
memcpy (fullname + pos, dp->d_name, len);
fullname[pos + len] = 0;
/* We want to return success if a link points to a nonexistent file,
but we want to return the status for what the link points to,
in case it is a directory. */
value = lstat (fullname, st_addr);
value = fstatat (fd, dp->d_name, st_addr, AT_SYMLINK_NOFOLLOW);
if (value == 0 && S_ISLNK (st_addr->st_mode))
stat (fullname, st_addr);
fstatat (fd, dp->d_name, st_addr, 0);
#ifdef MSDOS
_djstat_flags = save_djstat_flags;
#endif /* MSDOS */
SAFE_FREE ();
return value;
}
@ -886,18 +899,8 @@ On some FAT-based filesystems, only the date of last access is recorded,
so last access time will always be midnight of that day. */)
(Lisp_Object filename, Lisp_Object id_format)
{
Lisp_Object values[12];
Lisp_Object encoded;
struct stat s;
int lstat_result;
/* An array to hold the mode string generated by filemodestring,
including its terminating space and null byte. */
char modes[sizeof "-rwxr-xr-x "];
Lisp_Object handler;
struct gcpro gcpro1;
char *uname = NULL, *gname = NULL;
filename = Fexpand_file_name (filename, Qnil);
@ -913,9 +916,22 @@ so last access time will always be midnight of that day. */)
return call3 (handler, Qfile_attributes, filename, id_format);
}
GCPRO1 (filename);
encoded = ENCODE_FILE (filename);
UNGCPRO;
return file_attributes (AT_FDCWD, SSDATA (encoded), id_format);
}
static Lisp_Object
file_attributes (int fd, char const *name, Lisp_Object id_format)
{
Lisp_Object values[12];
struct stat s;
int lstat_result;
/* An array to hold the mode string generated by filemodestring,
including its terminating space and null byte. */
char modes[sizeof "-rwxr-xr-x "];
char *uname = NULL, *gname = NULL;
#ifdef WINDOWSNT
/* We usually don't request accurate owner and group info, because
@ -925,7 +941,7 @@ so last access time will always be midnight of that day. */)
w32_stat_get_owner_group = 1;
#endif
lstat_result = lstat (SSDATA (encoded), &s);
lstat_result = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW);
#ifdef WINDOWSNT
w32_stat_get_owner_group = 0;
@ -934,7 +950,7 @@ so last access time will always be midnight of that day. */)
if (lstat_result < 0)
return Qnil;
values[0] = (S_ISLNK (s.st_mode) ? Ffile_symlink_p (filename)
values[0] = (S_ISLNK (s.st_mode) ? emacs_readlinkat (fd, name)
: S_ISDIR (s.st_mode) ? Qt : Qnil);
values[1] = make_number (s.st_nlink);

View File

@ -82,6 +82,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#endif
#include "systime.h"
#include <allocator.h>
#include <careadlinkat.h>
#include <stat-time.h>
#ifdef HPUX
@ -2759,6 +2761,29 @@ If there is no error, returns nil. */)
return Qnil;
}
/* Relative to directory FD, return the symbolic link value of FILENAME.
On failure, return nil. */
Lisp_Object
emacs_readlinkat (int fd, char const *filename)
{
static struct allocator const emacs_norealloc_allocator =
{ xmalloc, NULL, xfree, memory_full };
Lisp_Object val;
char readlink_buf[1024];
char *buf = careadlinkat (fd, filename, readlink_buf, sizeof readlink_buf,
&emacs_norealloc_allocator, readlinkat);
if (!buf)
return Qnil;
val = build_string (buf);
if (buf[0] == '/' && strchr (buf, ':'))
val = concat2 (build_string ("/:"), val);
if (buf != readlink_buf)
xfree (buf);
val = DECODE_FILE (val);
return val;
}
DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
doc: /* Return non-nil if file FILENAME is the name of a symbolic link.
The value is the link target, as a string.
@ -2769,9 +2794,6 @@ points to a nonexistent file. */)
(Lisp_Object filename)
{
Lisp_Object handler;
char *buf;
Lisp_Object val;
char readlink_buf[READLINK_BUFSIZE];
CHECK_STRING (filename);
filename = Fexpand_file_name (filename, Qnil);
@ -2784,17 +2806,7 @@ points to a nonexistent file. */)
filename = ENCODE_FILE (filename);
buf = emacs_readlink (SSDATA (filename), readlink_buf);
if (! buf)
return Qnil;
val = build_string (buf);
if (buf[0] == '/' && strchr (buf, ':'))
val = concat2 (build_string ("/:"), val);
if (buf != readlink_buf)
xfree (buf);
val = DECODE_FILE (val);
return val;
return emacs_readlinkat (AT_FDCWD, SSDATA (filename));
}
DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0,

View File

@ -390,12 +390,14 @@ current_lock_owner (lock_info_type *owner, char *lfname)
lock_info_type local_owner;
intmax_t n;
char *at, *dot, *colon;
char readlink_buf[READLINK_BUFSIZE];
char *lfinfo = emacs_readlink (lfname, readlink_buf);
Lisp_Object lfinfo_object = emacs_readlinkat (AT_FDCWD, lfname);
char *lfinfo;
struct gcpro gcpro1;
/* If nonexistent lock file, all is well; otherwise, got strange error. */
if (!lfinfo)
if (NILP (lfinfo_object))
return errno == ENOENT ? 0 : -1;
lfinfo = SSDATA (lfinfo_object);
/* Even if the caller doesn't want the owner info, we still have to
read it to determine return value. */
@ -407,12 +409,9 @@ current_lock_owner (lock_info_type *owner, char *lfname)
at = strrchr (lfinfo, '@');
dot = strrchr (lfinfo, '.');
if (!at || !dot)
{
if (lfinfo != readlink_buf)
xfree (lfinfo);
return -1;
}
len = at - lfinfo;
GCPRO1 (lfinfo_object);
owner->user = xmalloc (len + 1);
memcpy (owner->user, lfinfo, len);
owner->user[len] = 0;
@ -445,8 +444,7 @@ current_lock_owner (lock_info_type *owner, char *lfname)
owner->host[len] = 0;
/* We're done looking at the link info. */
if (lfinfo != readlink_buf)
xfree (lfinfo);
UNGCPRO;
/* On current host? */
if (STRINGP (Fsystem_name ())

View File

@ -3294,6 +3294,7 @@ extern Lisp_Object close_file_unwind (Lisp_Object);
extern Lisp_Object restore_point_unwind (Lisp_Object);
extern _Noreturn void report_file_error (const char *, Lisp_Object);
extern bool internal_delete_file (Lisp_Object);
extern Lisp_Object emacs_readlinkat (int, const char *);
extern bool file_directory_p (const char *);
extern bool file_accessible_directory_p (const char *);
extern void init_fileio (void);
@ -3566,8 +3567,6 @@ extern int emacs_open (const char *, int, int);
extern int emacs_close (int);
extern ptrdiff_t emacs_read (int, char *, ptrdiff_t);
extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t);
enum { READLINK_BUFSIZE = 1024 };
extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]);
extern void unlock_all_files (void);
extern void lock_file (Lisp_Object);

View File

@ -30,9 +30,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <limits.h>
#include <unistd.h>
#include <allocator.h>
#include <c-ctype.h>
#include <careadlinkat.h>
#include <ignore-value.h>
#include <utimens.h>
@ -2247,22 +2245,6 @@ emacs_write (int fildes, const char *buf, ptrdiff_t nbyte)
return (bytes_written);
}
static struct allocator const emacs_norealloc_allocator =
{ xmalloc, NULL, xfree, memory_full };
/* Get the symbolic link value of FILENAME. Return a pointer to a
NUL-terminated string. If readlink fails, return NULL and set
errno. If the value fits in INITIAL_BUF, return INITIAL_BUF.
Otherwise, allocate memory and return a pointer to that memory. If
memory allocation fails, diagnose and fail without returning. If
successful, store the length of the symbolic link into *LINKLEN. */
char *
emacs_readlink (char const *filename, char initial_buf[READLINK_BUFSIZE])
{
return careadlinkat (AT_FDCWD, filename, initial_buf, READLINK_BUFSIZE,
&emacs_norealloc_allocator, careadlinkatcwd);
}
/* Return a struct timeval that is roughly equivalent to T.
Use the least timeval not less than T.

View File

@ -4274,6 +4274,30 @@ lstat (const char * path, struct stat * buf)
return stat_worker (path, buf, 0);
}
int
fstatat (int fd, char const *name, struct stat *st, int flags)
{
/* Rely on a hack: an open directory is modeled as file descriptor 0.
This is good enough for the current usage in Emacs, but is fragile.
FIXME: Add proper support for fdopendir, fstatatat, readlinkat.
Gnulib does this and can serve as a model. */
char fullname[MAX_PATH];
if (fd != AT_FDCWD)
{
if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
< 0)
{
errno = ENAMETOOLONG;
return -1;
}
name = fullname;
}
return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW));
}
/* Provide fstat and utime as well as stat for consistent handling of
file timestamps. */
int
@ -4816,6 +4840,28 @@ readlink (const char *name, char *buf, size_t buf_size)
return retval;
}
ssize_t
readlinkat (int fd, char const *name, char *buffer,
size_t buffer_size)
{
/* Rely on a hack: an open directory is modeled as file descriptor 0,
as in fstatat. FIXME: Add proper support for readlinkat. */
char fullname[MAX_PATH];
if (fd != AT_FDCWD)
{
if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
< 0)
{
errno = ENAMETOOLONG;
return -1;
}
name = fullname;
}
return readlink (name, buffer, buffer_size);
}
/* If FILE is a symlink, return its target (stored in a static
buffer); otherwise return FILE.
@ -5168,12 +5214,6 @@ careadlinkat (int fd, char const *filename,
char linkname[MAX_PATH];
ssize_t link_size;
if (fd != AT_FDCWD)
{
errno = EINVAL;
return NULL;
}
link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
if (link_size > 0)
@ -5191,14 +5231,6 @@ careadlinkat (int fd, char const *filename,
return NULL;
}
ssize_t
careadlinkatcwd (int fd, char const *filename, char *buffer,
size_t buffer_size)
{
(void) fd;
return readlink (filename, buffer, buffer_size);
}
/* Support for browsing other processes and their attributes. See
process.c for the Lisp bindings. */