diff options
| author | Paul Eggert | 2013-03-02 12:41:53 -0800 |
|---|---|---|
| committer | Paul Eggert | 2013-03-02 12:41:53 -0800 |
| commit | b5029e230d10ea412e4ff1d4867a0c884d130039 (patch) | |
| tree | 98c424b27b043f8289e215d06ec6a09de6c7e2c3 /src | |
| parent | 06b583dec7cbde714c8fb991a1e123f612b66e3a (diff) | |
| download | emacs-b5029e230d10ea412e4ff1d4867a0c884d130039.tar.gz emacs-b5029e230d10ea412e4ff1d4867a0c884d130039.zip | |
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
<http://lists.gnu.org/archive/html/emacs-devel/2013-02/msg00531.html>.
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
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 30 | ||||
| -rw-r--r-- | src/filelock.c | 92 |
2 files changed, 71 insertions, 51 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index d8b2833b2fa..7afae3df6f5 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,33 @@ | |||
| 1 | 2013-03-02 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | The lock for FILE is now .#FILE or .#-FILE (Bug#13807). | ||
| 4 | The old approach, which fell back on DIR/.#FILE.0 through | ||
| 5 | DIR/.#FILE.9, had race conditions that could not be easily fixed. | ||
| 6 | If DIR/.#FILE is a non-symlink file, Emacs now does not create a | ||
| 7 | lock file for DIR/FILE; that is, DIR/FILE is no longer partly | ||
| 8 | protected by a lock if DIR/.#FILE is a non-symlink file ("partly" | ||
| 9 | because the locking mechanism was never reliable in that case). | ||
| 10 | This patch fixes this and other bugs discovered by a code | ||
| 11 | inspection that was prompted by | ||
| 12 | <http://lists.gnu.org/archive/html/emacs-devel/2013-02/msg00531.html>. | ||
| 13 | Also, this patch switches to .#-FILE (not .#FILE) on MS-Windows, | ||
| 14 | to avoid interoperability problems between the MS-Windows and | ||
| 15 | non-MS-Windows implementations. MS-Windows and non-MS-Windows | ||
| 16 | instances of Emacs now ignore each others' locks. | ||
| 17 | * filelock.c (defined_WINDOWSNT): New constant. | ||
| 18 | (MAKE_LOCK_NAME, fill_in_lock_file_name): | ||
| 19 | Don't create DIR/.#FILE.0 through DIR/.#FILE.9. Instead, create | ||
| 20 | DIR/.#FILE symlinks on non-MS-Windows hosts, and DIR/.#-FILE | ||
| 21 | regular files on MS-Windows hosts. | ||
| 22 | (MAKE_LOCK_NAME, unlock_file, Ffile_locked_p): | ||
| 23 | Use SAFE_ALLOCA to avoid problems with long file names. | ||
| 24 | (MAX_LFINFO): Now a local constant, not a global macro. | ||
| 25 | (IS_LOCK_FILE): Remove. | ||
| 26 | (lock_file_1): Don't inspect errno if symlink call succeeds; | ||
| 27 | that's not portable. | ||
| 28 | (lock_file): Document that this function can return if lock | ||
| 29 | creation fails. | ||
| 30 | |||
| 1 | 2013-03-02 Andreas Schwab <schwab@linux-m68k.org> | 31 | 2013-03-02 Andreas Schwab <schwab@linux-m68k.org> |
| 2 | 32 | ||
| 3 | * lisp.h (XPNTR) [!USE_LSB_TAG]: Remove extra paren. (Bug#13734) | 33 | * 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 <http://www.gnu.org/licenses/>. */ | |||
| 64 | #define WTMP_FILE "/var/log/wtmp" | 64 | #define WTMP_FILE "/var/log/wtmp" |
| 65 | #endif | 65 | #endif |
| 66 | 66 | ||
| 67 | /* The strategy: to lock a file FN, create a symlink .#FN in FN's | 67 | /* On non-MS-Windows systems, use a symbolic link to represent a lock. |
| 68 | The strategy: to lock a file FN, create a symlink .#FN in FN's | ||
| 68 | directory, with link data `user@host.pid'. This avoids a single | 69 | directory, with link data `user@host.pid'. This avoids a single |
| 69 | mount (== failure) point for lock files. | 70 | mount (== failure) point for lock files. |
| 70 | 71 | ||
| @@ -97,7 +98,12 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 97 | has contributed this implementation for Emacs), and was designed by | 98 | has contributed this implementation for Emacs), and was designed by |
| 98 | Ethan Jacobson, Kimbo Mundy, and others. | 99 | Ethan Jacobson, Kimbo Mundy, and others. |
| 99 | 100 | ||
| 100 | --karl@cs.umb.edu/karl@hq.ileaf.com. */ | 101 | --karl@cs.umb.edu/karl@hq.ileaf.com. |
| 102 | |||
| 103 | On MS-Windows, symbolic links do not work well, so instead of a | ||
| 104 | symlink .#FN -> 'user@host.pid', the lock is a regular file .#-FN | ||
| 105 | with contents 'user@host.pid'. MS-Windows and non-MS-Windows | ||
| 106 | versions of Emacs ignore each other's locks. */ | ||
| 101 | 107 | ||
| 102 | 108 | ||
| 103 | /* Return the time of the last system boot. */ | 109 | /* Return the time of the last system boot. */ |
| @@ -291,55 +297,31 @@ typedef struct | |||
| 291 | /* Free the two dynamically-allocated pieces in PTR. */ | 297 | /* Free the two dynamically-allocated pieces in PTR. */ |
| 292 | #define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) | 298 | #define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) |
| 293 | 299 | ||
| 294 | |||
| 295 | /* Write the name of the lock file for FNAME into LOCKNAME. Length | ||
| 296 | will be that of FN plus two more for the leading `.#' plus 1 for | ||
| 297 | the trailing period plus one for the digit after it plus one for | ||
| 298 | the null. */ | ||
| 299 | #define MAKE_LOCK_NAME(LOCKNAME, FNAME) \ | ||
| 300 | (LOCKNAME = alloca (SBYTES (FNAME) + 2 + 1 + 1 + 1), \ | ||
| 301 | fill_in_lock_file_name (LOCKNAME, (FNAME))) | ||
| 302 | |||
| 303 | #ifdef WINDOWSNT | 300 | #ifdef WINDOWSNT |
| 304 | /* 256 chars for user, 1024 chars for host, 10 digits for each of 2 int's. */ | 301 | enum { defined_WINDOWSNT = 1 }; |
| 305 | #define MAX_LFINFO (256 + 1024 + 10 + 10 + 2) | ||
| 306 | /* min size: .@PID */ | ||
| 307 | #define IS_LOCK_FILE(ST) (MAX_LFINFO >= (ST).st_size && (ST).st_size >= 3) | ||
| 308 | #else | 302 | #else |
| 309 | #define IS_LOCK_FILE(ST) S_ISLNK ((ST).st_mode) | 303 | enum { defined_WINDOWSNT = 0 }; |
| 310 | #endif | 304 | #endif |
| 311 | 305 | ||
| 306 | /* Write the name of the lock file for FNAME into LOCKNAME. Length | ||
| 307 | will be that of FNAME plus two more for the leading ".#", | ||
| 308 | plus one for "-" if MS-Windows, plus one for the null. */ | ||
| 309 | #define MAKE_LOCK_NAME(lockname, fname) \ | ||
| 310 | (lockname = SAFE_ALLOCA (SBYTES (fname) + 2 + defined_WINDOWSNT + 1), \ | ||
| 311 | fill_in_lock_file_name (lockname, fname)) | ||
| 312 | |||
| 312 | static void | 313 | static void |
| 313 | fill_in_lock_file_name (register char *lockfile, register Lisp_Object fn) | 314 | fill_in_lock_file_name (char *lockfile, Lisp_Object fn) |
| 314 | { | 315 | { |
| 315 | ptrdiff_t length = SBYTES (fn); | 316 | char *last_slash = memrchr (SSDATA (fn), '/', SBYTES (fn)); |
| 316 | register char *p; | 317 | char *base = last_slash + 1; |
| 317 | struct stat st; | 318 | ptrdiff_t dirlen = base - SSDATA (fn); |
| 318 | int count = 0; | 319 | memcpy (lockfile, SSDATA (fn), dirlen); |
| 319 | 320 | lockfile[dirlen] = '.'; | |
| 320 | strcpy (lockfile, SSDATA (fn)); | 321 | lockfile[dirlen + 1] = '#'; |
| 321 | 322 | if (defined_WINDOWSNT) | |
| 322 | /* Shift the nondirectory part of the file name (including the null) | 323 | lockfile[dirlen + 2] = '-'; |
| 323 | right two characters. Here is one of the places where we'd have to | 324 | strcpy (lockfile + dirlen + 2 + defined_WINDOWSNT, base); |
| 324 | do something to support 14-character-max file names. */ | ||
| 325 | for (p = lockfile + length; p != lockfile && *p != '/'; p--) | ||
| 326 | p[2] = *p; | ||
| 327 | |||
| 328 | /* Insert the `.#'. */ | ||
| 329 | p[1] = '.'; | ||
| 330 | p[2] = '#'; | ||
| 331 | |||
| 332 | p = lockfile + length + 2; | ||
| 333 | |||
| 334 | while (lstat (lockfile, &st) == 0 && !IS_LOCK_FILE (st)) | ||
| 335 | { | ||
| 336 | if (count > 9) | ||
| 337 | { | ||
| 338 | *p = '\0'; | ||
| 339 | return; | ||
| 340 | } | ||
| 341 | sprintf (p, ".%d", count++); | ||
| 342 | } | ||
| 343 | } | 325 | } |
| 344 | 326 | ||
| 345 | static int | 327 | static int |
| @@ -356,8 +338,8 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) | |||
| 356 | { | 338 | { |
| 357 | /* Deny everybody else any kind of access to the file until we are | 339 | /* Deny everybody else any kind of access to the file until we are |
| 358 | done writing it and close the handle. This makes the entire | 340 | done writing it and close the handle. This makes the entire |
| 359 | open/write/close operation atomic, as far as other processes | 341 | open/write/close operation atomic, as far as other WINDOWSNT |
| 360 | are concerned. */ | 342 | processes are concerned. */ |
| 361 | int fd = _sopen (lfname, | 343 | int fd = _sopen (lfname, |
| 362 | _O_WRONLY | _O_BINARY | _O_CREAT | _O_EXCL | _O_NOINHERIT, | 344 | _O_WRONLY | _O_BINARY | _O_CREAT | _O_EXCL | _O_NOINHERIT, |
| 363 | _SH_DENYRW, S_IREAD | S_IWRITE); | 345 | _SH_DENYRW, S_IREAD | S_IWRITE); |
| @@ -380,7 +362,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) | |||
| 380 | } | 362 | } |
| 381 | #else | 363 | #else |
| 382 | err = symlink (lock_info_str, lfname); | 364 | err = symlink (lock_info_str, lfname); |
| 383 | if (errno == EEXIST && force) | 365 | if (err != 0 && errno == EEXIST && force) |
| 384 | { | 366 | { |
| 385 | unlink (lfname); | 367 | unlink (lfname); |
| 386 | err = symlink (lock_info_str, lfname); | 368 | err = symlink (lock_info_str, lfname); |
| @@ -440,6 +422,8 @@ read_lock_data (char *lfname) | |||
| 440 | #else | 422 | #else |
| 441 | int fd = emacs_open (lfname, O_RDONLY | O_BINARY, S_IREAD); | 423 | int fd = emacs_open (lfname, O_RDONLY | O_BINARY, S_IREAD); |
| 442 | ssize_t nbytes; | 424 | ssize_t nbytes; |
| 425 | /* 256 chars for user, 1024 chars for host, 10 digits for each of 2 int's. */ | ||
| 426 | enum { MAX_LFINFO = 256 + 1024 + 10 + 10 + 2 }; | ||
| 443 | char lfinfo[MAX_LFINFO + 1]; | 427 | char lfinfo[MAX_LFINFO + 1]; |
| 444 | 428 | ||
| 445 | if (fd < 0) | 429 | if (fd < 0) |
| @@ -601,6 +585,7 @@ lock_if_free (lock_info_type *clasher, register char *lfname) | |||
| 601 | decided to go ahead without locking. | 585 | decided to go ahead without locking. |
| 602 | 586 | ||
| 603 | When this returns, either the lock is locked for us, | 587 | When this returns, either the lock is locked for us, |
| 588 | or lock creation failed, | ||
| 604 | or the user has said to go ahead without locking. | 589 | or the user has said to go ahead without locking. |
| 605 | 590 | ||
| 606 | If the file is locked by someone else, this calls | 591 | If the file is locked by someone else, this calls |
| @@ -686,9 +671,10 @@ lock_file (Lisp_Object fn) | |||
| 686 | } | 671 | } |
| 687 | 672 | ||
| 688 | void | 673 | void |
| 689 | unlock_file (register Lisp_Object fn) | 674 | unlock_file (Lisp_Object fn) |
| 690 | { | 675 | { |
| 691 | register char *lfname; | 676 | char *lfname; |
| 677 | USE_SAFE_ALLOCA; | ||
| 692 | 678 | ||
| 693 | fn = Fexpand_file_name (fn, Qnil); | 679 | fn = Fexpand_file_name (fn, Qnil); |
| 694 | fn = ENCODE_FILE (fn); | 680 | fn = ENCODE_FILE (fn); |
| @@ -697,6 +683,8 @@ unlock_file (register Lisp_Object fn) | |||
| 697 | 683 | ||
| 698 | if (current_lock_owner (0, lfname) == 2) | 684 | if (current_lock_owner (0, lfname) == 2) |
| 699 | unlink (lfname); | 685 | unlink (lfname); |
| 686 | |||
| 687 | SAFE_FREE (); | ||
| 700 | } | 688 | } |
| 701 | 689 | ||
| 702 | void | 690 | void |
| @@ -762,9 +750,10 @@ t if it is locked by you, else a string saying which user has locked it. */) | |||
| 762 | (Lisp_Object filename) | 750 | (Lisp_Object filename) |
| 763 | { | 751 | { |
| 764 | Lisp_Object ret; | 752 | Lisp_Object ret; |
| 765 | register char *lfname; | 753 | char *lfname; |
| 766 | int owner; | 754 | int owner; |
| 767 | lock_info_type locker; | 755 | lock_info_type locker; |
| 756 | USE_SAFE_ALLOCA; | ||
| 768 | 757 | ||
| 769 | filename = Fexpand_file_name (filename, Qnil); | 758 | filename = Fexpand_file_name (filename, Qnil); |
| 770 | 759 | ||
| @@ -781,6 +770,7 @@ t if it is locked by you, else a string saying which user has locked it. */) | |||
| 781 | if (owner > 0) | 770 | if (owner > 0) |
| 782 | FREE_LOCK_INFO (locker); | 771 | FREE_LOCK_INFO (locker); |
| 783 | 772 | ||
| 773 | SAFE_FREE (); | ||
| 784 | return ret; | 774 | return ret; |
| 785 | } | 775 | } |
| 786 | 776 | ||