]> git.ipfire.org Git - pakfire.git/commitdiff
xfer: Improve how we create temporary files
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 1 Nov 2023 17:39:07 +0000 (17:39 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 1 Nov 2023 17:39:07 +0000 (17:39 +0000)
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 <michael.tremer@ipfire.org>
src/libpakfire/dist.c
src/libpakfire/include/pakfire/xfer.h
src/libpakfire/repo.c
src/libpakfire/xfer.c

index b7843304147acc66c025452f2a1714c5a85ad212..b5de7451b5dc6dc6aeb730c595e9d9ff05720b5b 100644 (file)
@@ -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;
 
index 02a21e8a442f656d8daa038f066c72f8daf401fc..aff9e90638fb77e9e9458f978080d11a8e9d32b7 100644 (file)
@@ -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);
 
index 5bf2ffcb4c4fd8a1cf6e407f9958f49fc7bba488..fc2f8f3c5acc171092b7219ba80964b1e5e58db4 100644 (file)
@@ -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;
 
index 24b9bca53c7da043bb286c7bccd5c2c66d9273bd..e7d1e5759d03a57208d2de7a3e3c9cd5f1a73656 100644 (file)
@@ -19,6 +19,7 @@
 #############################################################################*/
 
 #include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <sys/queue.h>
 #include <utime.h>
@@ -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