switch-root.c revision a8fbdf5424be099ba1b2b1ec261c02b8759d6b0c
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering This file is part of systemd.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Copyright 2012 Harald Hoyer, Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is free software; you can redistribute it and/or modify it
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering under the terms of the GNU Lesser General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is distributed in the hope that it will be useful, but
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Lesser General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering You should have received a copy of the GNU Lesser General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmekint switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) {
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek /* Don't try to unmount/move the old "/", there's no way to do it. */
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek static const char move_mounts[] =
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering temporary_old_root = strjoina(new_root, oldroot);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_error_errno(errno, "Failed to stat directory %s: %m", new_root);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Work-around for kernel design: the kernel refuses switching
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * root if any file systems are mounted MS_SHARED. Hence
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * remount them MS_PRIVATE here as a work-around.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_warning_errno(errno, "Failed to make \"/\" private mount: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering snprintf(new_mount, sizeof(new_mount), "%s%s", new_root, i);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Mount point seems to be mounted already or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * stat failed. Unmount the old mount
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_warning_errno(errno, "Failed to unmount %s: %m", i);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (mount(i, new_mount, NULL, mountflags, NULL) < 0) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_error_errno(errno, "Failed to move mount %s to %s, forcing unmount: %m", i, new_mount);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_warning_errno(errno, "Failed to unmount %s: %m", i);
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering log_error_errno(errno, "Failed to bind mount %s to %s: %m", i, new_mount);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Do not fail, if base_filesystem_create() fails. Not all
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * switch roots are like base_filesystem_create() wants them
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * to look like. They might even boot, if they are RO and
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * don't have the FS layout. Just ignore the error and
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering * switch_root() nevertheless. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_warning_errno(errno, "Failed to open root directory: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* We first try a pivot_root() so that we can umount the old
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering * root dir. In many cases (i.e. where rootfs is /), that's
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * not possible however, and hence we simply overmount root */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (pivot_root(new_root, temporary_old_root) >= 0) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Immediately get rid of the old root, if detach_oldroot is set.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * Since we are running off it we need to do this lazily. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (detach_oldroot && umount2(oldroot, MNT_DETACH) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_error_errno(errno, "Failed to lazily umount old root dir %s, %s: %m",
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering errno == ENOENT ? "ignoring" : "leaving it around");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_error_errno(errno, "Failed to mount moving %s to /: %m", new_root);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_error_errno(errno, "Failed to change root: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return log_error_errno(errno, "Failed to change directory: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_warning_errno(errno, "Failed to stat old root directory, leaving: %m");