mirror of
https://github.com/systemd/systemd.git
synced 2024-12-25 01:34:28 +03:00
Merge pull request #27670 from poettering/switch-root-umount-all
umount all mounts during MS_MOVE switch root
This commit is contained in:
commit
2191bcbbd6
@ -43,24 +43,28 @@
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
int umount_recursive(const char *prefix, int flags) {
|
||||
int umount_recursive_full(const char *prefix, int flags, char **keep) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int n = 0, r;
|
||||
bool again;
|
||||
|
||||
/* Try to umount everything recursively below a directory. Also, take care of stacked mounts, and
|
||||
* keep unmounting them until they are gone. */
|
||||
|
||||
do {
|
||||
f = fopen("/proc/self/mountinfo", "re"); /* Pin the file, in case we unmount /proc/ as part of the logic here */
|
||||
if (!f)
|
||||
return log_debug_errno(errno, "Failed to open /proc/self/mountinfo: %m");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
|
||||
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
|
||||
bool again = false;
|
||||
|
||||
again = false;
|
||||
|
||||
r = libmount_parse("/proc/self/mountinfo", NULL, &table, &iter);
|
||||
r = libmount_parse("/proc/self/mountinfo", f, &table, &iter);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse /proc/self/mountinfo: %m");
|
||||
|
||||
for (;;) {
|
||||
bool shall_keep = false;
|
||||
struct libmnt_fs *fs;
|
||||
const char *path;
|
||||
|
||||
@ -74,8 +78,21 @@ int umount_recursive(const char *prefix, int flags) {
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
if (!path_startswith(path, prefix))
|
||||
if (prefix && !path_startswith(path, prefix)) {
|
||||
log_debug("Not unmounting %s, outside of prefix: %s", path, prefix);
|
||||
continue;
|
||||
}
|
||||
|
||||
STRV_FOREACH(k, keep)
|
||||
/* Match against anything in the path to the dirs to keep, or below the dirs to keep */
|
||||
if (path_startswith(path, *k) || path_startswith(*k, path)) {
|
||||
shall_keep = true;
|
||||
break;
|
||||
}
|
||||
if (shall_keep) {
|
||||
log_debug("Not unmounting %s, referenced by keep list.", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (umount2(path, flags | UMOUNT_NOFOLLOW) < 0) {
|
||||
log_debug_errno(errno, "Failed to umount %s, ignoring: %m", path);
|
||||
@ -89,7 +106,12 @@ int umount_recursive(const char *prefix, int flags) {
|
||||
|
||||
break;
|
||||
}
|
||||
} while (again);
|
||||
|
||||
if (!again)
|
||||
break;
|
||||
|
||||
rewind(f);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
@ -12,7 +12,12 @@
|
||||
#include "macro.h"
|
||||
|
||||
int repeat_unmount(const char *path, int flags);
|
||||
int umount_recursive(const char *target, int flags);
|
||||
|
||||
int umount_recursive_full(const char *target, int flags, char **keep);
|
||||
|
||||
static inline int umount_recursive(const char *target, int flags) {
|
||||
return umount_recursive_full(target, flags, NULL);
|
||||
}
|
||||
|
||||
int bind_remount_recursive_with_mountinfo(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **deny_list, FILE *proc_self_mountinfo);
|
||||
static inline int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **deny_list) {
|
||||
|
@ -114,6 +114,14 @@ int switch_root(const char *new_root,
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Pivoting root file system failed, moving mounts instead: %m");
|
||||
|
||||
/* If we have to use MS_MOVE let's first try to get rid of *all* mounts we can, with the
|
||||
* exception of the path we want to switch to, plus everything leading to it and within
|
||||
* it. This is necessary because unlike pivot_root() just moving the mount to the root via
|
||||
* MS_MOVE won't magically unmount anything below it. Once the chroot() succeeds the mounts
|
||||
* below would still be around but invisible to us, because not accessible via
|
||||
* /proc/self/mountinfo. Hence, let's clean everything up first, as long as we still can. */
|
||||
(void) umount_recursive_full(NULL, MNT_DETACH, STRV_MAKE(new_root));
|
||||
|
||||
if (mount(".", "/", NULL, MS_MOVE, NULL) < 0)
|
||||
return log_error_errno(errno, "Failed to move %s to /: %m", new_root);
|
||||
|
||||
|
@ -117,7 +117,6 @@ simple_tests += files(
|
||||
'test-mempool.c',
|
||||
'test-mkdir.c',
|
||||
'test-modhex.c',
|
||||
'test-mount-util.c',
|
||||
'test-mountpoint-util.c',
|
||||
'test-net-naming-scheme.c',
|
||||
'test-nulstr-util.c',
|
||||
@ -319,6 +318,10 @@ tests += [
|
||||
'sources' : files('test-mempress.c'),
|
||||
'dependencies' : threads,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-mount-util.c'),
|
||||
'dependencies' : libmount,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-netlink-manual.c'),
|
||||
'dependencies' : libkmod,
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "libmount-util.h"
|
||||
#include "missing_magic.h"
|
||||
#include "missing_mount.h"
|
||||
#include "mkdir.h"
|
||||
@ -406,7 +407,7 @@ TEST(make_mount_switch_root) {
|
||||
assert_se(touch(s) >= 0);
|
||||
|
||||
for (int force_ms_move = 0; force_ms_move < 2; force_ms_move++) {
|
||||
r = safe_fork("(switch-root",
|
||||
r = safe_fork("(switch-root)",
|
||||
FORK_RESET_SIGNALS |
|
||||
FORK_CLOSE_ALL_FDS |
|
||||
FORK_DEATHSIG |
|
||||
@ -431,6 +432,93 @@ TEST(make_mount_switch_root) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(umount_recursive) {
|
||||
static const struct {
|
||||
const char *prefix;
|
||||
const char * const keep[3];
|
||||
} test_table[] = {
|
||||
{
|
||||
.prefix = NULL,
|
||||
.keep = {},
|
||||
},
|
||||
{
|
||||
.prefix = "/run",
|
||||
.keep = {},
|
||||
},
|
||||
{
|
||||
.prefix = NULL,
|
||||
.keep = { "/dev/shm", NULL },
|
||||
},
|
||||
{
|
||||
.prefix = "/dev",
|
||||
.keep = { "/dev/pts", "/dev/shm", NULL },
|
||||
},
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
FOREACH_ARRAY(t, test_table, ELEMENTSOF(test_table)) {
|
||||
|
||||
r = safe_fork("(umount-rec)",
|
||||
FORK_RESET_SIGNALS |
|
||||
FORK_CLOSE_ALL_FDS |
|
||||
FORK_DEATHSIG |
|
||||
FORK_WAIT |
|
||||
FORK_REOPEN_LOG |
|
||||
FORK_LOG |
|
||||
FORK_NEW_MOUNTNS |
|
||||
FORK_MOUNTNS_SLAVE,
|
||||
NULL);
|
||||
|
||||
if (r < 0 && ERRNO_IS_PRIVILEGE(r)) {
|
||||
log_notice("Skipping umount_recursive() test, lacking privileges");
|
||||
return;
|
||||
}
|
||||
|
||||
assert_se(r >= 0);
|
||||
if (r == 0) { /* child */
|
||||
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
|
||||
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *k = NULL;
|
||||
|
||||
/* Open /p/s/m file before we unmount everything (which might include /proc/) */
|
||||
f = fopen("/proc/self/mountinfo", "re");
|
||||
if (!f) {
|
||||
log_error_errno(errno, "Faile to open /proc/self/mountinfo: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
assert_se(k = strv_join((char**) t->keep, " "));
|
||||
log_info("detaching just %s (keep: %s)", strna(t->prefix), strna(empty_to_null(k)));
|
||||
|
||||
assert_se(umount_recursive_full(t->prefix, MNT_DETACH, (char**) t->keep) >= 0);
|
||||
|
||||
r = libmount_parse("/proc/self/mountinfo", f, &table, &iter);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct libmnt_fs *fs;
|
||||
|
||||
r = mnt_table_next_fs(table, iter, &fs);
|
||||
if (r == 1)
|
||||
break;
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
log_debug("left after complete umount: %s", mnt_fs_get_target(fs));
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
/* Create a dummy network interface for testing remount_sysfs(). */
|
||||
(void) system("ip link add dummy-test-mnt type dummy");
|
||||
|
Loading…
Reference in New Issue
Block a user