From 0f3be6ca4dbbac8350cd8f10a8968d31f7bc13b6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Nov 2016 18:38:06 +0100 Subject: 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; -- cgit v1.2.3-54-g00ecf From 546dbec532b49ddb649d364c7b9e9e91d7b377f0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Nov 2016 23:31:01 +0100 Subject: shared: make sure image_path_lock() return parameters are always initialized on success We forgot to initialize the "global" return parameter in one case. Fix that. --- src/shared/machine-image.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 6414ba5246..af102e3096 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -27,6 +27,7 @@ #include #include #include + #include "alloc-util.h" #include "btrfs-util.h" #include "chattr-util.h" @@ -37,8 +38,8 @@ #include "hashmap.h" #include "lockfile-util.h" #include "log.h" -#include "macro.h" #include "machine-image.h" +#include "macro.h" #include "mkdir.h" #include "path-util.h" #include "rm-rf.h" @@ -746,7 +747,8 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile release_lock_file(&t); return r; } - } + } else + *global = (LockFile) LOCK_FILE_INIT; *local = t; return 0; -- cgit v1.2.3-54-g00ecf From b6e953f24cadd03d461b4b886a1b6a8acdd2bb2d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Nov 2016 23:34:39 +0100 Subject: nspawn: add ability to run nspawn without container locks applied This adds a new undocumented env var $SYSTEMD_NSPAWN_LOCK. When set to "0", nspawn will not attempt to lock the image. Fixes: #4037 --- src/shared/machine-image.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index af102e3096..baf8713242 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -33,6 +33,7 @@ #include "chattr-util.h" #include "copy.h" #include "dirent-util.h" +#include "env-util.h" #include "fd-util.h" #include "fs-util.h" #include "hashmap.h" @@ -724,12 +725,17 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile * uses the device/inode number. This has the benefit that we * can even lock a tree that is a mount point, correctly. */ - if (path_equal(path, "/")) - return -EBUSY; - if (!path_is_absolute(path)) return -EINVAL; + if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) { + *local = *global = (LockFile) LOCK_FILE_INIT; + return 0; + } + + if (path_equal(path, "/")) + return -EBUSY; + if (stat(path, &st) >= 0) { if (asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) return -ENOMEM; @@ -784,6 +790,11 @@ int image_name_lock(const char *name, int operation, LockFile *ret) { if (!image_name_is_valid(name)) return -EINVAL; + if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) { + *ret = (LockFile) LOCK_FILE_INIT; + return 0; + } + if (streq(name, ".host")) return -EBUSY; -- cgit v1.2.3-54-g00ecf From 6a0f896b97ab4f744bf4404011f8ac9d2f978443 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Nov 2016 23:47:09 +0100 Subject: nspawn: try to wait for the container PID 1 to exit, before we exit Let's make the shutdown logic synchronous, so that there's a better chance to detach the loopback device after use. --- src/nspawn/nspawn.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 0ca0b2f4c8..8e4b94ef82 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -4349,8 +4349,13 @@ finish: kill(pid, SIGKILL); /* Try to flush whatever is still queued in the pty */ - if (master >= 0) + if (master >= 0) { (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, false); + master = safe_close(master); + } + + if (pid > 0) + (void) wait_for_terminate(pid, NULL); loop_remove(loop_nr, &image_fd); -- cgit v1.2.3-54-g00ecf From c67b008273478dd54a57c0950a0c69b2b544c85b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 19 Nov 2016 00:00:16 +0100 Subject: nspawn: remove temporary root directory on exit When mountint a loopback image, we need a temporary root directory we can mount stuff to. Make sure to actually remove it when exiting, so that we don't leave stuff around in /tmp unnecessarily. See: #4664 --- src/nspawn/nspawn.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 8e4b94ef82..29755a9e36 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3789,7 +3789,6 @@ static int run(int master, l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0); if (l < 0) return log_error_errno(errno, "Failed to read UID shift: %m"); - if (l != sizeof arg_uid_shift) { log_error("Short read while reading UID shift."); return -EIO; @@ -4023,7 +4022,7 @@ static int run(int master, terminate_machine(*pid); /* Normally redundant, but better safe than sorry */ - kill(*pid, SIGKILL); + (void) kill(*pid, SIGKILL); r = wait_for_container(*pid, &container_status); *pid = 0; @@ -4075,7 +4074,8 @@ int main(int argc, char *argv[]) { 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; - bool interactive, veth_created = false; + bool interactive, veth_created = false, remove_tmprootdir = false; + char tmprootdir[] = "/tmp/nspawn-root-XXXXXX"; log_parse_environment(); log_open(); @@ -4208,8 +4208,6 @@ int main(int argc, char *argv[]) { } } else { - char template[] = "/tmp/nspawn-root-XXXXXX"; - assert(arg_image); assert(!arg_template); @@ -4251,12 +4249,14 @@ int main(int argc, char *argv[]) { } } - if (!mkdtemp(template)) { + if (!mkdtemp(tmprootdir)) { r = log_error_errno(errno, "Failed to create temporary directory: %m"); goto finish; } - arg_directory = strdup(template); + remove_tmprootdir = true; + + arg_directory = strdup(tmprootdir); if (!arg_directory) { r = log_oom(); goto finish; @@ -4346,7 +4346,7 @@ finish: "STOPPING=1\nSTATUS=Terminating..."); if (pid > 0) - kill(pid, SIGKILL); + (void) kill(pid, SIGKILL); /* Try to flush whatever is still queued in the pty */ if (master >= 0) { @@ -4372,6 +4372,11 @@ finish: log_warning_errno(errno, "Can't remove image file '%s', ignoring: %m", arg_image); } + if (remove_tmprootdir) { + if (rmdir(tmprootdir) < 0) + log_debug_errno(errno, "Can't remove temporary root directory '%s', ignoring: %m", tmprootdir); + } + if (arg_machine) { const char *p; -- cgit v1.2.3-54-g00ecf From 17cbb288faa4a5c900d54a5de53f804116d897df Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 21 Nov 2016 20:02:43 +0100 Subject: nspawn: add fallback top normal copy/reflink when we cannot btrfs snapshot Given that other file systems (notably: xfs) support reflinks these days, let's extend the file system snapshotting logic to fall back to plan copies or reflinks when full btrfs subvolume snapshots are not available. This essentially makes "systemd-nspawn --ephemeral" and "systemd-nspawn --template=" available on non-btrfs subvolumes. Of course, both operations will still be slower on non-btrfs than on btrfs (simply because reflinking each file individually in a directory tree is still slower than doing this in one step for a whole subvolume), but it's probably good enough for many cases, and we should provide the users with the tools, they have to figure out what's good for them. Note that "machinectl clone" already had a fallback like this in place, this patch generalizes this, and adds similar support to our other cases. --- man/machinectl.xml | 6 +++--- man/systemd-nspawn.xml | 36 ++++++++++++------------------------ src/basic/btrfs-util.c | 39 ++++++++++++++++++++++++++++++--------- src/basic/btrfs-util.h | 4 +++- src/import/pull-common.c | 12 ++++++------ src/nspawn/nspawn.c | 25 ++++++++++++++++++------- src/shared/machine-image.c | 16 ++++++++-------- 7 files changed, 80 insertions(+), 58 deletions(-) diff --git a/man/machinectl.xml b/man/machinectl.xml index 5a6ec294d2..81192417d8 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -599,8 +599,8 @@ Clones a container or VM image. The arguments specify the name of the image to clone and the name of the newly cloned image. Note that plain directory container images are cloned into btrfs subvolume images with this command, if the underlying file system supports this. Note that cloning a container or VM - image is optimized for btrfs file systems, and might not be efficient on others, due to file system - limitations. + image is optimized for file systems that support copy-on-write, and might not be efficient on others, due to + file system limitations. Note that this command leaves host name, machine ID and all other settings that could identify the instance @@ -910,7 +910,7 @@ /var/lib/machines/ to make them available for control with machinectl. - Note that many image operations are only supported, + Note that some image operations are only supported, efficient or atomic on btrfs file systems. Due to this, if the pull-tar, pull-raw, import-tar, import-raw and diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index c29542236d..dbbf9890c8 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -181,25 +181,15 @@ - Directory or btrfs - subvolume to use as template for the container's root - directory. If this is specified and the container's root - directory (as configured by ) - does not yet exist it is created as btrfs - subvolume and populated from this template tree. Ideally, the - specified template path refers to the root of a - btrfs subvolume, in which case a simple - copy-on-write snapshot is taken, and populating the root - directory is instant. If the specified template path does not - refer to the root of a btrfs subvolume (or - not even to a btrfs file system at all), - the tree is copied, which can be substantially more - time-consuming. Note that if this option is used the - container's root directory (in contrast to the template - directory!) must be located on a btrfs file - system, so that the btrfs subvolume may be - created. May not be specified together with - or + Directory or btrfs subvolume to use as template for the container's root + directory. If this is specified and the container's root directory (as configured by + ) does not yet exist it is created as btrfs snapshot (if + supported) or plain directory (otherwise) and populated from this template tree. Ideally, the specified + template path refers to the root of a btrfs subvolume, in which case a simple copy-on-write + snapshot is taken, and populating the root directory is instant. If the specified template path does not refer + to the root of a btrfs subvolume (or not even to a btrfs file system at + all), the tree is copied (though possibly in a copy-on-write scheme — if the file system supports that), which + can be substantially more time-consuming. May not be specified together with or . Note that this switch leaves host name, machine ID and @@ -1052,14 +1042,12 @@ - Boot into an ephemeral <literal>btrfs</literal> snapshot of the host system + Boot into an ephemeral snapshot of the host system # systemd-nspawn -D / -xb - This runs a copy of the host system in a - btrfs snapshot which is removed immediately - when the container exits. All file system changes made during - runtime will be lost on shutdown, hence. + This runs a copy of the host system in a snapshot which is removed immediately when the container + exits. All file system changes made during runtime will be lost on shutdown, hence. diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 656bb13719..5f9e21dcba 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include "alloc-util.h" #include "btrfs-ctree.h" #include "btrfs-util.h" +#include "chattr-util.h" #include "copy.h" #include "fd-util.h" #include "fileio.h" @@ -45,6 +47,7 @@ #include "macro.h" #include "missing.h" #include "path-util.h" +#include "rm-rf.h" #include "selinux-util.h" #include "smack-util.h" #include "sparse-endian.h" @@ -1718,28 +1721,46 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag if (r < 0) return r; if (r == 0) { + bool plain_directory = false; + + /* If the source isn't a proper subvolume, fail unless fallback is requested */ if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY)) return -EISDIR; r = btrfs_subvol_make(new_path); - if (r < 0) + if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) { + /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */ + if (mkdir(new_path, 0755) < 0) + return r; + + plain_directory = true; + } else if (r < 0) return r; r = copy_directory_fd(old_fd, new_path, true); - if (r < 0) { - (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA); - return r; - } + if (r < 0) + goto fallback_fail; if (flags & BTRFS_SNAPSHOT_READ_ONLY) { - r = btrfs_subvol_set_read_only(new_path, true); - if (r < 0) { - (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA); - return r; + + if (plain_directory) { + /* Plain directories have no recursive read-only flag, but something pretty close to + * it: the IMMUTABLE bit. Let's use this here, if this is requested. */ + + if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE) + (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL); + } else { + r = btrfs_subvol_set_read_only(new_path, true); + if (r < 0) + goto fallback_fail; } } return 0; + + fallback_fail: + (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + return r; } r = extract_subvolume_name(new_path, &subvolume); diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h index 1d852d502c..04a2e1274b 100644 --- a/src/basic/btrfs-util.h +++ b/src/basic/btrfs-util.h @@ -45,10 +45,12 @@ typedef struct BtrfsQuotaInfo { } BtrfsQuotaInfo; typedef enum BtrfsSnapshotFlags { - BTRFS_SNAPSHOT_FALLBACK_COPY = 1, + BTRFS_SNAPSHOT_FALLBACK_COPY = 1, /* If the source isn't a subvolume, reflink everything */ BTRFS_SNAPSHOT_READ_ONLY = 2, BTRFS_SNAPSHOT_RECURSIVE = 4, BTRFS_SNAPSHOT_QUOTA = 8, + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 16, /* If the destination doesn't support subvolumes, reflink/copy instead */ + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 32, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */ } BtrfsSnapshotFlags; typedef enum BtrfsRemoveFlags { diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 2ae2a4174c..5ddc0c56f4 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -144,12 +144,12 @@ int pull_make_local_copy(const char *final, const char *image_root, const char * if (force_local) (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA); - if (r == -ENOTTY) { - r = copy_tree(final, p, false); - if (r < 0) - return log_error_errno(r, "Failed to copy image: %m"); - } else if (r < 0) + r = btrfs_subvol_snapshot(final, p, + BTRFS_SNAPSHOT_QUOTA| + BTRFS_SNAPSHOT_FALLBACK_COPY| + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY| + BTRFS_SNAPSHOT_RECURSIVE); + if (r < 0) return log_error_errno(r, "Failed to create local image: %m"); log_info("Created new local image '%s'.", local); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 29755a9e36..2770770cd0 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -4070,7 +4070,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, remove_image = false; + bool secondary = false, remove_directory = 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; @@ -4152,7 +4152,12 @@ int main(int argc, char *argv[]) { goto finish; } - r = btrfs_subvol_snapshot(arg_directory, np, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + r = btrfs_subvol_snapshot(arg_directory, np, + (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA); if (r < 0) { log_error_errno(r, "Failed to create snapshot %s from %s: %m", np, arg_directory); goto finish; @@ -4162,7 +4167,7 @@ int main(int argc, char *argv[]) { arg_directory = np; np = NULL; - remove_subvol = true; + remove_directory = true; } else { r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); @@ -4176,7 +4181,13 @@ int main(int argc, char *argv[]) { } if (arg_template) { - r = btrfs_subvol_snapshot(arg_template, arg_directory, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + r = btrfs_subvol_snapshot(arg_template, arg_directory, + (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA); if (r == -EEXIST) { if (!arg_quiet) log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template); @@ -4359,12 +4370,12 @@ finish: loop_remove(loop_nr, &image_fd); - if (remove_subvol && arg_directory) { + if (remove_directory && arg_directory) { int k; - k = btrfs_subvol_remove(arg_directory, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + k = rm_rf(arg_directory, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); if (k < 0) - log_warning_errno(k, "Cannot remove subvolume '%s', ignoring: %m", arg_directory); + log_warning_errno(k, "Cannot remove '%s', ignoring: %m", arg_directory); } if (remove_image && arg_image) { diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index baf8713242..712aff65b9 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -609,14 +609,14 @@ int image_clone(Image *i, const char *new_name, bool read_only) { new_path = strjoina("/var/lib/machines/", new_name); - r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); - if (r == -EOPNOTSUPP) { - /* No btrfs snapshots supported, create a normal directory then. */ - - r = copy_directory(i->path, new_path, false); - if (r >= 0) - (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); - } else if (r >= 0) + r = btrfs_subvol_snapshot(i->path, new_path, + (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA); + if (r >= 0) /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ (void) btrfs_subvol_auto_qgroup(new_path, 0, true); -- cgit v1.2.3-54-g00ecf From acbbf69b718260755a5dff60dd68ba239ac0d61b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Nov 2016 13:35:24 +0100 Subject: nspawn: don't require chown() if userns is not on Fixes: #4711 --- src/nspawn/nspawn-mount.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 95bb3c09b0..91cb0861d3 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -298,7 +298,7 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) { MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL); } -static int mkdir_userns(const char *path, mode_t mode, bool in_userns, uid_t uid_shift) { +static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) { int r; assert(path); @@ -307,16 +307,20 @@ static int mkdir_userns(const char *path, mode_t mode, bool in_userns, uid_t uid if (r < 0 && errno != EEXIST) return -errno; - if (!in_userns) { - r = lchown(path, uid_shift, uid_shift); - if (r < 0) - return -errno; - } + if ((mask & MOUNT_USE_USERNS) == 0) + return 0; + + if (mask & MOUNT_IN_USERNS) + return 0; + + r = lchown(path, uid_shift, uid_shift); + if (r < 0) + return -errno; return 0; } -static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, bool in_userns, uid_t uid_shift) { +static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) { const char *p, *e; int r; @@ -343,12 +347,12 @@ static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, boo if (prefix && path_startswith(prefix, t)) continue; - r = mkdir_userns(t, mode, in_userns, uid_shift); + r = mkdir_userns(t, mode, mask, uid_shift); if (r < 0) return r; } - return mkdir_userns(path, mode, in_userns, uid_shift); + return mkdir_userns(path, mode, mask, uid_shift); } int mount_all(const char *dest, @@ -422,7 +426,7 @@ int mount_all(const char *dest, if (mount_table[k].what && r > 0) continue; - r = mkdir_userns_p(dest, where, 0755, in_userns, uid_shift); + r = mkdir_userns_p(dest, where, 0755, mount_settings, uid_shift); if (r < 0 && r != -EEXIST) { if (fatal) return log_error_errno(r, "Failed to create directory %s: %m", where); -- cgit v1.2.3-54-g00ecf