]> git.ipfire.org Git - pakfire.git/commitdiff
snapshots: Do not modify an existing snapshot
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 16 Mar 2023 08:07:57 +0000 (08:07 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 16 Mar 2023 08:07:57 +0000 (08:07 +0000)
Instead, the routines will now write the new snapshot to a temporary
location and replace it more or less atomically.

Fixes: #13045 - Multiple concurrent instances can destroy the snapshot
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/build.c
src/libpakfire/include/pakfire/snapshot.h
src/libpakfire/snapshot.c
tests/libpakfire/snapshot.c

index afd9fed305aa18f3da476e5296f25d3708bee23e..411964d980b2d66f5f41fdda9f2575346407eadc 100644 (file)
@@ -1660,8 +1660,6 @@ static int pakfire_build_install_packages(struct pakfire_build* build,
        Initializes the build environment
 */
 static int pakfire_build_init(struct pakfire_build* build) {
-       FILE* f = NULL;
-       char path[PATH_MAX];
        int r;
 
        // Don't do it again
@@ -1670,30 +1668,14 @@ static int pakfire_build_init(struct pakfire_build* build) {
                return 0;
        }
 
-       // Compose path for snapshot
-       r = pakfire_cache_path(build->pakfire, path, "%s", "snapshot.tar.zst");
-       if (r) {
-               ERROR(build->pakfire, "Could not compose snapshot path: %m\n");
-               return 1;
-       }
-
        // Tells us whether we need to (re-)create the snapshot
        int snapshot_needs_update = 0;
 
        // Extract snapshot
        if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT)) {
-               // Open the snapshot
-               f = fopen(path, "r");
-
-               // Try restoring the snapshot
-               if (f) {
-                       r = pakfire_snapshot_restore(build->pakfire, f);
-                       fclose(f);
-
-                       // Exit on error
-                       if (r)
-                               return r;
-               }
+               r = pakfire_snapshot_restore(build->pakfire);
+               if (r)
+                       return r;
        }
 
        // Install or update any build dependencies
@@ -1703,17 +1685,8 @@ static int pakfire_build_init(struct pakfire_build* build) {
 
        // Update the snapshot if there were changes
        if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT) && snapshot_needs_update) {
-               // Open snapshot file for writing
-               f = fopen(path, "w");
-               if (!f) {
-                       ERROR(build->pakfire, "Could not open snapshot file for writing: %m\n");
-                       return 1;
-               }
-
                // Create a new snapshot
-               r = pakfire_snapshot_create(build->pakfire, f);
-               fclose(f);
-
+               r = pakfire_snapshot_create(build->pakfire);
                if (r)
                        return r;
        }
index fe4404979b6310b89e71f2a4b43837a3454058db..3a8e756772838c3efb0c4a2fc27273c673efeb62 100644 (file)
 
 #include <pakfire/pakfire.h>
 
-int pakfire_snapshot_create(struct pakfire* pakfire, FILE* f);
-int pakfire_snapshot_restore(struct pakfire* pakfire, FILE* f);
+int pakfire_snapshot_create(struct pakfire* pakfire);
+int pakfire_snapshot_restore(struct pakfire* pakfire);
+
+int pakfire_snapshot_compress(struct pakfire* pakfire, FILE* f);
+int pakfire_snapshot_extract(struct pakfire* pakfire, FILE* f);
 
 #endif
 
index 76c36fc6eccd428206650ea7b55f309eb7d73f03..c4dc8fd644bf210796b4ade79c2a70f25eeb5d63 100644 (file)
 #include <pakfire/private.h>
 #include <pakfire/repo.h>
 #include <pakfire/snapshot.h>
+#include <pakfire/string.h>
 #include <pakfire/util.h>
 
