diff options
-rw-r--r-- | man/systemd-nspawn.xml | 20 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 119 |
2 files changed, 117 insertions, 22 deletions
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 5cba40bcd4..41477b46d5 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -298,6 +298,26 @@ <listitem><para>Equivalent to <option>--link-journal=guest</option>.</para></listitem> </varlistentry> + + <varlistentry> + <term><option>--bind=</option></term> + <term><option>--bind-ro=</option></term> + + <listitem><para>Bind mount a file or + directory from the host into the + container. Either takes a path + argument -- in which case the + specified path will be mounted from + the host to the same path in the + container --, or a colon-separated + pair of paths -- in which case the + first specified path is the source in + the host, and the second path is the + destination in the container. The + <option>--bind-ro=</option> option + creates read-only bind + mount.</para></listitem> + </varlistentry> </variablelist> </refsect1> diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index b8962e9894..d35a8b547b 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -101,23 +101,30 @@ static uint64_t arg_retain = (1ULL << CAP_SYS_BOOT) | (1ULL << CAP_AUDIT_WRITE) | (1ULL << CAP_AUDIT_CONTROL); +static char **arg_bind = NULL; +static char **arg_bind_ro = NULL; static int help(void) { printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" "Spawn a minimal namespace container for debugging, testing and building.\n\n" - " -h --help Show this help\n" - " --version Print version string\n" - " -D --directory=NAME Root directory for the container\n" - " -b --boot Boot up full system (i.e. invoke init)\n" - " -u --user=USER Run the command under specified user or uid\n" - " -C --controllers=LIST Put the container in specified comma-separated cgroup hierarchies\n" - " --uuid=UUID Set a specific machine UUID for the container\n" - " --private-network Disable network in container\n" - " --read-only Mount the root directory read-only\n" - " --capability=CAP In addition to the default, retain specified capability\n" - " --link-journal=MODE Link up guest journal, one of no, auto, guest, host\n" - " -j Equivalent to --link-journal=host\n", + " -h --help Show this help\n" + " --version Print version string\n" + " -D --directory=NAME Root directory for the container\n" + " -b --boot Boot up full system (i.e. invoke init)\n" + " -u --user=USER Run the command under specified user or uid\n" + " -C --controllers=LIST Put the container in specified comma-separated\n" + " cgroup hierarchies\n" + " --uuid=UUID Set a specific machine UUID for the container\n" + " --private-network Disable network in container\n" + " --read-only Mount the root directory read-only\n" + " --capability=CAP In addition to the default, retain specified\n" + " capability\n" + " --link-journal=MODE Link up guest journal, one of no, auto, guest, host\n" + " -j Equivalent to --link-journal=host\n" + " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n" + " the container\n" + " --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n", program_invocation_short_name); return 0; @@ -131,7 +138,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_UUID, ARG_READ_ONLY, ARG_CAPABILITY, - ARG_LINK_JOURNAL + ARG_LINK_JOURNAL, + ARG_BIND, + ARG_BIND_RO }; static const struct option options[] = { @@ -146,6 +155,8 @@ static int parse_argv(int argc, char *argv[]) { { "read-only", no_argument, NULL, ARG_READ_ONLY }, { "capability", required_argument, NULL, ARG_CAPABILITY }, { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL }, + { "bind", required_argument, NULL, ARG_BIND }, + { "bind-ro", required_argument, NULL, ARG_BIND_RO }, { NULL, 0, NULL, 0 } }; @@ -258,6 +269,43 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_BIND: + case ARG_BIND_RO: { + _cleanup_free_ char *a = NULL, *b = NULL; + char *e; + char ***x; + int r; + + x = c == ARG_BIND ? &arg_bind : &arg_bind_ro; + + e = strchr(optarg, ':'); + if (e) { + a = strndup(optarg, e - optarg); + b = strdup(e + 1); + } else { + a = strdup(optarg); + b = strdup(optarg); + } + + if (!a || !b) + return log_oom(); + + if (!path_is_absolute(a) || !path_is_absolute(b)) { + log_error("Invalid bind mount specification: %s", optarg); + return -EINVAL; + } + + r = strv_extend(x, a); + if (r < 0) + return r; + + r = strv_extend(x, b); + if (r < 0) + return r; + + break; + } + case '?': return -EINVAL; @@ -303,14 +351,9 @@ static int mount_all(const char *dest) { char _cleanup_free_ *where = NULL; int t; - if (asprintf(&where, "%s/%s", dest, mount_table[k].where) < 0) { - log_oom(); - - if (r == 0) - r = -ENOMEM; - - break; - } + where = strjoin(dest, "/", mount_table[k].where, NULL); + if (!where) + return log_oom(); t = path_is_mount_point(where, true); if (t < 0) { @@ -326,7 +369,7 @@ static int mount_all(const char *dest) { if (mount_table[k].what && t > 0) continue; - mkdir_p_label(where, 0755); + mkdir_p(where, 0755); if (mount(mount_table[k].what, where, @@ -345,6 +388,32 @@ static int mount_all(const char *dest) { return r; } +static int mount_binds(const char *dest, char **l, unsigned long flags) { + char **x, **y; + + STRV_FOREACH_PAIR(x, y, l) { + _cleanup_free_ char *where = NULL; + + where = strjoin(dest, "/", *y, NULL); + if (!where) + return log_oom(); + + mkdir_p_label(where, 0755); + + if (mount(*x, where, "bind", MS_BIND, NULL) < 0) { + log_error("mount(%s) failed: %m", where); + return -errno; + } + + if (flags && mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|flags, NULL) < 0) { + log_error("mount(%s) failed: %m", where); + return -errno; + } + } + + return 0; +} + static int setup_timezone(const char *dest) { _cleanup_free_ char *where = NULL, *p = NULL, *q = NULL, *check = NULL, *what = NULL; char *z, *y; @@ -1346,6 +1415,12 @@ int main(int argc, char *argv[]) { if (setup_journal(arg_directory) < 0) goto child_fail; + if (mount_binds(arg_directory, arg_bind, 0) < 0) + goto child_fail; + + if (mount_binds(arg_directory, arg_bind_ro, MS_RDONLY) < 0) + goto child_fail; + if (chdir(arg_directory) < 0) { log_error("chdir(%s) failed: %m", arg_directory); goto child_fail; |