1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-26 03:22:00 +03:00

importd: make keeping pristine copy of downloaded images optional

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.
This commit is contained in:
Lennart Poettering 2024-02-22 18:50:32 +01:00
parent 8f20b498bd
commit b146afc449
6 changed files with 163 additions and 79 deletions

View File

@ -6,27 +6,31 @@
#include "sd-event.h" #include "sd-event.h"
typedef enum ImportFlags { typedef enum ImportFlags {
/* Public Flags (i.e. accessible via D-Bus, must stay stable! */
IMPORT_FORCE = 1 << 0, /* replace existing image */ IMPORT_FORCE = 1 << 0, /* replace existing image */
IMPORT_READ_ONLY = 1 << 1, /* make generated image read-only */ IMPORT_READ_ONLY = 1 << 1, /* make generated image read-only */
IMPORT_BTRFS_SUBVOL = 1 << 2, /* tar: preferably create images as btrfs subvols */ IMPORT_PULL_KEEP_DOWNLOAD = 1 << 2, /* keep a pristine copy of the downloaded file around */
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 */ /* Private flags */
IMPORT_DIRECT = 1 << 5, /* import without rename games */ IMPORT_BTRFS_SUBVOL = 1 << 3, /* tar: preferably create images as btrfs subvols */
IMPORT_SYNC = 1 << 6, /* fsync() right before we are done */ 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 */ /* When pulling these flags are defined too */
IMPORT_PULL_SETTINGS = 1 << 7, /* download .nspawn settings file */ IMPORT_PULL_SETTINGS = 1 << 8, /* download .nspawn settings file */
IMPORT_PULL_ROOTHASH = 1 << 8, /* only for raw: download .roothash file for verity */ IMPORT_PULL_ROOTHASH = 1 << 9, /* 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_ROOTHASH_SIGNATURE = 1 << 10, /* only for raw: download .roothash.p7s file for verity */
IMPORT_PULL_VERITY = 1 << 10, /* only for raw: download .verity 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 */ /* 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_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, 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 */ /* 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_TAR = IMPORT_FLAGS_MASK_TAR|IMPORT_PULL_KEEP_DOWNLOAD|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_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, _IMPORT_FLAGS_INVALID = -EINVAL,
} ImportFlags; } ImportFlags;

View File

@ -37,6 +37,7 @@ static bool arg_legend = true;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static const char *arg_host = NULL; static const char *arg_host = NULL;
static ImportFlags arg_import_flags = 0; 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_quiet = false;
static bool arg_ask_password = true; static bool arg_ask_password = true;
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; 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); "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; return 0;
} }
@ -586,7 +592,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
local); 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"); r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
@ -610,7 +616,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
local, local,
image_class_to_string(arg_image_class), image_class_to_string(arg_image_class),
import_verify_to_string(arg_verify), 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) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
@ -659,7 +665,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
local); 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"); r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
@ -683,7 +689,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
local, local,
image_class_to_string(arg_image_class), image_class_to_string(arg_image_class),
import_verify_to_string(arg_verify), 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) if (r < 0)
return bus_log_create_error(r); 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" " -P --class=portable Install as portable service image\n"
" -S --class=sysext Install as system extension image\n" " -S --class=sysext Install as system extension image\n"
" -C --class=confext Install as configuration 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", "\nSee the %2$s for details.\n",
program_invocation_short_name, program_invocation_short_name,
link, link,
@ -884,6 +892,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FORCE, ARG_FORCE,
ARG_FORMAT, ARG_FORMAT,
ARG_CLASS, ARG_CLASS,
ARG_KEEP_DOWNLOAD,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -901,6 +910,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "force", no_argument, NULL, ARG_FORCE }, { "force", no_argument, NULL, ARG_FORCE },
{ "format", required_argument, NULL, ARG_FORMAT }, { "format", required_argument, NULL, ARG_FORMAT },
{ "class", required_argument, NULL, ARG_CLASS }, { "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); assert(argv);
for (;;) { for (;;) {
c = getopt_long(argc, argv, "hH:M:jqmPSC", options, NULL); c = getopt_long(argc, argv, "hH:M:jqmPSCN", options, NULL);
if (c < 0) if (c < 0)
break; break;
@ -946,6 +956,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_READ_ONLY: case ARG_READ_ONLY:
arg_import_flags |= IMPORT_READ_ONLY; arg_import_flags |= IMPORT_READ_ONLY;
arg_import_flags_mask |= IMPORT_READ_ONLY;
break; break;
case 'q': case 'q':
@ -966,6 +977,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_FORCE: case ARG_FORCE:
arg_import_flags |= IMPORT_FORCE; arg_import_flags |= IMPORT_FORCE;
arg_import_flags_mask |= IMPORT_FORCE;
break; break;
case ARG_FORMAT: case ARG_FORMAT:
@ -1011,6 +1023,20 @@ static int parse_argv(int argc, char *argv[]) {
arg_image_class = IMAGE_CONFEXT; arg_image_class = IMAGE_CONFEXT;
break; 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 '?': case '?':
return -EINVAL; return -EINVAL;

View File

@ -386,6 +386,7 @@ static int transfer_start(Transfer *t) {
NULL, /* verify argument */ NULL, /* verify argument */
NULL, /* --class= */ NULL, /* --class= */
NULL, /* class argument */ NULL, /* class argument */
NULL, /* --keep-download= */
NULL, /* maybe --force */ NULL, /* maybe --force */
NULL, /* maybe --read-only */ NULL, /* maybe --read-only */
NULL, /* if so: the actual URL */ NULL, /* if so: the actual URL */
@ -466,6 +467,10 @@ static int transfer_start(Transfer *t) {
cmd[k++] = image_class_to_string(t->class); 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)) if (FLAGS_SET(t->flags, IMPORT_FORCE))
cmd[k++] = "--force"; cmd[k++] = "--force";
if (FLAGS_SET(t->flags, IMPORT_READ_ONLY)) 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, return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Image class '%s' not known", sclass); "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, return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Flags 0x%" PRIx64 " invalid", flags); "Flags 0x%" PRIx64 " invalid", flags);
} else { } else {

View File

@ -311,7 +311,7 @@ static int raw_pull_copy_auxiliary_file(
const char *suffix, const char *suffix,
char **path /* input + output (!) */) { char **path /* input + output (!) */) {
const char *local; _cleanup_free_ char *local = NULL;
int r; int r;
assert(i); assert(i);
@ -322,21 +322,29 @@ static int raw_pull_copy_auxiliary_file(
if (r < 0) if (r < 0)
return r; 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( if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD))
*path, r = copy_file_atomic(
local, *path,
0644, local,
COPY_REFLINK | 0644,
(FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) | COPY_REFLINK |
(FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0)); (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) if (r == -EEXIST)
log_warning_errno(r, "File %s already exists, not replacing.", local); log_warning_errno(r, "File %s already exists, not replacing.", local);
else if (r == -ENOENT) else if (r == -ENOENT)
log_debug_errno(r, "Skipping creation of auxiliary file, since none was found."); log_debug_errno(r, "Skipping creation of auxiliary file, since none was found.");
else if (r < 0) 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 else
log_info("Created new file %s.", local); 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) { static int raw_pull_make_local_copy(RawPull *i) {
_cleanup_(unlink_and_freep) char *tp = NULL; _cleanup_(unlink_and_freep) char *tp = NULL;
_cleanup_free_ char *f = NULL; _cleanup_free_ char *p = NULL;
_cleanup_close_ int dfd = -EBADF;
const char *p;
int r; int r;
assert(i); 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"); return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
} }
p = strjoina(i->image_root, "/", i->local, ".raw"); p = strjoin(i->image_root, "/", i->local, ".raw");
if (!p)
r = tempfn_random(p, NULL, &f);
if (r < 0)
return log_oom(); return log_oom();
dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); const char *source;
if (dfd < 0) if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD)) {
return log_error_errno(errno, "Failed to create writable copy of image: %m"); _cleanup_close_ int dfd = -EBADF;
_cleanup_free_ char *f = NULL;
tp = TAKE_PTR(f); r = tempfn_random(p, NULL, &f);
if (r < 0)
return log_oom();
/* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs, dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
* since it reduces fragmentation caused by not allowing in-place writes. */ if (dfd < 0)
(void) import_set_nocow_and_log(dfd, tp); return log_error_errno(errno, "Failed to create writable copy of image: %m");
r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK); tp = TAKE_PTR(f);
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); /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
(void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0); * since it reduces fragmentation caused by not allowing in-place writes. */
(void) import_set_nocow_and_log(dfd, tp);
dfd = safe_close(dfd); 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");
r = install_file(AT_FDCWD, tp, (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);
source = tp;
} else
source = i->final_path;
r = install_file(AT_FDCWD, source,
AT_FDCWD, p, AT_FDCWD, p,
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) | (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) | (i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
(i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0)); (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 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); tp = mfree(tp);
@ -628,7 +645,7 @@ static void raw_pull_job_on_finished(PullJob *j) {
r = install_file(AT_FDCWD, i->temp_path, r = install_file(AT_FDCWD, i->temp_path,
AT_FDCWD, i->final_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)); (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path); log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);

