chiark / gitweb /
nspawn: add --bind= and --bind-ro= to bind mount host paths into the container
authorLennart Poettering <lennart@poettering.net>
Mon, 25 Feb 2013 17:21:13 +0000 (18:21 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 25 Feb 2013 19:08:07 +0000 (20:08 +0100)
man/systemd-nspawn.xml
src/nspawn/nspawn.c

index 5cba40b..41477b4 100644 (file)
                                 <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>
index b8962e9..d35a8b5 100644 (file)
@@ -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;