mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-02 09:47:03 +03:00
nspawn: permit prefixing of source paths in --bind= and --overlay= with "+"
If a source path is prefixed with "+" it is taken relative to the container's root directory instead of the host. This permits easily establishing bind and overlay mounts based on data from the container rather than the host. This also reworks custom_mounts_prepare(), and turns it into two functions: one custom_mount_check_all() that remains in nspawn.c but purely verifies the validity of the custom mounts configured. And one called custom_mount_prepare_all() that actually does the preparation step, sorts the custom mounts, resolves relative paths, and allocates temporary directories as necessary.
This commit is contained in:
parent
e28c7cd066
commit
86c0dd4a71
@ -740,21 +740,17 @@
|
|||||||
<term><option>--bind=</option></term>
|
<term><option>--bind=</option></term>
|
||||||
<term><option>--bind-ro=</option></term>
|
<term><option>--bind-ro=</option></term>
|
||||||
|
|
||||||
<listitem><para>Bind mount a file or directory from the host
|
<listitem><para>Bind mount a file or directory from the host into the container. Takes one of: a path
|
||||||
into the container. Takes one of: a path argument — in which
|
argument — in which case the specified path will be mounted from the host to the same path in the container —,
|
||||||
case the specified path will be mounted from the host to the
|
or a colon-separated pair of paths — in which case the first specified path is the source in the host, and the
|
||||||
same path in the container —, or a colon-separated pair of
|
second path is the destination in the container —, or a colon-separated triple of source path, destination path
|
||||||
paths — in which case the first specified path is the source
|
and mount options. The source path may optionally be prefixed with a <literal>+</literal> character. If so, the
|
||||||
in the host, and the second path is the destination in the
|
source path is taken relative to the images root directory. This permits setting up bind mounts within the
|
||||||
container —, or a colon-separated triple of source path,
|
container image. Mount options are comma-separated and currently, only "rbind" and "norbind" are allowed,
|
||||||
destination path and mount options. Mount options are
|
controlling whether to create a recursive or a regular bind mount. Defaults to "rbind". Backslash escapes are
|
||||||
comma-separated and currently, only "rbind" and "norbind"
|
interpreted, so <literal>\:</literal> may be used to embed colons in either path. This option may be specified
|
||||||
are allowed. Defaults to "rbind". Backslash escapes are interpreted, so
|
multiple times for creating multiple independent bind mount points. The <option>--bind-ro=</option> option
|
||||||
<literal>\:</literal> may be used to embed colons in either path.
|
creates read-only bind mounts.</para></listitem>
|
||||||
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>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -808,6 +804,9 @@
|
|||||||
point for the overlay file system in the container. At least
|
point for the overlay file system in the container. At least
|
||||||
two paths have to be specified.</para>
|
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
|
<para>For details about overlay file systems, see <ulink
|
||||||
url="https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt">overlayfs.txt</ulink>. Note
|
url="https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt">overlayfs.txt</ulink>. Note
|
||||||
that the semantics of overlay file systems are substantially
|
that the semantics of overlay file systems are substantially
|
||||||
|
@ -81,7 +81,7 @@ void custom_mount_free_all(CustomMount *l, unsigned n) {
|
|||||||
free(l);
|
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;
|
const CustomMount *x = a, *y = b;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -97,6 +97,91 @@ int custom_mount_compare(const void *a, const void *b) {
|
|||||||
return 0;
|
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) {
|
int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) {
|
||||||
_cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL;
|
_cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL;
|
||||||
const char *p = s;
|
const char *p = s;
|
||||||
@ -111,22 +196,19 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only
|
|||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (r == 1) {
|
if (r == 1) {
|
||||||
destination = strdup(source);
|
destination = strdup(source[0] == '+' ? source+1 : source);
|
||||||
if (!destination)
|
if (!destination)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r == 2 && !isempty(p)) {
|
if (r == 2 && !isempty(p)) {
|
||||||
opts = strdup(p);
|
opts = strdup(p);
|
||||||
if (!opts)
|
if (!opts)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!path_is_absolute(source))
|
if (!source_path_is_valid(source))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!path_is_absolute(destination))
|
if (!path_is_absolute(destination))
|
||||||
return -EINVAL;
|
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_free_ char *upper = NULL, *destination = NULL;
|
||||||
_cleanup_strv_free_ char **lower = NULL;
|
_cleanup_strv_free_ char **lower = NULL;
|
||||||
CustomMount *m;
|
CustomMount *m;
|
||||||
unsigned k = 0;
|
int k;
|
||||||
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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||||
|
if (k < 0)
|
||||||
|
return k;
|
||||||
if (k < 2)
|
if (k < 2)
|
||||||
return -EADDRNOTAVAIL;
|
return -EADDRNOTAVAIL;
|
||||||
if (k == 2) {
|
if (k == 2) {
|
||||||
/* If two parameters are specified,
|
/* If two parameters are specified, the first one is the lower, the second one the upper directory. And
|
||||||
* the first one is the lower, the
|
* we'll also define the destination mount point the same as the upper. */
|
||||||
* second one the upper directory. And
|
|
||||||
* we'll also define the destination
|
if (!source_path_is_valid(lower[0]) ||
|
||||||
* mount point the same as the upper. */
|
!source_path_is_valid(lower[1]))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
upper = lower[1];
|
upper = lower[1];
|
||||||
lower[1] = NULL;
|
lower[1] = NULL;
|
||||||
|
|
||||||
destination = strdup(upper);
|
destination = strdup(upper[0] == '+' ? upper+1 : upper); /* take the destination without "+" prefix */
|
||||||
if (!destination)
|
if (!destination)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
} else {
|
} 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];
|
destination = lower[k - 1];
|
||||||
|
upper = lower[k - 2];
|
||||||
lower[k - 2] = NULL;
|
lower[k - 2] = NULL;
|
||||||
|
|
||||||
|
if (!path_is_absolute(destination))
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
m = custom_mount_add(l, n, CUSTOM_MOUNT_OVERLAY);
|
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;
|
struct stat source_st, dest_st;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
assert(dest);
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
if (m->options) {
|
if (m->options) {
|
||||||
@ -646,7 +732,7 @@ static int mount_tmpfs(
|
|||||||
return mount_verbose(LOG_ERR, "tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options);
|
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;
|
_cleanup_strv_free_ char **sv = NULL;
|
||||||
|
|
||||||
sv = strv_copy(lower);
|
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) {
|
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;
|
const char *options;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -685,23 +771,15 @@ static int mount_overlay(const char *dest, CustomMount *m) {
|
|||||||
if (!lower)
|
if (!lower)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
if (m->read_only) {
|
escaped_source = shell_escape(m->source, ",:");
|
||||||
_cleanup_free_ char *escaped_source = NULL;
|
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);
|
options = strjoina("lowerdir=", escaped_source, ":", lower);
|
||||||
} else {
|
else {
|
||||||
_cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL;
|
_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, ",:");
|
escaped_work_dir = shell_escape(m->work_dir, ",:");
|
||||||
if (!escaped_work_dir)
|
if (!escaped_work_dir)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
@ -59,15 +59,13 @@ typedef struct CustomMount {
|
|||||||
} CustomMount;
|
} CustomMount;
|
||||||
|
|
||||||
CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t);
|
CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t);
|
||||||
|
|
||||||
void custom_mount_free_all(CustomMount *l, unsigned n);
|
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 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 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 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_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);
|
int mount_sysfs(const char *dest, MountSettingsMask mount_settings);
|
||||||
|
|
||||||
|
@ -135,6 +135,11 @@ int register_machine(
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
r = is_device_node(cm->source);
|
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)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to stat %s: %m", cm->source);
|
return log_error_errno(r, "Failed to stat %s: %m", cm->source);
|
||||||
|
|
||||||
|
@ -280,14 +280,9 @@ static void help(void) {
|
|||||||
, program_invocation_short_name);
|
, program_invocation_short_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int custom_mounts_prepare(void) {
|
static int custom_mount_check_all(void) {
|
||||||
unsigned i;
|
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++) {
|
for (i = 0; i < arg_n_custom_mounts; i++) {
|
||||||
CustomMount *m = &arg_custom_mounts[i];
|
CustomMount *m = &arg_custom_mounts[i];
|
||||||
|
|
||||||
@ -301,19 +296,6 @@ static int custom_mounts_prepare(void) {
|
|||||||
return -EINVAL;
|
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;
|
return 0;
|
||||||
@ -1147,6 +1129,10 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
else
|
else
|
||||||
arg_use_cgns = r;
|
arg_use_cgns = r;
|
||||||
|
|
||||||
|
r = custom_mount_check_all();
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4284,7 +4270,7 @@ int main(int argc, char *argv[]) {
|
|||||||
remove_image = false;
|
remove_image = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = custom_mounts_prepare();
|
r = custom_mount_prepare_all(arg_directory, arg_custom_mounts, arg_n_custom_mounts);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user