[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] /srv/bzr/emacs/trunk r111877: Implement CLASH_DETECTION fo
From: |
Eli Zaretskii |
Subject: |
[Emacs-diffs] /srv/bzr/emacs/trunk r111877: Implement CLASH_DETECTION for MS-Windows. |
Date: |
Mon, 25 Feb 2013 19:36:03 +0200 |
User-agent: |
Bazaar (2.5.0) |
------------------------------------------------------------
revno: 111877
committer: Eli Zaretskii <address@hidden>
branch nick: trunk
timestamp: Mon 2013-02-25 19:36:03 +0200
message:
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.
modified:
doc/emacs/ChangeLog
doc/emacs/files.texi
etc/NEWS
lisp/ChangeLog
lisp/emacs-lisp/bytecomp.el
nt/ChangeLog
nt/config.nt
nt/inc/ms-w32.h
src/ChangeLog
src/filelock.c
src/makefile.w32-in
src/w32.c
src/w32proc.c
=== modified file 'doc/emacs/ChangeLog'
--- a/doc/emacs/ChangeLog 2013-02-22 17:13:05 +0000
+++ b/doc/emacs/ChangeLog 2013-02-25 17:36:03 +0000
@@ -1,3 +1,8 @@
+2013-02-25 Eli Zaretskii <address@hidden>
+
+ * files.texi (Interlocking): Don't refer to symlinks as the
+ exclusive means of locking files.
+
2013-02-22 Glenn Morris <address@hidden>
* ack.texi (Acknowledgments):
=== modified file 'doc/emacs/files.texi'
--- a/doc/emacs/files.texi 2013-02-21 06:03:02 +0000
+++ b/doc/emacs/files.texi 2013-02-25 17:36:03 +0000
@@ -734,10 +734,10 @@
@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 @@
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
address@hidden 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
=== modified file 'etc/NEWS'
--- a/etc/NEWS 2013-02-25 17:01:41 +0000
+++ b/etc/NEWS 2013-02-25 17:36:03 +0000
@@ -348,6 +348,13 @@
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
=== modified file 'lisp/ChangeLog'
--- a/lisp/ChangeLog 2013-02-25 14:29:41 +0000
+++ b/lisp/ChangeLog 2013-02-25 17:36:03 +0000
@@ -1,3 +1,9 @@
+2013-02-25 Eli Zaretskii <address@hidden>
+
+ * 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 <address@hidden>
* files.el (basic-save-buffer): Remove redundant directory-creation.
=== modified file 'lisp/emacs-lisp/bytecomp.el'
--- a/lisp/emacs-lisp/bytecomp.el 2013-02-23 21:14:36 +0000
+++ b/lisp/emacs-lisp/bytecomp.el 2013-02-25 17:36:03 +0000
@@ -1594,7 +1594,9 @@
(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))))
=== modified file 'nt/ChangeLog'
--- a/nt/ChangeLog 2013-02-16 14:16:07 +0000
+++ b/nt/ChangeLog 2013-02-25 17:36:03 +0000
@@ -1,3 +1,9 @@
+2013-02-25 Eli Zaretskii <address@hidden>
+
+ * inc/ms-w32.h (BOOT_TIME_FILE): Define.
+
+ * config.nt (CLASH_DETECTION): Define to 1.
+
2013-02-16 Eli Zaretskii <address@hidden>
* inc/ms-w32.h (__STDC__): Fiddle with value only for MSVC.
=== modified file 'nt/config.nt'
--- a/nt/config.nt 2013-02-13 00:52:04 +0000
+++ b/nt/config.nt 2013-02-25 17:36:03 +0000
@@ -75,7 +75,7 @@
/* 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."
=== modified file 'nt/inc/ms-w32.h'
--- a/nt/inc/ms-w32.h 2013-02-16 14:16:07 +0000
+++ b/nt/inc/ms-w32.h 2013-02-25 17:36:03 +0000
@@ -70,6 +70,18 @@
#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
=== modified file 'src/ChangeLog'
--- a/src/ChangeLog 2013-02-25 16:13:42 +0000
+++ b/src/ChangeLog 2013-02-25 17:36:03 +0000
@@ -1,5 +1,33 @@
2013-02-25 Eli Zaretskii <address@hidden>
+ 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
=== modified file 'src/filelock.c'
--- a/src/filelock.c 2013-02-24 19:45:17 +0000
+++ b/src/filelock.c 2013-02-25 17:36:03 +0000
@@ -43,6 +43,9 @@
#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 @@
#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:
address@hidden */
+#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 @@
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 @@
}
}
+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 @@
esprintf (lock_info_str, boot ? "address@hidden"pMd":%"pMd :
"address@hidden"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 @@
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 @@
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 @@
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 */
=== modified file 'src/makefile.w32-in'
--- a/src/makefile.w32-in 2013-02-25 05:55:37 +0000
+++ b/src/makefile.w32-in 2013-02-25 17:36:03 +0000
@@ -864,6 +864,7 @@
$(BLD)/filelock.$(O) : \
$(SRC)/filelock.c \
+ $(SRC)/w32.h \
$(NT_INC)/pwd.h \
$(NT_INC)/sys/file.h \
$(NT_INC)/sys/stat.h \
=== modified file 'src/w32.c'
--- a/src/w32.c 2013-02-22 16:00:14 +0000
+++ b/src/w32.c 2013-02-25 17:36:03 +0000
@@ -3402,10 +3402,13 @@
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)
=== modified file 'src/w32proc.c'
--- a/src/w32proc.c 2013-02-19 03:29:28 +0000
+++ b/src/w32proc.c 2013-02-25 17:36:03 +0000
@@ -2263,12 +2263,42 @@
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)
{
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Emacs-diffs] /srv/bzr/emacs/trunk r111877: Implement CLASH_DETECTION for MS-Windows.,
Eli Zaretskii <=