]> git.ipfire.org Git - pakfire.git/commitdiff
archive writer: Implement writing files
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 8 Feb 2025 11:36:26 +0000 (11:36 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 8 Feb 2025 11:36:26 +0000 (11:36 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/pakfire/archive_writer.c
src/pakfire/archive_writer.h

index 150254c345483c543690e11a46b6e9b47101e1b2..3206e1763f472d4d46fcf33e5ae4759265a074be 100644 (file)
@@ -27,6 +27,9 @@
 #include <archive_entry.h>
 
 #include <pakfire/archive_writer.h>
+#include <pakfire/file.h>
+#include <pakfire/filelist.h>
+#include <pakfire/hashes.h>
 #include <pakfire/progress.h>
 #include <pakfire/string.h>
 
@@ -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;
+}
index eb876c3119d0a0691cf1764e0dc17e3d6a426631..6a6aaad1e200e32f568845ff31d754242ff27b5f 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <stdio.h>
 
+#include <pakfire/filelist.h>
 #include <pakfire/pakfire.h>
 
 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 */