diff options
author | Eli Zaretskii <eliz@gnu.org> | 2013-02-25 19:36:03 +0200 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2013-02-25 19:36:03 +0200 |
commit | 343a2aefb528ce3c978ba2145705b9e37bfbe02a (patch) | |
tree | 4672030eea98dfc9bd077ac58400271d1c812918 /src | |
parent | aec32f66d0db82b562e904dfe7bb6d54796fe773 (diff) | |
download | emacs-343a2aefb528ce3c978ba2145705b9e37bfbe02a.tar.gz |
Implement CLASH_DETECTION for MS-Windows.
src/filelock.c [WINDOWSNT]: Include w32.h.
(MAKE_LOCK_NAME): Don't use 'lock', it clashes with MS runtime
function of that name. Up-case the macro arguments.
(IS_LOCK_FILE): New macro.
(fill_in_lock_file_name): Use IS_LOCK_FILE instead of S_ISLNK.
(create_lock_file): New function, with body extracted from
lock_file_1.
[WINDOWSNT]: Implement lock files by writing a regular file with
the lock information as its contents.
(read_lock_data): New function, on Posix platforms just calls
emacs_readlinkat.
[WINDOWSNT]: Read the lock info from the file.
(current_lock_owner): Call read_lock_data instead of calling
emacs_readlinkat directly.
(lock_file) [WINDOWSNT]: Run the file name through
dostounix_filename.
src/w32proc.c (sys_kill): Support the case of SIG = 0, in which case
just check if the process by that PID exists.
src/w32.c (sys_open): Don't reset the _O_CREAT flag if _O_EXCL is
also present, as doing so will fail to error out if the file
already exists.
src/makefile.w32-in ($(BLD)/filelock.$(O)): Depend on src/w32.h.
nt/inc/ms-w32.h (BOOT_TIME_FILE): Define.
nt/config.nt (CLASH_DETECTION): Define to 1.
lisp/emacs-lisp/bytecomp.el (byte-recompile-directory): Reject files
that match "\`\.#", to avoid compiling lock files, even if they
are readable (as they are on MS-Windows).
doc/emacs/files.texi (Interlocking): Don't refer to symlinks as the
exclusive means of locking files.
etc/NEWS: Mention support for lock files on MS-Windows.
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 28 | ||||
-rw-r--r-- | src/filelock.c | 113 | ||||
-rw-r--r-- | src/makefile.w32-in | 1 | ||||
-rw-r--r-- | src/w32.c | 11 | ||||
-rw-r--r-- | src/w32proc.c | 32 |
5 files changed, 164 insertions, 21 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index dc9b97c3c03..135d4d48b41 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,33 @@ 2013-02-25 Eli Zaretskii <eliz@gnu.org> + Implement CLASH_DETECTION for MS-Windows. + + * filelock.c [WINDOWSNT]: Include w32.h. + (MAKE_LOCK_NAME): Don't use 'lock', it clashes with MS runtime + function of that name. Up-case the macro arguments. + (IS_LOCK_FILE): New macro. + (fill_in_lock_file_name): Use IS_LOCK_FILE instead of S_ISLNK. + (create_lock_file): New function, with body extracted from + lock_file_1. + [WINDOWSNT]: Implement lock files by writing a regular file with + the lock information as its contents. + (read_lock_data): New function, on Posix platforms just calls + emacs_readlinkat. + [WINDOWSNT]: Read the lock info from the file. + (current_lock_owner): Call read_lock_data instead of calling + emacs_readlinkat directly. + (lock_file) [WINDOWSNT]: Run the file name through + dostounix_filename. + + * w32proc.c (sys_kill): Support the case of SIG = 0, in which case + just check if the process by that PID exists. + + * w32.c (sys_open): Don't reset the _O_CREAT flag if _O_EXCL is + also present, as doing so will fail to error out if the file + already exists. + + * makefile.w32-in ($(BLD)/filelock.$(O)): Depend on src/w32.h. + * textprop.c (Fadd_text_properties, Fremove_text_properties) (Fremove_list_of_text_properties): Skip all of the intervals in the region between START and END that already have resp. don't diff --git a/src/filelock.c b/src/filelock.c index cd2cd2e53a2..4d556de2454 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -43,6 +43,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "buffer.h" #include "coding.h" #include "systime.h" +#ifdef WINDOWSNT +#include "w32.h" /* for dostounix_filename */ +#endif #ifdef CLASH_DETECTION @@ -288,13 +291,22 @@ typedef struct #define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) -/* Write the name of the lock file for FN into LFNAME. Length will be - that of FN plus two more for the leading `.#' plus 1 for the - trailing period plus one for the digit after it plus one for the - null. */ -#define MAKE_LOCK_NAME(lock, file) \ - (lock = alloca (SBYTES (file) + 2 + 1 + 1 + 1), \ - fill_in_lock_file_name (lock, (file))) +/* Write the name of the lock file for FNAME into LOCKNAME. Length + will be that of FN plus two more for the leading `.#' plus 1 for + the trailing period plus one for the digit after it plus one for + the null. */ +#define MAKE_LOCK_NAME(LOCKNAME, FNAME) \ + (LOCKNAME = alloca (SBYTES (FNAME) + 2 + 1 + 1 + 1), \ + fill_in_lock_file_name (LOCKNAME, (FNAME))) + +#ifdef WINDOWSNT +/* 256 chars for user, 1024 chars for host, 10 digits for each of 2 int's. */ +#define MAX_LFINFO (256 + 1024 + 10 + 10 + 2) + /* min size: .@PID */ +#define IS_LOCK_FILE(ST) (MAX_LFINFO >= (ST).st_size && (ST).st_size >= 3) +#else +#define IS_LOCK_FILE(ST) S_ISLNK ((ST).st_mode) +#endif static void fill_in_lock_file_name (register char *lockfile, register Lisp_Object fn) @@ -318,7 +330,7 @@ fill_in_lock_file_name (register char *lockfile, register Lisp_Object fn) p = lockfile + length + 2; - while (lstat (lockfile, &st) == 0 && !S_ISLNK (st.st_mode)) + while (lstat (lockfile, &st) == 0 && !IS_LOCK_FILE (st)) { if (count > 9) { @@ -329,6 +341,49 @@ fill_in_lock_file_name (register char *lockfile, register Lisp_Object fn) } } +static int +create_lock_file (char *lfname, char *lock_info_str, bool force) +{ + int err; + +#ifdef WINDOWSNT + /* Symlinks are supported only by latest versions of Windows, and + creating them is a privileged operation that often triggers UAC + elevation prompts. Therefore, instead of using symlinks, we + create a regular file with the lock info written as its + contents. */ + { + int fd = emacs_open (lfname, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, + S_IREAD | S_IWRITE); + + if (fd < 0 && errno == EEXIST && force) + fd = emacs_open (lfname, O_WRONLY | O_BINARY | O_TRUNC, + S_IREAD | S_IWRITE); + if (fd >= 0) + { + ssize_t lock_info_len = strlen (lock_info_str); + + err = 0; + if (emacs_write (fd, lock_info_str, lock_info_len) != lock_info_len) + err = -1; + if (emacs_close (fd)) + err = -1; + } + else + err = -1; + } +#else + err = symlink (lock_info_str, lfname); + if (errno == EEXIST && force) + { + unlink (lfname); + err = symlink (lock_info_str, lfname); + } +#endif + + return err; +} + /* Lock the lock file named LFNAME. If FORCE, do so even if it is already locked. Return true if successful. */ @@ -355,13 +410,7 @@ lock_file_1 (char *lfname, bool force) esprintf (lock_info_str, boot ? "%s@%s.%"pMd":%"pMd : "%s@%s.%"pMd, user_name, host_name, pid, boot); - - err = symlink (lock_info_str, lfname); - if (errno == EEXIST && force) - { - unlink (lfname); - err = symlink (lock_info_str, lfname); - } + err = create_lock_file (lfname, lock_info_str, force); symlink_errno = errno; SAFE_FREE (); @@ -377,6 +426,32 @@ within_one_second (time_t a, time_t b) return (a - b >= -1 && a - b <= 1); } +static Lisp_Object +read_lock_data (char *lfname) +{ +#ifndef WINDOWSNT + return emacs_readlinkat (AT_FDCWD, lfname); +#else + int fd = emacs_open (lfname, O_RDONLY | O_BINARY, S_IREAD); + ssize_t nbytes; + char lfinfo[MAX_LFINFO + 1]; + + if (fd < 0) + return Qnil; + + nbytes = emacs_read (fd, lfinfo, MAX_LFINFO); + emacs_close (fd); + + if (nbytes > 0) + { + lfinfo[nbytes] = '\0'; + return build_string (lfinfo); + } + else + return Qnil; +#endif +} + /* Return 0 if nobody owns the lock file LFNAME or the lock is obsolete, 1 if another process owns it (and set OWNER (if non-null) to info), 2 if the current process owns it, @@ -390,7 +465,7 @@ current_lock_owner (lock_info_type *owner, char *lfname) lock_info_type local_owner; intmax_t n; char *at, *dot, *colon; - Lisp_Object lfinfo_object = emacs_readlinkat (AT_FDCWD, lfname); + Lisp_Object lfinfo_object = read_lock_data (lfname); char *lfinfo; struct gcpro gcpro1; @@ -552,6 +627,12 @@ lock_file (Lisp_Object fn) orig_fn = fn; GCPRO1 (fn); fn = Fexpand_file_name (fn, Qnil); +#ifdef WINDOWSNT + /* Ensure we have only '/' separators, to avoid problems with + looking (inside fill_in_lock_file_name) for backslashes in file + names encoded by some DBCS codepage. */ + dostounix_filename (SSDATA (fn), 1); +#endif encoded_fn = ENCODE_FILE (fn); /* Create the name of the lock-file for file fn */ diff --git a/src/makefile.w32-in b/src/makefile.w32-in index d60331198db..93f12900dde 100644 --- a/src/makefile.w32-in +++ b/src/makefile.w32-in @@ -864,6 +864,7 @@ $(BLD)/fileio.$(O) : \ $(BLD)/filelock.$(O) : \ $(SRC)/filelock.c \ + $(SRC)/w32.h \ $(NT_INC)/pwd.h \ $(NT_INC)/sys/file.h \ $(NT_INC)/sys/stat.h \ diff --git a/src/w32.c b/src/w32.c index 5011642adf2..aff9771e4bb 100644 --- a/src/w32.c +++ b/src/w32.c @@ -3402,10 +3402,13 @@ int sys_open (const char * path, int oflag, int mode) { const char* mpath = map_w32_filename (path, NULL); - /* Try to open file without _O_CREAT, to be able to write to hidden - and system files. Force all file handles to be - non-inheritable. */ - int res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode); + int res = -1; + + /* If possible, try to open file without _O_CREAT, to be able to + write to existing hidden and system files. Force all file + handles to be non-inheritable. */ + if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL)) + res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode); if (res < 0) res = _open (mpath, oflag | _O_NOINHERIT, mode); if (res >= 0 && res < MAXDESC) diff --git a/src/w32proc.c b/src/w32proc.c index 961791a40ed..84589388cd7 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -2263,12 +2263,42 @@ sys_kill (pid_t pid, int sig) pid = -pid; /* Only handle signals that will result in the process dying */ - if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP) + if (sig != 0 + && sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP) { errno = EINVAL; return -1; } + if (sig == 0) + { + /* It will take _some_ time before PID 4 or less on Windows will + be Emacs... */ + if (pid <= 4) + { + errno = EPERM; + return -1; + } + proc_hand = OpenProcess (PROCESS_QUERY_INFORMATION, 0, pid); + if (proc_hand == NULL) + { + DWORD err = GetLastError (); + + switch (err) + { + case ERROR_ACCESS_DENIED: /* existing process, but access denied */ + errno = EPERM; + return -1; + case ERROR_INVALID_PARAMETER: /* process PID does not exist */ + errno = ESRCH; + return -1; + } + } + else + CloseHandle (proc_hand); + return 0; + } + cp = find_child_pid (pid); if (cp == NULL) { |