From 9f01ec9157da30843df7866625350fcd9d9b3534 Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Sat, 31 Oct 2026 14:09:59 -0700 Subject: Lock the cached repolist with flock The slot lock has to be an flock, since an exec filter drops a record lock when it closes the stdout a fill writes through. The scan only got by with fcntl because nothing reopens its file while it runs. One kind of lock lets the two share their code, and on Linux, where the kinds never see each other, an flock on the scan's file now holds a scan off like any other. --- source/cgit.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'source/cgit.c') diff --git a/source/cgit.c b/source/cgit.c index f5f3138..e98210f 100644 --- a/source/cgit.c +++ b/source/cgit.c @@ -27,6 +27,7 @@ #include "ui-shared.h" #include "ui-snapshot.h" #include "ui-stats.h" +#include // Deliberately small. The whole body is read at once and handed to the auth // filter, so a filter never has to defend itself against a huge or a @@ -317,19 +318,13 @@ static void parse_args(int argc, const char **argv) } /* - * The lock is a fcntl lock rather than the mere existence of the lock file, + * The lock is an flock rather than the mere existence of the lock file, * because a lock the kernel drops with its process cannot outlive a scan * killed mid-run. A leftover file would count as a scan forever in progress. */ static int generate_cached_repolist(const char *path, const char *cached_rc) { struct strbuf locked_rc = STRBUF_INIT; - struct flock lock = { - .l_type = F_WRLCK, - .l_whence = SEEK_SET, - .l_start = 0, - .l_len = 0, - }; struct stat held, named; int err = 0; int fd; @@ -343,13 +338,13 @@ static int generate_cached_repolist(const char *path, const char *cached_rc) fprintf(stderr, "[cgit] Unable to open %s: %s (%d)\n", locked_rc.buf, strerror(err), err); goto out; } - if (fcntl(fd, F_SETLK, &lock) < 0) { + if (flock(fd, LOCK_EX | LOCK_NB) < 0) { // A lock held elsewhere only means concurrent requests, which // is not worth a line in the server log. Any other failure, a // filesystem without lock support say, silently costs a scan // on every request and does deserve one. err = errno; - if (err != EACCES && err != EAGAIN) + if (err != EWOULDBLOCK) fprintf(stderr, "[cgit] Unable to lock %s: %s (%d)\n", locked_rc.buf, strerror(err), err); close(fd); goto out; -- cgit 2.11.1