]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
importd: make keeping pristine copy of downloaded images optional
authorLennart Poettering <lennart@poettering.net>
Thu, 22 Feb 2024 17:50:32 +0000 (18:50 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 1 Mar 2024 21:25:42 +0000 (22:25 +0100)
Previously, when downloading an image, importd would first download them
into one image which it would then consider immutable (named after the
originating URL/etag), and then immediately make a copy of it (named
after the client chosen name).

This makes some sense in VM/container cases where the images are
typically mutable, and thus the original downloaded copy is of some
value.

For sysexts/confexts/portable this doesn't make much sense though, as
they are typically immutable. Hence make the concept optional.

This adds --keep-download=yes/no as a new option that controls the
above. Moreover it disables the behaviour for all image classes but
"machine". The behaviour remains enabled for "machine", for compat.

src/import/import-common.h
src/import/importctl.c
src/import/importd.c
src/import/pull-raw.c
src/import/pull-tar.c
src/import/pull.c

index 2bb20754624e77a4bbdb246ff69ee85f132502cf..219802492d177361027d1effa5764a2e2e2520b4 100644 (file)
@@ -6,27 +6,31 @@
 #include "sd-event.h"
 
 typedef enum ImportFlags {
+        /* Public Flags (i.e. accessible via D-Bus, must stay stable! */
         IMPORT_FORCE                   = 1 <<  0, /* replace existing image */
         IMPORT_READ_ONLY               = 1 <<  1, /* make generated image read-only */
-        IMPORT_BTRFS_SUBVOL            = 1 <<  2, /* tar: preferably create images as btrfs subvols */
-        IMPORT_BTRFS_QUOTA             = 1 <<  3, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
-        IMPORT_CONVERT_QCOW2           = 1 <<  4, /* raw: if we detect a qcow2 image, unpack it */
-        IMPORT_DIRECT                  = 1 <<  5, /* import without rename games */
-        IMPORT_SYNC                    = 1 <<  6, /* fsync() right before we are done */
+        IMPORT_PULL_KEEP_DOWNLOAD      = 1 <<  2, /* keep a pristine copy of the downloaded file around */
+
+        /* Private flags */
+        IMPORT_BTRFS_SUBVOL            = 1 <<  3, /* tar: preferably create images as btrfs subvols */
+        IMPORT_BTRFS_QUOTA             = 1 <<  4, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
+        IMPORT_CONVERT_QCOW2           = 1 <<  5, /* raw: if we detect a qcow2 image, unpack it */
+        IMPORT_DIRECT                  = 1 <<  6, /* import without rename games */
+        IMPORT_SYNC                    = 1 <<  7, /* fsync() right before we are done */
 
         /* When pulling these flags are defined too */
-        IMPORT_PULL_SETTINGS           = 1 <<  7, /* download .nspawn settings file */
-        IMPORT_PULL_ROOTHASH           = 1 <<  8, /* only for raw: download .roothash file for verity */
-        IMPORT_PULL_ROOTHASH_SIGNATURE = 1 <<  9, /* only for raw: download .roothash.p7s file for verity */
-        IMPORT_PULL_VERITY             = 1 << 10, /* only for raw: download .verity file for verity */
+        IMPORT_PULL_SETTINGS           = 1 <<  8, /* download .nspawn settings file */
+        IMPORT_PULL_ROOTHASH           = 1 <<  9, /* only for raw: download .roothash file for verity */
+        IMPORT_PULL_ROOTHASH_SIGNATURE = 1 << 10, /* only for raw: download .roothash.p7s file for verity */
+        IMPORT_PULL_VERITY             = 1 << 11, /* only for raw: download .verity file for verity */
 
         /* The supported flags for the tar and the raw importing */
         IMPORT_FLAGS_MASK_TAR          = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_BTRFS_SUBVOL|IMPORT_BTRFS_QUOTA|IMPORT_DIRECT|IMPORT_SYNC,
         IMPORT_FLAGS_MASK_RAW          = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_CONVERT_QCOW2|IMPORT_DIRECT|IMPORT_SYNC,
 
         /* The supported flags for the tar and the raw pulling */
-        IMPORT_PULL_FLAGS_MASK_TAR     = IMPORT_FLAGS_MASK_TAR|IMPORT_PULL_SETTINGS,
-        IMPORT_PULL_FLAGS_MASK_RAW     = IMPORT_FLAGS_MASK_RAW|IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY,
+        IMPORT_PULL_FLAGS_MASK_TAR     = IMPORT_FLAGS_MASK_TAR|IMPORT_PULL_KEEP_DOWNLOAD|IMPORT_PULL_SETTINGS,
+        IMPORT_PULL_FLAGS_MASK_RAW     = IMPORT_FLAGS_MASK_RAW|IMPORT_PULL_KEEP_DOWNLOAD|IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY,
 
         _IMPORT_FLAGS_INVALID = -EINVAL,
 } ImportFlags;
index 1fc13e1326ad6005d793e3627eac299b8d5d2fde..688e583d07e7f9dc8a343c51445a2800f5ac3ab1 100644 (file)
@@ -37,6 +37,7 @@ static bool arg_legend = true;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static const char *arg_host = NULL;
 static ImportFlags arg_import_flags = 0;
+static ImportFlags arg_import_flags_mask = 0; /* Indicates which flags have been explicitly set to on or to off */
 static bool arg_quiet = false;
 static bool arg_ask_password = true;
 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
@@ -59,6 +60,11 @@ static int settle_image_class(void) {
                                        "No image class specified, retry with --class= set to one of: %s.", j);
         }
 
