From: Michael Tremer Date: Mon, 21 Oct 2024 17:31:51 +0000 (+0000) Subject: archive: Streamline opening and reading from the archive X-Git-Tag: 0.9.30~976 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e4ada6dee5e2636c570623f6654187c9689b3fe6;p=pakfire.git archive: Streamline opening and reading from the archive 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 --- diff --git a/src/libpakfire/archive.c b/src/libpakfire/archive.c index 5b5b03415..4cef9efda 100644 --- a/src/libpakfire/archive.c +++ b/src/libpakfire/archive.c @@ -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;