From ed1b4ef8dff60ba943f553d5faeb049df9b621b7 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Sun, 24 Apr 2016 23:30:30 +0100 Subject: [PATCH] locking.c, locking.1: Make the protocol safe if the lockfile is (re)moved. Get the device/inode pair from the lockfile descriptor after we've opened it (so this is the file we're actually going to lock). Once we've acquired the lock, check that the file /name/ still has the same device/inode pair. If nobody is allowed to move or unlink the lockfile while we've got the lock, then we know (assuming this check passes) that we've actually locked the right file and not some deleted thing. --- locking.1 | 15 +++++++++++++++ locking.c | 11 +++++++++++ 2 files changed, 26 insertions(+) diff --git a/locking.1 b/locking.1 index 5183790..a9a37aa 100644 --- a/locking.1 +++ b/locking.1 @@ -97,6 +97,21 @@ for days, hours, minutes or seconds, respectively) for the lock to become available, and then give up. This only makes sense with the .B \-\-wait option, so that is turned on automatically. +.PP +It is safe to unlink or atomically replace the lockfile while holding +the lock, though these actions will release the lock immediately. To +safely delete the lockfile, for example, run +.IP +.B "locking lock rm lock" +.PP +Similarly, a file can be updated safely by +.IP +.nf +.ft B +locking file sh -c \e +\h'8m'"update-file file >file.new && mv file.new file" +.fi +.ft R .SH "BUGS" The .B locking diff --git a/locking.c b/locking.c index 68cbc74..bfc5c77 100644 --- a/locking.c +++ b/locking.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -95,6 +96,7 @@ int main(int argc, char *argv[]) int t = -1; int oflag; unsigned int ot = 0; + struct stat st, nst; time_t nt; pid_t kid; int rc; @@ -184,9 +186,18 @@ doneopts: ot = alarm(0); oalrm = signal(SIGALRM, alrm); if (t >= 0) alarm(t); +again: if ((fd = open(file, oflag, 0666)) < 0) die(111, "error opening `%s': %s", file, strerror(errno)); + if (fstat(fd, &st)) + die(111, "error from fstat on `%s': %s", file, strerror(errno)); err = fcntl(fd, f & f_wait ? F_SETLKW : F_SETLK, &l) >= 0 ? 0 : errno; + if (stat(file, &nst)) { + if (errno == ENOENT) { close(fd); goto again; } + else die(111, "error from stat on `%s': %s", file, strerror(errno)); + } + if (st.st_dev != nst.st_dev || st.st_ino != nst.st_ino) + { close(fd); goto again; } done: signal(SIGALRM, oalrm); if (!ot) -- 2.11.0