+        /* Keep the original pristine downloaded file as a copy only when dealing with machine images,
+         * because unlike sysext/confext/portable they are typically modified during runtime. */
+        if (!FLAGS_SET(arg_import_flags_mask, IMPORT_PULL_KEEP_DOWNLOAD))
+                SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, arg_image_class == IMAGE_MACHINE);
+
         return 0;
 }
 
@@ -586,7 +592,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
                                                local);
         }
 
-        if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~(IMPORT_FORCE|IMPORT_READ_ONLY)) == 0) {
+        if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~IMPORT_FORCE) == 0) {
                 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar");
                 if (r < 0)
                         return bus_log_create_error(r);
@@ -610,7 +616,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
                                 local,
                                 image_class_to_string(arg_image_class),
                                 import_verify_to_string(arg_verify),
-                                (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY));
+                                (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD));
         }
         if (r < 0)
                 return bus_log_create_error(r);
@@ -659,7 +665,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
                                                local);
         }
 
-        if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~(IMPORT_FORCE|IMPORT_READ_ONLY)) == 0) {
+        if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~IMPORT_FORCE) == 0) {
                 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw");
                 if (r < 0)
                         return bus_log_create_error(r);
@@ -683,7 +689,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
                                 local,
                                 image_class_to_string(arg_image_class),
                                 import_verify_to_string(arg_verify),
-                                (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY));
+                                (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD));
         }
         if (r < 0)
                 return bus_log_create_error(r);
@@ -860,6 +866,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "  -P --class=portable         Install as portable service image\n"
                "  -S --class=sysext           Install as system extension image\n"
                "  -C --class=confext          Install as configuration extension image\n"
+               "     --keep-download=BOOL     Control whether to keep pristine copy of download\n"
+               "  -N                          Shortcut for --keep-download=no\n"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -884,6 +892,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_FORCE,
                 ARG_FORMAT,
                 ARG_CLASS,
+                ARG_KEEP_DOWNLOAD,
         };
 
         static const struct option options[] = {
@@ -901,6 +910,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "force",           no_argument,       NULL, ARG_FORCE           },
                 { "format",          required_argument, NULL, ARG_FORMAT          },
                 { "class",           required_argument, NULL, ARG_CLASS           },
+                { "keep-download",   required_argument, NULL, ARG_KEEP_DOWNLOAD   },
                 {}
         };
 
