X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/b6bd159922608fa474026837771d63bf7eadcf97..ec64c2738515452c75a38c31537c2b495bb77c74:/src/filelock.c diff --git a/src/filelock.c b/src/filelock.c index 5ee2c039b8..de6aba8385 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -1,6 +1,6 @@ /* Lock files for editing. - Copyright (C) 1985-1987, 1993-1994, 1996, 1998-2011 - Free Software Foundation, Inc. + Copyright (C) 1985-1987, 1993-1994, 1996, 1998-2013 Free Software + Foundation, Inc. This file is part of GNU Emacs. @@ -23,7 +23,6 @@ along with GNU Emacs. If not, see . */ #include #include #include -#include #ifdef HAVE_PWD_H #include @@ -39,11 +38,17 @@ along with GNU Emacs. If not, see . */ #include +#include + #include "lisp.h" -#include "buffer.h" #include "character.h" +#include "buffer.h" #include "coding.h" #include "systime.h" +#ifdef WINDOWSNT +#include +#include "w32.h" /* for dostounix_filename */ +#endif #ifdef CLASH_DETECTION @@ -61,7 +66,8 @@ along with GNU Emacs. If not, see . */ #define WTMP_FILE "/var/log/wtmp" #endif -/* The strategy: to lock a file FN, create a symlink .#FN in FN's +/* Normally use a symbolic link to represent a lock. + The strategy: to lock a file FN, create a symlink .#FN in FN's directory, with link data `user@host.pid'. This avoids a single mount (== failure) point for lock files. @@ -94,16 +100,32 @@ along with GNU Emacs. If not, see . */ has contributed this implementation for Emacs), and was designed by Ethan Jacobson, Kimbo Mundy, and others. - --karl@cs.umb.edu/karl@hq.ileaf.com. */ + --karl@cs.umb.edu/karl@hq.ileaf.com. + + On some file systems, notably those of MS-Windows, symbolic links + do not work well, so instead of a symlink .#FN -> 'user@host.pid', + the lock is a regular file .#FN with contents 'user@host.pid'. To + establish a lock, a nonce file is created and then renamed to .#FN. + On MS-Windows this renaming is atomic unless the lock is forcibly + acquired. On other systems the renaming is atomic if the lock is + forcibly acquired; if not, the renaming is done via hard links, + which is good enough for lock-file purposes. + + To summarize, race conditions can occur with either: + + * Forced locks on MS-Windows systems. + + * Non-forced locks on non-MS-Windows systems that support neither + hard nor symbolic links. */ /* Return the time of the last system boot. */ static time_t boot_time; -static int boot_time_initialized; +static bool boot_time_initialized; #ifdef BOOT_TIME -static void get_boot_time_1 (const char *, int); +static void get_boot_time_1 (const char *, bool); #endif static time_t @@ -170,18 +192,18 @@ get_boot_time (void) { char cmd_string[sizeof WTMP_FILE ".19.gz"]; Lisp_Object tempname, filename; - int delete_flag = 0; + bool delete_flag = 0; filename = Qnil; - sprintf (cmd_string, "%s.%d", WTMP_FILE, counter); - tempname = build_string (cmd_string); + tempname = make_formatted_string + (cmd_string, "%s.%d", WTMP_FILE, counter); if (! NILP (Ffile_exists_p (tempname))) filename = tempname; else { - sprintf (cmd_string, "%s.%d.gz", WTMP_FILE, counter); - tempname = build_string (cmd_string); + tempname = make_formatted_string (cmd_string, "%s.%d.gz", + WTMP_FILE, counter); if (! NILP (Ffile_exists_p (tempname))) { Lisp_Object args[6]; @@ -225,13 +247,13 @@ get_boot_time (void) If FILENAME is zero, use the same file as before; if no FILENAME has ever been specified, this is the utmp file. - Use the newest reboot record if NEWEST is nonzero, + Use the newest reboot record if NEWEST, the first reboot record otherwise. Ignore all reboot records on or before BOOT_TIME. Success is indicated by setting BOOT_TIME to a larger value. */ void -get_boot_time_1 (const char *filename, int newest) +get_boot_time_1 (const char *filename, bool newest) { struct utmp ut, *utp; int desc; @@ -275,117 +297,236 @@ get_boot_time_1 (const char *filename, int newest) } #endif /* BOOT_TIME */ +/* An arbitrary limit on lock contents length. 8 K should be plenty + big enough in practice. */ +enum { MAX_LFINFO = 8 * 1024 }; + /* Here is the structure that stores information about a lock. */ typedef struct { - char *user; - char *host; - pid_t pid; - time_t boot_time; -} lock_info_type; - -/* Free the two dynamically-allocated pieces in PTR. */ -#define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) + /* Location of '@', '.', ':' in USER. If there's no colon, COLON + points to the end of USER. */ + char *at, *dot, *colon; + /* Lock file contents USER@HOST.PID with an optional :BOOT_TIME + appended. This memory is used as a lock file contents buffer, so + it needs room for MAX_LFINFO + 1 bytes. A string " (pid NNNN)" + may be appended to the USER@HOST while generating a diagnostic, + so make room for its extra bytes (as opposed to ".NNNN") too. */ + char user[MAX_LFINFO + 1 + sizeof " (pid )" - sizeof "."]; +} lock_info_type; -/* 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 = (char *) 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 FNAME plus two more for the leading ".#", plus one + for the null. */ +#define MAKE_LOCK_NAME(lockname, fname) \ + (lockname = SAFE_ALLOCA (SBYTES (fname) + 2 + 1), \ + fill_in_lock_file_name (lockname, fname)) static void -fill_in_lock_file_name (register char *lockfile, register Lisp_Object fn) +fill_in_lock_file_name (char *lockfile, Lisp_Object fn) { - register char *p; - struct stat st; - int count = 0; - - strcpy (lockfile, SSDATA (fn)); + char *last_slash = memrchr (SSDATA (fn), '/', SBYTES (fn)); + char *base = last_slash + 1; + ptrdiff_t dirlen = base - SSDATA (fn); + memcpy (lockfile, SSDATA (fn), dirlen); + lockfile[dirlen] = '.'; + lockfile[dirlen + 1] = '#'; + strcpy (lockfile + dirlen + 2, base); +} - /* Shift the nondirectory part of the file name (including the null) - right two characters. Here is one of the places where we'd have to - do something to support 14-character-max file names. */ - for (p = lockfile + strlen (lockfile); p != lockfile && *p != '/'; p--) - p[2] = *p; +/* For some reason Linux kernels return EPERM on file systems that do + not support hard or symbolic links. This symbol documents the quirk. + There is no way to tell whether a symlink call fails due to + permissions issues or because links are not supported, but luckily + the lock file code should work either way. */ +enum { LINKS_MIGHT_NOT_WORK = EPERM }; - /* Insert the `.#'. */ - p[1] = '.'; - p[2] = '#'; +/* Rename OLD to NEW. If FORCE, replace any existing NEW. + It is OK if there are temporarily two hard links to OLD. + Return 0 if successful, -1 (setting errno) otherwise. */ +static int +rename_lock_file (char const *old, char const *new, bool force) +{ +#ifdef WINDOWSNT + return sys_rename_replace (old, new, force); +#else + if (! force) + { + struct stat st; - p = p + strlen (p); + if (link (old, new) == 0) + return unlink (old) == 0 || errno == ENOENT ? 0 : -1; + if (errno != ENOSYS && errno != LINKS_MIGHT_NOT_WORK) + return -1; - while (lstat (lockfile, &st) == 0 && !S_ISLNK (st.st_mode)) - { - if (count > 9) + /* 'link' does not work on this file system. This can occur on + a GNU/Linux host mounting a FAT32 file system. Fall back on + 'rename' after checking that NEW does not exist. There is a + potential race condition since some other process may create + NEW immediately after the existence check, but it's the best + we can portably do here. */ + if (lstat (new, &st) == 0 || errno == EOVERFLOW) { - *p = '\0'; - return; + errno = EEXIST; + return -1; } - sprintf (p, ".%d", count++); + if (errno != ENOENT) + return -1; } + + return rename (old, new); +#endif } -/* Lock the lock file named LFNAME. - If FORCE is nonzero, we do so even if it is already locked. - Return 1 if successful, 0 if not. */ +/* Create the lock file FILE with contents CONTENTS. Return 0 if + successful, an errno value on failure. If FORCE, remove any + existing FILE if necessary. */ static int -lock_file_1 (char *lfname, int force) +create_lock_file (char *lfname, char *lock_info_str, bool force) { - register int err; - printmax_t boot, pid; - const char *user_name; - const char *host_name; - char *lock_info_str; - ptrdiff_t lock_info_size; - int symlink_errno; - USE_SAFE_ALLOCA; - - /* Call this first because it can GC. */ - boot = get_boot_time (); +#ifdef WINDOWSNT + /* Symlinks are supported only by later versions of Windows, and + creating them is a privileged operation that often triggers + User Account Control elevation prompts. Avoid the problem by + pretending that 'symlink' does not work. */ + int err = ENOSYS; +#else + int err = symlink (lock_info_str, lfname) == 0 ? 0 : errno; +#endif - if (STRINGP (Fuser_login_name (Qnil))) - user_name = SSDATA (Fuser_login_name (Qnil)); - else - user_name = ""; - if (STRINGP (Fsystem_name ())) - host_name = SSDATA (Fsystem_name ()); - else - host_name = ""; - lock_info_size = (strlen (user_name) + strlen (host_name) - + 2 * INT_STRLEN_BOUND (printmax_t) - + sizeof "@.:"); - SAFE_ALLOCA (lock_info_str, char *, lock_info_size); - pid = getpid (); - - 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) + if (err == EEXIST && force) { unlink (lfname); - err = symlink (lock_info_str, lfname); + err = symlink (lock_info_str, lfname) == 0 ? 0 : errno; } - symlink_errno = errno; - SAFE_FREE (); - errno = symlink_errno; - return err == 0; + if (err == ENOSYS || err == LINKS_MIGHT_NOT_WORK || err == ENAMETOOLONG) + { + static char const nonce_base[] = ".#-emacsXXXXXX"; + char *last_slash = strrchr (lfname, '/'); + ptrdiff_t lfdirlen = last_slash + 1 - lfname; + USE_SAFE_ALLOCA; + char *nonce = SAFE_ALLOCA (lfdirlen + sizeof nonce_base); + int fd; + bool need_fchmod; + mode_t world_readable = S_IRUSR | S_IRGRP | S_IROTH; + memcpy (nonce, lfname, lfdirlen); + strcpy (nonce + lfdirlen, nonce_base); + +#if HAVE_MKSTEMP + /* Prefer mkstemp if available, as it avoids a race between + mktemp and emacs_open. */ + fd = mkstemp (nonce); + need_fchmod = 1; +#else + mktemp (nonce); + fd = emacs_open (nonce, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, + world_readable); + need_fchmod = 0; +#endif + + if (fd < 0) + err = errno; + else + { + ptrdiff_t lock_info_len = strlen (lock_info_str); + err = 0; + if (emacs_write (fd, lock_info_str, lock_info_len) != lock_info_len + || (need_fchmod && fchmod (fd, world_readable) != 0)) + err = errno; + /* There is no need to call fsync here, as the contents of + the lock file need not survive system crashes. */ + if (emacs_close (fd) != 0) + err = errno; + if (!err && rename_lock_file (nonce, lfname, force) != 0) + err = errno; + if (err) + unlink (nonce); + } + + SAFE_FREE (); + } + + return err; } -/* Return 1 if times A and B are no more than one second apart. */ +/* Lock the lock file named LFNAME. + If FORCE, do so even if it is already locked. + Return 0 if successful, an error number on failure. */ static int +lock_file_1 (char *lfname, bool force) +{ + /* Call this first because it can GC. */ + printmax_t boot = get_boot_time (); + + Lisp_Object luser_name = Fuser_login_name (Qnil); + char const *user_name = STRINGP (luser_name) ? SSDATA (luser_name) : ""; + Lisp_Object lhost_name = Fsystem_name (); + char const *host_name = STRINGP (lhost_name) ? SSDATA (lhost_name) : ""; + char lock_info_str[MAX_LFINFO + 1]; + printmax_t pid = getpid (); + + if (sizeof lock_info_str + <= snprintf (lock_info_str, sizeof lock_info_str, + boot ? "%s@%s.%"pMd":%"pMd : "%s@%s.%"pMd, + user_name, host_name, pid, boot)) + return ENAMETOOLONG; + + return create_lock_file (lfname, lock_info_str, force); +} + +/* Return true if times A and B are no more than one second apart. */ + +static bool within_one_second (time_t a, time_t b) { return (a - b >= -1 && a - b <= 1); } +/* On systems lacking ELOOP, test for an errno value that shouldn't occur. */ +#ifndef ELOOP +# define ELOOP (-1) +#endif + +/* Read the data for the lock file LFNAME into LFINFO. Read at most + MAX_LFINFO + 1 bytes. Return the number of bytes read, or -1 + (setting errno) on error. */ + +static ptrdiff_t +read_lock_data (char *lfname, char lfinfo[MAX_LFINFO + 1]) +{ + ptrdiff_t nbytes; + + while ((nbytes = readlinkat (AT_FDCWD, lfname, lfinfo, MAX_LFINFO + 1)) < 0 + && errno == EINVAL) + { + int fd = emacs_open (lfname, O_RDONLY | O_BINARY | O_NOFOLLOW, 0); + if (0 <= fd) + { + ptrdiff_t read_bytes = emacs_read (fd, lfinfo, MAX_LFINFO + 1); + int read_errno = errno; + if (emacs_close (fd) != 0) + return -1; + errno = read_errno; + return read_bytes; + } + + if (errno != ELOOP) + return -1; + + /* readlinkat saw a non-symlink, but emacs_open saw a symlink. + The former must have been removed and replaced by the latter. + Try again. */ + QUIT; + } + + return nbytes; +} + /* 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, @@ -395,85 +536,78 @@ static int current_lock_owner (lock_info_type *owner, char *lfname) { int ret; - ptrdiff_t len; lock_info_type local_owner; - intmax_t n; - char *at, *dot, *colon; - char readlink_buf[READLINK_BUFSIZE]; - char *lfinfo = emacs_readlink (lfname, readlink_buf); - - /* If nonexistent lock file, all is well; otherwise, got strange error. */ - if (!lfinfo) - return errno == ENOENT ? 0 : -1; + ptrdiff_t lfinfolen; + intmax_t pid, boot_time; + char *at, *dot, *lfinfo_end; /* Even if the caller doesn't want the owner info, we still have to read it to determine return value. */ if (!owner) owner = &local_owner; + /* If nonexistent lock file, all is well; otherwise, got strange error. */ + lfinfolen = read_lock_data (lfname, owner->user); + if (lfinfolen < 0) + return errno == ENOENT ? 0 : -1; + if (MAX_LFINFO < lfinfolen) + return -1; + owner->user[lfinfolen] = 0; + /* Parse USER@HOST.PID:BOOT_TIME. If can't parse, return -1. */ /* The USER is everything before the last @. */ - at = strrchr (lfinfo, '@'); - dot = strrchr (lfinfo, '.'); - if (!at || !dot) - { - if (lfinfo != readlink_buf) - xfree (lfinfo); - return -1; - } - len = at - lfinfo; - owner->user = (char *) xmalloc (len + 1); - memcpy (owner->user, lfinfo, len); - owner->user[len] = 0; + owner->at = at = memrchr (owner->user, '@', lfinfolen); + if (!at) + return -1; + owner->dot = dot = strrchr (at, '.'); + if (!dot) + return -1; /* The PID is everything from the last `.' to the `:'. */ + if (! c_isdigit (dot[1])) + return -1; errno = 0; - n = strtoimax (dot + 1, NULL, 10); - owner->pid = - ((0 <= n && n <= TYPE_MAXIMUM (pid_t) - && (TYPE_MAXIMUM (pid_t) < INTMAX_MAX || errno != ERANGE)) - ? n : 0); + pid = strtoimax (dot + 1, &owner->colon, 10); + if (errno == ERANGE) + pid = -1; - colon = strchr (dot + 1, ':'); /* After the `:', if there is one, comes the boot time. */ - n = 0; - if (colon) + switch (owner->colon[0]) { - errno = 0; - n = strtoimax (colon + 1, NULL, 10); - } - owner->boot_time = - ((0 <= n && n <= TYPE_MAXIMUM (time_t) - && (TYPE_MAXIMUM (time_t) < INTMAX_MAX || errno != ERANGE)) - ? n : 0); + case 0: + boot_time = 0; + lfinfo_end = owner->colon; + break; - /* The host is everything in between. */ - len = dot - at - 1; - owner->host = (char *) xmalloc (len + 1); - memcpy (owner->host, at + 1, len); - owner->host[len] = 0; + case ':': + if (! c_isdigit (owner->colon[1])) + return -1; + boot_time = strtoimax (owner->colon + 1, &lfinfo_end, 10); + break; - /* We're done looking at the link info. */ - if (lfinfo != readlink_buf) - xfree (lfinfo); + default: + return -1; + } + if (lfinfo_end != owner->user + lfinfolen) + return -1; /* On current host? */ - if (STRINGP (Fsystem_name ()) - && strcmp (owner->host, SSDATA (Fsystem_name ())) == 0) + if (STRINGP (Vsystem_name) + && dot - (at + 1) == SBYTES (Vsystem_name) + && memcmp (at + 1, SSDATA (Vsystem_name), SBYTES (Vsystem_name)) == 0) { - if (owner->pid == getpid ()) + if (pid == getpid ()) ret = 2; /* We own it. */ - else if (owner->pid > 0 - && (kill (owner->pid, 0) >= 0 || errno == EPERM) - && (owner->boot_time == 0 - || within_one_second (owner->boot_time, get_boot_time ()))) + else if (0 < pid && pid <= TYPE_MAXIMUM (pid_t) + && (kill (pid, 0) >= 0 || errno == EPERM) + && (boot_time == 0 + || (boot_time <= TYPE_MAXIMUM (time_t) + && within_one_second (boot_time, get_boot_time ())))) ret = 1; /* An existing process on this machine owns it. */ - /* The owner process is dead or has a strange pid (<=0), so try to + /* The owner process is dead or has a strange pid, so try to zap the lockfile. */ - else if (unlink (lfname) < 0) - ret = -1; else - ret = 0; + return unlink (lfname); } else { /* If we wanted to support the check for stale locks on remote machines, @@ -481,11 +615,6 @@ current_lock_owner (lock_info_type *owner, char *lfname) ret = 1; } - /* Avoid garbage. */ - if (owner == &local_owner || ret <= 0) - { - FREE_LOCK_INFO (*owner); - } return ret; } @@ -497,29 +626,25 @@ current_lock_owner (lock_info_type *owner, char *lfname) Return -1 if cannot lock for any other reason. */ static int -lock_if_free (lock_info_type *clasher, register char *lfname) +lock_if_free (lock_info_type *clasher, char *lfname) { - while (lock_file_1 (lfname, 0) == 0) + int err; + while ((err = lock_file_1 (lfname, 0)) == EEXIST) { - int locker; - - if (errno != EEXIST) - return -1; - - locker = current_lock_owner (clasher, lfname); - if (locker == 2) - { - FREE_LOCK_INFO (*clasher); - return 0; /* We ourselves locked it. */ - } - else if (locker == 1) - return 1; /* Someone else has it. */ - else if (locker == -1) - return -1; /* current_lock_owner returned strange error. */ + switch (current_lock_owner (clasher, lfname)) + { + case 2: + return 0; /* We ourselves locked it. */ + case 1: + return 1; /* Someone else has it. */ + case -1: + return -1; /* current_lock_owner returned strange error. */ + } /* We deleted a stale lock; try again to lock the file. */ } - return 0; + + return err ? -1 : 0; } /* lock_file locks file FN, @@ -531,6 +656,7 @@ lock_if_free (lock_info_type *clasher, register char *lfname) decided to go ahead without locking. When this returns, either the lock is locked for us, + or lock creation failed, or the user has said to go ahead without locking. If the file is locked by someone else, this calls @@ -542,14 +668,16 @@ lock_if_free (lock_info_type *clasher, register char *lfname) void lock_file (Lisp_Object fn) { - register Lisp_Object attack, orig_fn, encoded_fn; - register char *lfname, *locker; - ptrdiff_t locker_size; + Lisp_Object orig_fn, encoded_fn; + char *lfname; lock_info_type lock_info; - printmax_t pid; struct gcpro gcpro1; USE_SAFE_ALLOCA; + /* Don't do locking if the user has opted out. */ + if (! create_lockfiles) + return; + /* Don't do locking while dumping Emacs. Uncompressing wtmp files uses call-process, which does not work in an uninitialized Emacs. */ @@ -559,6 +687,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 */ @@ -577,38 +711,35 @@ lock_file (Lisp_Object fn) call1 (intern ("ask-user-about-supersession-threat"), fn); } - UNGCPRO; - - /* Try to lock the lock. */ - if (lock_if_free (&lock_info, lfname) <= 0) - /* Return now if we have locked it, or if lock creation failed */ - return; - /* Else consider breaking the lock */ - locker_size = (strlen (lock_info.user) + strlen (lock_info.host) - + INT_STRLEN_BOUND (printmax_t) - + sizeof "@ (pid )"); - SAFE_ALLOCA (locker, char *, locker_size); - pid = lock_info.pid; - esprintf (locker, "%s@%s (pid %"pMd")", - lock_info.user, lock_info.host, pid); - FREE_LOCK_INFO (lock_info); - - attack = call2 (intern ("ask-user-about-lock"), fn, build_string (locker)); - SAFE_FREE (); - if (!NILP (attack)) - /* User says take the lock */ + /* Try to lock the lock. */ + if (0 < lock_if_free (&lock_info, lfname)) { - lock_file_1 (lfname, 1); - return; + /* Someone else has the lock. Consider breaking it. */ + Lisp_Object attack; + char *dot = lock_info.dot; + ptrdiff_t pidlen = lock_info.colon - (dot + 1); + static char const replacement[] = " (pid "; + int replacementlen = sizeof replacement - 1; + memmove (dot + replacementlen, dot + 1, pidlen); + strcpy (dot + replacementlen + pidlen, ")"); + memcpy (dot, replacement, replacementlen); + attack = call2 (intern ("ask-user-about-lock"), fn, + build_string (lock_info.user)); + /* Take the lock if the user said so. */ + if (!NILP (attack)) + lock_file_1 (lfname, 1); } - /* User says ignore the lock */ + + UNGCPRO; + SAFE_FREE (); } void -unlock_file (register Lisp_Object fn) +unlock_file (Lisp_Object fn) { - register char *lfname; + char *lfname; + USE_SAFE_ALLOCA; fn = Fexpand_file_name (fn, Qnil); fn = ENCODE_FILE (fn); @@ -617,6 +748,8 @@ unlock_file (register Lisp_Object fn) if (current_lock_owner (0, lfname) == 2) unlink (lfname); + + SAFE_FREE (); } void @@ -682,9 +815,10 @@ t if it is locked by you, else a string saying which user has locked it. */) (Lisp_Object filename) { Lisp_Object ret; - register char *lfname; + char *lfname; int owner; lock_info_type locker; + USE_SAFE_ALLOCA; filename = Fexpand_file_name (filename, Qnil); @@ -696,22 +830,11 @@ t if it is locked by you, else a string saying which user has locked it. */) else if (owner == 2) ret = Qt; else - ret = build_string (locker.user); - - if (owner > 0) - FREE_LOCK_INFO (locker); + ret = make_string (locker.user, locker.at - locker.user); + SAFE_FREE (); return ret; } - -/* Initialization functions. */ - -void -init_filelock (void) -{ - boot_time = 0; - boot_time_initialized = 0; -} #endif /* CLASH_DETECTION */ @@ -722,6 +845,10 @@ syms_of_filelock (void) doc: /* The directory for writing temporary files. */); Vtemporary_file_directory = Qnil; + DEFVAR_BOOL ("create-lockfiles", create_lockfiles, + doc: /* Non-nil means use lockfiles to avoid editing collisions. */); + create_lockfiles = 1; + #ifdef CLASH_DETECTION defsubr (&Sunlock_buffer); defsubr (&Slock_buffer);