summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Zaretskii <eliz@gnu.org>2013-02-25 19:36:03 +0200
committerEli Zaretskii <eliz@gnu.org>2013-02-25 19:36:03 +0200
commit343a2aefb528ce3c978ba2145705b9e37bfbe02a (patch)
tree4672030eea98dfc9bd077ac58400271d1c812918
parentaec32f66d0db82b562e904dfe7bb6d54796fe773 (diff)
downloademacs-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.
-rw-r--r--doc/emacs/ChangeLog5
-rw-r--r--doc/emacs/files.texi24
-rw-r--r--etc/NEWS7
-rw-r--r--lisp/ChangeLog6
-rw-r--r--lisp/emacs-lisp/bytecomp.el2
-rw-r--r--nt/ChangeLog6
-rw-r--r--nt/config.nt2
-rw-r--r--nt/inc/ms-w32.h12
-rw-r--r--src/ChangeLog28
-rw-r--r--src/filelock.c113
-rw-r--r--src/makefile.w32-in1
-rw-r--r--src/w32.c11
-rw-r--r--src/w32proc.c32
13 files changed, 215 insertions, 34 deletions
diff --git a/doc/emacs/ChangeLog b/doc/emacs/ChangeLog
index 33f530cbadc..44245441791 100644
--- a/doc/emacs/ChangeLog
+++ b/doc/emacs/ChangeLog
@@ -1,3 +1,8 @@
+2013-02-25 Eli Zaretskii <eliz@gnu.org>
+
+ * files.texi (Interlocking): Don't refer to symlinks as the
+ exclusive means of locking files.
+
2013-02-22 Glenn Morris <rgm@gnu.org>
* ack.texi (Acknowledgments):
diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi
index 7f7ae483cd5..1f78747eaa6 100644
--- a/doc/emacs/files.texi
+++ b/doc/emacs/files.texi
@@ -734,10 +734,10 @@ file.
@cindex locking files
When you make the first modification in an Emacs buffer that is
visiting a file, Emacs records that the file is @dfn{locked} by you.
-(It does this by creating a specially-named symbolic link in the same
-directory.) Emacs removes the lock when you save the changes. The
-idea is that the file is locked whenever an Emacs buffer visiting it
-has unsaved changes.
+(It does this by creating a specially-named symbolic link or regular
+file with special contents in the same directory.) Emacs removes the
+lock when you save the changes. The idea is that the file is locked
+whenever an Emacs buffer visiting it has unsaved changes.
@vindex create-lockfiles
You can prevent the creation of lock files by setting the variable
@@ -774,14 +774,14 @@ multiple names, Emacs does not prevent two users from editing it
simultaneously under different names.
A lock file cannot be written in some circumstances, e.g., if Emacs
-lacks the system permissions or the system does not support symbolic
-links. In these cases, Emacs can still detect the collision when you
-try to save a file, by checking the file's last-modification date. If
-the file has changed since the last time Emacs visited or saved it,
-that implies that changes have been made in some other way, and will
-be lost if Emacs proceeds with saving. Emacs then displays a warning
-message and asks for confirmation before saving; answer @kbd{yes} to
-save, and @kbd{no} or @kbd{C-g} cancel the save.
+lacks the system permissions or cannot create lock files for some
+other reason. In these cases, Emacs can still detect the collision
+when you try to save a file, by checking the file's last-modification
+date. If the file has changed since the last time Emacs visited or
+saved it, that implies that changes have been made in some other way,
+and will be lost if Emacs proceeds with saving. Emacs then displays a
+warning message and asks for confirmation before saving; answer
+@kbd{yes} to save, and @kbd{no} or @kbd{C-g} cancel the save.
If you are notified that simultaneous editing has already taken
place, one way to compare the buffer to its file is the @kbd{M-x
diff --git a/etc/NEWS b/etc/NEWS
index 07f52adb2b1..7d1110d5dc6 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -348,6 +348,13 @@ Setting it has no effect, and %t in the mode-line format is ignored.
Likewise, `file-name-buffer-file-type-alist' is now obsolete, and
modifying it has no effect.
+---
+** Lock files now work on MS-Windows.
+This allows to avoid losing your edits if the same file is being
+edited in another Emacs session or by another user. See the node
+"Interlocking" in the Emacs User Manual for the details. To disable
+file locking, customize `create-lockfiles' to nil.
+
** Improved fullscreen support on Mac OS X.
Both native (>= OSX 10.7) and "old style" fullscreen are supported.
Customize `ns-use-native-fullscreen' to change style. For >= 10.7
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 07c3df9e279..c8fb65e89e8 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,9 @@
+2013-02-25 Eli Zaretskii <eliz@gnu.org>
+
+ * 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).
+
2013-02-25 Stefan Monnier <monnier@iro.umontreal.ca>
* files.el (basic-save-buffer): Remove redundant directory-creation.
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index f5861550c9a..e0837033c74 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -1594,7 +1594,9 @@ that already has a `.elc' file."
(setq directories (nconc directories (list source))))
;; It is an ordinary file. Decide whether to compile it.
(if (and (string-match emacs-lisp-file-regexp source)
+ ;; The next 2 tests avoid compiling lock files
(file-readable-p source)
+ (not (string-match "\\`\\.#" file))
(not (auto-save-file-name-p source))
(not (string-equal dir-locals-file
(file-name-nondirectory source))))
diff --git a/nt/ChangeLog b/nt/ChangeLog
index a8bb8c74c91..fab143fcddc 100644
--- a/nt/ChangeLog
+++ b/nt/ChangeLog
@@ -1,3 +1,9 @@
+2013-02-25 Eli Zaretskii <eliz@gnu.org>
+
+ * inc/ms-w32.h (BOOT_TIME_FILE): Define.
+
+ * config.nt (CLASH_DETECTION): Define to 1.
+
2013-02-16 Eli Zaretskii <eliz@gnu.org>
* inc/ms-w32.h (__STDC__): Fiddle with value only for MSVC.
diff --git a/nt/config.nt b/nt/config.nt
index 61e56174bb4..cc4e91d9af0 100644
--- a/nt/config.nt
+++ b/nt/config.nt
@@ -75,7 +75,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
/* Define if you want lock files to be written, so that Emacs can tell
instantly when you try to modify a file that someone else has modified in
his/her Emacs. */
-#undef CLASH_DETECTION
+#define CLASH_DETECTION 1
/* Short copyright string for this version of Emacs. */
#define COPYRIGHT "Copyright (C) 2013 Free Software Foundation, Inc."
diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h
index 66f586a4f76..9473fbe3ca6 100644
--- a/nt/inc/ms-w32.h
+++ b/nt/inc/ms-w32.h
@@ -70,6 +70,18 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#define HAVE___BUILTIN_UNWIND_INIT 1
#endif
+/* This isn't perfect, as some systems might have the page file in
+ another place. Also, I suspect that the time stamp of that file
+ might also change when Windows enlarges the file due to
+ insufficient VM. Still, this seems to be the most reliable way;
+ the alternative (of using GetSystemTimes) won't work on laptops
+ that hibernate, because the system clock is stopped then. Other
+ possibility would be to run "net statistics workstation" and parse
+ the output, but that's gross. So this should do; if the file is
+ not there, the boot time will be returned as zero, and filelock.c
+ already handles that. */
+#define BOOT_TIME_FILE "C:/pagefile.sys"
+
/* ============================================================ */
/* Here, add any special hacks needed to make Emacs work on this
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)
{