]> git.ipfire.org Git - pakfire.git/commitdiff
archive: Move extraction code back to where it belongs
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 21 Oct 2024 14:37:54 +0000 (14:37 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 21 Oct 2024 14:37:54 +0000 (14:37 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/archive.c
src/libpakfire/compress.c
src/libpakfire/include/pakfire/archive.h
src/libpakfire/include/pakfire/compress.h
src/libpakfire/include/pakfire/pakfire.h
src/libpakfire/pakfire.c

index fc6bdd5694cdc341580a58726f264f60ea66eee3..4db24b368922a905941d1e2a94edb3a818ec3dc6 100644 (file)
@@ -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;
 }
index 22cb9fd7cc10bdf7490020838678acc6587894aa..df24650e264f7d4f484ea2883b0447dfd5ab6f05 100644 (file)
@@ -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 {
index 2ad18890b1e0c0bf168dd5ae77a604325d783056..4332a63ddaad155d7f1c10818814fe5f9c54415f 100644 (file)
@@ -61,8 +61,34 @@ int pakfire_archive_lint(struct pakfire_archive* archive,
 
 #ifdef PAKFIRE_PRIVATE
 
+#include <archive_entry.h>
+
 #include <pakfire/pakfire.h>
 
+// 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);
 
index cf6a21c49e6e16f15b8ff0a5698a1701f0d00760..f4f8005b7501e4e8f871c8ac11ccae415e51bdf2 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <archive.h>
 
+#include <pakfire/archive.h>
 #include <pakfire/ctx.h>
 #include <pakfire/pakfire.h>
 
@@ -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
index 87acf16b1fb2ef97b025d4d417fec96e972bbee2..ecceb176482a2f5a08badf924e87fc8cf03a5d72 100644 (file)
@@ -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
 
index c1f9250a9fa897c3188d9a8a297654f75d1a10cf..d41757d8d44f4026a00a375c8a5050fbb3fe98e5 100644 (file)
@@ -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