From b1447b57e93d9de9e7dd651737396476da79b8ed Mon Sep 17 00:00:00 2001 From: Alberto Leiva Popper Date: Wed, 16 Oct 2024 13:43:34 -0600 Subject: [PATCH] Add lockfile to the cache To prevent other (well-behaved) instances from accidentally reading and writing the same directory simultaneously. Fixes #149. --- src/cache.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/cache.h | 1 + src/sig.c | 13 ++++++-- 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/cache.c b/src/cache.c index 741e61c4..8cb0e641 100644 --- a/src/cache.c +++ b/src/cache.c @@ -1,9 +1,12 @@ #include "cache.h" +#include #include +#include #include #include #include +#include #include #include "alloc.h" @@ -56,6 +59,9 @@ static struct rpki_cache { struct cache_table fallback; } cache; +/* "Is the lockfile ours?" */ +static volatile sig_atomic_t lockfile_owned; + struct cache_cage { struct cache_node *refresh; struct cache_node *fallback; @@ -70,6 +76,7 @@ struct cache_commit { STAILQ_HEAD(cache_commits, cache_commit) commits = STAILQ_HEAD_INITIALIZER(commits); +#define LOCKFILE ".lock" #define INDEX_FILE "index.json" #define TAGNAME_VERSION "fort-version" @@ -183,7 +190,7 @@ reset_cache_dir(void) deleted = 0; FOREACH_DIR_FILE(dir, file) - if (!S_ISDOTS(file)) { + if (!S_ISDOTS(file) && strcmp(file->d_name, LOCKFILE) != 0) { error = file_rm_rf(file->d_name); if (error) goto end; @@ -214,6 +221,67 @@ init_cachedir_tag(void) "# https://bford.info/cachedir/\n"); } +static int +lock_cache(void) +{ + int fd; + int error; + + pr_op_debug("touch " LOCKFILE); + + /* + * Suppose we get SIGTERM in the middle of this function. + * + * 1. open() then lockfile_owned = 1, we're interrupted between them: + * The handler doesn't delete our lock. + * 2. lockfile_owned = 1 then open(), we're interrupted between them: + * The handler deletes some other instance's lock. + * + * 1 is better because we already couldn't guarantee the lock was + * deleted on every situation. (SIGKILL) + */ + + fd = open(LOCKFILE, O_CREAT | O_EXCL, 0644); + if (fd < 0) { + error = errno; + pr_op_err("Cannot create lockfile '%s/" LOCKFILE "': %s", + config_get_local_repository(), strerror(error)); + return error; + } + close(fd); + + lockfile_owned = 1; + return 0; +} + +static void +unlock_cache(void) +{ + pr_op_debug("rm " LOCKFILE); + + if (!lockfile_owned) { + pr_op_debug("The cache wasn't locked."); + return; + } + + if (unlink(LOCKFILE) < 0) { + int error = errno; + pr_op_err("Cannot remove lockfile: %s", strerror(error)); + if (error != ENOENT) + return; + } + + lockfile_owned = 0; +} + +/* THIS FUNCTION CAN BE CALLED FROM A SIGNAL HANDLER. */ +void +cache_atexit(void) +{ + if (lockfile_owned) + unlink(LOCKFILE); +} + int cache_setup(void) { @@ -234,6 +302,17 @@ cache_setup(void) } init_tables(); + + errno = 0; + error = atexit(cache_atexit); + if (error) { + int err2 = errno; + pr_op_err("Cannot register cache's exit function."); + pr_op_err("Error message attempt 1: %s", strerror(error)); + pr_op_err("Error message attempt 2: %s", strerror(err2)); + return error; + } + return 0; } @@ -353,13 +432,16 @@ cache_prepare(void) { int error; + error = lock_cache(); + if (error) + return error; + if (load_index_file() != 0) { error = reset_cache_dir(); if (error) - return error; + goto fail; } - // XXX Lock the cache directory error = file_mkdir("rsync", true); if (error) goto fail; @@ -380,6 +462,7 @@ cache_prepare(void) return 0; fail: cache_foreach(delete_node); + unlock_cache(); return error; } @@ -1090,6 +1173,7 @@ cache_commit(void) { cleanup_cache(); write_index_file(); + unlock_cache(); cache_foreach(delete_node); } diff --git a/src/cache.h b/src/cache.h index 0fab4cc9..75939810 100644 --- a/src/cache.h +++ b/src/cache.h @@ -7,6 +7,7 @@ int cache_setup(void); /* Init this module */ void cache_teardown(void); /* Destroy this module */ +void cache_atexit(void); int cache_prepare(void); /* Prepare cache for new validation cycle */ void cache_commit(void); /* Finish validation cycle */ diff --git a/src/sig.c b/src/sig.c index 96918a15..daa7e376 100644 --- a/src/sig.c +++ b/src/sig.c @@ -10,6 +10,7 @@ #include #include +#include "cache.h" #include "log.h" /* @@ -50,7 +51,10 @@ print_stack_trace(void) static void do_cleanup(int signum) { - print_stack_trace(); + if (signum == SIGSEGV || signum == SIGBUS) + print_stack_trace(); + + cache_atexit(); /* Trigger default handler */ signal(signum, SIG_DFL); @@ -62,7 +66,12 @@ void register_signal_handlers(void) { /* Important: All of these need to terminate by default */ - int const cleanups[] = { SIGSEGV, SIGBUS, 0 }; + int const cleanups[] = { + SIGFPE, SIGSEGV, SIGBUS, SIGABRT, SIGSYS, /* 24.2.1 */ + SIGTERM, SIGINT, SIGQUIT, SIGHUP, /* 24.2.2 */ + SIGUSR1, SIGUSR2, /* 24.2.7 */ + 0 + }; struct sigaction action; unsigned int i; -- 2.47.2