]> git.ipfire.org Git - people/stevee/pakfire.git/commitdiff
compress: Resolve hardlinks when writing archives
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 15 Mar 2023 11:30:41 +0000 (11:30 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 15 Mar 2023 11:36:13 +0000 (11:36 +0000)
Fixes: #13014
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/compress.c

index b956e2aec20d7b676fb73c02fdeb2198de0a67f3..8a5d793ba1ec1a56707a9e38279f668990633f96 100644 (file)
@@ -848,6 +848,9 @@ struct pakfire_compress {
        // The archive to write to
        struct archive* archive;
 
+       // Resolver for hardlinks
+       struct archive_entry_linkresolver* linkresolver;
+
        // The filelist of all files to write
        struct pakfire_filelist* filelist;
 
@@ -900,25 +903,10 @@ static int pakfire_compress_progressbar_create(struct pakfire_progressbar** prog
        return 0;
 }
 
-static int __pakfire_compress(struct pakfire* pakfire, struct pakfire_file* file, void* p) {
-       struct archive_entry* entry = NULL;
+static int __pakfire_compress_entry(struct pakfire* pakfire, struct pakfire_file* file,
+               struct pakfire_compress* data, struct archive_entry* entry) {
        FILE* f = NULL;
-       int r = 1;
-
-       struct pakfire_compress* data = (struct pakfire_compress*)p;
-
-       // Fetch type
-       const mode_t type = pakfire_file_get_type(file);
-
-       // Fetch the filelist
-       const size_t size = pakfire_file_get_size(file);
-
-       // Generate file metadata into an archive entry
-       entry = pakfire_file_archive_entry(file, data->digests);
-       if (!entry) {
-               r = 1;
-               goto ERROR;
-       }
+       int r;
 
        // Write the header
        r = archive_write_header(data->archive, entry);
@@ -928,8 +916,8 @@ static int __pakfire_compress(struct pakfire* pakfire, struct pakfire_file* file
                goto ERROR;
        }
 
-       // Copy the data of regular files
-       if (type == S_IFREG) {
+       // Copy the data if there is any
+       if (archive_entry_size(entry)) {
                // Open the file
                f = pakfire_file_open(file);
                if (!f) {
@@ -948,15 +936,81 @@ static int __pakfire_compress(struct pakfire* pakfire, struct pakfire_file* file
        if (r)
                goto ERROR;
 
+ERROR:
+       if (f)
+               fclose(f);
+
+       return r;
+}
+
+static int __pakfire_compress(struct pakfire* pakfire, struct pakfire_file* file, void* p) {
+       struct archive_entry* entry = NULL;
+       struct archive_entry* sparse_entry = NULL;
+       int r = 1;
+
+       struct pakfire_compress* data = (struct pakfire_compress*)p;
+
+       // Fetch the file size
+       const size_t size = pakfire_file_get_size(file);
+
+       // Generate file metadata into an archive entry
+       entry = pakfire_file_archive_entry(file, data->digests);
+       if (!entry) {
+               r = 1;
+               goto ERROR;
+       }
+
+       // Perform search for hardlinks
+       archive_entry_linkify(data->linkresolver, &entry, &sparse_entry);
+
+       // Write the main entry
+       if (entry) {
+               r = __pakfire_compress_entry(pakfire, file, data, entry);
+               if (r)
+                       goto ERROR;
+       }
+
+       // Write the sparse entry
+       if (sparse_entry) {
+               r = __pakfire_compress_entry(pakfire, file, data, sparse_entry);
+               if (r)
+                       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(data->linkresolver, &entry, &sparse_entry);
+               if (!entry)
+                       break;
+
+               // Write the entry to the archive
+               r = __pakfire_compress_entry(pakfire, file, data, entry);
+               if (r)
+                       goto ERROR;
+       }
+
        // Update the progressbar
        if (data->progressbar)
                pakfire_progressbar_increment(data->progressbar, size);
 
 ERROR:
-       if (f)
-               fclose(f);
        if (entry)
                archive_entry_free(entry);
+       if (sparse_entry)
+               archive_entry_free(sparse_entry);
 
        return r;
 }
@@ -989,6 +1043,16 @@ int pakfire_compress(struct pakfire* pakfire, struct archive* archive,
                pakfire_progressbar_start(data.progressbar, size);
        }
 
+       // Setup the link resolver
+       data.linkresolver = archive_entry_linkresolver_new();
+       if (!data.linkresolver) {
+               ERROR(pakfire, "Could not setup link resolver: m\n");
+               goto ERROR;
+       }
+
+       // Set the link resolver strategy
+       archive_entry_linkresolver_set_strategy(data.linkresolver, archive_format(archive));
+
        // Walk through the entire filelist
        r = pakfire_filelist_walk(filelist, __pakfire_compress, &data);
        if (r)
@@ -1001,6 +1065,8 @@ int pakfire_compress(struct pakfire* pakfire, struct archive* archive,
 ERROR:
        if (data.progressbar)
                pakfire_progressbar_unref(data.progressbar);
+       if (data.linkresolver)
+               archive_entry_linkresolver_free(data.linkresolver);
 
        return r;
 }