#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>
} compression;
unsigned int compression_level;
+ // Checksums
+ int checksums;
+
// File Handle
FILE* f;
// 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
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;
+}