mirror of
https://github.com/systemd/systemd.git
synced 2024-12-26 03:22:00 +03:00
import: add new "--direct" mode + add controls for turning certain features on/off
This reworks/modernizes the tar/raw import logic and adds the following new features: - Adds the ability to control btrfs subvol and quota behaviour which was previously always on via an env var and cmdline arg - Adds control whether to sync() stuff after writing it, similar via env var + cmdline arg - Similar, the QCOW2 unpacking logic that was previously the implied default may now be controlled via env var + cmdline arg. - adds a "direct" mode. In this mode, the systemd-import tool can be used as a simple tool for decompressing/unpacking/installing arbitrary files, without all the additional meta data and auxiliary resources, i.e. outside of the immediate disk image context. Via the new --offset= and --size-max= switches the downloaded data can be written to specific locations of a file (which is particularly useful to use the tool to download fs images and write them to a partition location before actually creating the partition). We'll later use the latter feature for "sysupdate" concept, where images can be directly be written to partitions. That way the systemd-import binary will be used as backend for both "systemd-importd" and "systemd-sysupdate" and share most of the same code.
This commit is contained in:
parent
235be6bcea
commit
d32a5841fb
@ -13,6 +13,7 @@
|
|||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
#include "hostname-util.h"
|
||||||
#include "import-common.h"
|
#include "import-common.h"
|
||||||
#include "os-util.h"
|
#include "os-util.h"
|
||||||
#include "process-util.h"
|
#include "process-util.h"
|
||||||
@ -327,3 +328,38 @@ int import_mangle_os_tree(const char *path) {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool import_validate_local(const char *name, ImportFlags flags) {
|
||||||
|
|
||||||
|
/* By default we insist on a valid hostname for naming images. But optionally we relax that, in which
|
||||||
|
* case it can be any path name */
|
||||||
|
|
||||||
|
if (FLAGS_SET(flags, IMPORT_DIRECT))
|
||||||
|
return path_is_valid(name);
|
||||||
|
|
||||||
|
return hostname_is_valid(name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
|
||||||
|
log_notice("Transfer aborted.");
|
||||||
|
sd_event_exit(sd_event_source_get_event(s), EINTR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int import_allocate_event_with_signals(sd_event **ret) {
|
||||||
|
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
r = sd_event_default(&event);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to allocate event loop: %m");
|
||||||
|
|
||||||
|
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
|
||||||
|
(void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
|
||||||
|
(void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(event);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -3,11 +3,19 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "sd-event.h"
|
||||||
|
|
||||||
typedef enum ImportFlags {
|
typedef enum ImportFlags {
|
||||||
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_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_FLAGS_MASK = IMPORT_FORCE|IMPORT_READ_ONLY,
|
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,
|
||||||
} ImportFlags;
|
} ImportFlags;
|
||||||
|
|
||||||
int import_make_read_only_fd(int fd);
|
int import_make_read_only_fd(int fd);
|
||||||
@ -17,3 +25,7 @@ int import_fork_tar_c(const char *path, pid_t *ret);
|
|||||||
int import_fork_tar_x(const char *path, pid_t *ret);
|
int import_fork_tar_x(const char *path, pid_t *ret);
|
||||||
|
|
||||||
int import_mangle_os_tree(const char *path);
|
int import_mangle_os_tree(const char *path);
|
||||||
|
|
||||||
|
bool import_validate_local(const char *name, ImportFlags flags);
|
||||||
|
|
||||||
|
int import_allocate_event_with_signals(sd_event **ret);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "import-common.h"
|
#include "import-common.h"
|
||||||
#include "import-compress.h"
|
#include "import-compress.h"
|
||||||
#include "import-raw.h"
|
#include "import-raw.h"
|
||||||
|
#include "install-file.h"
|
||||||
#include "io-util.h"
|
#include "io-util.h"
|
||||||
#include "machine-pool.h"
|
#include "machine-pool.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
@ -52,23 +53,27 @@ struct RawImport {
|
|||||||
uint64_t written_compressed;
|
uint64_t written_compressed;
|
||||||
uint64_t written_uncompressed;
|
uint64_t written_uncompressed;
|
||||||
|
|
||||||
struct stat st;
|
struct stat input_stat;
|
||||||
|
struct stat output_stat;
|
||||||
|
|
||||||
unsigned last_percent;
|
unsigned last_percent;
|
||||||
RateLimit progress_ratelimit;
|
RateLimit progress_ratelimit;
|
||||||
|
|
||||||
|
uint64_t offset;
|
||||||
|
uint64_t size_max;
|
||||||
};
|
};
|
||||||
|
|
||||||
RawImport* raw_import_unref(RawImport *i) {
|
RawImport* raw_import_unref(RawImport *i) {
|
||||||
if (!i)
|
if (!i)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
sd_event_unref(i->event);
|
sd_event_source_unref(i->input_event_source);
|
||||||
|
|
||||||
unlink_and_free(i->temp_path);
|
unlink_and_free(i->temp_path);
|
||||||
|
|
||||||
import_compress_free(&i->compress);
|
import_compress_free(&i->compress);
|
||||||
|
|
||||||
sd_event_source_unref(i->input_event_source);
|
sd_event_unref(i->event);
|
||||||
|
|
||||||
safe_close(i->output_fd);
|
safe_close(i->output_fd);
|
||||||
|
|
||||||
@ -107,6 +112,8 @@ int raw_import_new(
|
|||||||
.last_percent = UINT_MAX,
|
.last_percent = UINT_MAX,
|
||||||
.image_root = TAKE_PTR(root),
|
.image_root = TAKE_PTR(root),
|
||||||
.progress_ratelimit = { 100 * USEC_PER_MSEC, 1 },
|
.progress_ratelimit = { 100 * USEC_PER_MSEC, 1 },
|
||||||
|
.offset = UINT64_MAX,
|
||||||
|
.size_max = UINT64_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (event)
|
if (event)
|
||||||
@ -118,7 +125,6 @@ int raw_import_new(
|
|||||||
}
|
}
|
||||||
|
|
||||||
*ret = TAKE_PTR(i);
|
*ret = TAKE_PTR(i);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,13 +133,13 @@ static void raw_import_report_progress(RawImport *i) {
|
|||||||
assert(i);
|
assert(i);
|
||||||
|
|
||||||
/* We have no size information, unless the source is a regular file */
|
/* We have no size information, unless the source is a regular file */
|
||||||
if (!S_ISREG(i->st.st_mode))
|
if (!S_ISREG(i->input_stat.st_mode))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (i->written_compressed >= (uint64_t) i->st.st_size)
|
if (i->written_compressed >= (uint64_t) i->input_stat.st_size)
|
||||||
percent = 100;
|
percent = 100;
|
||||||
else
|
else
|
||||||
percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
|
percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->input_stat.st_size);
|
||||||
|
|
||||||
if (percent == i->last_percent)
|
if (percent == i->last_percent)
|
||||||
return;
|
return;
|
||||||
@ -149,11 +155,18 @@ static void raw_import_report_progress(RawImport *i) {
|
|||||||
|
|
||||||
static int raw_import_maybe_convert_qcow2(RawImport *i) {
|
static int raw_import_maybe_convert_qcow2(RawImport *i) {
|
||||||
_cleanup_close_ int converted_fd = -1;
|
_cleanup_close_ int converted_fd = -1;
|
||||||
_cleanup_free_ char *t = NULL;
|
_cleanup_(unlink_and_freep) char *t = NULL;
|
||||||
|
_cleanup_free_ char *f = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
|
|
||||||
|
/* Do QCOW2 conversion if enabled and not in direct mode */
|
||||||
|
if ((i->flags & (IMPORT_CONVERT_QCOW2|IMPORT_DIRECT)) != IMPORT_CONVERT_QCOW2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
assert(i->final_path);
|
||||||
|
|
||||||
r = qcow2_detect(i->output_fd);
|
r = qcow2_detect(i->output_fd);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
|
return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
|
||||||
@ -161,26 +174,26 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* This is a QCOW2 image, let's convert it */
|
/* This is a QCOW2 image, let's convert it */
|
||||||
r = tempfn_random(i->final_path, NULL, &t);
|
r = tempfn_random(i->final_path, NULL, &f);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
|
converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
|
||||||
if (converted_fd < 0)
|
if (converted_fd < 0)
|
||||||
return log_error_errno(errno, "Failed to create %s: %m", t);
|
return log_error_errno(errno, "Failed to create %s: %m", f);
|
||||||
|
|
||||||
|
t = TAKE_PTR(f);
|
||||||
|
|
||||||
(void) import_set_nocow_and_log(converted_fd, t);
|
(void) import_set_nocow_and_log(converted_fd, t);
|
||||||
|
|
||||||
log_info("Unpacking QCOW2 file.");
|
log_info("Unpacking QCOW2 file.");
|
||||||
|
|
||||||
r = qcow2_convert(i->output_fd, converted_fd);
|
r = qcow2_convert(i->output_fd, converted_fd);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
(void) unlink(t);
|
|
||||||
return log_error_errno(r, "Failed to convert qcow2 image: %m");
|
return log_error_errno(r, "Failed to convert qcow2 image: %m");
|
||||||
}
|
|
||||||
|
|
||||||
(void) unlink(i->temp_path);
|
unlink_and_free(i->temp_path);
|
||||||
free_and_replace(i->temp_path, t);
|
i->temp_path = TAKE_PTR(t);
|
||||||
CLOSE_AND_REPLACE(i->output_fd, converted_fd);
|
CLOSE_AND_REPLACE(i->output_fd, converted_fd);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -191,10 +204,25 @@ static int raw_import_finish(RawImport *i) {
|
|||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
assert(i->output_fd >= 0);
|
assert(i->output_fd >= 0);
|
||||||
assert(i->temp_path);
|
|
||||||
assert(i->final_path);
|
|
||||||
|
|
||||||
/* In case this was a sparse file, make sure the file system is right */
|
/* Nothing of what is below applies to block devices */
|
||||||
|
if (S_ISBLK(i->output_stat.st_mode)) {
|
||||||
|
|
||||||
|
if (i->flags & IMPORT_SYNC) {
|
||||||
|
if (fsync(i->output_fd) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to synchronize block device: %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(S_ISREG(i->output_stat.st_mode));
|
||||||
|
|
||||||
|
/* If an offset is specified we only are supposed to affect part of an existing output file or block
|
||||||
|
* device, thus don't manipulate file properties in that case */
|
||||||
|
|
||||||
|
if (i->offset == UINT64_MAX) {
|
||||||
|
/* In case this was a sparse file, make sure the file size is right */
|
||||||
if (i->written_uncompressed > 0) {
|
if (i->written_uncompressed > 0) {
|
||||||
if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
|
if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
|
||||||
return log_error_errno(errno, "Failed to truncate file: %m");
|
return log_error_errno(errno, "Failed to truncate file: %m");
|
||||||
@ -204,21 +232,17 @@ static int raw_import_finish(RawImport *i) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (S_ISREG(i->st.st_mode)) {
|
if (S_ISREG(i->input_stat.st_mode)) {
|
||||||
(void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
|
(void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
|
||||||
(void) copy_xattr(i->input_fd, i->output_fd, 0);
|
(void) copy_xattr(i->input_fd, i->output_fd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i->flags & IMPORT_READ_ONLY) {
|
|
||||||
r = import_make_read_only_fd(i->output_fd);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i->flags & IMPORT_FORCE)
|
r = install_file(AT_FDCWD, i->temp_path ?: i->local,
|
||||||
(void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
AT_FDCWD, i->final_path,
|
||||||
|
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
|
||||||
r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
|
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
|
||||||
|
(i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to move image into place: %m");
|
return log_error_errno(r, "Failed to move image into place: %m");
|
||||||
|
|
||||||
@ -231,11 +255,27 @@ static int raw_import_open_disk(RawImport *i) {
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
|
assert(i->local);
|
||||||
assert(!i->final_path);
|
assert(!i->final_path);
|
||||||
assert(!i->temp_path);
|
assert(!i->temp_path);
|
||||||
assert(i->output_fd < 0);
|
assert(i->output_fd < 0);
|
||||||
|
|
||||||
|
if (i->flags & IMPORT_DIRECT) {
|
||||||
|
(void) mkdir_parents_label(i->local, 0700);
|
||||||
|
|
||||||
|
/* In direct mode we just open/create the local path and truncate it (like shell >
|
||||||
|
* redirection would do it) — except if an offset was passed, in which case we are supposed
|
||||||
|
* to operate on a section of the file only, in which case we apparently work on an some
|
||||||
|
* existing thing (i.e. are not the sole thing stored in the file), in which case we will
|
||||||
|
* neither truncate nor create. */
|
||||||
|
|
||||||
|
i->output_fd = open(i->local, O_RDWR|O_NOCTTY|O_CLOEXEC|(i->offset == UINT64_MAX ? O_TRUNC|O_CREAT : 0), 0664);
|
||||||
|
if (i->output_fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open destination '%s': %m", i->local);
|
||||||
|
|
||||||
|
if (i->offset == UINT64_MAX)
|
||||||
|
(void) import_set_nocow_and_log(i->output_fd, i->local);
|
||||||
|
} else {
|
||||||
i->final_path = strjoin(i->image_root, "/", i->local, ".raw");
|
i->final_path = strjoin(i->image_root, "/", i->local, ".raw");
|
||||||
if (!i->final_path)
|
if (!i->final_path)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
@ -248,9 +288,23 @@ static int raw_import_open_disk(RawImport *i) {
|
|||||||
|
|
||||||
i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
|
i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
|
||||||
if (i->output_fd < 0)
|
if (i->output_fd < 0)
|
||||||
return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
|
return log_error_errno(errno, "Failed to open destination '%s': %m", i->temp_path);
|
||||||
|
|
||||||
(void) import_set_nocow_and_log(i->output_fd, i->temp_path);
|
(void) import_set_nocow_and_log(i->output_fd, i->temp_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fstat(i->output_fd, &i->output_stat) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to stat() output file: %m");
|
||||||
|
|
||||||
|
if (!S_ISREG(i->output_stat.st_mode) && !S_ISBLK(i->output_stat.st_mode))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EBADFD),
|
||||||
|
"Target file is not a regular file or block device");
|
||||||
|
|
||||||
|
if (i->offset != UINT64_MAX) {
|
||||||
|
if (lseek(i->output_fd, i->offset, SEEK_SET) == (off_t) -1)
|
||||||
|
return log_error_errno(errno, "Failed to seek to offset: %m");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +319,7 @@ static int raw_import_try_reflink(RawImport *i) {
|
|||||||
if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED)
|
if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!S_ISREG(i->st.st_mode))
|
if (!S_ISREG(i->input_stat.st_mode) || !S_ISREG(i->output_stat.st_mode))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
p = lseek(i->input_fd, 0, SEEK_CUR);
|
p = lseek(i->input_fd, 0, SEEK_CUR);
|
||||||
@ -280,21 +334,57 @@ static int raw_import_try_reflink(RawImport *i) {
|
|||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
log_debug_errno(r, "Couldn't establish reflink, using copy: %m");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int raw_import_write(const void *p, size_t sz, void *userdata) {
|
static int raw_import_write(const void *p, size_t sz, void *userdata) {
|
||||||
RawImport *i = userdata;
|
RawImport *i = userdata;
|
||||||
|
bool too_much = false;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(i);
|
||||||
|
assert(p);
|
||||||
|
assert(sz > 0);
|
||||||
|
|
||||||
|
if (i->written_uncompressed >= UINT64_MAX - sz)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
|
||||||
|
|
||||||
|
if (i->size_max != UINT64_MAX) {
|
||||||
|
if (i->written_uncompressed >= i->size_max) {
|
||||||
|
too_much = true;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i->written_uncompressed + sz > i->size_max) {
|
||||||
|
too_much = true;
|
||||||
|
sz = i->size_max - i->written_uncompressed; /* since we have the data in memory
|
||||||
|
* already, we might as well write it to
|
||||||
|
* disk to the max */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate sparse file if we created/truncated the file */
|
||||||
|
if (S_ISREG(i->output_stat.st_mode)) {
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
|
|
||||||
n = sparse_write(i->output_fd, p, sz, 64);
|
n = sparse_write(i->output_fd, p, sz, 64);
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
return (int) n;
|
return log_error_errno((int) n, "Failed to write file: %m");
|
||||||
if ((size_t) n < sz)
|
if ((size_t) n < sz)
|
||||||
return -EIO;
|
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
|
||||||
|
} else {
|
||||||
|
r = loop_write(i->output_fd, p, sz, false);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to write file: %m");
|
||||||
|
}
|
||||||
|
|
||||||
i->written_uncompressed += sz;
|
i->written_uncompressed += sz;
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (too_much)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "File too large");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,15 +472,22 @@ static int raw_import_on_defer(sd_event_source *s, void *userdata) {
|
|||||||
return raw_import_process(i);
|
return raw_import_process(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags) {
|
int raw_import_start(
|
||||||
|
RawImport *i,
|
||||||
|
int fd,
|
||||||
|
const char *local,
|
||||||
|
uint64_t offset,
|
||||||
|
uint64_t size_max,
|
||||||
|
ImportFlags flags) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
assert(local);
|
assert(local);
|
||||||
assert(!(flags & ~IMPORT_FLAGS_MASK));
|
assert(!(flags & ~IMPORT_FLAGS_MASK_RAW));
|
||||||
|
assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT));
|
||||||
|
|
||||||
if (!hostname_is_valid(local, 0))
|
if (!import_validate_local(local, flags))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (i->input_fd >= 0)
|
if (i->input_fd >= 0)
|
||||||
@ -405,8 +502,10 @@ int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags)
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
i->flags = flags;
|
i->flags = flags;
|
||||||
|
i->offset = offset;
|
||||||
|
i->size_max = size_max;
|
||||||
|
|
||||||
if (fstat(fd, &i->st) < 0)
|
if (fstat(fd, &i->input_stat) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i);
|
r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i);
|
||||||
@ -422,5 +521,5 @@ int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags)
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
i->input_fd = fd;
|
i->input_fd = fd;
|
||||||
return r;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,4 @@ RawImport* raw_import_unref(RawImport *import);
|
|||||||
|
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref);
|
||||||
|
|
||||||
int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags);
|
int raw_import_start(RawImport *i, int fd, const char *local, uint64_t offset, uint64_t size_max, ImportFlags flags);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "import-common.h"
|
#include "import-common.h"
|
||||||
#include "import-compress.h"
|
#include "import-compress.h"
|
||||||
#include "import-tar.h"
|
#include "import-tar.h"
|
||||||
|
#include "install-file.h"
|
||||||
#include "io-util.h"
|
#include "io-util.h"
|
||||||
#include "machine-pool.h"
|
#include "machine-pool.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
@ -54,7 +55,7 @@ struct TarImport {
|
|||||||
uint64_t written_compressed;
|
uint64_t written_compressed;
|
||||||
uint64_t written_uncompressed;
|
uint64_t written_uncompressed;
|
||||||
|
|
||||||
struct stat st;
|
struct stat input_stat;
|
||||||
|
|
||||||
pid_t tar_pid;
|
pid_t tar_pid;
|
||||||
|
|
||||||
@ -136,13 +137,13 @@ static void tar_import_report_progress(TarImport *i) {
|
|||||||
assert(i);
|
assert(i);
|
||||||
|
|
||||||
/* We have no size information, unless the source is a regular file */
|
/* We have no size information, unless the source is a regular file */
|
||||||
if (!S_ISREG(i->st.st_mode))
|
if (!S_ISREG(i->input_stat.st_mode))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (i->written_compressed >= (uint64_t) i->st.st_size)
|
if (i->written_compressed >= (uint64_t) i->input_stat.st_size)
|
||||||
percent = 100;
|
percent = 100;
|
||||||
else
|
else
|
||||||
percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
|
percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->input_stat.st_size);
|
||||||
|
|
||||||
if (percent == i->last_percent)
|
if (percent == i->last_percent)
|
||||||
return;
|
return;
|
||||||
@ -157,12 +158,11 @@ static void tar_import_report_progress(TarImport *i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int tar_import_finish(TarImport *i) {
|
static int tar_import_finish(TarImport *i) {
|
||||||
|
const char *d;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
assert(i->tar_fd >= 0);
|
assert(i->tar_fd >= 0);
|
||||||
assert(i->temp_path);
|
|
||||||
assert(i->final_path);
|
|
||||||
|
|
||||||
i->tar_fd = safe_close(i->tar_fd);
|
i->tar_fd = safe_close(i->tar_fd);
|
||||||
|
|
||||||
@ -175,22 +175,20 @@ static int tar_import_finish(TarImport *i) {
|
|||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = import_mangle_os_tree(i->temp_path);
|
assert_se(d = i->temp_path ?: i->local);
|
||||||
|
|
||||||
|
r = import_mangle_os_tree(d);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (i->flags & IMPORT_READ_ONLY) {
|
r = install_file(
|
||||||
r = import_make_read_only(i->temp_path);
|
AT_FDCWD, d,
|
||||||
|
AT_FDCWD, i->final_path,
|
||||||
|
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
|
||||||
|
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
|
||||||
|
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return log_error_errno(r, "Failed to move '%s' into place: %m", i->final_path ?: i->local);
|
||||||
}
|
|
||||||
|
|
||||||
if (i->flags & IMPORT_FORCE)
|
|
||||||
(void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
|
||||||
|
|
||||||
r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to move image into place: %m");
|
|
||||||
|
|
||||||
i->temp_path = mfree(i->temp_path);
|
i->temp_path = mfree(i->temp_path);
|
||||||
|
|
||||||
@ -198,14 +196,19 @@ static int tar_import_finish(TarImport *i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int tar_import_fork_tar(TarImport *i) {
|
static int tar_import_fork_tar(TarImport *i) {
|
||||||
|
const char *d, *root;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
|
assert(i->local);
|
||||||
assert(!i->final_path);
|
assert(!i->final_path);
|
||||||
assert(!i->temp_path);
|
assert(!i->temp_path);
|
||||||
assert(i->tar_fd < 0);
|
assert(i->tar_fd < 0);
|
||||||
|
|
||||||
|
if (i->flags & IMPORT_DIRECT) {
|
||||||
|
d = i->local;
|
||||||
|
root = NULL;
|
||||||
|
} else {
|
||||||
i->final_path = path_join(i->image_root, i->local);
|
i->final_path = path_join(i->image_root, i->local);
|
||||||
if (!i->final_path)
|
if (!i->final_path)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
@ -214,17 +217,33 @@ static int tar_import_fork_tar(TarImport *i) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
(void) mkdir_parents_label(i->temp_path, 0700);
|
d = i->temp_path;
|
||||||
|
root = i->image_root;
|
||||||
r = btrfs_subvol_make_fallback(i->temp_path, 0755);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path);
|
|
||||||
if (r > 0) { /* actually btrfs subvol */
|
|
||||||
(void) import_assign_pool_quota_and_warn(i->image_root);
|
|
||||||
(void) import_assign_pool_quota_and_warn(i->temp_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
|
assert(d);
|
||||||
|
|
||||||
|
(void) mkdir_parents_label(d, 0700);
|
||||||
|
|
||||||
|
if (FLAGS_SET(i->flags, IMPORT_DIRECT|IMPORT_FORCE))
|
||||||
|
(void) rm_rf(d, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
||||||
|
|
||||||
|
if (i->flags & IMPORT_BTRFS_SUBVOL)
|
||||||
|
r = btrfs_subvol_make_fallback(d, 0755);
|
||||||
|
else
|
||||||
|
r = mkdir(d, 0755) < 0 ? -errno : 0;
|
||||||
|
if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
|
||||||
|
* because in that case our temporary path collided */
|
||||||
|
r = 0;
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to create directory/subvolume %s: %m", d);
|
||||||
|
if (r > 0 && (i->flags & IMPORT_BTRFS_QUOTA)) { /* actually btrfs subvol */
|
||||||
|
if (!(i->flags & IMPORT_DIRECT))
|
||||||
|
(void) import_assign_pool_quota_and_warn(root);
|
||||||
|
(void) import_assign_pool_quota_and_warn(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
i->tar_fd = import_fork_tar_x(d, &i->tar_pid);
|
||||||
if (i->tar_fd < 0)
|
if (i->tar_fd < 0)
|
||||||
return i->tar_fd;
|
return i->tar_fd;
|
||||||
|
|
||||||
@ -327,9 +346,9 @@ int tar_import_start(TarImport *i, int fd, const char *local, ImportFlags flags)
|
|||||||
assert(i);
|
assert(i);
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
assert(local);
|
assert(local);
|
||||||
assert(!(flags & ~IMPORT_FLAGS_MASK));
|
assert(!(flags & ~IMPORT_FLAGS_MASK_TAR));
|
||||||
|
|
||||||
if (!hostname_is_valid(local, 0))
|
if (!import_validate_local(local, flags))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (i->input_fd >= 0)
|
if (i->input_fd >= 0)
|
||||||
@ -345,7 +364,7 @@ int tar_import_start(TarImport *i, int fd, const char *local, ImportFlags flags)
|
|||||||
|
|
||||||
i->flags = flags;
|
i->flags = flags;
|
||||||
|
|
||||||
if (fstat(fd, &i->st) < 0)
|
if (fstat(fd, &i->input_stat) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
|
r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
|
||||||
@ -361,5 +380,5 @@ int tar_import_start(TarImport *i, int fd, const char *local, ImportFlags flags)
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
i->input_fd = fd;
|
i->input_fd = fd;
|
||||||
return r;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -8,26 +8,114 @@
|
|||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "discover-image.h"
|
#include "discover-image.h"
|
||||||
|
#include "env-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "hostname-util.h"
|
#include "hostname-util.h"
|
||||||
#include "import-raw.h"
|
#include "import-raw.h"
|
||||||
#include "import-tar.h"
|
#include "import-tar.h"
|
||||||
#include "import-util.h"
|
#include "import-util.h"
|
||||||
|
#include "io-util.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
|
#include "parse-argument.h"
|
||||||
|
#include "parse-util.h"
|
||||||
#include "signal-util.h"
|
#include "signal-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
#include "terminal-util.h"
|
||||||
#include "verbs.h"
|
#include "verbs.h"
|
||||||
|
|
||||||
static const char *arg_image_root = "/var/lib/machines";
|
static const char *arg_image_root = "/var/lib/machines";
|
||||||
static ImportFlags arg_import_flags = 0;
|
static ImportFlags arg_import_flags = IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
|
||||||
|
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
|
||||||
|
|
||||||
static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
|
static int normalize_local(const char *local, char **ret) {
|
||||||
log_notice("Transfer aborted.");
|
_cleanup_free_ char *ll = NULL;
|
||||||
sd_event_exit(sd_event_source_get_event(s), EINTR);
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (arg_import_flags & IMPORT_DIRECT) {
|
||||||
|
|
||||||
|
if (!local)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No local path specified.");
|
||||||
|
|
||||||
|
if (!path_is_absolute(local)) {
|
||||||
|
ll = path_join(arg_image_root, local);
|
||||||
|
if (!ll)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
local = ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path_is_valid(local))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"Local path name '%s' is not valid.", local);
|
||||||
|
} else {
|
||||||
|
if (local) {
|
||||||
|
if (!hostname_is_valid(local, 0))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"Local image name '%s' is not valid.",
|
||||||
|
local);
|
||||||
|
} else
|
||||||
|
local = "imported";
|
||||||
|
|
||||||
|
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
|
||||||
|
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
|
||||||
|
if (r < 0) {
|
||||||
|
if (r != -ENOENT)
|
||||||
|
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||||
|
} else
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
|
||||||
|
"Image '%s' already exists.",
|
||||||
|
local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ll) {
|
||||||
|
ll = strdup(local);
|
||||||
|
if (!ll)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(ll);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int open_source(const char *path, const char *local, int *ret_open_fd) {
|
||||||
|
_cleanup_close_ int open_fd = -1;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
assert(local);
|
||||||
|
assert(ret_open_fd);
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||||
|
if (open_fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open raw image to import: %m");
|
||||||
|
|
||||||
|
retval = open_fd;
|
||||||
|
|
||||||
|
if (arg_offset != UINT64_MAX)
|
||||||
|
log_info("Importing '%s', saving at offset %" PRIu64 " in '%s'.", path, arg_offset, local);
|
||||||
|
else
|
||||||
|
log_info("Importing '%s', saving as '%s'.", path, local);
|
||||||
|
} else {
|
||||||
|
_cleanup_free_ char *pretty = NULL;
|
||||||
|
|
||||||
|
retval = STDIN_FILENO;
|
||||||
|
|
||||||
|
(void) fd_get_path(STDIN_FILENO, &pretty);
|
||||||
|
|
||||||
|
if (arg_offset != UINT64_MAX)
|
||||||
|
log_info("Importing '%s', saving at offset %" PRIu64 " in '%s'.", strempty(pretty), arg_offset, local);
|
||||||
|
else
|
||||||
|
log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret_open_fd = TAKE_FD(open_fd);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static void on_tar_finished(TarImport *import, int error, void *userdata) {
|
static void on_tar_finished(TarImport *import, int error, void *userdata) {
|
||||||
sd_event *event = userdata;
|
sd_event *event = userdata;
|
||||||
assert(import);
|
assert(import);
|
||||||
@ -40,9 +128,9 @@ static void on_tar_finished(TarImport *import, int error, void *userdata) {
|
|||||||
|
|
||||||
static int import_tar(int argc, char *argv[], void *userdata) {
|
static int import_tar(int argc, char *argv[], void *userdata) {
|
||||||
_cleanup_(tar_import_unrefp) TarImport *import = NULL;
|
_cleanup_(tar_import_unrefp) TarImport *import = NULL;
|
||||||
|
_cleanup_free_ char *ll = NULL, *normalized = NULL;
|
||||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||||
const char *path = NULL, *local = NULL;
|
const char *path = NULL, *local = NULL;
|
||||||
_cleanup_free_ char *ll = NULL;
|
|
||||||
_cleanup_close_ int open_fd = -1;
|
_cleanup_close_ int open_fd = -1;
|
||||||
int r, fd;
|
int r, fd;
|
||||||
|
|
||||||
@ -51,64 +139,44 @@ static int import_tar(int argc, char *argv[], void *userdata) {
|
|||||||
|
|
||||||
if (argc >= 3)
|
if (argc >= 3)
|
||||||
local = empty_or_dash_to_null(argv[2]);
|
local = empty_or_dash_to_null(argv[2]);
|
||||||
else if (path)
|
else if (path) {
|
||||||
local = basename(path);
|
_cleanup_free_ char *l = NULL;
|
||||||
|
|
||||||
if (local) {
|
r = path_extract_filename(path, &l);
|
||||||
r = tar_strip_suffixes(local, &ll);
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
|
||||||
|
|
||||||
|
r = tar_strip_suffixes(l, &ll);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
local = ll;
|
local = ll;
|
||||||
|
|
||||||
if (!hostname_is_valid(local, 0))
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"Local image name '%s' is not valid.",
|
|
||||||
local);
|
|
||||||
|
|
||||||
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
|
|
||||||
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
|
|
||||||
if (r < 0) {
|
|
||||||
if (r != -ENOENT)
|
|
||||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
|
||||||
} else
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
|
|
||||||
"Image '%s' already exists.",
|
|
||||||
local);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
local = "imported";
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
|
||||||
if (open_fd < 0)
|
|
||||||
return log_error_errno(errno, "Failed to open tar image to import: %m");
|
|
||||||
|
|
||||||
fd = open_fd;
|
|
||||||
|
|
||||||
log_info("Importing '%s', saving as '%s'.", path, local);
|
|
||||||
} else {
|
|
||||||
_cleanup_free_ char *pretty = NULL;
|
|
||||||
|
|
||||||
fd = STDIN_FILENO;
|
|
||||||
|
|
||||||
(void) fd_get_path(fd, &pretty);
|
|
||||||
log_info("Importing '%s', saving as '%s'.", strna(pretty), local);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sd_event_default(&event);
|
r = normalize_local(local, &normalized);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to allocate event loop: %m");
|
return r;
|
||||||
|
|
||||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
|
fd = open_source(path, normalized, &open_fd);
|
||||||
(void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
|
if (fd < 0)
|
||||||
(void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
|
return r;
|
||||||
|
|
||||||
|
r = import_allocate_event_with_signals(&event);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
|
||||||
|
log_info("File system synchronization on completion is off.");
|
||||||
|
|
||||||
r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
|
r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to allocate importer: %m");
|
return log_error_errno(r, "Failed to allocate importer: %m");
|
||||||
|
|
||||||
r = tar_import_start(import, fd, local, arg_import_flags);
|
r = tar_import_start(
|
||||||
|
import,
|
||||||
|
fd,
|
||||||
|
normalized,
|
||||||
|
arg_import_flags & IMPORT_FLAGS_MASK_TAR);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to import image: %m");
|
return log_error_errno(r, "Failed to import image: %m");
|
||||||
|
|
||||||
@ -132,9 +200,9 @@ static void on_raw_finished(RawImport *import, int error, void *userdata) {
|
|||||||
|
|
||||||
static int import_raw(int argc, char *argv[], void *userdata) {
|
static int import_raw(int argc, char *argv[], void *userdata) {
|
||||||
_cleanup_(raw_import_unrefp) RawImport *import = NULL;
|
_cleanup_(raw_import_unrefp) RawImport *import = NULL;
|
||||||
|
_cleanup_free_ char *ll = NULL, *normalized = NULL;
|
||||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||||
const char *path = NULL, *local = NULL;
|
const char *path = NULL, *local = NULL;
|
||||||
_cleanup_free_ char *ll = NULL;
|
|
||||||
_cleanup_close_ int open_fd = -1;
|
_cleanup_close_ int open_fd = -1;
|
||||||
int r, fd;
|
int r, fd;
|
||||||
|
|
||||||
@ -143,64 +211,46 @@ static int import_raw(int argc, char *argv[], void *userdata) {
|
|||||||
|
|
||||||
if (argc >= 3)
|
if (argc >= 3)
|
||||||
local = empty_or_dash_to_null(argv[2]);
|
local = empty_or_dash_to_null(argv[2]);
|
||||||
else if (path)
|
else if (path) {
|
||||||
local = basename(path);
|
_cleanup_free_ char *l = NULL;
|
||||||
|
|
||||||
if (local) {
|
r = path_extract_filename(path, &l);
|
||||||
r = raw_strip_suffixes(local, &ll);
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
|
||||||
|
|
||||||
|
r = raw_strip_suffixes(l, &ll);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
local = ll;
|
local = ll;
|
||||||
|
|
||||||
if (!hostname_is_valid(local, 0))
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"Local image name '%s' is not valid.",
|
|
||||||
local);
|
|
||||||
|
|
||||||
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
|
|
||||||
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
|
|
||||||
if (r < 0) {
|
|
||||||
if (r != -ENOENT)
|
|
||||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
|
||||||
} else
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
|
|
||||||
"Image '%s' already exists.",
|
|
||||||
local);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
local = "imported";
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
|
||||||
if (open_fd < 0)
|
|
||||||
return log_error_errno(errno, "Failed to open raw image to import: %m");
|
|
||||||
|
|
||||||
fd = open_fd;
|
|
||||||
|
|
||||||
log_info("Importing '%s', saving as '%s'.", path, local);
|
|
||||||
} else {
|
|
||||||
_cleanup_free_ char *pretty = NULL;
|
|
||||||
|
|
||||||
fd = STDIN_FILENO;
|
|
||||||
|
|
||||||
(void) fd_get_path(fd, &pretty);
|
|
||||||
log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r = sd_event_default(&event);
|
r = normalize_local(local, &normalized);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to allocate event loop: %m");
|
return r;
|
||||||
|
|
||||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
|
fd = open_source(path, normalized, &open_fd);
|
||||||
(void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
|
if (fd < 0)
|
||||||
(void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
|
return fd;
|
||||||
|
|
||||||
|
r = import_allocate_event_with_signals(&event);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
|
||||||
|
log_info("File system synchronization on completion is off.");
|
||||||
|
|
||||||
r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
|
r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to allocate importer: %m");
|
return log_error_errno(r, "Failed to allocate importer: %m");
|
||||||
|
|
||||||
r = raw_import_start(import, fd, local, arg_import_flags);
|
r = raw_import_start(
|
||||||
|
import,
|
||||||
|
fd,
|
||||||
|
normalized,
|
||||||
|
arg_offset,
|
||||||
|
arg_size_max,
|
||||||
|
arg_import_flags & IMPORT_FLAGS_MASK_RAW);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to import image: %m");
|
return log_error_errno(r, "Failed to import image: %m");
|
||||||
|
|
||||||
@ -214,17 +264,32 @@ static int import_raw(int argc, char *argv[], void *userdata) {
|
|||||||
|
|
||||||
static int help(int argc, char *argv[], void *userdata) {
|
static int help(int argc, char *argv[], void *userdata) {
|
||||||
|
|
||||||
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
printf("%1$s [OPTIONS...] {COMMAND} ...\n"
|
||||||
"Import container or virtual machine images.\n\n"
|
"\n%4$sImport container or virtual machine images.%5$s\n"
|
||||||
|
"\n%2$sCommands:%3$s\n"
|
||||||
|
" tar FILE [NAME] Import a TAR image\n"
|
||||||
|
" raw FILE [NAME] Import a RAW image\n"
|
||||||
|
"\n%2$sOptions:%3$s\n"
|
||||||
" -h --help Show this help\n"
|
" -h --help Show this help\n"
|
||||||
" --version Show package version\n"
|
" --version Show package version\n"
|
||||||
" --force Force creation of image\n"
|
" --force Force creation of image\n"
|
||||||
" --image-root=PATH Image root directory\n"
|
" --image-root=PATH Image root directory\n"
|
||||||
" --read-only Create a read-only image\n\n"
|
" --read-only Create a read-only image\n"
|
||||||
"Commands:\n"
|
" --direct Import directly to specified file\n"
|
||||||
" tar FILE [NAME] Import a TAR image\n"
|
" --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
|
||||||
" raw FILE [NAME] Import a RAW image\n",
|
" instead of a directory\n"
|
||||||
program_invocation_short_name);
|
" --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
|
||||||
|
" subvolume\n"
|
||||||
|
" --convert-qcow2=BOOL Controls whether to convert QCOW2 images to\n"
|
||||||
|
" regular disk images\n"
|
||||||
|
" --sync=BOOL Controls whether to sync() before completing\n"
|
||||||
|
" --offset=BYTES Offset to seek to in destination\n"
|
||||||
|
" --size-max=BYTES Maximum number of bytes to write to destination\n",
|
||||||
|
program_invocation_short_name,
|
||||||
|
ansi_underline(),
|
||||||
|
ansi_normal(),
|
||||||
|
ansi_highlight(),
|
||||||
|
ansi_normal());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -236,6 +301,13 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_FORCE,
|
ARG_FORCE,
|
||||||
ARG_IMAGE_ROOT,
|
ARG_IMAGE_ROOT,
|
||||||
ARG_READ_ONLY,
|
ARG_READ_ONLY,
|
||||||
|
ARG_DIRECT,
|
||||||
|
ARG_BTRFS_SUBVOL,
|
||||||
|
ARG_BTRFS_QUOTA,
|
||||||
|
ARG_CONVERT_QCOW2,
|
||||||
|
ARG_SYNC,
|
||||||
|
ARG_OFFSET,
|
||||||
|
ARG_SIZE_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
@ -244,10 +316,17 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "force", no_argument, NULL, ARG_FORCE },
|
{ "force", no_argument, NULL, ARG_FORCE },
|
||||||
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
|
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
|
||||||
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
|
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
|
||||||
|
{ "direct", no_argument, NULL, ARG_DIRECT },
|
||||||
|
{ "btrfs-subvol", required_argument, NULL, ARG_BTRFS_SUBVOL },
|
||||||
|
{ "btrfs-quota", required_argument, NULL, ARG_BTRFS_QUOTA },
|
||||||
|
{ "convert-qcow2", required_argument, NULL, ARG_CONVERT_QCOW2 },
|
||||||
|
{ "sync", required_argument, NULL, ARG_SYNC },
|
||||||
|
{ "offset", required_argument, NULL, ARG_OFFSET },
|
||||||
|
{ "size-max", required_argument, NULL, ARG_SIZE_MAX },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int r, c;
|
||||||
|
|
||||||
assert(argc >= 0);
|
assert(argc >= 0);
|
||||||
assert(argv);
|
assert(argv);
|
||||||
@ -274,6 +353,68 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
arg_import_flags |= IMPORT_READ_ONLY;
|
arg_import_flags |= IMPORT_READ_ONLY;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_DIRECT:
|
||||||
|
arg_import_flags |= IMPORT_DIRECT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_BTRFS_SUBVOL:
|
||||||
|
r = parse_boolean_argument("--btrfs-subvol=", optarg, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_BTRFS_QUOTA:
|
||||||
|
r = parse_boolean_argument("--btrfs-quota=", optarg, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_CONVERT_QCOW2:
|
||||||
|
r = parse_boolean_argument("--convert-qcow2=", optarg, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
SET_FLAG(arg_import_flags, IMPORT_CONVERT_QCOW2, r);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_SYNC:
|
||||||
|
r = parse_boolean_argument("--sync=", optarg, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_OFFSET: {
|
||||||
|
uint64_t u;
|
||||||
|
|
||||||
|
r = safe_atou64(optarg, &u);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse --offset= argument: %s", optarg);
|
||||||
|
if (!FILE_SIZE_VALID(u))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", optarg);
|
||||||
|
|
||||||
|
arg_offset = u;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ARG_SIZE_MAX: {
|
||||||
|
uint64_t u;
|
||||||
|
|
||||||
|
r = parse_size(optarg, 1024, &u);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse --size-max= argument: %s", optarg);
|
||||||
|
if (!FILE_SIZE_VALID(u))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", optarg);
|
||||||
|
|
||||||
|
arg_size_max = u;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -281,6 +422,15 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make sure offset+size is still in the valid range if both set */
|
||||||
|
if (arg_offset != UINT64_MAX && arg_size_max != UINT64_MAX &&
|
||||||
|
((arg_size_max > (UINT64_MAX - arg_offset)) ||
|
||||||
|
!FILE_SIZE_VALID(arg_offset + arg_size_max)))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset und maximum size out of range.");
|
||||||
|
|
||||||
|
if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,6 +445,31 @@ static int import_main(int argc, char *argv[]) {
|
|||||||
return dispatch_verb(argc, argv, verbs, NULL);
|
return dispatch_verb(argc, argv, verbs, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_env(void) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Let's make these relatively low-level settings also controllable via env vars. User can then set
|
||||||
|
* them to systemd-import if they like to tweak behaviour */
|
||||||
|
|
||||||
|
r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
|
||||||
|
if (r >= 0)
|
||||||
|
SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
|
||||||
|
else if (r != -ENXIO)
|
||||||
|
log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
|
||||||
|
|
||||||
|
r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
|
||||||
|
if (r >= 0)
|
||||||
|
SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
|
||||||
|
else if (r != -ENXIO)
|
||||||
|
log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
|
||||||
|
|
||||||
|
r = getenv_bool("SYSTEMD_IMPORT_SYNC");
|
||||||
|
if (r >= 0)
|
||||||
|
SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
|
||||||
|
else if (r != -ENXIO)
|
||||||
|
log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
|
||||||
|
}
|
||||||
|
|
||||||
static int run(int argc, char *argv[]) {
|
static int run(int argc, char *argv[]) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -302,9 +477,11 @@ static int run(int argc, char *argv[]) {
|
|||||||
log_parse_environment();
|
log_parse_environment();
|
||||||
log_open();
|
log_open();
|
||||||
|
|
||||||
|
parse_env();
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
return 0;
|
return r;
|
||||||
|
|
||||||
(void) ignore_signals(SIGPIPE);
|
(void) ignore_signals(SIGPIPE);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user