From: Michael Tremer Date: Thu, 16 Mar 2023 08:07:57 +0000 (+0000) Subject: snapshots: Do not modify an existing snapshot X-Git-Tag: 0.9.29~305 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=549c98ad5280a18a01294fc924c5d6d37ce475db;p=pakfire.git snapshots: Do not modify an existing snapshot 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 --- diff --git a/src/libpakfire/build.c b/src/libpakfire/build.c index afd9fed30..411964d98 100644 --- a/src/libpakfire/build.c +++ b/src/libpakfire/build.c @@ -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; } diff --git a/src/libpakfire/include/pakfire/snapshot.h b/src/libpakfire/include/pakfire/snapshot.h index fe4404979..3a8e75677 100644 --- a/src/libpakfire/include/pakfire/snapshot.h +++ b/src/libpakfire/include/pakfire/snapshot.h @@ -27,8 +27,11 @@ #include -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 diff --git a/src/libpakfire/snapshot.c b/src/libpakfire/snapshot.c index 76c36fc6e..c4dc8fd64 100644 --- a/src/libpakfire/snapshot.c +++ b/src/libpakfire/snapshot.c @@ -34,8 +34,23 @@ #include #include #include +#include #include +#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; } diff --git a/tests/libpakfire/snapshot.c b/tests/libpakfire/snapshot.c index bd4c44ba6..7bb3c86d4 100644 --- a/tests/libpakfire/snapshot.c +++ b/tests/libpakfire/snapshot.c @@ -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;