@@ -910,7 +920,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argv);
 
         for (;;) {
-                c = getopt_long(argc, argv, "hH:M:jqmPSC", options, NULL);
+                c = getopt_long(argc, argv, "hH:M:jqmPSCN", options, NULL);
                 if (c < 0)
                         break;
 
@@ -946,6 +956,7 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case ARG_READ_ONLY:
                         arg_import_flags |= IMPORT_READ_ONLY;
+                        arg_import_flags_mask |= IMPORT_READ_ONLY;
                         break;
 
                 case 'q':
@@ -966,6 +977,7 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case ARG_FORCE:
                         arg_import_flags |= IMPORT_FORCE;
+                        arg_import_flags_mask |= IMPORT_FORCE;
                         break;
 
                 case ARG_FORMAT:
@@ -1011,6 +1023,20 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_image_class = IMAGE_CONFEXT;
                         break;
 
+                case ARG_KEEP_DOWNLOAD:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --keep-download= value: %s", optarg);
+
+                        SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, r);
+                        arg_import_flags_mask |= IMPORT_PULL_KEEP_DOWNLOAD;
+                        break;
+
+                case 'N':
+                        arg_import_flags_mask &= ~IMPORT_PULL_KEEP_DOWNLOAD;
+                        arg_import_flags_mask |= IMPORT_PULL_KEEP_DOWNLOAD;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
index 41c329b0f28e7cabd42e5a6936a223ed99c49940..a07ceb22884e50b029c29100d407e8969cdda074 100644 (file)
@@ -386,6 +386,7 @@ static int transfer_start(Transfer *t) {
                         NULL, /* verify argument */
                         NULL, /* --class= */
                         NULL, /* class argument */
+                        NULL, /* --keep-download= */
                         NULL, /* maybe --force */
                         NULL, /* maybe --read-only */
                         NULL, /* if so: the actual URL */
@@ -466,6 +467,10 @@ static int transfer_start(Transfer *t) {
                         cmd[k++] = image_class_to_string(t->class);
                 }
 
+                if (IN_SET(t->type, TRANSFER_PULL_TAR, TRANSFER_PULL_RAW))
+                        cmd[k++] = FLAGS_SET(t->flags, IMPORT_PULL_KEEP_DOWNLOAD) ?
+                                "--keep-download=yes" : "--keep-download=no";
+
                 if (FLAGS_SET(t->flags, IMPORT_FORCE))
                         cmd[k++] = "--force";
                 if (FLAGS_SET(t->flags, IMPORT_READ_ONLY))
@@ -1041,7 +1046,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                                  "Image class '%s' not known", sclass);
 
