From 8c0cb50d76e865bfe6bb37c11f64992c82f6fd10 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Mon, 21 Oct 2024 14:37:54 +0000 Subject: [PATCH] archive: Move extraction code back to where it belongs Signed-off-by: Michael Tremer --- src/libpakfire/archive.c | 439 ++++++++++++++++++---- src/libpakfire/compress.c | 339 ----------------- src/libpakfire/include/pakfire/archive.h | 26 ++ src/libpakfire/include/pakfire/compress.h | 30 +- src/libpakfire/include/pakfire/pakfire.h | 2 +- src/libpakfire/pakfire.c | 36 +- 6 files changed, 423 insertions(+), 449 deletions(-) diff --git a/src/libpakfire/archive.c b/src/libpakfire/archive.c index fc6bdd569..4db24b368 100644 --- a/src/libpakfire/archive.c +++ b/src/libpakfire/archive.c @@ -82,6 +82,9 @@ struct pakfire_archive { // Digests struct pakfire_digests digests; + // Progress (when extracting) + struct pakfire_progress* progress; + // Verify Status int verify; }; @@ -156,19 +159,107 @@ ERROR: return NULL; } -static int pakfire_archive_walk(struct pakfire_archive* archive, - pakfire_walk_callback callback, pakfire_walk_filter_callback filter_callback, void* data) { +/* + Helper function to conditionally walk through an archive + and perform actions based on the callback. +*/ +static int pakfire_archive_walk(struct pakfire_archive* archive, struct archive* a, + pakfire_archive_walk_callback walk_callback, + pakfire_archive_walk_filter_callback filter_callback, void* p) { + struct archive_entry* entry = NULL; int r; - // Open the archive file - struct archive* a = open_archive(archive, NULL); + // Walk through the archive + for (;;) { + r = archive_read_next_header(a, &entry); + + // Handle the return code + switch (r) { + // Fall through if everything is okay + case ARCHIVE_OK: + break; + + // Return OK when we reached the end of the archive + case ARCHIVE_EOF: + return 0; + + // Raise any other errors + default: + return r; + } + + // Call the filter callback before we call the actual callback + if (filter_callback) { + r = filter_callback(archive->ctx, archive, a, entry, p); + + // Handle the return code + switch (r) { + case PAKFIRE_WALK_OK: + break; + + case PAKFIRE_WALK_END: + CTX_DEBUG(archive->ctx, "Filter callback sent END\n"); + return 0; + + case PAKFIRE_WALK_SKIP: + CTX_DEBUG(archive->ctx, "Filter callback sent SKIP\n"); + continue; + + case PAKFIRE_WALK_DONE: + CTX_DEBUG(archive->ctx, "Filter callback sent DONE\n"); + + // Clear the callback function + filter_callback = NULL; + break; + + case PAKFIRE_WALK_AGAIN: + CTX_DEBUG(archive->ctx, "Filter callback sent AGAIN\n"); + return -EAGAIN; + + // Raise any other errors + default: + CTX_DEBUG(archive->ctx, "Filter callback returned an error: %d\n", r); + return r; + } + } + + // Run callback + if (walk_callback) { + r = walk_callback(archive->ctx, archive, a, entry, p); + + // Handle the return code + switch (r) { + case PAKFIRE_WALK_OK: + break; + + case PAKFIRE_WALK_DONE: + CTX_DEBUG(archive->ctx, "Callback sent DONE\n"); + return 0; + + // Raise any other errors + default: + return r; + } + } + } + + return 0; +} + +static int pakfire_archive_open_and_walk(struct pakfire_archive* archive, + pakfire_archive_walk_callback walk_callback, pakfire_archive_walk_filter_callback filter_callback, void* data) { + struct archive* a = NULL; + int r; + + // Open the archive + a = open_archive(archive, NULL); if (!a) return -errno; - // Walk through the archive - r = pakfire_walk(archive->ctx, a, callback, filter_callback, data); + // Walk... + r = pakfire_archive_walk(archive, a, walk_callback, filter_callback, data); - // Close the archive +ERROR: if (a) archive_read_free(a); @@ -184,6 +275,8 @@ static void pakfire_archive_free(struct pakfire_archive* archive) { for (unsigned int i = 0; i < archive->num_scriptlets; i++) pakfire_scriptlet_unref(archive->scriptlets[i]); + if (archive->progress) + pakfire_progress_unref(archive->progress); if (archive->filelist) pakfire_filelist_unref(archive->filelist); if (archive->package) @@ -208,7 +301,6 @@ PAKFIRE_EXPORT struct pakfire_archive* pakfire_archive_unref(struct pakfire_arch return archive; pakfire_archive_free(archive); - return NULL; } @@ -354,10 +446,8 @@ ERROR: return r; } -static int __pakfire_archive_read_metadata(struct pakfire_ctx* ctx, struct archive* a, - struct archive_entry* entry, void* p) { - struct pakfire_archive* archive = (struct pakfire_archive*)p; - +static int __pakfire_archive_read_metadata(struct pakfire_ctx* ctx, + struct pakfire_archive* archive, struct archive* a, struct archive_entry* entry, void* p) { char* data = NULL; size_t length = 0; int r; @@ -369,8 +459,7 @@ static int __pakfire_archive_read_metadata(struct pakfire_ctx* ctx, struct archi // Load the file into memory r = pakfire_archive_copy_data_to_buffer(archive, a, entry, &data, &length); if (r) { - CTX_ERROR(archive->ctx, "Could not read data from archive: %s\n", - archive_error_string(a)); + CTX_ERROR(ctx, "Could not read data from archive: %s\n", archive_error_string(a)); goto ERROR; } @@ -404,9 +493,7 @@ ERROR: } static int __pakfire_archive_filter_metadata(struct pakfire_ctx* ctx, - struct archive* a, struct archive_entry* entry, void* p) { - struct pakfire_archive* archive = (struct pakfire_archive*)p; - + struct pakfire_archive* archive, struct archive* a, struct archive_entry* entry, void* p) { const char* path = archive_entry_pathname(entry); // Format >= 6 @@ -428,35 +515,50 @@ static int __pakfire_archive_filter_metadata(struct pakfire_ctx* ctx, } static int pakfire_archive_read_metadata(struct pakfire_archive* archive) { + struct archive* a = NULL; int r; + CTX_DEBUG(archive->ctx, "Reading archive metadata...\n"); + + // Open the archive + a = open_archive(archive, NULL); + if (!a) { + r = -errno; + goto ERROR; + } + // Check if the archive file actually has any contect if (!archive->stat.st_size) { CTX_ERROR(archive->ctx, "Trying to open an empty archive file\n"); - return -EINVAL; + r = -EINVAL; + goto ERROR; } - CTX_DEBUG(archive->ctx, "Reading archive metadata...\n"); - // Walk through the archive - r = pakfire_archive_walk(archive, __pakfire_archive_read_metadata, - __pakfire_archive_filter_metadata, archive); + r = pakfire_archive_walk(archive, a, + __pakfire_archive_read_metadata, __pakfire_archive_filter_metadata, NULL); if (r) - return r; + goto ERROR; // Check if we could successfully read something if (!archive->format) { CTX_DEBUG(archive->ctx, "Archive has an unknown format\n"); - return -ENOMSG; + r = -ENOMSG; + goto ERROR; } // Check if we have read some metadata if (!archive->metadata) { CTX_DEBUG(archive->ctx, "Archive has no metadata\n"); - return -ENOMSG; + r = -ENOMSG; + goto ERROR; } - return 0; +ERROR: + if (a) + archive_read_close(a); + + return r; } static int pakfire_archive_try_open(struct pakfire_archive* archive) { @@ -507,7 +609,7 @@ PAKFIRE_EXPORT int pakfire_archive_open(struct pakfire_archive** archive, // Store path r = pakfire_string_set(a->path, path); if (r < 0) - return r; + goto ERROR; // Try to open the archive r = pakfire_archive_try_open(a); @@ -571,7 +673,7 @@ static int64_t pakfire_archive_metadata_get_int64( } static int pakfire_archive_filter_payload(struct pakfire_ctx* ctx, - struct archive* a, struct archive_entry* entry, void* p) { + struct pakfire_archive* archive, struct archive* a, struct archive_entry* entry, void* p) { const char* path = archive_entry_pathname(entry); if (!path) return PAKFIRE_WALK_ERROR; @@ -648,8 +750,8 @@ static cookie_io_functions_t pakfire_archive_read_functions = { }; // Tries to find a matching file in the archive -static int pakfire_archive_read_filter(struct pakfire_ctx* ctx, struct archive* a, - struct archive_entry* e, void* data) { +static int pakfire_archive_read_filter(struct pakfire_ctx* ctx, + struct pakfire_archive* archive, struct archive* a, struct archive_entry* e, void* data) { struct pakfire_archive_read_cookie* cookie = data; const char* symlink = NULL; int r; @@ -697,8 +799,8 @@ static int pakfire_archive_read_filter(struct pakfire_ctx* ctx, struct archive* } // Reads a matching file into memory -static int __pakfire_archive_read(struct pakfire_ctx* ctx, struct archive* a, - struct archive_entry* e, void* data) { +static int __pakfire_archive_read(struct pakfire_ctx* ctx, + struct pakfire_archive* archive, struct archive* a, struct archive_entry* e, void* data) { struct pakfire_archive_read_cookie* cookie = data; // Create a file descriptor @@ -756,7 +858,7 @@ AGAIN: goto ERROR; // Walk through the archive - r = pakfire_walk(archive->ctx, cookie->a, __pakfire_archive_read, + r = pakfire_archive_walk(archive, cookie->a, __pakfire_archive_read, pakfire_archive_read_filter, cookie); if (r) { switch (-r) { @@ -879,70 +981,267 @@ int pakfire_archive_link_or_copy(struct pakfire_archive* archive, const char* pa return r; } -static int __pakfire_archive_extract(struct pakfire_archive* archive, - const char* path, int flags) { - struct pakfire_filelist* filelist = NULL; +/* + Extraction +*/ + +struct pakfire_extract_state { + struct pakfire_archive* archive; + + // Archive + struct archive* a; + + // Prefix + const char* prefix; + + // Flags + int flags; +}; + +static void pakfire_extract_progress(void* data) { + struct pakfire_extract_state* state = data; + + // Fetch how many bytes have been read + const size_t position = archive_filter_bytes(state->a, -1); + + // Update progress + pakfire_progress_update(state->archive->progress, position); +} + +static int pakfire_archive_extract_one(struct pakfire_ctx* ctx, + struct pakfire_archive* archive, struct archive* a, struct archive_entry* entry, void* data) { + struct pakfire_file* file = NULL; + struct vfs_cap_data cap_data = {}; + char buffer[PATH_MAX]; + int r; + + struct pakfire_extract_state* state = data; + + // Fetch path + const char* path = archive_entry_pathname(entry); + + // Make sure we have a leading slash on the filelist + if (!pakfire_string_startswith(path, "/")) { + r = pakfire_string_format(buffer, "/%s", path); + if (r < 0) + goto ERROR; + + // Store the new name + archive_entry_set_pathname(entry, buffer); + + // Update the path pointer + path = archive_entry_pathname(entry); + } + + // Generate a file object + r = pakfire_file_create_from_archive_entry(&file, archive->pakfire, entry); + if (r) + goto ERROR; + + // Add entry to filelist (if requested) + if (archive->filelist) { + // Append the file to the list + r = pakfire_filelist_add(archive->filelist, file); + if (r) + goto ERROR; + } + + const int configfile = pakfire_file_has_flag(file, PAKFIRE_FILE_CONFIG); + + // Prepend the prefix + if (*state->prefix) { + // Compose file path + r = pakfire_path_append(buffer, state->prefix, path); + if (r < 0) { + CTX_ERROR(ctx, "Could not compose file path: %m\n"); + goto ERROR; + } + + // Set file path + archive_entry_set_pathname(entry, buffer); + + // Update hardlink destination + const char* link = archive_entry_hardlink(entry); + if (link) { + r = pakfire_path_append(buffer, state->prefix, link); + if (r < 0) { + CTX_ERROR(ctx, "Could not compose hardlink path: %m\n"); + goto ERROR; + } + + // Set hardlink path + archive_entry_set_hardlink(entry, buffer); + } + } + + if (configfile) { + // Fetch path again since we changed it + path = archive_entry_pathname(entry); + + if (pakfire_path_exists(path)) { + CTX_DEBUG(ctx, "The configuration file %s exists\n", + pakfire_file_get_path(file)); + + r = pakfire_string_format(buffer, "%s.paknew", path); + if (r < 0) { + CTX_ERROR(ctx, "Could not compose path for configuration file: %m\n"); + goto ERROR; + } + + // Set the path again + archive_entry_set_pathname(entry, buffer); + } + } + + // We are done if we are running in dry-run mode + if (state->flags & PAKFIRE_EXTRACT_DRY_RUN) + goto ERROR; + + struct archive* writer = pakfire_get_disk_writer(archive->pakfire); + + // Fetch path again since we changed it + path = archive_entry_pathname(entry); + + CTX_DEBUG(ctx, "Extracting %s\n", path); + + // Remove any extended attributes which we never write to disk + archive_entry_xattr_clear(entry); + + // Set capabilities + if (pakfire_file_has_caps(file)) { + r = pakfire_file_write_fcaps(file, &cap_data); + if (r) + goto ERROR; + + // Store capabilities in archive entry + archive_entry_xattr_add_entry(entry, + "security.capability", &cap_data, sizeof(cap_data)); + } + + // Write payload + r = archive_read_extract2(state->a, entry, writer); + switch (r) { + case ARCHIVE_OK: + r = 0; + break; + + case ARCHIVE_WARN: + CTX_ERROR(ctx, "%s\n", archive_error_string(writer)); + + // Pretend everything has been okay + r = 0; + break; + + case ARCHIVE_FATAL: + CTX_ERROR(ctx, "%s\n", archive_error_string(writer)); + r = 1; + break; + } + +ERROR: + if (file) + pakfire_file_unref(file); + + return r; +} + +static int __pakfire_archive_extract(struct pakfire_archive* archive, const char* path, int flags) { struct pakfire_package* pkg = NULL; + char prefix[PATH_MAX] = "/"; struct archive* a = NULL; - char prefix[PATH_MAX] = ""; - int r = 1; + int r; + + CTX_DEBUG(archive->ctx, "Extracting %s\n", archive->path); + + int progress_flags = PAKFIRE_PROGRESS_SHOW_PERCENTAGE; + + // Should we show any progress? + if (flags & PAKFIRE_EXTRACT_NO_PROGRESS) + progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS; + + // Show throughput? + if (flags & PAKFIRE_EXTRACT_SHOW_THROUGHPUT) + progress_flags |= PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED; // Fetch package pkg = pakfire_archive_get_package(archive); - if (!pkg) + if (!pkg) { + r = -errno; + goto ERROR; + } + + // Open the archive + a = open_archive(archive, NULL); + if (!a) { + r = -errno; goto ERROR; + } // Fetch NEVRA const char* nevra = pakfire_package_get_string(pkg, PAKFIRE_PKG_NEVRA); - CTX_DEBUG(archive->ctx, "Extracting %s\n", archive->path); + // Create the progress indicator + r = pakfire_progress_create(&archive->progress, archive->ctx, progress_flags, NULL); + if (r < 0) + goto ERROR; + + // Set the title + r = pakfire_progress_set_title(archive->progress, "%s", nevra); + if (r < 0) + goto ERROR; // Copy everything to path if set if (path) { r = pakfire_string_set(prefix, path); - if (r) + if (r < 0) goto ERROR; // Set prefix for source packages } else if (pakfire_package_is_source(pkg)) { r = pakfire_path(archive->pakfire, prefix, "/usr/src/packages/%s", nevra); - if (r) + if (r < 0) goto ERROR; // Otherwise extract relative to the pakfire root } else { r = pakfire_path(archive->pakfire, prefix, "%s", "/"); - if (r) + if (r < 0) goto ERROR; } // Load the filelist (if not done already) if (!archive->filelist) { - r = pakfire_filelist_create(&filelist, archive->pakfire); - if (r) + r = pakfire_filelist_create(&archive->filelist, archive->pakfire); + if (r < 0) goto ERROR; } - // Open the archive - a = open_archive(archive, NULL); - if (!a) { - r = 1; + // Create state + struct pakfire_extract_state state = { + .archive = archive, + .a = a, + .prefix = prefix, + .flags = flags, + }; + + // Register progress callback + archive_read_extract_set_progress_callback(a, pakfire_extract_progress, &state); + + // Start progress + r = pakfire_progress_start(archive->progress, archive->stat.st_size); + if (r < 0) goto ERROR; - } - // Extract - r = pakfire_extract(archive->pakfire, a, archive->stat.st_size, - filelist, prefix, nevra, pakfire_archive_filter_payload, flags); + // Walk through the entire archive and extract everything + r = pakfire_archive_walk(archive, a, + pakfire_archive_extract_one, pakfire_archive_filter_payload, &state); if (r) goto ERROR; - // Store the filelist permanently - if (!archive->filelist) - archive->filelist = pakfire_filelist_ref(filelist); - ERROR: - if (filelist) - pakfire_filelist_unref(filelist); + // Finish the progress + pakfire_progress_finish(archive->progress); + if (pkg) pakfire_package_unref(pkg); if (a) @@ -1370,7 +1669,7 @@ struct pakfire_scriptlet* pakfire_archive_get_scriptlet( systemd sysusers */ static int pakfire_archive_filter_systemd_sysusers(struct pakfire_ctx* ctx, - struct archive* a, struct archive_entry* e, void* data) { + struct pakfire_archive* archive, struct archive* a, struct archive_entry* e, void* data) { const char* path = archive_entry_pathname(e); if (!pakfire_path_match("usr/lib/sysusers.d/*.conf", path)) @@ -1393,13 +1692,11 @@ static ssize_t pakfire_archive_stream_payload( } static int pakfire_archive_handle_systemd_sysusers(struct pakfire_ctx* ctx, - struct archive* a, struct archive_entry* e, void* data) { + struct pakfire_archive* archive, struct archive* a, struct archive_entry* e, void* data) { struct pakfire_jail* jail = NULL; char replace[PATH_MAX]; int r; - struct pakfire* pakfire = data; - // Fetch path const char* path = archive_entry_pathname(e); @@ -1411,7 +1708,7 @@ static int pakfire_archive_handle_systemd_sysusers(struct pakfire_ctx* ctx, const char* argv[] = { "/usr/bin/systemd-sysusers", replace, "-", NULL }; // Create a new jail - r = pakfire_jail_create(&jail, pakfire); + r = pakfire_jail_create(&jail, archive->pakfire); if (r) goto ERROR; @@ -1427,8 +1724,18 @@ ERROR: } int pakfire_archive_apply_systemd_sysusers(struct pakfire_archive* archive) { - pakfire_archive_walk(archive, pakfire_archive_handle_systemd_sysusers, - pakfire_archive_filter_systemd_sysusers, archive->pakfire); + struct archive* a = NULL; + + // Open the archive + a = open_archive(archive, NULL); + if (!a) + return -errno; + + pakfire_archive_walk(archive, a, pakfire_archive_handle_systemd_sysusers, + pakfire_archive_filter_systemd_sysusers, NULL); + + if (a) + archive_read_free(a); return 0; } diff --git a/src/libpakfire/compress.c b/src/libpakfire/compress.c index 22cb9fd7c..df24650e2 100644 --- a/src/libpakfire/compress.c +++ b/src/libpakfire/compress.c @@ -521,345 +521,6 @@ ERROR: return NULL; } -/* - Helper function to conditionally walk through an archive - and perform actions based on the callback. -*/ -int pakfire_walk(struct pakfire_ctx* ctx, struct archive* archive, - pakfire_walk_callback callback, pakfire_walk_filter_callback filter_callback, - void* p) { - struct archive_entry* entry = NULL; - int r; - - // Walk through the archive - for (;;) { - r = archive_read_next_header(archive, &entry); - - // Handle the return code - switch (r) { - // Fall through if everything is okay - case ARCHIVE_OK: - break; - - // Return OK when we reached the end of the archive - case ARCHIVE_EOF: - return 0; - - // Raise any other errors - default: - return r; - } - - // Call the filter callback before we call the actual callback - if (filter_callback) { - r = filter_callback(ctx, archive, entry, p); - - // Handle the return code - switch (r) { - case PAKFIRE_WALK_OK: - break; - - case PAKFIRE_WALK_END: - CTX_DEBUG(ctx, "Filter callback sent END\n"); - return 0; - - case PAKFIRE_WALK_SKIP: - CTX_DEBUG(ctx, "Filter callback sent SKIP\n"); - continue; - - case PAKFIRE_WALK_DONE: - CTX_DEBUG(ctx, "Filter callback sent DONE\n"); - - // Clear the callback function - filter_callback = NULL; - break; - - case PAKFIRE_WALK_AGAIN: - CTX_DEBUG(ctx, "Filter callback sent AGAIN\n"); - return -EAGAIN; - - // Raise any other errors - default: - CTX_DEBUG(ctx, "Filter callback returned an error: %d\n", r); - return r; - } - } - - // Run callback - if (callback) { - r = callback(ctx, archive, entry, p); - - // Handle the return code - switch (r) { - case PAKFIRE_WALK_OK: - break; - - case PAKFIRE_WALK_DONE: - CTX_DEBUG(ctx, "Callback sent DONE\n"); - return 0; - - // Raise any other errors - default: - return r; - } - } - } - - return 0; -} - -// Common extraction - -struct pakfire_extract { - // Reference to Pakfire - struct pakfire* pakfire; - - // Flags - int flags; - - // The archive to extract - struct archive* archive; - - // The filelist of all extracted files - struct pakfire_filelist* filelist; - - // Prepend this prefix - const char* prefix; - - // The writer - struct archive* writer; - - // The progress indicator - struct pakfire_progress* progress; -}; - -static void pakfire_extract_progress(void* p) { - struct pakfire_extract* data = (struct pakfire_extract*)p; - - // Fetch how many bytes have been read - const size_t position = archive_filter_bytes(data->archive, -1); - - // Update progress - pakfire_progress_update(data->progress, position); -} - -static int __pakfire_extract(struct pakfire_ctx* ctx, struct archive* a, - struct archive_entry* entry, void* p) { - struct pakfire_file* file = NULL; - struct vfs_cap_data cap_data = {}; - char buffer[PATH_MAX]; - int r; - - struct pakfire_extract* data = (struct pakfire_extract*)p; - - // Fetch path - const char* path = archive_entry_pathname(entry); - - // Make sure we have a leading slash on the filelist - if (!pakfire_string_startswith(path, "/")) { - r = pakfire_string_format(buffer, "/%s", path); - if (r) - goto ERROR; - - // Store the new name - archive_entry_set_pathname(entry, buffer); - - // Update the path pointer - path = archive_entry_pathname(entry); - } - - // Generate a file object - r = pakfire_file_create_from_archive_entry(&file, data->pakfire, entry); - if (r) - goto ERROR; - - // Add entry to filelist (if requested) - if (data->filelist) { - // Append the file to the list - r = pakfire_filelist_add(data->filelist, file); - if (r) - goto ERROR; - } - - const int configfile = pakfire_file_has_flag(file, PAKFIRE_FILE_CONFIG); - - // Prepend the prefix - if (*data->prefix) { - // Compose file path - r = pakfire_path_append(buffer, data->prefix, path); - if (r) { - CTX_ERROR(ctx, "Could not compose file path: %m\n"); - goto ERROR; - } - - // Set file path - archive_entry_set_pathname(entry, buffer); - - // Update hardlink destination - const char* link = archive_entry_hardlink(entry); - if (link) { - r = pakfire_path_append(buffer, data->prefix, link); - if (r) { - CTX_ERROR(ctx, "Could not compose hardlink path: %m\n"); - goto ERROR; - } - - // Set hardlink path - archive_entry_set_hardlink(entry, buffer); - } - } - - if (configfile) { - // Fetch path again since we changed it - path = archive_entry_pathname(entry); - - if (pakfire_path_exists(path)) { - CTX_DEBUG(ctx, "The configuration file %s exists\n", - pakfire_file_get_path(file)); - - r = pakfire_string_format(buffer, "%s.paknew", path); - if (r) { - CTX_ERROR(ctx, "Could not compose path for configuration file: %m\n"); - goto ERROR; - } - - // Set the path again - archive_entry_set_pathname(entry, buffer); - } - } - - // Create file & extract payload - if (data->writer) { - // Fetch path again since we changed it - path = archive_entry_pathname(entry); - - CTX_DEBUG(ctx, "Extracting %s\n", path); - - // Remove any extended attributes which we never write to disk - archive_entry_xattr_clear(entry); - - // Set capabilities - if (pakfire_file_has_caps(file)) { - r = pakfire_file_write_fcaps(file, &cap_data); - if (r) - goto ERROR; - - // Store capabilities in archive entry - archive_entry_xattr_add_entry(entry, "security.capability", - &cap_data, sizeof(cap_data)); - } - - // Write payload - r = archive_read_extract2(data->archive, entry, data->writer); - switch (r) { - case ARCHIVE_OK: - r = 0; - break; - - case ARCHIVE_WARN: - CTX_ERROR(ctx, "%s\n", archive_error_string(data->writer)); - - // Pretend everything has been okay - r = 0; - break; - - case ARCHIVE_FATAL: - CTX_ERROR(ctx, "%s\n", archive_error_string(data->writer)); - r = 1; - break; - } - } - -ERROR: - if (file) - pakfire_file_unref(file); - - return r; -} - -int pakfire_extract(struct pakfire* pakfire, struct archive* archive, - size_t size, struct pakfire_filelist* filelist, - const char* prefix, const char* message, - pakfire_walk_filter_callback filter_callback, int flags) { - int progress_flags = PAKFIRE_PROGRESS_SHOW_PERCENTAGE; - int r = 1; - - struct pakfire_ctx* ctx = pakfire_ctx(pakfire); - - // Use / if no prefix is set - if (!prefix) - prefix = "/"; - - struct pakfire_extract data = { - .pakfire = pakfire, - .archive = archive, - .filelist = filelist, - .prefix = prefix, - .flags = flags, - .writer = NULL, - }; - - // Is this a dry run? - const int dry_run = flags & PAKFIRE_EXTRACT_DRY_RUN; - - // Allocate writer - if (!dry_run) { - data.writer = pakfire_make_archive_disk_writer(pakfire, 1); - if (!data.writer) { - CTX_ERROR(ctx, "Could not create disk writer: %m\n"); - r = 1; - goto ERROR; - } - } - - // Should we show any progress? - if (flags & PAKFIRE_EXTRACT_NO_PROGRESS) - progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS; - - // Show throughput? - if (flags & PAKFIRE_EXTRACT_SHOW_THROUGHPUT) - progress_flags |= PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED; - - // Create the progress indicator - r = pakfire_progress_create(&data.progress, ctx, progress_flags, NULL); - if (r) - goto ERROR; - - // Set the title - r = pakfire_progress_set_title(data.progress, "%s", message); - if (r) - goto ERROR; - - // Register progress callback - archive_read_extract_set_progress_callback(data.archive, - pakfire_extract_progress, &data); - - // Start progress - r = pakfire_progress_start(data.progress, size); - if (r) - goto ERROR; - - // Walk through the entire archive - r = pakfire_walk(ctx, archive, __pakfire_extract, filter_callback, &data); - if (r) - goto ERROR; - - // Finish the progress - r = pakfire_progress_finish(data.progress); - if (r) - goto ERROR; - -ERROR: - if (data.progress) - pakfire_progress_unref(data.progress); - if (data.writer) - archive_write_free(data.writer); - if (ctx) - pakfire_ctx_unref(ctx); - - return r; -} - // Common compression struct pakfire_compress { diff --git a/src/libpakfire/include/pakfire/archive.h b/src/libpakfire/include/pakfire/archive.h index 2ad18890b..4332a63dd 100644 --- a/src/libpakfire/include/pakfire/archive.h +++ b/src/libpakfire/include/pakfire/archive.h @@ -61,8 +61,34 @@ int pakfire_archive_lint(struct pakfire_archive* archive, #ifdef PAKFIRE_PRIVATE +#include + #include +// Walk + +typedef int (*pakfire_archive_walk_callback)(struct pakfire_ctx* ctx, + struct pakfire_archive* archive, struct archive* a, struct archive_entry* e, void* p); +typedef int (*pakfire_archive_walk_filter_callback)(struct pakfire_ctx* ctx, + struct pakfire_archive* archive, struct archive* a, struct archive_entry* e, void* p); + +enum pakfire_archive_walk_codes { + PAKFIRE_WALK_OK = 0, + PAKFIRE_WALK_ERROR = 1, + + // After this code has been sent, we will not process any further entries + PAKFIRE_WALK_END = -10, + + // Request the next entry (only in filter callback) + PAKFIRE_WALK_SKIP = -20, + + // Like PAKFIRE_WALK_OK, but the callback will not be called again + PAKFIRE_WALK_DONE = -30, + + // Start again from the beginning + PAKFIRE_WALK_AGAIN = -40, +}; + int pakfire_archive_copy(struct pakfire_archive* archive, const char* path); int pakfire_archive_link_or_copy(struct pakfire_archive* archive, const char* path); diff --git a/src/libpakfire/include/pakfire/compress.h b/src/libpakfire/include/pakfire/compress.h index cf6a21c49..f4f8005b7 100644 --- a/src/libpakfire/include/pakfire/compress.h +++ b/src/libpakfire/include/pakfire/compress.h @@ -25,6 +25,7 @@ #include +#include #include #include @@ -37,33 +38,6 @@ FILE* pakfire_xzfopen(FILE* f, const char* mode); // ZSTD FILE* pakfire_zstdfopen(FILE* f, const char* mode); -// Walk - -typedef int (*pakfire_walk_callback) - (struct pakfire_ctx* ctx, struct archive* a, struct archive_entry* e, void* p); -typedef int (*pakfire_walk_filter_callback) - (struct pakfire_ctx* ctx, struct archive* a, struct archive_entry* e, void* p); - -enum pakfire_walk_codes { - PAKFIRE_WALK_OK = 0, - PAKFIRE_WALK_ERROR = 1, - - // After this code has been sent, we will not process any further entries - PAKFIRE_WALK_END = -10, - - // Request the next entry (only in filter callback) - PAKFIRE_WALK_SKIP = -20, - - // Like PAKFIRE_WALK_OK, but the callback will not be called again - PAKFIRE_WALK_DONE = -30, - - // Start again from the beginning - PAKFIRE_WALK_AGAIN = -40, -}; - -int pakfire_walk(struct pakfire_ctx* ctx, struct archive* archive, - pakfire_walk_callback callback, pakfire_walk_filter_callback filter_callback, void* p); - // Extract enum pakfire_extract_flags { PAKFIRE_EXTRACT_DRY_RUN = (1 << 0), @@ -73,7 +47,7 @@ enum pakfire_extract_flags { int pakfire_extract(struct pakfire* pakfire, struct archive* archive, size_t size, struct pakfire_filelist* filelist, const char* prefix, - const char* message, pakfire_walk_filter_callback filter_callback, + const char* message, pakfire_archive_walk_filter_callback filter_callback, int flags); // Algorithms diff --git a/src/libpakfire/include/pakfire/pakfire.h b/src/libpakfire/include/pakfire/pakfire.h index 87acf16b1..ecceb1764 100644 --- a/src/libpakfire/include/pakfire/pakfire.h +++ b/src/libpakfire/include/pakfire/pakfire.h @@ -161,7 +161,7 @@ int pakfire_repo_walk(struct pakfire* pakfire, // Archive helpers struct archive* pakfire_make_archive_disk_reader(struct pakfire* pakfire, int internal); -struct archive* pakfire_make_archive_disk_writer(struct pakfire* pakfire, int internal); +struct archive* pakfire_get_disk_writer(struct pakfire* pakfire); #endif diff --git a/src/libpakfire/pakfire.c b/src/libpakfire/pakfire.c index c1f9250a9..d41757d8d 100644 --- a/src/libpakfire/pakfire.c +++ b/src/libpakfire/pakfire.c @@ -117,6 +117,9 @@ struct pakfire { // States unsigned int in_free:1; + + // Disk Reader/Writer + struct archive* writer; }; /* @@ -424,10 +427,10 @@ static void pakfire_free(struct pakfire* pakfire) { if (pakfire->pool) pool_free(pakfire->pool); - + if (pakfire->writer) + archive_write_free(pakfire->writer); if (pakfire->config) pakfire_config_unref(pakfire->config); - if (pakfire->ctx) pakfire_ctx_unref(pakfire->ctx); @@ -1673,11 +1676,7 @@ ERROR: return pakfire_map_id(pakfire, &pakfire->group.subgids, 0); } -struct archive* pakfire_make_archive_disk_writer(struct pakfire* pakfire, int internal) { - struct archive* archive = archive_write_disk_new(); - if (!archive) - return NULL; - +struct archive* pakfire_get_disk_writer(struct pakfire* pakfire) { // Set flags for extracting files const int flags = ARCHIVE_EXTRACT_ACL | @@ -1687,17 +1686,24 @@ struct archive* pakfire_make_archive_disk_writer(struct pakfire* pakfire, int in ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_XATTR; - archive_write_disk_set_options(archive, flags); + if (!pakfire->writer) { + // Create a new writer + pakfire->writer = archive_write_disk_new(); + if (!pakfire->writer) { + CTX_ERROR(pakfire->ctx, "Could not set up writer: %m\n"); + return NULL; + } - // Install our own routine for user/group lookups - if (internal) { - archive_write_disk_set_user_lookup(archive, pakfire, pakfire_uid_lookup, NULL); - archive_write_disk_set_group_lookup(archive, pakfire, pakfire_gid_lookup, NULL); - } else { - archive_write_disk_set_standard_lookup(archive); + archive_write_disk_set_options(pakfire->writer, flags); + + // Install our own routine for user/group lookups + archive_write_disk_set_user_lookup(pakfire->writer, + pakfire, pakfire_uid_lookup, NULL); + archive_write_disk_set_group_lookup(pakfire->writer, + pakfire, pakfire_gid_lookup, NULL); } - return archive; + return pakfire->writer; } // Convenience functions to install/erase/update packages -- 2.39.5