nspawn: add --bind= and --bind-ro= to bind mount host paths into the container

This commit is contained in:
Lennart Poettering
2013-02-25 20:08:07 +01:00
parent 2d0614d3d3
commit 17fe052346
2 changed files with 117 additions and 22 deletions
+20
View File
@@ -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>
+97 -22
View 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;