summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/namespace.c122
1 files changed, 84 insertions, 38 deletions
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 32d8ca63ef..cb34a79859 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -61,12 +61,14 @@ typedef struct MountEntry {
bool ignore:1; /* Ignore if path does not exist? */
bool has_prefix:1; /* Already is prefixed by the root dir? */
bool read_only:1; /* Shall this mount point be read-only? */
+ bool applied:1; /* Already applied */
char *path_malloc; /* Use this instead of 'path_const' if we had to allocate memory */
const char *source_const; /* The source path, for bind mounts */
char *source_malloc;
const char *options_const;/* Mount options for tmpfs */
char *options_malloc;
unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */
+ unsigned n_followed;
} MountEntry;
/* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted
@@ -410,7 +412,6 @@ static int mount_path_compare(const void *a, const void *b) {
/* If the paths are equal, check the mode */
if (p->mode < q->mode)
return -1;
-
if (p->mode > q->mode)
return 1;
@@ -454,8 +455,10 @@ static void drop_duplicates(MountEntry *m, unsigned *n) {
for (f = m, t = m, previous = NULL; f < m + *n; f++) {
/* The first one wins (which is the one with the more restrictive mode), see mount_path_compare()
- * above. */
- if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous))) {
+ * above. Note that we only drop duplicates that haven't been mounted yet. */
+ if (previous &&
+ path_equal(mount_entry_path(f), mount_entry_path(previous)) &&
+ !f->applied && !previous->applied) {
log_debug("%s is duplicate.", mount_entry_path(f));
previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */
mount_entry_done(f);
@@ -817,36 +820,37 @@ static int mount_tmpfs(const MountEntry *m) {
return 1;
}
-static int mount_entry_chase(
+static int follow_symlink(
const char *root_directory,
- const MountEntry *m,
- const char *path,
- bool chase_nonexistent,
- char **location) {
+ MountEntry *m) {
- char *chased;
+ _cleanup_free_ char *target = NULL;
int r;
- assert(m);
+ /* Let's chase symlinks, but only one step at a time. That's because depending where the symlink points we
+ * might need to change the order in which we mount stuff. Hence: let's normalize piecemeal, and do one step at
+ * a time by specifying CHASE_STEP. This function returns 0 if we resolved one step, and > 0 if we reached the
+ * end and already have a fully normalized name. */
- /* Since mount() will always follow symlinks and we need to take the different root directory into account we
- * chase the symlinks on our own first. This is called for the destination path, as well as the source path (if
- * that applies). The result is stored in "location". */
+ r = chase_symlinks(mount_entry_path(m), root_directory, CHASE_STEP|CHASE_NONEXISTENT, &target);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to chase symlinks '%s': %m", mount_entry_path(m));
+ if (r > 0) /* Reached the end, nothing more to resolve */
+ return 1;
- r = chase_symlinks(path, root_directory, CHASE_TRAIL_SLASH | (chase_nonexistent ? CHASE_NONEXISTENT : 0), &chased);
- if (r == -ENOENT && m->ignore) {
- log_debug_errno(r, "Path %s does not exist, ignoring.", path);
- return 0;
+ if (m->n_followed >= CHASE_SYMLINKS_MAX) { /* put a boundary on things */
+ log_debug("Symlink loop on '%s'.", mount_entry_path(m));
+ return -ELOOP;
}
- if (r < 0)
- return log_debug_errno(r, "Failed to follow symlinks on %s: %m", path);
- log_debug("Followed symlinks %s → %s.", path, chased);
+ log_debug("Followed mount entry path symlink %s → %s.", mount_entry_path(m), target);
- free(*location);
- *location = chased;
+ free_and_replace(m->path_malloc, target);
+ m->has_prefix = true;
- return 1;
+ m->n_followed ++;
+
+ return 0;
}
static int apply_mount(
@@ -859,10 +863,6 @@ static int apply_mount(
assert(m);
- r = mount_entry_chase(root_directory, m, mount_entry_path(m), !IN_SET(m->mode, INACCESSIBLE, READONLY, READWRITE), &m->path_malloc);
- if (r <= 0)
- return r;
-
log_debug("Applying namespace mount on %s", mount_entry_path(m));
switch (m->mode) {
@@ -875,8 +875,12 @@ static int apply_mount(
* inaccessible path. */
(void) umount_recursive(mount_entry_path(m), 0);
- if (lstat(mount_entry_path(m), &target) < 0)
+ if (lstat(mount_entry_path(m), &target) < 0) {
+ if (errno == ENOENT && m->ignore)
+ return 0;
+
return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", mount_entry_path(m));
+ }
what = mode_to_inaccessible_node(target.st_mode);
if (!what) {
@@ -889,6 +893,8 @@ static int apply_mount(
case READONLY:
case READWRITE:
r = path_is_mount_point(mount_entry_path(m), root_directory, 0);
+ if (r == -ENOENT && m->ignore)
+ return 0;
if (r < 0)
return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m));
if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
@@ -901,16 +907,29 @@ static int apply_mount(
rbind = false;
_fallthrough_;
- case BIND_MOUNT_RECURSIVE:
- /* Also chase the source mount */
+ case BIND_MOUNT_RECURSIVE: {
+ _cleanup_free_ char *chased = NULL;
- r = mount_entry_chase(root_directory, m, mount_entry_source(m), false, &m->source_malloc);
- if (r <= 0)
- return r;
+ /* Since mount() will always follow symlinks we chase the symlinks on our own first. Note that bind
+ * mount source paths are always relative to the host root, hence we pass NULL as root directory to
+ * chase_symlinks() here. */
+
+ r = chase_symlinks(mount_entry_source(m), NULL, CHASE_TRAIL_SLASH, &chased);
+ if (r == -ENOENT && m->ignore) {
+ log_debug_errno(r, "Path %s does not exist, ignoring.", mount_entry_source(m));
+ return 0;
+ }
+ if (r < 0)
+ return log_debug_errno(r, "Failed to follow symlinks on %s: %m", mount_entry_source(m));
+
+ log_debug("Followed source symlinks %s → %s.", mount_entry_source(m), chased);
+
+ free_and_replace(m->source_malloc, chased);
what = mount_entry_source(m);
make = true;
break;
+ }
case EMPTY_DIR:
case TMPFS:
@@ -1340,11 +1359,38 @@ int setup_namespace(
goto finish;
}
- /* First round, add in all special mounts we need */
- for (m = mounts; m < mounts + n_mounts; ++m) {
- r = apply_mount(root, m);
- if (r < 0)
- goto finish;
+ /* First round, establish all mounts we need */
+ for (;;) {
+ bool again = false;
+
+ for (m = mounts; m < mounts + n_mounts; ++m) {
+
+ if (m->applied)
+ continue;
+
+ r = follow_symlink(root, m);
+ if (r < 0)
+ goto finish;
+ if (r == 0) {
+ /* We hit a symlinked mount point. The entry got rewritten and might point to a
+ * very different place now. Let's normalize the changed list, and start from
+ * the beginning. After all to mount the entry at the new location we might
+ * need some other mounts first */
+ again = true;
+ break;
+ }
+
+ r = apply_mount(root, m);
+ if (r < 0)
+ goto finish;
+
+ m->applied = true;
+ }
+
+ if (!again)
+ break;
+
+ normalize_mounts(root_directory, mounts, &n_mounts);
}
/* Create a blacklist we can pass to bind_mount_recursive() */