]> git.ipfire.org Git - pakfire.git/commitdiff
archive: Be more efficient when reading single files
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 10 Mar 2023 16:34:48 +0000 (16:34 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 10 Mar 2023 16:35:35 +0000 (16:35 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/archive.c

index f840e1afdf52e96a13c75009b813051321c63dfb..69849ec760d5aaf563d65d02eedf9092d3e70163 100644 (file)
@@ -525,13 +525,6 @@ static int64_t pakfire_archive_metadata_get_int64(
        return json_object_get_int64(object);
 }
 
-struct pakfire_archive_read {
-       struct pakfire_archive* archive;
-       const char* path;
-       char** data;
-       size_t* length;
-};
-
 static int __pakfire_archive_filter_payload(struct pakfire* pakfire,
                struct archive* a, struct archive_entry* entry, void* p) {
        const char* path = archive_entry_pathname(entry);
@@ -556,44 +549,62 @@ static int __pakfire_archive_filter_payload(struct pakfire* pakfire,
 
 PAKFIRE_EXPORT int pakfire_archive_read(struct pakfire_archive* archive,
                const char* path, char** data, size_t* length) {
+       int found = 0;
        int r;
 
-       // Strip leading / from filenames, because tarballs don't use any leading slashes
-       path = pakfire_path_relpath("/", path);
+       // Check if path is absolute
+       if (!path || *path != '/') {
+               errno = EINVAL;
+               return 1;
 
-       // Check inputs
-       if (!path || !data || !length) {
+       // Check other inputs
+       } else if (!data || !length) {
                errno = EINVAL;
                return 1;
        }
 
-       // Reset data
-       *data = NULL;
+       // Strip leading / from filenames, because tarballs don't use any leading slashes
+       path = pakfire_path_relpath("/", path);
+       if (!path)
+               return 1;
 
-       int __pakfire_archive_read(struct pakfire* pakfire, struct archive* a,
+       // Tries to find a matching file in the archive
+       int __pakfire_archive_read_filter(struct pakfire* pakfire, struct archive* a,
                        struct archive_entry* e, void* __data) {
-               // Fetch the path of this entry
+               // Stop reading the archive after we have found our file
+               if (found)
+                       return PAKFIRE_WALK_END;
+
+               // Fetch path
                const char* p = archive_entry_pathname(e);
                if (!p)
-                       return 1;
+                       return PAKFIRE_WALK_ERROR;
 
-               if (strcmp(path, p) == 0) {
-                       r = pakfire_archive_copy_data_to_buffer(pakfire, a, e, data, length);
-                       if (r)
-                               return r;
-               }
+               // We found a match
+               if (strcmp(path, p) == 0)
+                       return PAKFIRE_WALK_OK;
 
-               return 0;
+               // Otherwise we skip the file
+               return PAKFIRE_WALK_SKIP;
+       }
+
+       // Reads a matching file into memory
+       int __pakfire_archive_read(struct pakfire* pakfire, struct archive* a,
+                       struct archive_entry* e, void* __data) {
+               // We have found our file
+               found = 1;
+
+               return pakfire_archive_copy_data_to_buffer(pakfire, a, e, data, length);
        }
 
        // Walk through the archive
        r = pakfire_archive_walk(archive,
-               __pakfire_archive_read, __pakfire_archive_filter_payload, NULL);
+               __pakfire_archive_read, __pakfire_archive_read_filter, NULL);
        if (r)
                return r;
 
        // Nothing found
-       if (!*data) {
+       if (!found) {
                // No such file or directory
                errno = ENOENT;