mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 12:13:33 +08:00
Merge pull request #28144 from poettering/procfs-submounts-move
pid1: when setting up a new procfs instance for a service, mount submounts from host into it
This commit is contained in:
commit
9e35e9779d
@ -29,7 +29,6 @@ static inline int open_tmpfile_linkable(const char *target, int flags, char **re
|
||||
}
|
||||
int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file);
|
||||
|
||||
|
||||
typedef enum LinkTmpfileFlags {
|
||||
LINK_TMPFILE_REPLACE = 1 << 0,
|
||||
LINK_TMPFILE_SYNC = 1 << 1,
|
||||
|
@ -1178,6 +1178,10 @@ static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) {
|
||||
}
|
||||
} else if (r < 0)
|
||||
return r;
|
||||
else
|
||||
/* 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("/proc", entry_path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1251,7 +1251,11 @@ static void sub_mount_drop(SubMount *s, size_t n) {
|
||||
}
|
||||
}
|
||||
|
||||
static int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_mounts) {
|
||||
static int get_sub_mounts(
|
||||
const char *prefix,
|
||||
bool clone_tree,
|
||||
SubMount **ret_mounts,
|
||||
size_t *ret_n_mounts) {
|
||||
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
|
||||
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
|
||||
SubMount *mounts = NULL;
|
||||
@ -1301,12 +1305,15 @@ static int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret
|
||||
continue;
|
||||
}
|
||||
|
||||
mount_fd = open_tree(AT_FDCWD, path, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_RECURSIVE);
|
||||
if (clone_tree)
|
||||
mount_fd = open_tree(AT_FDCWD, path, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_RECURSIVE);
|
||||
else
|
||||
mount_fd = open(path, O_CLOEXEC|O_PATH);
|
||||
if (mount_fd < 0) {
|
||||
if (errno == ENOENT) /* The path may be hidden by another over-mount or already unmounted. */
|
||||
continue;
|
||||
|
||||
return log_debug_errno(errno, "Failed to open tree of mounted filesystem '%s': %m", path);
|
||||
return log_debug_errno(errno, "Failed to open subtree of mounted filesystem '%s': %m", path);
|
||||
}
|
||||
|
||||
p = strdup(path);
|
||||
@ -1353,8 +1360,8 @@ int remount_and_move_sub_mounts(
|
||||
unsigned long flags,
|
||||
const char *options) {
|
||||
|
||||
SubMount *mounts = NULL; /* avoid false maybe-uninitialized warning */
|
||||
size_t n = 0; /* avoid false maybe-uninitialized warning */
|
||||
SubMount *mounts = NULL;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
CLEANUP_ARRAY(mounts, n, sub_mount_array_free);
|
||||
@ -1374,7 +1381,7 @@ int remount_and_move_sub_mounts(
|
||||
return mount_nofollow_verbose(LOG_DEBUG, what, where, type, flags, options);
|
||||
|
||||
/* Get the list of sub-mounts and duplicate them. */
|
||||
r = get_sub_mounts(where, &mounts, &n);
|
||||
r = get_sub_mounts(where, /* clone_tree= */ true, &mounts, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1390,6 +1397,57 @@ int remount_and_move_sub_mounts(
|
||||
return move_sub_mounts(mounts, n);
|
||||
}
|
||||
|
||||
int bind_mount_submounts(
|
||||
const char *source,
|
||||
const char *target) {
|
||||
|
||||
SubMount *mounts = NULL;
|
||||
size_t n = 0;
|
||||
int ret = 0, r;
|
||||
|
||||
/* Bind mounts all child mounts of 'source' to 'target'. Useful when setting up a new procfs instance
|
||||
* with new mount options to copy the original submounts over. */
|
||||
|
||||
assert(source);
|
||||
assert(target);
|
||||
|
||||
CLEANUP_ARRAY(mounts, n, sub_mount_array_free);
|
||||
|
||||
r = get_sub_mounts(source, /* clone_tree= */ false, &mounts, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
FOREACH_ARRAY(m, mounts, n) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const char *suffix;
|
||||
|
||||
if (isempty(m->path))
|
||||
continue;
|
||||
|
||||
assert_se(suffix = path_startswith(m->path, source));
|
||||
|
||||
t = path_join(target, suffix);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
r = path_is_mount_point(t, NULL, 0);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to detect if '%s' already is a mount point, ignoring: %m", t);
|
||||
continue;
|
||||
}
|
||||
if (r > 0) {
|
||||
log_debug("Not bind mounting '%s' from '%s' to '%s', since there's already a mountpoint.", suffix, source, target);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(m->mount_fd), t, NULL, MS_BIND|MS_REC, NULL);
|
||||
if (r < 0 && ret == 0)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int remount_sysfs(const char *where) {
|
||||
return remount_and_move_sub_mounts("sysfs", where, "sysfs", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
|
||||
}
|
||||
|
@ -132,6 +132,10 @@ int remount_and_move_sub_mounts(
|
||||
const char *options);
|
||||
int remount_sysfs(const char *where);
|
||||
|
||||
int bind_mount_submounts(
|
||||
const char *source,
|
||||
const char *target);
|
||||
|
||||
/* Creates a mount point (not parents) based on the source path or stat - ie, a file or a directory */
|
||||
int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode);
|
||||
int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode);
|
||||
|
@ -568,6 +568,77 @@ TEST(fd_make_mount_point) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(bind_mount_submounts) {
|
||||
_cleanup_(rmdir_and_freep) char *a = NULL, *b = NULL;
|
||||
_cleanup_free_ char *x = NULL;
|
||||
int r;
|
||||
|
||||
assert_se(mkdtemp_malloc(NULL, &a) >= 0);
|
||||
r = mount_nofollow_verbose(LOG_INFO, "tmpfs", a, "tmpfs", 0, NULL);
|
||||
if (r < 0 && ERRNO_IS_PRIVILEGE(r)) {
|
||||
(void) log_tests_skipped("Skipping bind_mount_submounts() test, lacking privileges");
|
||||
return;
|
||||
}
|
||||
assert_se(r >= 0);
|
||||
|
||||
assert_se(x = path_join(a, "foo"));
|
||||
assert_se(touch(x) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(a, "x"));
|
||||
assert_se(mkdir(x, 0755) >= 0);
|
||||
assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", x, "tmpfs", 0, NULL) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(a, "x/xx"));
|
||||
assert_se(touch(x) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(a, "y"));
|
||||
assert_se(mkdir(x, 0755) >= 0);
|
||||
assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", x, "tmpfs", 0, NULL) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(a, "y/yy"));
|
||||
assert_se(touch(x) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(mkdtemp_malloc(NULL, &b) >= 0);
|
||||
assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", b, "tmpfs", 0, NULL) >= 0);
|
||||
|
||||
assert_se(x = path_join(b, "x"));
|
||||
assert_se(mkdir(x, 0755) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(b, "y"));
|
||||
assert_se(mkdir(x, 0755) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(bind_mount_submounts(a, b) >= 0);
|
||||
|
||||
assert_se(x = path_join(b, "foo"));
|
||||
assert_se(access(x, F_OK) < 0 && errno == ENOENT);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(b, "x/xx"));
|
||||
assert_se(access(x, F_OK) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(b, "y/yy"));
|
||||
assert_se(access(x, F_OK) >= 0);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(b, "x"));
|
||||
assert_se(path_is_mount_point(x, NULL, 0) > 0);
|
||||
free(x);
|
||||
|
||||
assert_se(x = path_join(b, "y"));
|
||||
assert_se(path_is_mount_point(x, NULL, 0) > 0);
|
||||
|
||||
assert_se(umount_recursive(a, 0) >= 0);
|
||||
assert_se(umount_recursive(b, 0) >= 0);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
/* Create a dummy network interface for testing remount_sysfs(). */
|
||||
(void) system("ip link add dummy-test-mnt type dummy");
|
||||
|
@ -211,3 +211,21 @@ for opt in nice on-{active,boot,calendar,startup,unit-active,unit-inactive} prop
|
||||
(! systemd-run "--$opt=" true)
|
||||
(! systemd-run "--$opt=''" true)
|
||||
done
|
||||
|
||||
# Let's make sure that ProtectProc= properly moves submounts of the original /proc over to the new proc
|
||||
|
||||
A=$(cat /proc/sys/kernel/random/boot_id)
|
||||
B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/sys/kernel/random/boot_id)
|
||||
assert_eq "$A" "$B"
|
||||
|
||||
V="/tmp/version.$RANDOM"
|
||||
A="$(cat /proc/version).piff"
|
||||
echo "$A" > "$V"
|
||||
mount --bind "$V" /proc/version
|
||||
|
||||
B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)
|
||||
|
||||
assert_eq "$A" "$B"
|
||||
|
||||
umount /proc/version
|
||||
rm "$V"
|
||||
|
Loading…
Reference in New Issue
Block a user