From 0f3be6ca4dbbac8350cd8f10a8968d31f7bc13b6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Nov 2016 18:38:06 +0100 Subject: [PATCH] nspawn: support ephemeral boots from images Previously --ephemeral was only supported with container trees in btrfs subvolumes (i.e. in combination with --directory=). This adds support for --ephemeral in conjunction with disk images (i.e. --image=) too. As side effect this fixes that --ephemeral was accepted but ignored when using -M on a container that turned out to be an image. Fixes: #4664 --- man/systemd-nspawn.xml | 20 +++++------- src/basic/missing.h | 4 +++ src/nspawn/nspawn.c | 72 ++++++++++++++++++++++++++++++------------ 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index f153034296..c29542236d 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -211,13 +211,8 @@ - If specified, the container is run with a - temporary btrfs snapshot of its root - directory (as configured with ), - that is removed immediately when the container terminates. - This option is only supported if the root file system is - btrfs. May not be specified together with - or + If specified, the container is run with a temporary snapshot of its file system that is removed + immediately when the container terminates. May not be specified together with . Note that this switch leaves host name, machine ID and all other settings that could identify the instance @@ -252,11 +247,12 @@ Partitions Specification. - Any other partitions, such as foreign partitions, swap - partitions or EFI system partitions are not mounted. May not - be specified together with , - or - . + On GPT images, if an EFI System Partition (ESP) is discovered, it is automatically mounted to + /efi (or /boot as fallback) in case a directory by this name exists + and is empty. + + Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified + together with , . diff --git a/src/basic/missing.h b/src/basic/missing.h index a5ae5d9e79..8833617dc6 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -143,6 +143,10 @@ #define GRND_RANDOM 0x0002 #endif +#ifndef FS_NOCOW_FL +#define FS_NOCOW_FL 0x00800000 +#endif + #ifndef BTRFS_IOCTL_MAGIC #define BTRFS_IOCTL_MAGIC 0x94 #endif diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index a6adbbe879..0ca0b2f4c8 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1143,11 +1143,6 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_ephemeral && arg_image) { - log_error("--ephemeral and --image= may not be combined."); - return -EINVAL; - } - if (arg_ephemeral && !IN_SET(arg_link_journal, LINK_NO, LINK_AUTO)) { log_error("--ephemeral and --link-journal= may not be combined."); return -EINVAL; @@ -2605,7 +2600,7 @@ static int determine_names(void) { r = image_find(arg_machine, &i); if (r < 0) return log_error_errno(r, "Failed to find image for machine '%s': %m", arg_machine); - else if (r == 0) { + if (r == 0) { log_error("No image for machine '%s': %m", arg_machine); return -ENOENT; } @@ -2615,14 +2610,14 @@ static int determine_names(void) { else r = free_and_strdup(&arg_directory, i->path); if (r < 0) - return log_error_errno(r, "Invalid image directory: %m"); + return log_oom(); if (!arg_ephemeral) arg_read_only = arg_read_only || i->read_only; } else arg_directory = get_current_dir_name(); - if (!arg_directory && !arg_machine) { + if (!arg_directory && !arg_image) { log_error("Failed to determine path, please use -D or -i."); return -EINVAL; } @@ -2633,7 +2628,6 @@ static int determine_names(void) { arg_machine = gethostname_malloc(); else arg_machine = strdup(basename(arg_image ?: arg_directory)); - if (!arg_machine) return log_oom(); @@ -4077,7 +4071,7 @@ int main(int argc, char *argv[]) { _cleanup_fdset_free_ FDSet *fds = NULL; int r, n_fd_passed, loop_nr = -1, ret = EXIT_SUCCESS; char veth_name[IFNAMSIZ] = ""; - bool secondary = false, remove_subvol = false; + bool secondary = false, remove_subvol = false, remove_image = false; pid_t pid = 0; union in_addr_union exposed = {}; _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; @@ -4148,7 +4142,7 @@ int main(int argc, char *argv[]) { else r = tempfn_random(arg_directory, "machine.", &np); if (r < 0) { - log_error_errno(r, "Failed to generate name for snapshot: %m"); + log_error_errno(r, "Failed to generate name for directory snapshot: %m"); goto finish; } @@ -4219,19 +4213,46 @@ int main(int argc, char *argv[]) { assert(arg_image); assert(!arg_template); - r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); - if (r == -EBUSY) { - r = log_error_errno(r, "Disk image %s is currently busy.", arg_image); - goto finish; - } - if (r < 0) { - r = log_error_errno(r, "Failed to create image lock: %m"); - goto finish; + if (arg_ephemeral) { + _cleanup_free_ char *np = NULL; + + r = tempfn_random(arg_image, "machine.", &np); + if (r < 0) { + log_error_errno(r, "Failed to generate name for image snapshot: %m"); + goto finish; + } + + r = image_path_lock(np, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); + if (r < 0) { + r = log_error_errno(r, "Failed to create image lock: %m"); + goto finish; + } + + r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL); + if (r < 0) { + r = log_error_errno(r, "Failed to copy image file: %m"); + goto finish; + } + + free(arg_image); + arg_image = np; + np = NULL; + + remove_image = true; + } else { + r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); + if (r == -EBUSY) { + r = log_error_errno(r, "Disk image %s is currently busy.", arg_image); + goto finish; + } + if (r < 0) { + r = log_error_errno(r, "Failed to create image lock: %m"); + goto finish; + } } if (!mkdtemp(template)) { - log_error_errno(errno, "Failed to create temporary directory: %m"); - r = -errno; + r = log_error_errno(errno, "Failed to create temporary directory: %m"); goto finish; } @@ -4255,6 +4276,10 @@ int main(int argc, char *argv[]) { &secondary); if (r < 0) goto finish; + + /* Now that we mounted the image, let's try to remove it again, if it is ephemeral */ + if (remove_image && unlink(arg_image) >= 0) + remove_image = false; } r = custom_mounts_prepare(); @@ -4337,6 +4362,11 @@ finish: log_warning_errno(k, "Cannot remove subvolume '%s', ignoring: %m", arg_directory); } + if (remove_image && arg_image) { + if (unlink(arg_image) < 0) + log_warning_errno(errno, "Can't remove image file '%s', ignoring: %m", arg_image); + } + if (arg_machine) { const char *p;