From: Michael Tremer Date: Wed, 1 Nov 2023 17:39:07 +0000 (+0000) Subject: xfer: Improve how we create temporary files X-Git-Tag: 0.9.30~1330 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=90a6b8e4f704c3c628e810b2bcccf6b864b7ea24;p=pakfire.git xfer: Improve how we create temporary files We will now create invisible files in the destination file system which gives us the advantage that the file will be written where it should be and we won't have to move it later. We can simply link the insivible file to become visible in the file system on success. Otherwise we just close the file handle and the data will be freed again. Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/dist.c b/src/libpakfire/dist.c index b78433041..b5de7451b 100644 --- a/src/libpakfire/dist.c +++ b/src/libpakfire/dist.c @@ -296,8 +296,8 @@ static int pakfire_dist_download_source(struct pakfire_httpclient* httpclient, if (r) goto ERROR; - // Set the target - r = pakfire_xfer_set_target(xfer, cache_path); + // Set the output path + r = pakfire_xfer_set_output_path(xfer, cache_path); if (r) goto ERROR; diff --git a/src/libpakfire/include/pakfire/xfer.h b/src/libpakfire/include/pakfire/xfer.h index 02a21e8a4..aff9e9063 100644 --- a/src/libpakfire/include/pakfire/xfer.h +++ b/src/libpakfire/include/pakfire/xfer.h @@ -74,12 +74,11 @@ int pakfire_xfer_add_param(struct pakfire_xfer* xfer, // Output int pakfire_xfer_set_output(struct pakfire_xfer* xfer, FILE* f); int pakfire_xfer_set_output_buffer(struct pakfire_xfer* xfer, char** buffer, size_t* length); +int pakfire_xfer_set_output_path(struct pakfire_xfer* xfer, const char* path); // Input int pakfire_xfer_set_input(struct pakfire_xfer* xfer, FILE* f); -int pakfire_xfer_set_target(struct pakfire_xfer* xfer, const char* path); - // Authentication int pakfire_xfer_auth(struct pakfire_xfer* xfer); diff --git a/src/libpakfire/repo.c b/src/libpakfire/repo.c index 5bf2ffcb4..fc2f8f3c5 100644 --- a/src/libpakfire/repo.c +++ b/src/libpakfire/repo.c @@ -471,7 +471,7 @@ static int pakfire_repo_download_database(struct pakfire_repo* repo, goto ERROR; // Set path - r = pakfire_xfer_set_target(xfer, path); + r = pakfire_xfer_set_output_path(xfer, path); if (r) goto ERROR; @@ -692,8 +692,8 @@ static int pakfire_repo_download_metadata(struct pakfire_repo* repo, const char* if (r) goto ERROR; - // Set the target path - r = pakfire_xfer_set_target(xfer, path); + // Set the output path + r = pakfire_xfer_set_output_path(xfer, path); if (r) goto ERROR; @@ -1254,8 +1254,8 @@ int pakfire_repo_download_package(struct pakfire_repo* repo, struct pakfire_http goto ERROR; } - // Set target - r = pakfire_xfer_set_target(x, cache_path); + // Set output path + r = pakfire_xfer_set_output_path(x, cache_path); if (r) goto ERROR; diff --git a/src/libpakfire/xfer.c b/src/libpakfire/xfer.c index 24b9bca53..e7d1e5759 100644 --- a/src/libpakfire/xfer.c +++ b/src/libpakfire/xfer.c @@ -19,6 +19,7 @@ #############################################################################*/ #include +#include #include #include #include @@ -76,9 +77,6 @@ struct pakfire_xfer { size_t expected_size; size_t xferred; - // Temporary file - char tempfile[PATH_MAX]; - // File handles for streams FILE* fin; FILE* fout; @@ -107,10 +105,6 @@ static void pakfire_xfer_free(struct pakfire_xfer* xfer) { if (xfer->handle) curl_easy_cleanup(xfer->handle); - // Unlink the temporary file - if (*xfer->tempfile) - unlink(xfer->tempfile); - // Close any streams if (xfer->fin) fclose(xfer->fin); @@ -206,6 +200,10 @@ static size_t pakfire_xfer_write( } } + // If there is no output steam, we just pretent that we have consumed the data + if (!xfer->fin) + return nmemb; + // Write everything to the allocated file descriptor return fwrite(data, size, nmemb, xfer->fin); } @@ -592,9 +590,53 @@ int pakfire_xfer_set_input(struct pakfire_xfer* xfer, FILE* f) { return 0; } -int pakfire_xfer_set_target( - struct pakfire_xfer* xfer, const char* path) { - return pakfire_string_set(xfer->path, path); +static int pakfire_xfer_allocate_tmpfile(struct pakfire_xfer* xfer, const char* path) { + char dirname[PATH_MAX]; + FILE* f = NULL; + int fd = -1; + int r; + + // Find the directory name + r = pakfire_path_dirname(dirname, path); + if (r) + return r; + + // Ensure the directory exists + r = pakfire_mkdir(dirname, 0755); + if (r) + return r; + + // Open a new temporary file + fd = open(dirname, O_TMPFILE|O_RDWR, 0600); + if (fd < 0) { + CTX_ERROR(xfer->ctx, "Could not open temporary file in %s: %s\n", + dirname, strerror(errno)); + return -errno; + } + + // Turn the file descriptor into a FILE handle + f = fdopen(fd, "w+"); + if (!f) + return -errno; + + // Set the handle as output + return pakfire_xfer_set_output(xfer, f); +} + +int pakfire_xfer_set_output_path(struct pakfire_xfer* xfer, const char* path) { + int r; + + // Store the output path + r = pakfire_string_set(xfer->path, path); + if (r) + return r; + + // Allocate a temporary file + r = pakfire_xfer_allocate_tmpfile(xfer, path); + if (r) + return r; + + return 0; } int pakfire_xfer_auth(struct pakfire_xfer* xfer) { @@ -669,6 +711,8 @@ static int pakfire_xfer_save(struct pakfire_xfer* xfer) { CTX_DEBUG(xfer->ctx, "Download successful. Storing result in %s\n", xfer->path); + int fd = fileno(xfer->fin); + // Make sure the parent directory exists r = pakfire_mkparentdir(xfer->path, 0755); if (r) @@ -678,7 +722,7 @@ static int pakfire_xfer_save(struct pakfire_xfer* xfer) { unlink(xfer->path); // Move the temporary file to its destination - r = link(xfer->tempfile, xfer->path); + r = linkat(fd, "", AT_FDCWD, xfer->path, AT_EMPTY_PATH); if (r) { CTX_ERROR(xfer->ctx, "Could not link destination file %s: %m\n", xfer->path); @@ -973,25 +1017,6 @@ static int pakfire_xfer_prepare_progress(struct pakfire_xfer* xfer, return 0; } -static int pakfire_xfer_prepare_tmpfile(struct pakfire_xfer* xfer) { - char path[PATH_MAX] = PAKFIRE_TMP_DIR "/pakfire-download.XXXXXX"; - FILE* f = NULL; - int r; - - // Allocate a temporary file - f = pakfire_mktemp(path, 0600); - if (!f) - return -errno; - - // Set this as output - r = pakfire_xfer_set_output(xfer, f); - if (r) - return r; - - // Store the path to the temporary file - return pakfire_string_set(xfer->tempfile, path); -} - static int pakfire_xfer_prepare_url(struct pakfire_xfer* xfer) { int r; @@ -1107,15 +1132,6 @@ int pakfire_xfer_prepare(struct pakfire_xfer* xfer, struct pakfire_progress* pro } } - // If we do not have an output file, we will create a temporary file - if (!xfer->fout && !xfer->fin) { - r = pakfire_xfer_prepare_tmpfile(xfer); - if (r) { - CTX_ERROR(xfer->ctx, "Could not open a temporary file: %s\n", strerror(-r)); - return r; - } - } - // Authentication if (xfer->auth) { // Request SPNEGO