From 5193ad037ff3dc7e6e3bd720b2c856e7bd9b5166 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Sat, 8 Feb 2025 11:36:26 +0000 Subject: [PATCH] archive writer: Implement writing files Signed-off-by: Michael Tremer --- src/pakfire/archive_writer.c | 195 +++++++++++++++++++++++++++++++++++ src/pakfire/archive_writer.h | 4 + 2 files changed, 199 insertions(+) diff --git a/src/pakfire/archive_writer.c b/src/pakfire/archive_writer.c index 150254c3..3206e176 100644 --- a/src/pakfire/archive_writer.c +++ b/src/pakfire/archive_writer.c @@ -27,6 +27,9 @@ #include #include +#include +#include +#include #include #include @@ -52,6 +55,9 @@ struct pakfire_archive_writer { } compression; unsigned int compression_level; + // Checksums + int checksums; + // File Handle FILE* f; @@ -81,6 +87,9 @@ static int pakfire_archive_writer_setup_format( // Use some good compression self->compression_level = 20; + + // Hash the files + self->checksums = PAKFIRE_HASH_SHA3_512|PAKFIRE_HASH_BLAKE2B512; break; // Fail on invalid inputs @@ -313,3 +322,189 @@ int pakfire_archive_writer_set_title(struct pakfire_archive_writer* self, return pakfire_progress_set_title(self->progress, "%s", buffer); } + +static int pakfire_archive_writer_write_payload(struct pakfire_archive_writer* self, + struct pakfire_file* file, struct archive_entry* entry) { + ssize_t bytes_written = 0; + ssize_t bytes_read = 0; + char buffer[64 * 1024]; + FILE* f = NULL; + int r = 0; + + // Fetch the path + const char* path = pakfire_file_get_path(file); + + // Open the file + f = pakfire_file_fopen(file, "r"); + if (!f) { + r = -errno; + goto ERROR; + } + + // Loop through the entire length of the file + while (!feof(f)) { + // Read a block from file + bytes_read = fread(buffer, 1, sizeof(buffer), f); + + // Check if any error occured + if (ferror(f)) { + ERROR(self->ctx, "Failed to read %s: %m\n", path); + r = -errno; + goto ERROR; + } + + // Write the block to the archive + bytes_written = archive_write_data(self->archive, buffer, bytes_read); + if (bytes_written < bytes_read) { + ERROR(self->ctx, "Failed to write %s: %s\n", + path, archive_error_string(self->archive)); + r = -errno; + goto ERROR; + } + + // Update progress + r = pakfire_progress_increment(self->progress, bytes_written); + if (r < 0) + goto ERROR; + } + +ERROR: + if (f) + fclose(f); + + return r; +} + +static int pakfire_archive_writer_write_entry(struct pakfire_archive_writer* self, + struct pakfire_file* file, struct archive_entry* entry) { + int r; + + // Fetch the path of the file in the archive + const char* path = archive_entry_pathname(entry); + + // Remove any leading slahes + while (*path == '/') + path++; + + // Restore the updated path name + archive_entry_set_pathname(entry, path); + + // Write the header + r = archive_write_header(self->archive, entry); + if (r) { + ERROR(self->ctx, "Error writing file header for %s: %s\n", + pakfire_file_get_path(file), archive_error_string(self->archive)); + r = -EINVAL; + goto ERROR; + } + + // Copy the payload, if libarchive wants us to. + // It will set the size of the entry to zero if it does not need the payload, + // for example when we are writing a hardlink. + if (archive_entry_size(entry)) { + r = pakfire_archive_writer_write_payload(self, file, entry); + if (r < 0) + goto ERROR; + } + + // Write trailer + r = archive_write_finish_entry(self->archive); + if (r) { + ERROR(self->ctx, "Failed to write the trailer: %s\n", + archive_error_string(self->archive)); + r = -EINVAL; + goto ERROR; + } + +ERROR: + + return r; +} + +static int pakfire_archive_writer_write_file( + struct pakfire_ctx* ctx, struct pakfire_file* file, void* data) { + struct pakfire_archive_writer* self = data; + struct archive_entry* sparse_entry = NULL; + struct archive_entry* entry = NULL; + int r = 0; + + // Generate file metadata into an archive entry + entry = pakfire_file_archive_entry(file, self->checksums); + if (!entry) { + r = -errno; + goto ERROR; + } + + // Perform search for hardlinks + archive_entry_linkify(self->linkresolver, &entry, &sparse_entry); + + // Write the main entry + if (entry) { + r = pakfire_archive_writer_write_entry(self, file, entry); + if (r < 0) + goto ERROR; + } + + // Write the sparse entry + if (sparse_entry) { + r = pakfire_archive_writer_write_entry(self, file, sparse_entry); + if (r < 0) + goto ERROR; + } + + // Query the link resolver for any more entries + for (;;) { + // Free the entry + if (entry) { + archive_entry_free(entry); + entry = NULL; + } + + // Free the sparse entry + if (sparse_entry) { + archive_entry_free(sparse_entry); + sparse_entry = NULL; + } + + // Fetch the next entry + archive_entry_linkify(self->linkresolver, &entry, &sparse_entry); + if (!entry) + break; + + // Write the entry to the archive + r = pakfire_archive_writer_write_entry(self, file, entry); + if (r < 0) + goto ERROR; + } + +ERROR: + if (sparse_entry) + archive_entry_free(sparse_entry); + if (entry) + archive_entry_free(entry); + + return r; +} + +int pakfire_archive_writer_write_files( + struct pakfire_archive_writer* self, struct pakfire_filelist* files) { + int r; + + // Fetch the total amount of data we are going to write + const size_t size = pakfire_filelist_total_size(files); + + // Start the progress + r = pakfire_progress_start(self->progress, size); + if (r < 0) + goto ERROR; + + // Walk through the entire filelist + r = pakfire_filelist_walk(files, pakfire_archive_writer_write_file, self, 0, NULL); + if (r < 0) + goto ERROR; + +ERROR: + pakfire_progress_finish(self->progress); + + return r; +} diff --git a/src/pakfire/archive_writer.h b/src/pakfire/archive_writer.h index eb876c31..6a6aaad1 100644 --- a/src/pakfire/archive_writer.h +++ b/src/pakfire/archive_writer.h @@ -23,6 +23,7 @@ #include +#include #include struct pakfire_archive_writer; @@ -40,4 +41,7 @@ struct pakfire_archive_writer* pakfire_archive_writer_unref(struct pakfire_archi int pakfire_archive_writer_set_title(struct pakfire_archive_writer* self, const char* format, ...); +int pakfire_archive_writer_write_files( + struct pakfire_archive_writer* self, struct pakfire_filelist* files); + #endif /* PAKFIRE_ARCHIVE_WRITER_H */ -- 2.39.5