emacs-diffs
[Top][All Lists]
Advanced

[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)
     {


reply via email to

[Prev in Thread] Current Thread [Next in Thread]