summaryrefslogtreecommitdiff
path: root/src
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 /src
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.
Diffstat (limited to 'src')
-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
5 files changed, 164 insertions, 21 deletions
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)
{