#include "buffer.h"
#include "coding.h"
#include "systime.h"
+#ifdef WINDOWSNT
+#include <share.h>
+#include "w32.h" /* for dostounix_filename */
+#endif
#ifdef CLASH_DETECTION
#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)
p[1] = '.';
p[2] = '#';
- p = p + length + 2;
+ 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)
{
}
}
+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. */
+ {
+ /* Deny everybody else any kind of access to the file until we are
+ done writing it and close the handle. This makes the entire
+ open/write/close operation atomic, as far as other processes
+ are concerned. */
+ int fd = _sopen (lfname,
+ _O_WRONLY | _O_BINARY | _O_CREAT | _O_EXCL | _O_NOINHERIT,
+ _SH_DENYRW, S_IREAD | S_IWRITE);
+
+ if (fd < 0 && errno == EEXIST && force)
+ fd = _sopen (lfname, _O_WRONLY | _O_BINARY | _O_TRUNC |_O_NOINHERIT,
+ _SH_DENYRW, 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. */
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 ();
return (a - b >= -1 && a - b <= 1);
}
\f
+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,
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;
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 */