From e79d4297ea7fede05c53e7763dfd5e0d77b59a16 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 17 Jun 2023 17:27:22 +0200 Subject: [PATCH] kernel: Make the root inode be a mountpoint as well + add pivot_root() --- apps/CMakeLists.txt | 1 + apps/pivot_root.cpp | 19 +++++++++++++ kernel/src/fs/Mount.cpp | 14 +++++---- kernel/src/fs/Mount.h | 19 ++++++++++++- kernel/src/fs/VFS.cpp | 52 ++++++++++++++++++++++++++++++---- kernel/src/fs/VFS.h | 1 + kernel/src/sys/mount.cpp | 13 +++++++++ libluna/include/luna/Syscall.h | 3 +- 8 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 apps/pivot_root.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 489f69e7..5d6ef751 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -31,3 +31,4 @@ luna_app(time.cpp time) luna_app(ln.cpp ln) luna_app(mktemp.cpp mktemp) luna_app(sysfuzz.cpp sysfuzz) +luna_app(pivot_root.cpp pivot_root) diff --git a/apps/pivot_root.cpp b/apps/pivot_root.cpp new file mode 100644 index 00000000..ea5875ca --- /dev/null +++ b/apps/pivot_root.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +Result luna_main(int argc, char** argv) +{ + StringView new_root; + StringView put_old; + + os::ArgumentParser parser; + parser.add_description("Move the current root directory to another directory and replace it with another mount."); + parser.add_system_program_info("pivot_root"_sv); + parser.add_positional_argument(new_root, "new_root", true); + parser.add_positional_argument(put_old, "put_old", true); + parser.parse(argc, argv); + + long rc = syscall(SYS_pivot_root, new_root.chars(), put_old.chars()); + return Result::from_syscall(rc); +} diff --git a/kernel/src/fs/Mount.cpp b/kernel/src/fs/Mount.cpp index a62e8a62..5be4809a 100644 --- a/kernel/src/fs/Mount.cpp +++ b/kernel/src/fs/Mount.cpp @@ -6,14 +6,18 @@ Result> MountInode::create(SharedPtr source, S { auto inode = TRY(adopt_shared_if_nonnull(new (std::nothrow) MountInode())); - inode->m_source = source; inode->m_mountee = fs; inode->m_mount_root_inode = fs->root_inode(); - auto parent = TRY(source->find("..")); - TRY(fs->set_mount_dir(parent)); + if (source) + { + inode->m_source = source; - source->add_handle(); + auto parent = TRY(source->find("..")); + TRY(fs->set_mount_dir(parent)); + + source->add_handle(); + } g_mounts.append(inode.ptr()); @@ -22,5 +26,5 @@ Result> MountInode::create(SharedPtr source, S MountInode::~MountInode() { - m_source->remove_handle(); + if (m_source) m_source->remove_handle(); } diff --git a/kernel/src/fs/Mount.h b/kernel/src/fs/Mount.h index 97f31758..e3f74faf 100644 --- a/kernel/src/fs/Mount.h +++ b/kernel/src/fs/Mount.h @@ -142,10 +142,27 @@ class MountInode : public VFS::Inode, public LinkedListNode return m_mount_root_inode->replace_entry(inode, name); } + Result set_source(SharedPtr source) + { + if (m_source) m_source->remove_handle(); + + m_source = source; + + if (source) + { + auto parent = TRY(source->find("..")); + TRY(m_mountee->set_mount_dir(parent)); + + source->add_handle(); + } + + return {}; + } + virtual ~MountInode(); private: - SharedPtr m_source; + SharedPtr m_source {}; SharedPtr m_mountee; SharedPtr m_mount_root_inode; diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index 739b90e9..3de5af10 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -8,11 +8,11 @@ namespace VFS { - SharedPtr root_fs; + SharedPtr g_root_inode = {}; Inode& root_inode() { - return *root_fs->root_inode(); + return *g_root_inode; } static constexpr int MAX_SYMLINKS = 8; @@ -40,7 +40,7 @@ namespace VFS SharedPtr symlink_root; - if (PathParser::is_absolute(link.chars())) symlink_root = root_fs->root_inode(); + if (PathParser::is_absolute(link.chars())) symlink_root = g_root_inode; else symlink_root = parent_inode; @@ -60,7 +60,7 @@ namespace VFS { SharedPtr current_inode; - if (PathParser::is_absolute(path) || !working_directory) current_inode = root_fs->root_inode(); + if (PathParser::is_absolute(path) || !working_directory) current_inode = g_root_inode; else current_inode = working_directory; @@ -180,7 +180,47 @@ namespace VFS Result mount_root(SharedPtr fs) { - root_fs = fs; + check(!g_root_inode); + + g_root_inode = TRY(MountInode::create({}, fs)); + + return {}; + } + + Result pivot_root(const char* new_root, const char* put_old, SharedPtr working_directory) + { + auto root_parser = TRY(PathParser::create(new_root)); + auto new_root_parent = TRY(root_parser.dirname()); + auto new_root_path = TRY(root_parser.basename()); + + auto new_root_parent_inode = TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, working_directory)); + auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars())); + + if (new_root_inode->type() != VFS::InodeType::Directory) return err(ENOTDIR); + if (!new_root_inode->is_mountpoint()) return err(EINVAL); + if (new_root_inode->fs() == g_root_inode->fs()) return err(EBUSY); + + auto parser = TRY(PathParser::create(put_old)); + auto parent_path = TRY(parser.dirname()); + auto child = TRY(parser.basename()); + + kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root); + + auto parent_inode = TRY(resolve_path(parent_path.chars(), Credentials {}, working_directory)); + + auto inode = TRY(parent_inode->find(child.chars())); + if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR); + if (inode->is_mountpoint()) return err(EBUSY); + if (inode->fs() != new_root_inode->fs()) return err(EINVAL); + + auto mount = g_root_inode; + + TRY(parent_inode->replace_entry(mount, child.chars())); + ((MountInode*)mount.ptr())->set_source(inode); + + g_root_inode = new_root_inode; + TRY(new_root_parent_inode->replace_entry(((MountInode*)g_root_inode.ptr())->source(), new_root_path.chars())); + ((MountInode*)g_root_inode.ptr())->set_source({}); return {}; } @@ -213,6 +253,8 @@ namespace VFS auto parent_path = TRY(parser.dirname()); auto child = TRY(parser.basename()); + if (child.view() == "/") return err(EBUSY); + kdbgln("vfs: Unmounting filesystem on target %s", path); auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory)); diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index d47aafe2..4533033c 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -297,6 +297,7 @@ namespace VFS Inode& root_inode(); Result mount_root(SharedPtr fs); + Result pivot_root(const char* new_root, const char* put_old, SharedPtr working_directory); Result mount(const char* path, SharedPtr fs, Credentials auth, SharedPtr working_directory = {}); diff --git a/kernel/src/sys/mount.cpp b/kernel/src/sys/mount.cpp index 661e0187..e9e6a6c5 100644 --- a/kernel/src/sys/mount.cpp +++ b/kernel/src/sys/mount.cpp @@ -36,3 +36,16 @@ Result sys_umount(Registers*, SyscallArgs args) return 0; } + +Result sys_pivot_root(Registers*, SyscallArgs args) +{ + auto new_root = TRY(MemoryManager::strdup_from_user(args[0])); + auto put_old = TRY(MemoryManager::strdup_from_user(args[1])); + + auto* current = Scheduler::current(); + if (current->auth.euid != 0) return err(EPERM); + + TRY(VFS::pivot_root(new_root.chars(), put_old.chars(), current->current_directory)); + + return 0; +} diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 9f7f4284..5a6d6609 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -5,7 +5,8 @@ _e(lseek) _e(mkdir) _e(execve) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) _e(getuid) _e(geteuid) \ _e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(fchmodat) _e(fchownat) _e(ioctl) \ _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \ - _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) + _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ + _e(pivot_root) enum Syscalls {