+#define pakfire_snapshot_path(pakfire, path) \
+       __pakfire_snapshot_path(pakfire, path, sizeof(path))
+
+static int __pakfire_snapshot_path(struct pakfire* pakfire, char* path, const size_t length) {
+       int r;
+
+       // Compose path for snapshot
+       r = __pakfire_cache_path(pakfire, path, length, "%s", "snapshot.tar.zst");
+       if (r)
+               ERROR(pakfire, "Could not compose snapshot path: %m\n");
+
+       return r;
+}
+
 static struct archive* pakfire_snapshot_create_archive(struct pakfire* pakfire, FILE* f) {
        struct archive* a = archive_write_new();
        if (!a) {
@@ -82,12 +97,13 @@ ERROR:
        return NULL;
 }
 
-int pakfire_snapshot_create(struct pakfire* pakfire, FILE* snapshot) {
+int pakfire_snapshot_compress(struct pakfire* pakfire, FILE* f) {
        struct pakfire_filelist* filelist = NULL;
        struct archive* archive = NULL;
        int r = 1;
 
-       if (!snapshot) {
+       // Check input
+       if (!f) {
                errno = EINVAL;
                return 1;
        }
@@ -99,7 +115,7 @@ int pakfire_snapshot_create(struct pakfire* pakfire, FILE* snapshot) {
 
        const char* root = pakfire_get_path(pakfire);
 
-       INFO(pakfire, "Creating snapshot of %s...\n", root);
+       DEBUG(pakfire, "Creating snapshot of %s...\n", root);
 
        // Scan for all files
        r = pakfire_filelist_scan(filelist, root, NULL, NULL);
@@ -114,7 +130,7 @@ int pakfire_snapshot_create(struct pakfire* pakfire, FILE* snapshot) {
        }
 
        // Create a new archive
-       archive = pakfire_snapshot_create_archive(pakfire, snapshot);
+       archive = pakfire_snapshot_create_archive(pakfire, f);
        if (!archive) {
                ERROR(pakfire, "Could not open archive for writing\n");
                goto ERROR;
@@ -145,10 +161,64 @@ ERROR:
        return r;
 }
 
-static int pakfire_snapshot_extract(struct pakfire* pakfire, FILE* f) {
+int pakfire_snapshot_create(struct pakfire* pakfire) {
+       FILE* f = NULL;
+       char tmppath[PATH_MAX];
+       char path[PATH_MAX];
+       int r;
+
+       // Compose the destination path
+       r = pakfire_snapshot_path(pakfire, path);
+       if (r)
+               goto ERROR;
+
+       // Make the temporary path
+       r = pakfire_string_format(tmppath, "%s.XXXXXX", path);
+       if (r)
+               goto ERROR;
+
+       // Create a new temporary file
+       f = pakfire_mktemp(tmppath, 0644);
+       if (!f) {
+               ERROR(pakfire, "Could not create a new snapshot: %m\n");
+               r = 1;
+               goto ERROR;
+       }
+
+       // Write the snapshot
+       r = pakfire_snapshot_compress(pakfire, f);
+       if (r)
+               goto ERROR;
+
+       // Unlink any previous snapshots
+       unlink(path);
+
+       // Link the new snapshot
+       r = link(tmppath, path);
+       if (r) {
+               ERROR(pakfire, "Could not create snapshot %s: %m\n", path);
+               goto ERROR;
+       }
+
+ERROR:
+       if (f)
+               fclose(f);
+       if (*tmppath)
+               unlink(tmppath);
+
+       return r;
+}
+
+int pakfire_snapshot_extract(struct pakfire* pakfire, FILE* f) {
        struct stat st;
        int r = 1;
 
+       // Check input
+       if (!f) {
+               errno = EINVAL;
+               return 1;
+       }
+
        // Stat the input file
        r = fstat(fileno(f), &st);
        if (r) {
@@ -186,20 +256,35 @@ ERROR:
        return r;
 }
 
-int pakfire_snapshot_restore(struct pakfire* pakfire, FILE* f) {
+int pakfire_snapshot_restore(struct pakfire* pakfire) {
+       struct pakfire_repo* repo = NULL;
        struct pakfire_db* db = NULL;
+       char path[PATH_MAX];
+       FILE* f = NULL;
+       int r;
 
+       // Make path
+       r = pakfire_snapshot_path(pakfire, path);
+       if (r)
+               goto ERROR;
+
+       // Open the snapshot
+       f = fopen(path, "r");
        if (!f) {
-               errno = EINVAL;
+               // It is not fatal if the snapshot doesn't exist
+               if (errno == ENOENT)
+                       return 0;
+
+               ERROR(pakfire, "Could not open snapshot file %s: %m\n", path);
                return 1;
        }
 
        // Extract the archive
-       int r = pakfire_snapshot_extract(pakfire, f);
+       r = pakfire_snapshot_extract(pakfire, f);
        if (r)
-               return r;
+               goto ERROR;
 
-       struct pakfire_repo* repo = pakfire_get_installed_repo(pakfire);
+       repo = pakfire_get_installed_repo(pakfire);
        if (!repo)
                goto ERROR;
 
@@ -215,6 +300,8 @@ ERROR:
                pakfire_repo_unref(repo);
        if (db)
                pakfire_db_unref(db);
+       if (f)
+               fclose(f);
 
        return r;
 }
index bd4c44ba650c4cf8cd721daecc933fe8aa108aed..7bb3c86d40f4e35d227cc6fdee94df156234705d 100644 (file)
@@ -32,7 +32,7 @@ static int test_create_and_restore(const struct test* t) {
        ASSERT(f);
 
        // Create the snapshot
-       ASSERT_SUCCESS(pakfire_snapshot_create(t->pakfire, f));
+       ASSERT_SUCCESS(pakfire_snapshot_compress(t->pakfire, f));
 
        // Determine the size of the snapshot
        size_t size = ftell(f);
@@ -46,7 +46,7 @@ static int test_create_and_restore(const struct test* t) {
        rewind(f);
 
        // Perform a restore
-       ASSERT_SUCCESS(pakfire_snapshot_restore(t->pakfire, f));
+       ASSERT_SUCCESS(pakfire_snapshot_extract(t->pakfire, f));
 
        // Everything passed
        r = EXIT_SUCCESS;
@@ -62,10 +62,10 @@ static int test_invalid_inputs(const struct test* t) {
        int r = EXIT_FAILURE;
 
        // pakfire_snapshot_create
-       ASSERT_ERRNO(pakfire_snapshot_create(t->pakfire, NULL), EINVAL);
+       ASSERT_ERRNO(pakfire_snapshot_compress(t->pakfire, NULL), EINVAL);
 
        // pakfire_snapshot_restore
-       ASSERT_ERRNO(pakfire_snapshot_restore(t->pakfire, NULL), EINVAL);
+       ASSERT_ERRNO(pakfire_snapshot_extract(t->pakfire, NULL), EINVAL);
 
        // Everything passed
        r = EXIT_SUCCESS;