#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;
// 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)
// 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;