Diff options
-rw-r--r--source/cache.c4
-rw-r--r--source/cgit.c2
-rwxr-xr-xtests/t0003-cache.sh27
3 files changed, 31 insertions, 2 deletions
diff --git a/source/cache.c b/source/cache.c
index 4802ab5..12151b8 100644
--- a/source/cache.c
+++ b/source/cache.c
@@ -202,7 +202,9 @@ static int lock_slot(struct cache_slot *slot)
{
struct stat held, named;
- slot->lock_fd = open(slot->lock_path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ // Close-on-exec, so that a program the fill runs gets the file only
+ // as its stdout, and holds the lock no longer than it can write there.
+ slot->lock_fd = open(slot->lock_path, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (slot->lock_fd == -1)
return errno;
// An flock rather than a POSIX record lock. The descriptor is put on
diff --git a/source/cgit.c b/source/cgit.c
index f81af79..f5f3138 100644
--- a/source/cgit.c
+++ b/source/cgit.c
@@ -337,7 +337,7 @@ static int generate_cached_repolist(const char *path, const char *cached_rc)
FILE *f;
strbuf_addf(&locked_rc, "%s.lock", cached_rc);
- fd = open(locked_rc.buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ fd = open(locked_rc.buf, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
err = errno;
fprintf(stderr, "[cgit] Unable to open %s: %s (%d)\n", locked_rc.buf, strerror(err), err);
diff --git a/tests/t0003-cache.sh b/tests/t0003-cache.sh
index 7e92b05..945ac63 100755
--- a/tests/t0003-cache.sh
+++ b/tests/t0003-cache.sh
@@ -267,4 +267,31 @@ test_expect_success 'a slot stays locked while an exec filter runs' '
! grep "Unable to publish" first.err
'
+# An flock lasts until the last copy of its descriptor closes, so a filter
+# may hold the slot's lock file only as the stdout it writes the page to. A
+# stray copy would keep the slot locked for as long as the filter, or
+# anything it leaves running, lives on.
+test_expect_success PERL 'a filter holds the lock file only as its stdout' '
+ rm -rf cache3 && mkdir cache3 &&
+ write_script fd-filter "$PERL_PATH" <<-\EOF &&
+ use POSIX qw(fstat);
+ 1 while <STDIN>;
+ my @lock = stat $ENV{LOCK_FILE} or die "$!\n";
+ my @fds = grep {
+ my @fd = fstat($_);
+ @fd && $fd[0] == $lock[0] && $fd[1] == $lock[1]
+ } 0 .. 255;
+ print "lock file on fd @fds.\n";
+ EOF
+ {
+ echo "cache-size=1" &&
+ sed "/^cache-size=/d" bigkeyrc &&
+ echo "repo.readme=:file-1" &&
+ echo "repo.about-filter=exec:$PWD/fd-filter"
+ } >fdrc &&
+ LOCK_FILE="$PWD/cache3/00000000.lock" CGIT_CONFIG="$PWD/fdrc" \
+ QUERY_STRING="url=bigpage/about/" cgit >fd.out &&
+ grep -F "lock file on fd 1." fd.out
+'
+
test_done