-                if (flags & ~(IMPORT_FORCE|IMPORT_READ_ONLY))
+                if (flags & ~(IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD))
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                                  "Flags 0x%" PRIx64 " invalid", flags);
         } else {
index 50785fe431ccf1c2dca969d3bcc7ee97f9c59f22..f3e6b3af8c33846ceac8cf220f76aa39a23a7048 100644 (file)
@@ -311,7 +311,7 @@ static int raw_pull_copy_auxiliary_file(
                 const char *suffix,
                 char **path /* input + output (!) */) {
 
-        const char *local;
+        _cleanup_free_ char *local = NULL;
         int r;
 
         assert(i);
@@ -322,21 +322,29 @@ static int raw_pull_copy_auxiliary_file(
         if (r < 0)
                 return r;
 
-        local = strjoina(i->image_root, "/", i->local, suffix);
+        local = strjoin(i->image_root, "/", i->local, suffix);
+        if (!local)
+                return log_oom();
 
-        r = copy_file_atomic(
-                        *path,
-                        local,
-                        0644,
-                        COPY_REFLINK |
-                        (FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
-                        (FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
+        if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD))
+                r = copy_file_atomic(
+                                *path,
+                                local,
+                                0644,
+                                COPY_REFLINK |
+                                (FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
+                                (FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
+        else
+                r = install_file(AT_FDCWD, *path,
+                                 AT_FDCWD, local,
+                                 (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
+                                 (i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
         if (r == -EEXIST)
                 log_warning_errno(r, "File %s already exists, not replacing.", local);
         else if (r == -ENOENT)
                 log_debug_errno(r, "Skipping creation of auxiliary file, since none was found.");
         else if (r < 0)
-                log_warning_errno(r, "Failed to copy file %s, ignoring: %m", local);
+                log_warning_errno(r, "Failed to install file %s, ignoring: %m", local);
         else
                 log_info("Created new file %s.", local);
 
@@ -345,9 +353,7 @@ static int raw_pull_copy_auxiliary_file(
 
 static int raw_pull_make_local_copy(RawPull *i) {
         _cleanup_(unlink_and_freep) char *tp = NULL;
-        _cleanup_free_ char *f = NULL;
-        _cleanup_close_ int dfd = -EBADF;
-        const char *p;
+        _cleanup_free_ char *p = NULL;
         int r;
 
         assert(i);
@@ -375,38 +381,49 @@ static int raw_pull_make_local_copy(RawPull *i) {
                         return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
         }
 
-        p = strjoina(i->image_root, "/", i->local, ".raw");
-
-        r = tempfn_random(p, NULL, &f);
-        if (r < 0)
+        p = strjoin(i->image_root, "/", i->local, ".raw");
+        if (!p)
                 return log_oom();
 
-        dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
-        if (dfd < 0)
-                return log_error_errno(errno, "Failed to create writable copy of image: %m");
+        const char *source;
+        if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD)) {
+                _cleanup_close_ int dfd = -EBADF;
+                _cleanup_free_ char *f = NULL;
+
+                r = tempfn_random(p, NULL, &f);
+                if (r < 0)
+                        return log_oom();
 
-        tp = TAKE_PTR(f);
+                dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+                if (dfd < 0)
+                        return log_error_errno(errno, "Failed to create writable copy of image: %m");
 
-        /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
-         * since it reduces fragmentation caused by not allowing in-place writes. */
-        (void) import_set_nocow_and_log(dfd, tp);
+                tp = TAKE_PTR(f);
 
-        r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
-        if (r < 0)
-                return log_error_errno(r, "Failed to make writable copy of image: %m");
+                /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
+                 * since it reduces fragmentation caused by not allowing in-place writes. */
+                (void) import_set_nocow_and_log(dfd, tp);
+
+                r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make writable copy of image: %m");
+
+                (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
+                (void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0);
 
-        (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
-        (void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0);
+                dfd = safe_close(dfd);
 
-        dfd = safe_close(dfd);
+                source = tp;
+        } else
+                source = i->final_path;
 
-        r = install_file(AT_FDCWD, tp,
+        r = install_file(AT_FDCWD, source,
                          AT_FDCWD, p,
                          (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
                          (i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
                          (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
         if (r < 0)
-                return log_error_errno(errno, "Failed to move local image into place '%s': %m", p);
+                return log_error_errno(r, "Failed to move local image into place '%s': %m", p);
 
         tp = mfree(tp);
 
@@ -628,7 +645,7 @@ static void raw_pull_job_on_finished(PullJob *j) {
 
                         r = install_file(AT_FDCWD, i->temp_path,
                                          AT_FDCWD, i->final_path,
-                                         INSTALL_READ_ONLY|
+                                         (i->flags & IMPORT_PULL_KEEP_DOWNLOAD ? INSTALL_READ_ONLY : 0) |
                                          (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
                         if (r < 0) {
                                 log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
index ae573f1b5403e8e340523a77458d86dce012797d..7fc71fe6f35c78a7ed783753a8866472f04f2464 100644 (file)
@@ -221,6 +221,7 @@ static int tar_pull_determine_path(
 static int tar_pull_make_local_copy(TarPull *i) {
         _cleanup_(rm_rf_subvolume_and_freep) char *t = NULL;
         _cleanup_free_ char *p = NULL;
+        const char *source;
         int r;
 
         assert(i);
@@ -235,24 +236,29 @@ static int tar_pull_make_local_copy(TarPull *i) {
         if (!p)
                 return log_oom();
 
-        r = tempfn_random(p, NULL, &t);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
+        if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD)) {
+                r = tempfn_random(p, NULL, &t);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
 
-        if (i->flags & IMPORT_BTRFS_SUBVOL)
-                r = btrfs_subvol_snapshot_at(
-                                AT_FDCWD, i->final_path,
-                                AT_FDCWD, t,
-                                (i->flags & IMPORT_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
-                                BTRFS_SNAPSHOT_FALLBACK_COPY|
-                                BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
-                                BTRFS_SNAPSHOT_RECURSIVE);
-        else
-                r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create local image: %m");
+                if (i->flags & IMPORT_BTRFS_SUBVOL)
+                        r = btrfs_subvol_snapshot_at(
+                                        AT_FDCWD, i->final_path,
+                                        AT_FDCWD, t,
+                                        (i->flags & IMPORT_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
+                                        BTRFS_SNAPSHOT_FALLBACK_COPY|
+                                        BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
+                                        BTRFS_SNAPSHOT_RECURSIVE);
+                else
+                        r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create local image: %m");
+
+                source = t;
+        } else
+                source = i->final_path;
 
-        r = install_file(AT_FDCWD, t,
+        r = install_file(AT_FDCWD, source,
                          AT_FDCWD, p,
                          (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
                          (i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
@@ -265,28 +271,36 @@ static int tar_pull_make_local_copy(TarPull *i) {
         log_info("Created new local image '%s'.", i->local);
 
         if (FLAGS_SET(i->flags, IMPORT_PULL_SETTINGS)) {
-                const char *local_settings;
+                _cleanup_free_ char *local_settings = NULL;
                 assert(i->settings_job);
 
                 r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
                 if (r < 0)
                         return r;
 
-                local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
+                local_settings = strjoin(i->image_root, "/", i->local, ".nspawn");
+                if (!local_settings)
+                        return log_oom();
 
-                r = copy_file_atomic(
-                                i->settings_path,
-                                local_settings,
-                                0664,
-                                COPY_REFLINK |
-                                (FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
-                                (FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
+                if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD))
+                        r = copy_file_atomic(
+                                        i->settings_path,
+                                        local_settings,
+                                        0664,
+                                        COPY_REFLINK |
+                                        (FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
+                                        (FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
+                else
+                        r = install_file(AT_FDCWD, i->settings_path,
+                                         AT_FDCWD, local_settings,
+                                         (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
+                                         (i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
                 if (r == -EEXIST)
                         log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
                 else if (r == -ENOENT)
                         log_debug_errno(r, "Skipping creation of settings file, since none was found.");
                 else if (r < 0)
-                        log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
+                        log_warning_errno(r, "Failed to install settings files %s, ignoring: %m", local_settings);
                 else
                         log_info("Created new settings file %s.", local_settings);
         }
@@ -435,7 +449,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
                         r = install_file(
                                         AT_FDCWD, i->temp_path,
                                         AT_FDCWD, i->final_path,
-                                        INSTALL_READ_ONLY|
+                                        (i->flags & IMPORT_PULL_KEEP_DOWNLOAD ? INSTALL_READ_ONLY : 0) |
                                         (i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
                         if (r < 0) {
                                 log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);
index 19cfbe0d8d2638f4a6b9f83b28c60ee768de7bbd..d4ad0b2ea375887cc78b79571eb09ddcec54271f 100644 (file)
@@ -270,7 +270,9 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --offset=BYTES           Offset to seek to in destination\n"
                "     --size-max=BYTES         Maximum number of bytes to write to destination\n"
                "     --class=CLASS            Select image class (machine, sysext, confext,\n"
-               "                              portable)\n",
+               "                              portable)\n"
+               "     --keep-download=BOOL     Keep a copy pristine copy of the downloaded file\n"
+               "                              around\n",
                program_invocation_short_name,
                ansi_underline(),
                ansi_normal(),
@@ -300,6 +302,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_OFFSET,
                 ARG_SIZE_MAX,
                 ARG_CLASS,
+                ARG_KEEP_DOWNLOAD,
         };
 
         static const struct option options[] = {
@@ -321,11 +324,12 @@ static int parse_argv(int argc, char *argv[]) {
                 { "offset",             required_argument, NULL, ARG_OFFSET             },
                 { "size-max",           required_argument, NULL, ARG_SIZE_MAX           },
                 { "class",              required_argument, NULL, ARG_CLASS              },
+                { "keep-download",      required_argument, NULL, ARG_KEEP_DOWNLOAD      },
                 {}
         };
 
         int c, r;
-        bool auto_settings = true;
+        bool auto_settings = true, auto_keep_download = true;
 
         assert(argc >= 0);
         assert(argv);
@@ -492,6 +496,15 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_KEEP_DOWNLOAD:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --keep-download= argument: %s", optarg);
+
+                        SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, r);
+                        auto_keep_download = false;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -518,6 +531,11 @@ static int parse_argv(int argc, char *argv[]) {
         if (auto_settings && arg_class != IMAGE_MACHINE)
                 arg_import_flags &= ~IMPORT_PULL_SETTINGS;
 
+        /* Keep the original pristine downloaded file as a copy only when dealing with machine images,
+         * because unlike sysext/confext/portable they are typically modified during runtime. */
+        if (auto_keep_download)
+                SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, arg_class == IMAGE_MACHINE);
+
         return 1;
 }