]> git.ipfire.org Git - pakfire.git/commitdiff
archive: Streamline opening and reading from the archive
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 21 Oct 2024 17:31:51 +0000 (17:31 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 21 Oct 2024 17:31:51 +0000 (17:31 +0000)
We will now always create a copy of the file descriptor so that we can
independently run mutliple operations on the same archive without
breaking anything.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/archive.c

index 5b5b0341583dcd8d11bea8dd20b6894cd15894d9..4cef9efda749a112f67cf9959b565ac5b41a493e 100644 (file)
@@ -89,22 +89,118 @@ struct pakfire_archive {
        int verify;
 };
 
-static FILE* pakfire_archive_clone_file(struct pakfire_archive* archive) {
-       int fd = fileno(archive->f);
-       if (fd < 0) {
-               CTX_ERROR(archive->ctx, "Could not fetch the archive's file descriptor: %m\n");
-               return NULL;
+struct pakfire_archive_file {
+       FILE* f;
+
+       // Buffer to store a block of data
+       char buffer[128 * 1024];
+
+       enum {
+               PAKFIRE_ARCHIVE_FILE_CANT_SEEK = (1 << 0),
+       } flags;
+};
+
+static ssize_t archive_file_read(struct archive* a, void* data, const void** buffer) {
+       struct pakfire_archive_file* file = data;
+
+       // Read a block of data into the buffer
+       size_t bytes_read = fread(file->buffer, 1, sizeof(file->buffer), file->f);
+
+       if (bytes_read < sizeof(file->buffer) && ferror(file->f)) {
+               archive_set_error(a, errno, "Error reading file");
+       }
+
+       // Return the buffer
+       *buffer = file->buffer;
+
+       return bytes_read;
+}
+
+static int64_t archive_file_skip(struct archive* a, void* data, off_t skip) {
+       struct pakfire_archive_file* file = data;
+       int r;
+
+       // Skip if we don't support seek
+       if (file->flags & PAKFIRE_ARCHIVE_FILE_CANT_SEEK)
+               return 0;
+
+       // Skip if we have been asked to do nothing
+       if (skip == 0)
+               return 0;
+
+       // Perform seek()
+       r = fseeko(file->f, skip, SEEK_CUR);
+       if (r < 0) {
+               file->flags |= PAKFIRE_ARCHIVE_FILE_CANT_SEEK;
+               return 0;
        }
 
+       return skip;
+}
+
+static int archive_file_close(struct archive* a, void* data) {
+       struct pakfire_archive_file* file = data;
+
+       // Close the file
+       if (file->f)
+               fclose(file->f);
+
+       free(file);
+
+       return ARCHIVE_OK;
+}
+
+/*
+       This is a custom open function to an archive.
+
+       I clones the file descriptor so that we can have an independent
+       file descriptor for multiple operations.
+*/
+static int archive_read_file_open(struct archive* a, FILE* f) {
+       struct pakfire_archive_file* file = NULL;
+       int fd = -EBADF;
+
+       // Fetch the underlying file descriptor
+       fd = fileno(f);
+       if (fd < 0)
+               goto ERROR;
+
        // Duplicate the file descriptor
        fd = dup(fd);
-       if (fd < 0) {
-               CTX_ERROR(archive->ctx, "Could not duplicate the file descriptor: %m\n");
-               return NULL;
-       }
+       if (fd < 0)
+               goto ERROR;
+
+       // Re-open the file handle
+       f = fdopen(fd, "r");
+       if (!f)
+               goto ERROR;
+
+       // Start reading at the beginning
+       rewind(f);
+
+       // Allocate an object
+       file = calloc(1, sizeof(*file));
+       if (!file)
+               goto ERROR;
+
+       // Store the file handle
+       file->f = f;
+
+       // Let the kernel know, that we will read the file sequentially
+       posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+
+       // Open the archive
+       return archive_read_open2(a,
+               file, NULL, archive_file_read, archive_file_skip, archive_file_close);
+
+ERROR:
+       // Store an error message
+       archive_set_error(a, errno, "%m");
+
+       if (fd >= 0)
+               close(fd);
 
-       // Re-open a file handle
-       return fdopen(fd, "r");
+       return ARCHIVE_FATAL;
 }
 
 static int pakfire_archive_compute_digests(struct pakfire_archive* archive) {
@@ -125,11 +221,7 @@ static int pakfire_archive_compute_digests(struct pakfire_archive* archive) {
 /*
        A helper function that opens the archive for reading
 */
-static struct archive* open_archive(struct pakfire_archive* archive, FILE* f) {
-       // If no special file descriptor has been given we use the open one
-       if (!f)
-               f = archive->f;
-
+static struct archive* open_archive(struct pakfire_archive* archive) {
        // Create a new archive object
        struct archive* a = archive_read_new();
        if (!a)
@@ -141,11 +233,8 @@ static struct archive* open_archive(struct pakfire_archive* archive, FILE* f) {
        // Archives are compressed using Zstandard
        archive_read_support_filter_zstd(a);
 
-       // Start reading from the beginning
-       rewind(f);
-
        // Try opening the archive file
-       int r = archive_read_open_FILE(a, f);
+       int r = archive_read_file_open(a, archive->f);
        if (r)
                goto ERROR;
 
@@ -153,6 +242,9 @@ static struct archive* open_archive(struct pakfire_archive* archive, FILE* f) {
        return a;
 
 ERROR:
+       CTX_ERROR(archive->ctx, "Could not open archive %s: %s\n",
+               archive->path, archive_error_string(a));
+
        if (a)
                archive_read_free(a);
 
@@ -252,7 +344,7 @@ static int pakfire_archive_open_and_walk(struct pakfire_archive* archive,
        int r;
 
        // Open the archive
-       a = open_archive(archive, NULL);
+       a = open_archive(archive);
        if (!a)
                return -errno;
 
@@ -521,7 +613,7 @@ static int pakfire_archive_read_metadata(struct pakfire_archive* archive) {
        CTX_DEBUG(archive->ctx, "Reading archive metadata...\n");
 
        // Open the archive
-       a = open_archive(archive, NULL);
+       a = open_archive(archive);
        if (!a) {
                r = -errno;
                goto ERROR;
@@ -705,9 +797,6 @@ struct pakfire_archive_read_cookie {
        // The path we are reading
        char path[PATH_MAX];
 
-       // A copy of the underlying file descriptor
-       FILE* f;
-
        // The opened archive
        struct archive* a;
 
@@ -735,8 +824,6 @@ static int pakfire_archive_cookie_close(void* c) {
                pakfire_archive_unref(cookie->archive);
        if (cookie->a)
                archive_read_free(cookie->a);
-       if (cookie->f)
-               fclose(cookie->f);
 
        // Free the cookie
        free(cookie);
@@ -843,17 +930,9 @@ PAKFIRE_EXPORT FILE* pakfire_archive_read(struct pakfire_archive* archive, const
                goto ERROR;
        }
 
-       // Clone the archive file descriptor to read the file independently
-       cookie->f = pakfire_archive_clone_file(archive);
-       if (!cookie->f) {
-               CTX_ERROR(archive->ctx, "Could not duplicate file descriptor for %s: %m\n",
-                       archive->path);
-               goto ERROR;
-       }
-
        // Open the archive
 AGAIN:
-       cookie->a = open_archive(archive, cookie->f);
+       cookie->a = open_archive(archive);
        if (!cookie->a)
                goto ERROR;
 
@@ -865,8 +944,6 @@ AGAIN:
                        case EAGAIN:
                                if (cookie->a)
                                        archive_read_free(cookie->a);
-                               if (cookie->f)
-                                       rewind(cookie->f);
                                goto AGAIN;
 
                        default:
@@ -1169,7 +1246,7 @@ static int __pakfire_archive_extract(struct pakfire_archive* archive, const char
        }
 
        // Open the archive
-       a = open_archive(archive, NULL);
+       a = open_archive(archive);
        if (!a) {
                r = -errno;
                goto ERROR;
@@ -1734,7 +1811,7 @@ int pakfire_archive_apply_systemd_sysusers(struct pakfire_archive* archive) {
        struct archive* a = NULL;
 
        // Open the archive
-       a = open_archive(archive, NULL);
+       a = open_archive(archive);
        if (!a)
                return -errno;