_MOUNT_MODE_MAX,
} MountMode;
+typedef enum MountEntryState {
+ MOUNT_PENDING,
+ MOUNT_APPLIED,
+ MOUNT_SKIPPED,
+ _MOUNT_ENTRY_STATE_MAX,
+ _MOUNT_ENTRY_STATE_INVALID = -EINVAL,
+} MountEntryState;
+
typedef struct MountEntry {
const char *path_const; /* Memory allocated on stack or static */
MountMode mode:5;
bool nosuid:1; /* Shall set MS_NOSUID on the mount itself */
bool noexec:1; /* Shall set MS_NOEXEC on the mount itself */
bool exec:1; /* Shall clear MS_NOEXEC on the mount itself */
- bool applied:1; /* Already applied */
+ MountEntryState state; /* Whether it was already processed or skipped */
char *path_malloc; /* Use this instead of 'path_const' if we had to allocate memory */
const char *unprefixed_path_const; /* If the path was amended with a prefix, these will save the original */
char *unprefixed_path_malloc;
* 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) {
+ f->state == MOUNT_PENDING && previous->state == MOUNT_PENDING) {
log_debug("%s (%s) is duplicate.", mount_entry_path(f), mount_mode_to_string(f->mode));
/* Propagate the flags to the remaining entry */
previous->read_only = previous->read_only || mount_entry_read_only(f);
(void) rmdir(dev);
(void) rmdir(temporary_mount);
- return 0;
+ return 1;
fail:
if (devpts)
if (r > 0) /* make this a NOP if /dev is already a mount point */
return 0;
- return mount_nofollow_verbose(LOG_DEBUG, "/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
+ r = mount_nofollow_verbose(LOG_DEBUG, "/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
}
static int mount_bind_sysfs(const MountEntry *m) {
return 0;
/* Bind mount the host's version so that we get all child mounts of it, too. */
- return mount_nofollow_verbose(LOG_DEBUG, "/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
+ r = mount_nofollow_verbose(LOG_DEBUG, "/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
}
static int mount_private_apivfs(
/* We lack permissions to mount a new instance, and it is not already mounted. But we can
* access the host's, so as a final fallback bind-mount it to the destination, as most likely
* we are inside a user manager in an unprivileged user namespace. */
- return mount_nofollow_verbose(LOG_DEBUG, bind_source, entry_path, /* fstype = */ NULL, MS_BIND|MS_REC, /* opts = */ NULL);
+ r = mount_nofollow_verbose(LOG_DEBUG, bind_source, entry_path, /* fstype = */ NULL, MS_BIND|MS_REC, /* opts = */ NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
} else if (r < 0)
return r;
/* We mounted a new instance now. Let's bind mount the children over now. This matters for nspawn
* where a bunch of files are overmounted, in particular the boot id. */
(void) bind_mount_submounts(bind_source, entry_path);
- return 0;
+ return 1;
}
static int mount_private_sysfs(const MountEntry *m, const NamespaceParameters *p) {
if (r < 0)
return log_debug_errno(r, "Failed to fix label of '%s' as '%s': %m", entry_path, inner_path);
- return 0;
+ return 1;
}
static int mount_run(const MountEntry *m) {
if (r < 0)
return r;
- return 0;
+ return 1;
}
static int mount_image(
if (r < 0)
return log_debug_errno(r, "Failed to mount image %s on %s: %m", mount_entry_source(m), mount_entry_path(m));
- return 0;
+ return 1;
}
static int mount_overlay(const MountEntry *m) {
r = mount_nofollow_verbose(LOG_DEBUG, "overlay", mount_entry_path(m), "overlay", MS_RDONLY, options);
if (r == -ENOENT && m->ignore)
return 0;
+ if (r < 0)
+ return r;
- return r;
+ return 1;
}
static int follow_symlink(
const char *what;
int r;
+ /* Return 1 when the mount should be post-processed (remounted r/o, etc.), 0 otherwise. In most
+ * cases post-processing is the right thing, the typical exception is when the mount is gracefully
+ * skipped. */
+
assert(m);
assert(p);
mount_entry_path(m));
if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY
* and MS_NOEXEC bits for the mount point if needed. */
- return 0;
+ return 1;
/* This isn't a mount point yet, let's make it one. */
what = mount_entry_path(m);
break;
}
log_debug("Successfully mounted %s to %s", what, mount_entry_path(m));
- return 0;
+ return 1;
}
static int make_read_only(const MountEntry *m, char **deny_list, FILE *proc_self_mountinfo) {
assert(m);
assert(proc_self_mountinfo);
+ if (m->state != MOUNT_APPLIED)
+ return 0;
+
if (mount_entry_read_only(m) || m->mode == PRIVATE_DEV) {
new_flags |= MS_RDONLY;
flags_mask |= MS_RDONLY;
assert(m);
assert(proc_self_mountinfo);
+ if (m->state != MOUNT_APPLIED)
+ return 0;
+
if (mount_entry_noexec(m)) {
new_flags |= MS_NOEXEC;
flags_mask |= MS_NOEXEC;
assert(m);
assert(proc_self_mountinfo);
+ if (m->state != MOUNT_APPLIED)
+ return 0;
+
submounts = !IN_SET(m->mode, EMPTY_DIR, TMPFS);
if (submounts)
FOREACH_ARRAY(m, ml->mounts, ml->n_mounts) {
- if (m->applied)
+ if (m->state != MOUNT_PENDING)
continue;
/* ExtensionImages/Directories are first opened in the propagate directory, not in the root_directory */
break;
}
+ /* Returns 1 if the mount should be post-processed, 0 otherwise */
r = apply_one_mount(root, m, p);
if (r < 0) {
mount_entry_path_debug_string(root, m, error_path);
return r;
}
-
- m->applied = true;
+ m->state = r == 0 ? MOUNT_SKIPPED : MOUNT_APPLIED;
}
if (!again)