From b5029e230d10ea412e4ff1d4867a0c884d130039 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 2 Mar 2013 12:41:53 -0800 Subject: [PATCH] The lock for FILE is now .#FILE or .#-FILE. The old approach, which fell back on DIR/.#FILE.0 through DIR/.#FILE.9, had race conditions that could not be easily fixed. If DIR/.#FILE is a non-symlink file, Emacs now does not create a lock file for DIR/FILE; that is, DIR/FILE is no longer partly protected by a lock if DIR/.#FILE is a non-symlink file ("partly" because the locking mechanism was never reliable in that case). This patch fixes this and other bugs discovered by a code inspection that was prompted by . Also, this patch switches to .#-FILE (not .#FILE) on MS-Windows, to avoid interoperability problems between the MS-Windows and non-MS-Windows implementations. MS-Windows and non-MS-Windows instances of Emacs now ignore each others' locks. * etc/NEWS: Document this. * src/filelock.c (defined_WINDOWSNT): New constant. (MAKE_LOCK_NAME, fill_in_lock_file_name): Don't create DIR/.#FILE.0 through DIR/.#FILE.9. Instead, create DIR/.#FILE symlinks on non-MS-Windows hosts, and DIR/.#-FILE regular files on MS-Windows hosts. (MAKE_LOCK_NAME, unlock_file, Ffile_locked_p): Use SAFE_ALLOCA to avoid problems with long file names. (MAX_LFINFO): Now a local constant, not a global macro. (IS_LOCK_FILE): Remove. (lock_file_1): Don't inspect errno if symlink call succeeds; that's not portable. (lock_file): Document that this function can return if lock creation fails. Fixes: debbugs:13807 --- etc/ChangeLog | 4 +++ etc/NEWS | 12 +++++++ src/ChangeLog | 30 ++++++++++++++++ src/filelock.c | 92 ++++++++++++++++++++++---------------------------- 4 files changed, 87 insertions(+), 51 deletions(-) diff --git a/etc/ChangeLog b/etc/ChangeLog index a13ab60be36..7ebb2a751ab 100644 --- a/etc/ChangeLog +++ b/etc/ChangeLog @@ -1,3 +1,7 @@ +2013-03-02 Paul Eggert + + * NEWS: The lock for FILE is now .#FILE or .#-FILE (Bug#13807). + 2013-03-01 Michael Albinus * NEWS: Fix Tramp "adb" entry. Extend list of discontinued Tramp diff --git a/etc/NEWS b/etc/NEWS index b344f643069..73f7991823f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -320,6 +320,18 @@ text-property on the first char. ** The `defalias-fset-function' property lets you catch calls to defalias and redirect them to your own function instead of `fset'. +** The lock for 'DIR/FILE' is now 'DIR/.#FILE' or 'DIR/.#-FILE'. +When you edit DIR/FILE, Emacs normally creates a symbolic link +DIR/.#FILE as a lock that warns other instances of Emacs that DIR/FILE +is being edited. Formerly, if there was already a non-symlink file +named DIR/.#FILE, Emacs fell back on the lock names DIR/.#FILE.0 +through DIR/.#FILE.9. These fallbacks have been removed, so that +Emacs now no longer locks DIR/FILE in that case. + +On MS-Windows the lock is a regular file DIR/.#-FILE, not a symlink. +MS-Windows and non-MS-Windows implementations of Emacs ignore each +other's locks. + ** The 9th element returned by `file-attributes' is now unspecified. Formerly, it was t if the file's gid would change if file were deleted and recreated. This value has been inaccurate for years on many diff --git a/src/ChangeLog b/src/ChangeLog index d8b2833b2fa..7afae3df6f5 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,33 @@ +2013-03-02 Paul Eggert + + The lock for FILE is now .#FILE or .#-FILE (Bug#13807). + The old approach, which fell back on DIR/.#FILE.0 through + DIR/.#FILE.9, had race conditions that could not be easily fixed. + If DIR/.#FILE is a non-symlink file, Emacs now does not create a + lock file for DIR/FILE; that is, DIR/FILE is no longer partly + protected by a lock if DIR/.#FILE is a non-symlink file ("partly" + because the locking mechanism was never reliable in that case). + This patch fixes this and other bugs discovered by a code + inspection that was prompted by + . + Also, this patch switches to .#-FILE (not .#FILE) on MS-Windows, + to avoid interoperability problems between the MS-Windows and + non-MS-Windows implementations. MS-Windows and non-MS-Windows + instances of Emacs now ignore each others' locks. + * filelock.c (defined_WINDOWSNT): New constant. + (MAKE_LOCK_NAME, fill_in_lock_file_name): + Don't create DIR/.#FILE.0 through DIR/.#FILE.9. Instead, create + DIR/.#FILE symlinks on non-MS-Windows hosts, and DIR/.#-FILE + regular files on MS-Windows hosts. + (MAKE_LOCK_NAME, unlock_file, Ffile_locked_p): + Use SAFE_ALLOCA to avoid problems with long file names. + (MAX_LFINFO): Now a local constant, not a global macro. + (IS_LOCK_FILE): Remove. + (lock_file_1): Don't inspect errno if symlink call succeeds; + that's not portable. + (lock_file): Document that this function can return if lock + creation fails. + 2013-03-02 Andreas Schwab * lisp.h (XPNTR) [!USE_LSB_TAG]: Remove extra paren. (Bug#13734) diff --git a/src/filelock.c b/src/filelock.c index 78cd60a12e1..d52e9faeb44 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -64,7 +64,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 +/* On non-MS-Windows systems, 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. @@ -97,7 +98,12 @@ 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 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'. MS-Windows and non-MS-Windows + versions of Emacs ignore each other's locks. */ /* Return the time of the last system boot. */ @@ -291,55 +297,31 @@ typedef struct /* Free the two dynamically-allocated pieces in PTR. */ #define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) - -/* 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) +enum { defined_WINDOWSNT = 1 }; #else -#define IS_LOCK_FILE(ST) S_ISLNK ((ST).st_mode) +enum { defined_WINDOWSNT = 0 }; #endif +/* 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 "-" if MS-Windows, plus one for the null. */ +#define MAKE_LOCK_NAME(lockname, fname) \ + (lockname = SAFE_ALLOCA (SBYTES (fname) + 2 + defined_WINDOWSNT + 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) { - ptrdiff_t length = SBYTES (fn); - register char *p; - struct stat st; - int count = 0; - - strcpy (lockfile, SSDATA (fn)); - - /* 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 + length; p != lockfile && *p != '/'; p--) - p[2] = *p; - - /* Insert the `.#'. */ - p[1] = '.'; - p[2] = '#'; - - p = lockfile + length + 2; - - while (lstat (lockfile, &st) == 0 && !IS_LOCK_FILE (st)) - { - if (count > 9) - { - *p = '\0'; - return; - } - sprintf (p, ".%d", count++); - } + 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] = '#'; + if (defined_WINDOWSNT) + lockfile[dirlen + 2] = '-'; + strcpy (lockfile + dirlen + 2 + defined_WINDOWSNT, base); } static int @@ -356,8 +338,8 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) { /* 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. */ + open/write/close operation atomic, as far as other WINDOWSNT + processes are concerned. */ int fd = _sopen (lfname, _O_WRONLY | _O_BINARY | _O_CREAT | _O_EXCL | _O_NOINHERIT, _SH_DENYRW, S_IREAD | S_IWRITE); @@ -380,7 +362,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) } #else err = symlink (lock_info_str, lfname); - if (errno == EEXIST && force) + if (err != 0 && errno == EEXIST && force) { unlink (lfname); err = symlink (lock_info_str, lfname); @@ -440,6 +422,8 @@ read_lock_data (char *lfname) #else int fd = emacs_open (lfname, O_RDONLY | O_BINARY, S_IREAD); ssize_t nbytes; + /* 256 chars for user, 1024 chars for host, 10 digits for each of 2 int's. */ + enum { MAX_LFINFO = 256 + 1024 + 10 + 10 + 2 }; char lfinfo[MAX_LFINFO + 1]; if (fd < 0) @@ -601,6 +585,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 @@ -686,9 +671,10 @@ lock_file (Lisp_Object fn) } 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); @@ -697,6 +683,8 @@ unlock_file (register Lisp_Object fn) if (current_lock_owner (0, lfname) == 2) unlink (lfname); + + SAFE_FREE (); } void @@ -762,9 +750,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); @@ -781,6 +770,7 @@ t if it is locked by you, else a string saying which user has locked it. */) if (owner > 0) FREE_LOCK_INFO (locker); + SAFE_FREE (); return ret; } -- 2.39.2