]> git.ipfire.org Git - pakfire.git/commitdiff
archive writer: Configure libarchive depending on the format
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 8 Feb 2025 10:55:57 +0000 (10:55 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 8 Feb 2025 10:55:57 +0000 (10:55 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/pakfire/archive_writer.c

index c734ffc1315acbe96944a96226acad4d4efffbb6..2e3fdf83f20ef3596af46af7cc049ba9f65f0eee 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 
+// libarchive
+#include <archive.h>
+#include <archive_entry.h>
+
 #include <pakfire/archive_writer.h>
+#include <pakfire/string.h>
 
 struct pakfire_archive_writer {
        struct pakfire_ctx* ctx;
@@ -34,11 +39,162 @@ struct pakfire_archive_writer {
        // Format
        pakfire_archive_writer_format format;
 
+       // Container
+       enum pakfire_archive_writer_container {
+               PAKFIRE_CONTAINER_PAX,
+       } container;
+
+       // Compression
+       enum pakfire_archive_writer_compression {
+               PAKFIRE_COMPRESSION_NONE,
+               PAKFIRE_COMPRESSION_ZSTD,
+       } compression;
+       unsigned int compression_level;
+
        // File Handle
        FILE* f;
+
+       // Archive
+       struct archive* archive;
 };
 
+/*
+ * This function configures this object depending on the format.
+ */
+static int pakfire_archive_writer_setup_format(
+               struct pakfire_archive_writer* self, pakfire_archive_writer_format format) {
+       switch (format) {
+               // The Pakfire Archive Format
+               case PAKFIRE_FORMAT_ARCHIVE:
+                       // We use the PAX tar format
+                       self->container = PAKFIRE_CONTAINER_PAX;
+
+                       // We use Zstandard compression
+                       self->compression = PAKFIRE_COMPRESSION_ZSTD;
+
+                       // Use some good compression
+                       self->compression_level = 20;
+                       break;
+
+               // Fail on invalid inputs
+               default:
+                       ERROR(self->ctx, "Invalid archive format\n");
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int pakfire_archive_writer_setup_archive(struct pakfire_archive_writer* self) {
+       char value[64];
+       int r;
+
+       // Open a new archive
+       self->archive = archive_write_new();
+       if (!self->archive) {
+               ERROR(self->ctx, "archive_write_new() failed\n");
+               return -ENOTSUP;
+       }
+
+       // Set the container format
+       switch (self->container) {
+               case PAKFIRE_CONTAINER_PAX:
+                       // Use the PAX format
+                       r = archive_write_set_format_pax(self->archive);
+                       if (r) {
+                               ERROR(self->ctx, "Could not set format to PAX: %s\n",
+                                       archive_error_string(self->archive));
+                               return -ENOTSUP;
+                       }
+
+                       // Store any extended attributes in the SCHILY headers
+                       r = archive_write_set_format_option(self->archive, "pax", "xattrheader", "SCHILY");
+                       if (r) {
+                               ERROR(self->ctx, "Could not set xattrheader option: %s\n",
+                                       archive_error_string(self->archive));
+                               return -ENOTSUP;
+                       }
+                       break;
+       }
+
+       // Set the compression
+       switch (self->compression) {
+               case PAKFIRE_COMPRESSION_NONE:
+                       break;
+
+               // Zstandard
+               case PAKFIRE_COMPRESSION_ZSTD:
+                       r = archive_write_add_filter_zstd(self->archive);
+                       if (r) {
+                               ERROR(self->ctx, "Could not enable Zstandard compression: %s\n",
+                                       archive_error_string(self->archive));
+                               return -ENOTSUP;
+                       }
+
+                       // Do not pad the last block
+                       archive_write_set_bytes_in_last_block(self->archive, 1);
+
+#if ARCHIVE_VERSION_NUMBER >= 3007002
+                       // Enable long mode (supported from libarchive >= 3.7.2)
+                       r = archive_write_set_filter_option(self->archive, NULL, "long", "31");
+                       if (r) {
+                               ERROR(self->ctx, "Failed to enable Zstandard long mode: %s\n",
+                                       archive_error_string(self->archive));
+                               return -ENOTSUP;
+                       }
+#endif /* ARCHIVE_VERSION_NUMBER >= 3007002 */
+
+#if ARCHIVE_VERSION_NUMBER >= 3006000
+                       // Fetch numbers of processors
+                       long processors = sysconf(_SC_NPROCESSORS_ONLN);
+
+                       if (processors > 1) {
+                               r = pakfire_string_format(value, "%ld", processors);
+                               if (r)
+                                       return r;
+
+                               // Try using multiple threads
+                               r = archive_write_set_filter_option(self->archive, NULL, "threads", value);
+                               if (r) {
+                                       ERROR(self->ctx, "Could not enable %ld threads for compression: %s\n",
+                                               processors, archive_error_string(self->archive));
+                                       return -ENOTSUP;
+                               }
+                       }
+#endif
+                       break;
+       }
+
+       // Set compression level
+       if (self->compression_level) {
+               // Format the value
+               r = pakfire_string_format(value, "%u", self->compression_level);
+               if (r < 0)
+                       return r;
+
+               // Set the value
+               r = archive_write_set_filter_option(self->archive, NULL, "compression-level", value);
+               if (r) {
+                       ERROR(self->ctx, "Could not set compression level %s: %s\n",
+                               value, archive_error_string(self->archive));
+                       return -ENOTSUP;
+               }
+       }
+
+       // Write archive to f
+       r = archive_write_open_FILE(self->archive, self->f);
+       if (r) {
+               ERROR(self->ctx, "archive_write_open_FILE() failed: %s\n",
+                       archive_error_string(self->archive));
+               return -ENOTSUP;
+       }
+
+       return 0;
+}
+
 static void pakfire_archive_writer_free(struct pakfire_archive_writer* self) {
+       if (self->archive)
+               archive_write_free(self->archive);
        if (self->pakfire)
                pakfire_unref(self->pakfire);
        if (self->ctx)
@@ -65,21 +221,19 @@ int pakfire_archive_writer_create(struct pakfire_archive_writer** writer,
        // Initialize the reference counter
        self->nrefs = 1;
 
-       // Store the format
-       switch (format) {
-               case PAKFIRE_FORMAT_ARCHIVE:
-                       self->format = format;
-                       break;
-
-               default:
-                       ERROR(self->ctx, "Invalid archive format\n");
-                       r = -EINVAL;
-                       goto ERROR;
-       }
-
        // Store the file handle
        self->f = f;
 
+       // Setup format
+       r = pakfire_archive_writer_setup_format(self, format);
+       if (r < 0)
+               goto ERROR;
+
+       // Setup archive
+       r = pakfire_archive_writer_setup_archive(self);
+       if (r < 0)
+               goto ERROR;
+
        // Return the pointer
        *writer = self;
        return 0;