From c4e34a612c81266773cf8358cb38a43d2e43474e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 12 Dec 2014 16:59:15 +0100 Subject: [PATCH] nspawn: allow spawning ephemeral nspawn containers based on the root file system of the OS This works now: # systemd-nspawn -xb -D / -M foobar Which boots up an ephemeral container, based on the host's root file system. Or in other words: you can now run the very same host OS you booted your system with also in a container, on top of it, without having it interfere. Great for testing whether the init system you are hacking on still boots without reboot the system! --- src/nspawn/nspawn.c | 20 ++++++++++++++--- src/shared/util.c | 52 +++++++++++++++++++++++++++++++++++++++++++-- src/shared/util.h | 1 + 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index f6f20abda..651a45126 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2942,8 +2942,8 @@ int main(int argc, char *argv[]) { if (arg_directory) { assert(!arg_image); - if (path_equal(arg_directory, "/")) { - log_error("Spawning container on root directory not supported."); + if (path_equal(arg_directory, "/") && !arg_ephemeral) { + log_error("Spawning container on root directory is not supported. Consider using --ephemeral."); r = -EINVAL; goto finish; } @@ -2964,7 +2964,21 @@ int main(int argc, char *argv[]) { } else if (arg_ephemeral) { char *np; - r = tempfn_random(arg_directory, &np); + /* If the specified path is a mount point we + * generate the new snapshot immediately + * inside it under a random name. However if + * the specified is not a mount point we + * create the new snapshot in the parent + * directory, just next to it. */ + r = path_is_mount_point(arg_directory, false); + if (r < 0) { + log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory); + goto finish; + } + if (r > 0) + r = tempfn_random_child(arg_directory, &np); + else + r = tempfn_random(arg_directory, &np); if (r < 0) { log_error_errno(r, "Failed to generate name for snapshot: %m"); goto finish; diff --git a/src/shared/util.c b/src/shared/util.c index 254b5637a..ee95a4b6f 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -6977,6 +6977,14 @@ int tempfn_xxxxxx(const char *p, char **ret) { assert(p); assert(ret); + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.waldoXXXXXX + */ + fn = basename(p); if (!filename_is_valid(fn)) return -EINVAL; @@ -6987,7 +6995,7 @@ int tempfn_xxxxxx(const char *p, char **ret) { strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), "."), fn), "XXXXXX"); - *ret = t; + *ret = path_kill_slashes(t); return 0; } @@ -7000,6 +7008,14 @@ int tempfn_random(const char *p, char **ret) { assert(p); assert(ret); + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.waldobaa2a261115984a9 + */ + fn = basename(p); if (!filename_is_valid(fn)) return -EINVAL; @@ -7018,7 +7034,39 @@ int tempfn_random(const char *p, char **ret) { *x = 0; - *ret = t; + *ret = path_kill_slashes(t); + return 0; +} + +int tempfn_random_child(const char *p, char **ret) { + char *t, *x; + uint64_t u; + unsigned i; + + assert(p); + assert(ret); + + /* Turns this: + * /foo/bar/waldo + * Into this: + * /foo/bar/waldo/.3c2b6219aa75d7d0 + */ + + t = new(char, strlen(p) + 2 + 16 + 1); + if (!t) + return -ENOMEM; + + x = stpcpy(stpcpy(t, p), "/."); + + u = random_u64(); + for (i = 0; i < 16; i++) { + *(x++) = hexchar(u & 0xF); + u >>= 4; + } + + *x = 0; + + *ret = path_kill_slashes(t); return 0; } diff --git a/src/shared/util.h b/src/shared/util.h index 9a878ca1a..a8ccf20bb 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -1018,6 +1018,7 @@ int fflush_and_check(FILE *f); int tempfn_xxxxxx(const char *p, char **ret); int tempfn_random(const char *p, char **ret); +int tempfn_random_child(const char *p, char **ret); bool is_localhost(const char *hostname); -- 2.30.2