diff options
-rw-r--r-- | man/systemd-nspawn.xml | 29 | ||||
-rw-r--r-- | src/nspawn/nspawn-mount.c | 166 | ||||
-rw-r--r-- | src/nspawn/nspawn-mount.h | 4 | ||||
-rw-r--r-- | src/nspawn/nspawn-register.c | 5 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 26 |
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; |