From 5e5bfa6e1c915e795dd99ab65275c4bf9af667ed Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Tue, 21 Jul 2015 15:48:38 -0700 Subject: nspawn: add (no)rbind option to --bind and --bind-ro --bind and --bind-ro perform the bind mount non-recursively. It is sometimes (often?) desirable to do a recursive mount. This patch adds an optional set of bind mount options in the form of: --bind=src-path:dst-path:options options are comma separated and currently only "rbind" and "norbind" are allowed. Default value is "rbind". --- man/systemd-nspawn.xml | 7 +++++-- src/nspawn/nspawn.c | 56 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 4966749259..6165fe1357 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -576,12 +576,15 @@ Bind mount a file or directory from the host - into the container. Either takes a path argument -- in which + into the container. Takes one of: 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. Backslash escapes are interpreted so + container --, or a colon-separated triple of source path, + destination path and mount options. Mount options are comma + separated and currently only "rbind" and "norbind" + are allowed. Defaults to "rbind". Backslash escapes are interpreted so \: may be used to embed colons in either path. This option may be specified multiple times for creating multiple independent bind mount points. The diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 837947ee28..edf5b9fc57 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -257,9 +257,11 @@ static void help(void) { " try-guest, try-host\n" " -j Equivalent to --link-journal=try-guest\n" " --read-only Mount the root directory read-only\n" - " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n" + " --bind=PATH[:PATH[:OPTIONS]]\n" + " 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" + " --bind-ro=PATH[:PATH[:OPTIONS]\n" + " Similar, but creates a read-only bind mount\n" " --tmpfs=PATH:[OPTIONS] Mount an empty tmpfs to the specified directory\n" " --overlay=PATH[:PATH...]:PATH\n" " Create an overlay mount from the host to \n" @@ -656,14 +658,15 @@ static int parse_argv(int argc, char *argv[]) { case ARG_BIND: case ARG_BIND_RO: { const char *current = optarg; - _cleanup_free_ char *source = NULL, *destination = NULL; + _cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL; CustomMount *m; - r = extract_many_words(¤t, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL); + r = extract_many_words(¤t, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, &opts, NULL); switch (r) { case 1: destination = strdup(source); case 2: + case 3: break; case -ENOMEM: return log_oom(); @@ -687,8 +690,9 @@ static int parse_argv(int argc, char *argv[]) { m->source = source; m->destination = destination; m->read_only = c == ARG_BIND_RO; + m->options = opts; - source = destination = NULL; + source = destination = opts = NULL; break; } @@ -1158,13 +1162,53 @@ static int mount_all(const char *dest, bool userns) { return 0; } +static int parse_mount_bind_options(const char *options, unsigned long *mount_flags, char **mount_opts) { + const char *p = options; + unsigned long flags = *mount_flags; + char *opts = NULL; + + assert(options); + + for (;;) { + _cleanup_free_ char *word = NULL; + int r = extract_first_word(&p, &word, ",", EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to extract mount option: %m"); + if (r == 0) + break; + + if (streq(word, "rbind")) + flags |= MS_REC; + else if (streq(word, "norbind")) + flags &= ~MS_REC; + else { + log_error("Invalid bind mount option: %s", word); + return -EINVAL; + } + } + + *mount_flags = flags; + /* in the future mount_opts will hold string options for mount(2) */ + *mount_opts = opts; + + return 0; +} + static int mount_bind(const char *dest, CustomMount *m) { struct stat source_st, dest_st; const char *where; + unsigned long mount_flags = MS_BIND | MS_REC; + _cleanup_free_ char *mount_opts = NULL; int r; assert(m); + if (m->options) { + r = parse_mount_bind_options(m->options, &mount_flags, &mount_opts); + if (r < 0) + return r; + } + if (stat(m->source, &source_st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", m->source); @@ -1201,7 +1245,7 @@ static int mount_bind(const char *dest, CustomMount *m) { if (r < 0 && r != -EEXIST) return log_error_errno(r, "Failed to create mount point %s: %m", where); - if (mount(m->source, where, NULL, MS_BIND, NULL) < 0) + if (mount(m->source, where, NULL, mount_flags, mount_opts) < 0) return log_error_errno(errno, "mount(%s) failed: %m", where); if (m->read_only) { -- cgit v1.2.3-54-g00ecf