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.
This commit is contained in:
Eli Zaretskii 2013-02-25 19:36:03 +02:00
parent aec32f66d0
commit 343a2aefb5
13 changed files with 215 additions and 34 deletions

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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))))

View file

@ -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.

View file

@ -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."

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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 \

View file

@ -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)

View file

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