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:
parent
aec32f66d0
commit
343a2aefb5
13 changed files with 215 additions and 34 deletions
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
7
etc/NEWS
7
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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
113
src/filelock.c
113
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 */
|
||||
|
|
|
@ -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 \
|
||||
|
|
11
src/w32.c
11
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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue