]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Add lockfile to the cache
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 16 Oct 2024 19:43:34 +0000 (13:43 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 16 Oct 2024 19:43:34 +0000 (13:43 -0600)
To prevent other (well-behaved) instances from accidentally reading and
writing the same directory simultaneously.

Fixes #149.

src/cache.c
src/cache.h
src/sig.c

index 741e61c40ef820d572098bd6309cc0f1a4e17941..8cb0e64108f4393ceba154b8678092eeaab3f710 100644 (file)
@@ -1,9 +1,12 @@
 #include "cache.h"
 
+#include <fcntl.h>
 #include <ftw.h>
+#include <signal.h>
 #include <stdbool.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #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);
 }
 
index 0fab4cc946f562c754b51ed22970e451cff95f3c..75939810834a63e770607c02ec2b8b874735e1f3 100644 (file)
@@ -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 */
index 96918a15b00cb792555d2dbe5e262f823f762a63..daa7e376353c3723706344f26f8d61b82b19873e 100644 (file)
--- a/src/sig.c
+++ b/src/sig.c
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#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;