summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/systemd-nspawn.xml29
-rw-r--r--src/nspawn/nspawn-mount.c166
-rw-r--r--src/nspawn/nspawn-mount.h4
-rw-r--r--src/nspawn/nspawn-register.c5
-rw-r--r--src/nspawn/nspawn.c26
5 files changed, 148 insertions, 82 deletions
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index dbbf9890c8..84fa9cadef 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -740,21 +740,17 @@
<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. 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 —, 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
- <literal>\:</literal> may be used to embed colons in either path.
- This option may be specified multiple times for
- creating multiple independent bind mount points. The
- <option>--bind-ro=</option> option creates read-only bind
- mounts.</para></listitem>
+ <listitem><para>Bind mount a file or directory from the host 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 —, or a colon-separated triple of source path, destination path
+ and mount options. The source path may optionally be prefixed with a <literal>+</literal> character. If so, the
+ source path is taken relative to the images root directory. This permits setting up bind mounts within the
+ container image. Mount options are comma-separated and currently, only "rbind" and "norbind" are allowed,
+ controlling whether to create a recursive or a regular bind mount. Defaults to "rbind". Backslash escapes are
+ interpreted, so <literal>\:</literal> may be used to embed colons in either path. This option may be specified
+ multiple times for creating multiple independent bind mount points. The <option>--bind-ro=</option> option
+ creates read-only bind mounts.</para></listitem>
</varlistentry>
<varlistentry>
@@ -808,6 +804,9 @@
point for the overlay file system in the container. At least
two paths have to be specified.</para>
+ <para>The source paths may optionally be prefixed with <literal>+</literal> character. If so they are taken
+ relative to the image's root directory.</para>
+
<para>For details about overlay file systems, see <ulink
url="https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt">overlayfs.txt</ulink>. Note
that the semantics of overlay file systems are substantially
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 6782c74b80..291a88a9ac 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -81,7 +81,7 @@ void custom_mount_free_all(CustomMount *l, unsigned n) {
free(l);
}
-int custom_mount_compare(const void *a, const void *b) {
+static int custom_mount_compare(const void *a, const void *b) {
const CustomMount *x = a, *y = b;
int r;
@@ -97,6 +97,91 @@ int custom_mount_compare(const void *a, const void *b) {
return 0;
}
+static bool source_path_is_valid(const char *p) {
+ assert(p);
+
+ if (*p == '+')
+ p++;
+
+ return path_is_absolute(p);
+}
+
+static char *resolve_source_path(const char *dest, const char *source) {
+
+ if (!source)
+ return NULL;
+
+ if (source[0] == '+')
+ return prefix_root(dest, source + 1);
+
+ return strdup(source);
+}
+
+int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n) {
+ unsigned i;
+ int r;
+
+ /* Prepare all custom mounts. This will make source we know all temporary directories. This is called in the
+ * parent process, so that we know the temporary directories to remove on exit before we fork off the
+ * children. */
+
+ assert(l || n == 0);
+
+ /* Order the custom mounts, and make sure we have a working directory */
+ qsort_safe(l, n, sizeof(CustomMount), custom_mount_compare);
+
+ for (i = 0; i < n; i++) {
+ CustomMount *m = l + i;
+
+ if (m->source) {
+ char *s;
+
+ s = resolve_source_path(dest, m->source);
+ if (!s)
+ return log_oom();
+
+ free(m->source);
+ m->source = s;
+ }
+
+ if (m->type == CUSTOM_MOUNT_OVERLAY) {
+ char **j;
+
+ STRV_FOREACH(j, m->lower) {
+ char *s;
+
+ s = resolve_source_path(dest, *j);
+ if (!s)
+ return log_oom();
+
+ free(*j);
+ *j = s;
+ }
+
+ if (m->work_dir) {
+ char *s;
+
+ s = resolve_source_path(dest, m->work_dir);
+ if (!s)
+ return log_oom();
+
+ free(m->work_dir);
+ m->work_dir = s;
+ } else {
+ assert(m->source);
+
+ r = tempfn_random(m->source, NULL, &m->work_dir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire working directory: %m");
+ }
+
+ (void) mkdir_label(m->work_dir, 0700);
+ }
+ }
+
+ return 0;
+}
+
int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) {
_cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL;
const char *p = s;
@@ -111,22 +196,19 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only
return r;
if (r == 0)
return -EINVAL;
-
if (r == 1) {
- destination = strdup(source);
+ destination = strdup(source[0] == '+' ? source+1 : source);
if (!destination)
return -ENOMEM;
}
-
if (r == 2 && !isempty(p)) {
opts = strdup(p);
if (!opts)
return -ENOMEM;
}
- if (!path_is_absolute(source))
+ if (!source_path_is_valid(source))
return -EINVAL;
-
if (!path_is_absolute(destination))
return -EINVAL;
@@ -184,40 +266,43 @@ int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_o
_cleanup_free_ char *upper = NULL, *destination = NULL;
_cleanup_strv_free_ char **lower = NULL;
CustomMount *m;
- unsigned k = 0;
- char **i;
- int r;
-
- r = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
- if (r < 0)
- return r;
-
- STRV_FOREACH(i, lower) {
- if (!path_is_absolute(*i))
- return -EINVAL;
-
- k++;
- }
+ int k;
+ k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (k < 0)
+ return k;
if (k < 2)
return -EADDRNOTAVAIL;
if (k == 2) {
- /* If two parameters are specified,
- * the first one is the lower, the
- * second one the upper directory. And
- * we'll also define the destination
- * mount point the same as the upper. */
+ /* If two parameters are specified, the first one is the lower, the second one the upper directory. And
+ * we'll also define the destination mount point the same as the upper. */
+
+ if (!source_path_is_valid(lower[0]) ||
+ !source_path_is_valid(lower[1]))
+ return -EINVAL;
+
upper = lower[1];
lower[1] = NULL;
- destination = strdup(upper);
+ destination = strdup(upper[0] == '+' ? upper+1 : upper); /* take the destination without "+" prefix */
if (!destination)
return -ENOMEM;
-
} else {
- upper = lower[k - 2];
+ int i;
+
+ /* If more than two parameters are specified, the last one is the destination, the second to last one
+ * the "upper", and all before that the "lower" directories. */
+
+ for (i = 0; i < k - 1; i++)
+ if (!source_path_is_valid(lower[i]))
+ return -EINVAL;
+
destination = lower[k - 1];
+ upper = lower[k - 2];
lower[k - 2] = NULL;
+
+ if (!path_is_absolute(destination))
+ return -EINVAL;
}
m = custom_mount_add(l, n, CUSTOM_MOUNT_OVERLAY);
@@ -556,6 +641,7 @@ static int mount_bind(const char *dest, CustomMount *m) {
struct stat source_st, dest_st;
int r;
+ assert(dest);
assert(m);
if (m->options) {
@@ -646,7 +732,7 @@ static int mount_tmpfs(
return mount_verbose(LOG_ERR, "tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options);
}
-static char *joined_and_escaped_lower_dirs(char * const *lower) {
+static char *joined_and_escaped_lower_dirs(char **lower) {
_cleanup_strv_free_ char **sv = NULL;
sv = strv_copy(lower);
@@ -663,7 +749,7 @@ static char *joined_and_escaped_lower_dirs(char * const *lower) {
static int mount_overlay(const char *dest, CustomMount *m) {
- _cleanup_free_ char *lower = NULL, *where = NULL;
+ _cleanup_free_ char *lower = NULL, *where = NULL, *escaped_source = NULL;
const char *options;
int r;
@@ -685,23 +771,15 @@ static int mount_overlay(const char *dest, CustomMount *m) {
if (!lower)
return log_oom();
- if (m->read_only) {
- _cleanup_free_ char *escaped_source = NULL;
-
- escaped_source = shell_escape(m->source, ",:");
- if (!escaped_source)
- return log_oom();
+ escaped_source = shell_escape(m->source, ",:");
+ if (!escaped_source)
+ return log_oom();
+ if (m->read_only)
options = strjoina("lowerdir=", escaped_source, ":", lower);
- } else {
- _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL;
+ else {
+ _cleanup_free_ char *escaped_work_dir = NULL;
- assert(m->work_dir);
- (void) mkdir_label(m->work_dir, 0700);
-
- escaped_source = shell_escape(m->source, ",:");
- if (!escaped_source)
- return log_oom();
escaped_work_dir = shell_escape(m->work_dir, ",:");
if (!escaped_work_dir)
return log_oom();
diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h
index 83acd01e00..dc8be438ac 100644
--- a/src/nspawn/nspawn-mount.h
+++ b/src/nspawn/nspawn-mount.h
@@ -59,15 +59,13 @@ typedef struct CustomMount {
} CustomMount;
CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t);
-
void custom_mount_free_all(CustomMount *l, unsigned n);
+int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n);
int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only);
int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s);
int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only);
-int custom_mount_compare(const void *a, const void *b);
-
int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
int mount_sysfs(const char *dest, MountSettingsMask mount_settings);
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index 06c56d9ec8..e3ab39faea 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -135,6 +135,11 @@ int register_machine(
continue;
r = is_device_node(cm->source);
+ if (r == -ENOENT) {
+ /* The bind source might only appear as the image is put together, hence don't complain */
+ log_debug_errno(r, "Bind mount source %s not found, ignoring: %m", cm->source);
+ continue;
+ }
if (r < 0)
return log_error_errno(r, "Failed to stat %s: %m", cm->source);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index ea50be25ea..84c213785c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -280,14 +280,9 @@ static void help(void) {
, program_invocation_short_name);
}
-static int custom_mounts_prepare(void) {
+static int custom_mount_check_all(void) {
unsigned i;
- int r;
-
- /* Ensure the mounts are applied prefix first. */
- qsort_safe(arg_custom_mounts, arg_n_custom_mounts, sizeof(CustomMount), custom_mount_compare);
- /* Allocate working directories for the overlay file systems that need it */
for (i = 0; i < arg_n_custom_mounts; i++) {
CustomMount *m = &arg_custom_mounts[i];
@@ -301,19 +296,6 @@ static int custom_mounts_prepare(void) {
return -EINVAL;
}
}
-
- if (m->type != CUSTOM_MOUNT_OVERLAY)
- continue;
-
- if (m->work_dir)
- continue;
-
- if (m->read_only)
- continue;
-
- r = tempfn_random(m->source, NULL, &m->work_dir);
- if (r < 0)
- return log_error_errno(r, "Failed to generate work directory from %s: %m", m->source);
}
return 0;
@@ -1147,6 +1129,10 @@ static int parse_argv(int argc, char *argv[]) {
else
arg_use_cgns = r;
+ r = custom_mount_check_all();
+ if (r < 0)
+ return r;
+
return 1;
}
@@ -4284,7 +4270,7 @@ int main(int argc, char *argv[]) {
remove_image = false;
}
- r = custom_mounts_prepare();
+ r = custom_mount_prepare_all(arg_directory, arg_custom_mounts, arg_n_custom_mounts);
if (r < 0)
goto finish;