From bb385a92ec4f8f5bbc93641aaa274bd735826574 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 12 Oct 2012 17:19:54 +0200 Subject: [PATCH] Fix bug #12587 with slow startup on MS-Windows with Netlogon service. src/fileio.c (check_existing): New function. (make_temp_name, Ffile_exists_p, Ffile_writable_p): Call it instead of calling 'stat', when what's needed is to check whether a file exists. This avoids expensive system calls on MS-Windows. src/w32.c (init_environment): Call 'check_existing' instead of 'stat'. src/lread.c (openp) [WINDOWSNT]: Call 'access' instead of 'stat' to determine whether a file exists and is not a directory. src/lisp.h (check_existing): Add prototype. --- src/ChangeLog | 16 ++++++++++++++++ src/fileio.c | 29 ++++++++++++++++++++--------- src/lisp.h | 1 + src/lread.c | 8 +++++++- src/w32.c | 3 +-- 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 47b2de24844..76f0226c0bf 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,19 @@ +2012-10-12 Eli Zaretskii + + * fileio.c (check_existing): New function. + (make_temp_name, Ffile_exists_p, Ffile_writable_p): Call it + instead of calling 'stat', when what's needed is to check whether + a file exists. This avoids expensive system calls on MS-Windows. + (Bug#12587) + + * w32.c (init_environment): Call 'check_existing' instead of + 'stat'. + + * lread.c (openp) [WINDOWSNT]: Call 'access' instead of 'stat' to + determine whether a file exists and is not a directory. + + * lisp.h (check_existing): Add prototype. + 2012-10-12 Jan Djärv * nsfont.m (nsfont_open): Remove font cache, it is not GC correct. diff --git a/src/fileio.c b/src/fileio.c index b4eda01afcc..5de5bc2b021 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -52,6 +52,7 @@ along with GNU Emacs. If not, see . */ #define NOMINMAX 1 #include #include +#include #endif /* not WINDOWSNT */ #ifdef MSDOS @@ -668,7 +669,6 @@ make_temp_name (Lisp_Object prefix, bool base64_p) while (1) { - struct stat ignored; unsigned num = make_temp_name_count; p[0] = make_temp_name_tbl[num & 63], num >>= 6; @@ -680,7 +680,7 @@ make_temp_name (Lisp_Object prefix, bool base64_p) make_temp_name_count += 25229; make_temp_name_count %= 225307; - if (stat (data, &ignored) < 0) + if (!check_existing (data)) { /* We want to return only if errno is ENOENT. */ if (errno == ENOENT) @@ -2423,6 +2423,21 @@ On Unix, this is a name starting with a `/' or a `~'. */) return file_name_absolute_p (SSDATA (filename)) ? Qt : Qnil; } +/* Return true if FILENAME exists. */ +bool +check_existing (const char *filename) +{ +#ifdef DOS_NT + /* The full emulation of Posix 'stat' is too expensive on + DOS/Windows, when all we want to know is whether the file exists. + So we use 'access' instead, which is much more lightweight. */ + return (access (filename, F_OK) >= 0); +#else + struct stat st; + return (stat (filename, &st) >= 0); +#endif +} + /* Return true if file FILENAME exists and can be executed. */ static bool @@ -2490,7 +2505,6 @@ Use `file-symlink-p' to test for such links. */) { Lisp_Object absname; Lisp_Object handler; - struct stat statbuf; CHECK_STRING (filename); absname = Fexpand_file_name (filename, Qnil); @@ -2503,7 +2517,7 @@ Use `file-symlink-p' to test for such links. */) absname = ENCODE_FILE (absname); - return (stat (SSDATA (absname), &statbuf) >= 0) ? Qt : Qnil; + return (check_existing (SSDATA (absname))) ? Qt : Qnil; } DEFUN ("file-executable-p", Ffile_executable_p, Sfile_executable_p, 1, 1, 0, @@ -2584,7 +2598,6 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0, { Lisp_Object absname, dir, encoded; Lisp_Object handler; - struct stat statbuf; CHECK_STRING (filename); absname = Fexpand_file_name (filename, Qnil); @@ -2596,7 +2609,7 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0, return call2 (handler, Qfile_writable_p, absname); encoded = ENCODE_FILE (absname); - if (stat (SSDATA (encoded), &statbuf) >= 0) + if (check_existing (SSDATA (encoded))) return (check_writable (SSDATA (encoded)) ? Qt : Qnil); @@ -2611,9 +2624,7 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0, /* The read-only attribute of the parent directory doesn't affect whether a file or directory can be created within it. Some day we should check ACLs though, which do affect this. */ - if (stat (SDATA (dir), &statbuf) < 0) - return Qnil; - return S_ISDIR (statbuf.st_mode) ? Qt : Qnil; + return (access (SDATA (dir), D_OK) < 0) ? Qnil : Qt; #else return (check_writable (!NILP (dir) ? SSDATA (dir) : "") ? Qt : Qnil); diff --git a/src/lisp.h b/src/lisp.h index 62e287561cd..7afe7b373fe 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3182,6 +3182,7 @@ extern void internal_delete_file (Lisp_Object); extern void syms_of_fileio (void); extern Lisp_Object make_temp_name (Lisp_Object, bool); extern Lisp_Object Qdelete_file; +extern bool check_existing (const char *); /* Defined in search.c. */ extern void shrink_regexp_cache (void); diff --git a/src/lread.c b/src/lread.c index dbbde694cf6..6d4c0d990af 100644 --- a/src/lread.c +++ b/src/lread.c @@ -1449,7 +1449,6 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *sto bool absolute = 0; ptrdiff_t want_length; Lisp_Object filename; - struct stat st; struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6; Lisp_Object string, tail, encoded_fn; ptrdiff_t max_suffix_len = 0; @@ -1543,11 +1542,18 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *sto } else { +#ifndef WINDOWSNT + struct stat st; +#endif const char *pfn; encoded_fn = ENCODE_FILE (string); pfn = SSDATA (encoded_fn); +#ifdef WINDOWSNT + exists = access (pfn, F_OK) == 0 && access (pfn, D_OK) < 0; +#else exists = (stat (pfn, &st) == 0 && ! S_ISDIR (st.st_mode)); +#endif if (exists) { /* Check that we can access or open it. */ diff --git a/src/w32.c b/src/w32.c index b50cd13517d..15903dbceb3 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1612,7 +1612,6 @@ init_environment (char ** argv) LPBYTE lpval; DWORD dwType; char locale_name[32]; - struct stat ignored; char default_home[MAX_PATH]; int appdata = 0; @@ -1653,7 +1652,7 @@ init_environment (char ** argv) /* For backwards compatibility, check if a .emacs file exists in C:/ If not, then we can try to default to the appdata directory under the user's profile, which is more likely to be writable. */ - if (stat ("C:/.emacs", &ignored) < 0) + if (check_existing ("C:/.emacs")) { HRESULT profile_result; /* Dynamically load ShGetFolderPath, as it won't exist on versions -- 2.11.4.GIT