diff options
| author | Paul Eggert | 2013-01-31 22:30:51 -0800 |
|---|---|---|
| committer | Paul Eggert | 2013-01-31 22:30:51 -0800 |
| commit | 8654f9d7d6d7c3ee97232a34a40250dcbc57af8e (patch) | |
| tree | ff488081b57dabdfe7e85c11a34e8ac522b5bdb4 /src | |
| parent | 44b12dd6994a6214b9d6f73539b441080611369b (diff) | |
| download | emacs-8654f9d7d6d7c3ee97232a34a40250dcbc57af8e.tar.gz emacs-8654f9d7d6d7c3ee97232a34a40250dcbc57af8e.zip | |
Use fdopendir, fstatat and readlinkat, for efficiency.
On my host, this speeds up directory-files-and-attributes by a
factor of 3, when applied to Emacs's src directory.
These functions are standardized by POSIX and are common these
days; fall back on a (slower) gnulib implementation if the host
is too old to supply them.
* .bzrignore: Add lib/dirent.h.
* lib/Makefile.am (libgnu_a_SOURCES): Add openat-die.c, save-cwd.c.
* lib/careadlinkat.c, lib/careadlinkat.h: Merge from gnulib,
incorporating: 2013-01-29 careadlinkat: do not provide careadlinkatcwd.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
* lib/dirent.in.h, lib/fdopendir.c, lib/fstatat.c, lib/openat-priv.h:
* lib/openat-proc.c, lib/openat.h, m4/dirent_h.m4, m4/fdopendir.m4:
* m4/fstatat.m4: New files, from gnulib.
* lib/openat-die.c, lib/save-cwd.c, lib/save-cwd.h: New files.
These last three are specific to Emacs and are not copied from gnulib.
They are simpler than the gnulib versions and are tuned for Emacs.
* admin/merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat.
(GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h.
Avoid dup, open, opendir.
* nt/inc/sys/stat.h (fstatat):
* nt/inc/unistd.h (readlinkat): New decls.
* src/conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove.
* src/dired.c: Include <fcntl.h>.
(open_directory): New function, which uses open and fdopendir
rather than opendir. DOS_NT platforms still use opendir, though.
(directory_files_internal, file_name_completion): Use it.
(file_attributes): New function, with most of the old Ffile_attributes.
(directory_files_internal, Ffile_attributes): Use it.
(file_attributes, file_name_completion_stat): First arg is now fd,
not dir name. All uses changed. Use fstatat rather than lstat +
stat.
(file_attributes): Use emacs_readlinkat rather than Ffile_symlink_p.
* src/fileio.c: Include <allocator.h>, <careadlinkat.h>.
(emacs_readlinkat): New function, with much of the old
Ffile_symlink_p, but with an fd argument for speed.
It uses readlinkat rather than careadlinkatcwd, so that it
need not assume the working directory.
(Ffile_symlink_p): Use it.
* src/filelock.c (current_lock_owner): Use emacs_readlinkat
rather than emacs_readlink.
* src/lisp.h (emacs_readlinkat): New decl.
(READLINK_BUFSIZE, emacs_readlink): Remove.
* src/sysdep.c: Do not include <allocator.h>, <careadlinkat.h>.
(emacs_norealloc_allocator, emacs_readlink): Remove.
This stuff is moved to fileio.c.
* src/w32.c (fstatat, readlinkat): New functions.
(careadlinkat): Don't check that fd == AT_FDCWD.
(careadlinkatcwd): Remove; no longer needed.
Fixes: debbugs:13539
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 31 | ||||
| -rw-r--r-- | src/conf_post.h | 4 | ||||
| -rw-r--r-- | src/dired.c | 120 | ||||
| -rw-r--r-- | src/fileio.c | 40 | ||||
| -rw-r--r-- | src/filelock.c | 18 | ||||
| -rw-r--r-- | src/lisp.h | 3 | ||||
| -rw-r--r-- | src/sysdep.c | 18 | ||||
| -rw-r--r-- | src/w32.c | 60 |
8 files changed, 180 insertions, 114 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 5c13e35306d..2156d7f19c9 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,34 @@ | |||
| 1 | 2013-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 | |||
| 1 | 2013-01-31 Glenn Morris <rgm@gnu.org> | 32 | 2013-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; | |||
| 54 | static Lisp_Object Qfile_attributes_lessp; | 55 | static Lisp_Object Qfile_attributes_lessp; |
| 55 | 56 | ||
| 56 | static ptrdiff_t scmp (const char *, const char *, ptrdiff_t); | 57 | static ptrdiff_t scmp (const char *, const char *, ptrdiff_t); |
| 58 | static 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. */ |
| 59 | static ptrdiff_t | 61 | static ptrdiff_t |
| @@ -66,6 +68,44 @@ dirent_namelen (struct dirent *dp) | |||
| 66 | #endif | 68 | #endif |
| 67 | } | 69 | } |
| 68 | 70 | ||
| 71 | static DIR * | ||
| 72 | open_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 |
| 70 | Lisp_Object | 110 | Lisp_Object |
| 71 | directory_files_internal_w32_unwind (Lisp_Object arg) | 111 | directory_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 | ||
| 416 | static int file_name_completion_stat (Lisp_Object dirname, struct dirent *dp, | 444 | static int file_name_completion_stat (int, struct dirent *, struct stat *); |
| 417 | struct stat *st_addr); | ||
| 418 | static Lisp_Object Qdefault_directory; | 445 | static Lisp_Object Qdefault_directory; |
| 419 | 446 | ||
| 420 | static Lisp_Object | 447 | static 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 | ||
| 774 | static int | 800 | static int |
| 775 | file_name_completion_stat (Lisp_Object dirname, struct dirent *dp, | 801 | file_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, | |||
| 886 | so last access time will always be midnight of that day. */) | 899 | so 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 | |||
| 923 | static Lisp_Object | ||
| 924 | file_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. */ | ||
| 2766 | Lisp_Object | ||
| 2767 | emacs_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 | |||
| 2762 | DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0, | 2787 | DEFUN ("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. |
| 2764 | The value is the link target, as a string. | 2789 | The 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 | ||
| 2800 | DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0, | 2812 | DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0, |
diff --git a/src/filelock.c b/src/filelock.c index f21240f8340..228fe98e8c7 100644 --- a/src/filelock.c +++ b/src/filelock.c | |||
| @@ -390,12 +390,14 @@ current_lock_owner (lock_info_type *owner, char *lfname) | |||
| 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); | |||
| 3294 | extern Lisp_Object restore_point_unwind (Lisp_Object); | 3294 | extern Lisp_Object restore_point_unwind (Lisp_Object); |
| 3295 | extern _Noreturn void report_file_error (const char *, Lisp_Object); | 3295 | extern _Noreturn void report_file_error (const char *, Lisp_Object); |
| 3296 | extern bool internal_delete_file (Lisp_Object); | 3296 | extern bool internal_delete_file (Lisp_Object); |
| 3297 | extern Lisp_Object emacs_readlinkat (int, const char *); | ||
| 3297 | extern bool file_directory_p (const char *); | 3298 | extern bool file_directory_p (const char *); |
| 3298 | extern bool file_accessible_directory_p (const char *); | 3299 | extern bool file_accessible_directory_p (const char *); |
| 3299 | extern void init_fileio (void); | 3300 | extern void init_fileio (void); |
| @@ -3566,8 +3567,6 @@ extern int emacs_open (const char *, int, int); | |||
| 3566 | extern int emacs_close (int); | 3567 | extern int emacs_close (int); |
| 3567 | extern ptrdiff_t emacs_read (int, char *, ptrdiff_t); | 3568 | extern ptrdiff_t emacs_read (int, char *, ptrdiff_t); |
| 3568 | extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t); | 3569 | extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t); |
| 3569 | enum { READLINK_BUFSIZE = 1024 }; | ||
| 3570 | extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]); | ||
| 3571 | 3570 | ||
| 3572 | extern void unlock_all_files (void); | 3571 | extern void unlock_all_files (void); |
| 3573 | extern void lock_file (Lisp_Object); | 3572 | extern void lock_file (Lisp_Object); |
diff --git a/src/sysdep.c b/src/sysdep.c index 0e9a6826005..57ca8265a65 100644 --- a/src/sysdep.c +++ b/src/sysdep.c | |||
| @@ -30,9 +30,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 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 | |||
| 2251 | static 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. */ | ||
| 2260 | char * | ||
| 2261 | emacs_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. |
| @@ -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 | ||
| 4277 | int | ||
| 4278 | fstatat (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. */ |
| 4279 | int | 4303 | int |
| @@ -4816,6 +4840,28 @@ readlink (const char *name, char *buf, size_t buf_size) | |||
| 4816 | return retval; | 4840 | return retval; |
| 4817 | } | 4841 | } |
| 4818 | 4842 | ||
| 4843 | ssize_t | ||
| 4844 | readlinkat (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 | ||
| 5194 | ssize_t | ||
| 5195 | careadlinkatcwd (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. */ |