Merge pull request #23979 from DaanDeMeyer/nspawn-relative-paths

nspawn: Support relative paths for --bind and --overlay
This commit is contained in:
Luca Boccassi 2022-07-12 22:41:10 +01:00 committed by GitHub
commit 768456e8e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 51 deletions

View file

@ -1357,7 +1357,8 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
source path is taken relative to the image's root directory. This permits setting up bind mounts within the
container image. The source path may be specified as empty string, in which case a temporary directory below
the host's <filename>/var/tmp/</filename> directory is used. It is automatically removed when the container is
shut down. The <option>--bind-ro=</option> option creates read-only bind mounts. Backslash escapes are interpreted,
shut down. If the source path is not absolute, it is resolved relative to the current working directory.
The <option>--bind-ro=</option> option creates read-only bind mounts. 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.</para>
@ -1455,33 +1456,25 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
<term><option>--overlay=</option></term>
<term><option>--overlay-ro=</option></term>
<listitem><para>Combine multiple directory trees into one
overlay file system and mount it into the container. Takes a
list of colon-separated paths to the directory trees to
combine and the destination mount point.</para>
<listitem><para>Combine multiple directory trees into one overlay file system and mount it into the
container. Takes a list of colon-separated paths to the directory trees to combine and the
destination mount point.</para>
<para>Backslash escapes are interpreted in the paths, so
<literal>\:</literal> may be used to embed colons in the paths.
<para>Backslash escapes are interpreted in the paths, so <literal>\:</literal> may be used to embed
colons in the paths.</para>
<para>If three or more paths are specified, then the last specified path is the destination mount
point in the container, all paths specified before refer to directory trees on the host and are
combined in the specified order into one overlay file system. The left-most path is hence the lowest
directory tree, the second-to-last path the highest directory tree in the stacking order. If
<option>--overlay-ro=</option> is used instead of <option>--overlay=</option>, a read-only overlay
file system is created. If a writable overlay file system is created, all changes made to it are
written to the highest directory tree in the stacking order, i.e. the second-to-last specified.
</para>
<para>If three or more paths are specified, then the last
specified path is the destination mount point in the
container, all paths specified before refer to directory trees
on the host and are combined in the specified order into one
overlay file system. The left-most path is hence the lowest
directory tree, the second-to-last path the highest directory
tree in the stacking order. If <option>--overlay-ro=</option>
is used instead of <option>--overlay=</option>, a read-only
overlay file system is created. If a writable overlay file
system is created, all changes made to it are written to the
highest directory tree in the stacking order, i.e. the
second-to-last specified.</para>
<para>If only two paths are specified, then the second
specified path is used both as the top-level directory tree in
the stacking order as seen from the host, as well as the mount
point for the overlay file system in the container. At least
two paths have to be specified.</para>
<para>If only two paths are specified, then the second specified path is used both as the top-level
directory tree in the stacking order as seen from the host, as well as the mount 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. The uppermost source path may also be specified as an
@ -1489,7 +1482,8 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
used. The directory is removed automatically when the container is shut down. This behaviour is
useful in order to make read-only container directories writable while the container is running. For
example, use <literal>--overlay=+/var::/var</literal> in order to automatically overlay a writable
temporary directory on a read-only <filename>/var/</filename> directory.</para>
temporary directory on a read-only <filename>/var/</filename> directory. If a source path is not
absolute, it is resolved relative to the current working directory.</para>
<para>For details about overlay file systems, see <ulink
url="https://docs.kernel.org/filesystems/overlayfs.html">Overlay Filesystem</ulink>.

View file

@ -83,13 +83,38 @@ static int custom_mount_compare(const CustomMount *a, const CustomMount *b) {
return CMP(a->type, b->type);
}
static bool source_path_is_valid(const char *p) {
static int source_path_parse(const char *p, char **ret) {
assert(p);
assert(ret);
if (*p == '+')
p++;
if (isempty(p))
return -EINVAL;
return path_is_absolute(p);
if (*p == '+') {
if (!path_is_absolute(p + 1))
return -EINVAL;
char *s = strdup(p);
if (!s)
return -ENOMEM;
*ret = TAKE_PTR(s);
return 0;
}
return path_make_absolute_cwd(p, ret);
}
static int source_path_parse_nullable(const char *p, char **ret) {
assert(p);
assert(ret);
if (isempty(p)) {
*ret = NULL;
return 0;
}
return source_path_parse(p, ret);
}
static char *resolve_source_path(const char *dest, const char *source) {
@ -212,15 +237,14 @@ int custom_mount_prepare_all(const char *dest, CustomMount *l, size_t n) {
}
int bind_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_only) {
_cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL;
const char *p = s;
_cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL, *p = NULL;
CustomMount *m;
int r;
assert(l);
assert(n);
r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
r = extract_many_words(&s, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
if (r < 0)
return r;
if (r == 0)
@ -230,16 +254,15 @@ int bind_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_only)
if (!destination)
return -ENOMEM;
}
if (r == 2 && !isempty(p)) {
opts = strdup(p);
if (r == 2 && !isempty(s)) {
opts = strdup(s);
if (!opts)
return -ENOMEM;
}
if (isempty(source))
source = mfree(source);
else if (!source_path_is_valid(source))
return -EINVAL;
r = source_path_parse_nullable(source, &p);
if (r < 0)
return r;
if (!path_is_absolute(destination))
return -EINVAL;
@ -248,7 +271,7 @@ int bind_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_only)
if (!m)
return -ENOMEM;
m->source = TAKE_PTR(source);
m->source = TAKE_PTR(p);
m->destination = TAKE_PTR(destination);
m->read_only = read_only;
m->options = TAKE_PTR(opts);
@ -296,7 +319,7 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
_cleanup_free_ char *upper = NULL, *destination = NULL;
_cleanup_strv_free_ char **lower = NULL;
CustomMount *m;
int k;
int r, k;
k = strv_split_full(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (k < 0)
@ -304,12 +327,22 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
if (k < 2)
return -EADDRNOTAVAIL;
if (k == 2) {
_cleanup_free_ char *p = NULL;
/* 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;
r = source_path_parse(lower[0], &p);
if (r < 0)
return r;
free_and_replace(lower[0], p);
r = source_path_parse(lower[1], &p);
if (r < 0)
return r;
free_and_replace(lower[1], p);
upper = TAKE_PTR(lower[1]);
@ -317,22 +350,29 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
if (!destination)
return -ENOMEM;
} else {
_cleanup_free_ char *p = NULL;
/* 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. */
destination = lower[k - 1];
upper = TAKE_PTR(lower[k - 2]);
STRV_FOREACH(i, lower)
if (!source_path_is_valid(*i))
return -EINVAL;
STRV_FOREACH(i, lower) {
r = source_path_parse(*i, &p);
if (r < 0)
return r;
free_and_replace(*i, p);
}
/* If the upper directory is unspecified, then let's create it automatically as a throw-away directory
* in /var/tmp */
if (isempty(upper))
upper = mfree(upper);
else if (!source_path_is_valid(upper))
return -EINVAL;
r = source_path_parse_nullable(upper, &p);
if (r < 0)
return r;
free_and_replace(upper, p);
if (!path_is_absolute(destination))
return -EINVAL;