View File

@ -221,6 +221,7 @@ static int tar_pull_determine_path(
static int tar_pull_make_local_copy(TarPull *i) { static int tar_pull_make_local_copy(TarPull *i) {
_cleanup_(rm_rf_subvolume_and_freep) char *t = NULL; _cleanup_(rm_rf_subvolume_and_freep) char *t = NULL;
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;
const char *source;
int r; int r;
assert(i); assert(i);
@ -235,24 +236,29 @@ static int tar_pull_make_local_copy(TarPull *i) {
if (!p) if (!p)
return log_oom(); return log_oom();
r = tempfn_random(p, NULL, &t); if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD)) {
if (r < 0) r = tempfn_random(p, NULL, &t);
return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p); if (r < 0)
return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
if (i->flags & IMPORT_BTRFS_SUBVOL) if (i->flags & IMPORT_BTRFS_SUBVOL)
r = btrfs_subvol_snapshot_at( r = btrfs_subvol_snapshot_at(
AT_FDCWD, i->final_path, AT_FDCWD, i->final_path,
AT_FDCWD, t, AT_FDCWD, t,
(i->flags & IMPORT_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)| (i->flags & IMPORT_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
BTRFS_SNAPSHOT_FALLBACK_COPY| BTRFS_SNAPSHOT_FALLBACK_COPY|
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY| BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
BTRFS_SNAPSHOT_RECURSIVE); BTRFS_SNAPSHOT_RECURSIVE);
else else
r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL); r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to create local image: %m"); return log_error_errno(r, "Failed to create local image: %m");
r = install_file(AT_FDCWD, t, source = t;
} else
source = i->final_path;
r = install_file(AT_FDCWD, source,
AT_FDCWD, p, AT_FDCWD, p,
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) | (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 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); log_info("Created new local image '%s'.", i->local);
if (FLAGS_SET(i->flags, IMPORT_PULL_SETTINGS)) { if (FLAGS_SET(i->flags, IMPORT_PULL_SETTINGS)) {
const char *local_settings; _cleanup_free_ char *local_settings = NULL;
assert(i->settings_job); assert(i->settings_job);
r = tar_pull_determine_path(i, ".nspawn", &i->settings_path); r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
if (r < 0) if (r < 0)
return r; 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( if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD))
i->settings_path, r = copy_file_atomic(
local_settings, i->settings_path,
0664, local_settings,
COPY_REFLINK | 0664,
(FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) | COPY_REFLINK |
(FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0)); (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) if (r == -EEXIST)
log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
else if (r == -ENOENT) else if (r == -ENOENT)
log_debug_errno(r, "Skipping creation of settings file, since none was found."); log_debug_errno(r, "Skipping creation of settings file, since none was found.");
else if (r < 0) 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 else
log_info("Created new settings file %s.", local_settings); 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( r = install_file(
AT_FDCWD, i->temp_path, AT_FDCWD, i->temp_path,
AT_FDCWD, i->final_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)); (i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path); log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);

View File

@ -270,7 +270,9 @@ static int help(int argc, char *argv[], void *userdata) {
" --offset=BYTES Offset to seek to in destination\n" " --offset=BYTES Offset to seek to in destination\n"
" --size-max=BYTES Maximum number of bytes to write to destination\n" " --size-max=BYTES Maximum number of bytes to write to destination\n"
" --class=CLASS Select image class (machine, sysext, confext,\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, program_invocation_short_name,
ansi_underline(), ansi_underline(),
ansi_normal(), ansi_normal(),
@ -300,6 +302,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_OFFSET, ARG_OFFSET,
ARG_SIZE_MAX, ARG_SIZE_MAX,
ARG_CLASS, ARG_CLASS,
ARG_KEEP_DOWNLOAD,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -321,11 +324,12 @@ static int parse_argv(int argc, char *argv[]) {
{ "offset", required_argument, NULL, ARG_OFFSET }, { "offset", required_argument, NULL, ARG_OFFSET },
{ "size-max", required_argument, NULL, ARG_SIZE_MAX }, { "size-max", required_argument, NULL, ARG_SIZE_MAX },
{ "class", required_argument, NULL, ARG_CLASS }, { "class", required_argument, NULL, ARG_CLASS },
{ "keep-download", required_argument, NULL, ARG_KEEP_DOWNLOAD },
{} {}
}; };
int c, r; int c, r;
bool auto_settings = true; bool auto_settings = true, auto_keep_download = true;
assert(argc >= 0); assert(argc >= 0);
assert(argv); assert(argv);
@ -492,6 +496,15 @@ static int parse_argv(int argc, char *argv[]) {
break; 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 '?': case '?':
return -EINVAL; return -EINVAL;
@ -518,6 +531,11 @@ static int parse_argv(int argc, char *argv[]) {
if (auto_settings && arg_class != IMAGE_MACHINE) if (auto_settings && arg_class != IMAGE_MACHINE)
arg_import_flags &= ~IMPORT_PULL_SETTINGS; 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; return 1;
} }