From bc22456f89bed0dd2ead58a7157af0d7673b4a7e Mon Sep 17 00:00:00 2001 From: Jesse Buhagiar Date: Sun, 11 Aug 2019 23:56:39 +1000 Subject: [PATCH] Kernel: Added unmount ability to VFS It is now possible to unmount file systems from the VFS via `umount`. It works via looking up the `fsid` of the filesystem from the `Inode`'s metatdata so I'm not sure how fragile it is. It seems to work for now though as something to get us going. --- Kernel/FileSystem/Ext2FileSystem.cpp | 13 +++++++++++++ Kernel/FileSystem/Ext2FileSystem.h | 2 ++ Kernel/FileSystem/FileSystem.h | 4 +++- Kernel/FileSystem/VirtualFileSystem.cpp | 23 +++++++++++++++++++++++ Kernel/FileSystem/VirtualFileSystem.h | 3 +++ Kernel/Process.cpp | 19 +++++++++++++++++++ Kernel/Process.h | 1 + Kernel/Syscall.cpp | 4 +++- Kernel/Syscall.h | 1 + Kernel/build-root-filesystem.sh | 2 +- Libraries/LibC/unistd.cpp | 6 ++++++ Libraries/LibC/unistd.h | 1 + Userland/umount.cpp | 22 ++++++++++++++++++++++ 13 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 Userland/umount.cpp diff --git a/Kernel/FileSystem/Ext2FileSystem.cpp b/Kernel/FileSystem/Ext2FileSystem.cpp index 9a36110752..9fa73c2500 100644 --- a/Kernel/FileSystem/Ext2FileSystem.cpp +++ b/Kernel/FileSystem/Ext2FileSystem.cpp @@ -1395,3 +1395,16 @@ unsigned Ext2FS::free_inode_count() const LOCKER(m_lock); return super_block().s_free_inodes_count; } + +KResult Ext2FS::prepare_to_unmount() const +{ + LOCKER(m_lock); // Acquire lock for this FS + for (auto it = m_inode_cache.begin(); it != m_inode_cache.end(); ++it) { + if (it->value.ptr()->ref_count() > 1) + return KResult(-EBUSY); + } + + dbg() << "here!"; + m_inode_cache.clear(); + return KSuccess; +} diff --git a/Kernel/FileSystem/Ext2FileSystem.h b/Kernel/FileSystem/Ext2FileSystem.h index 43b184b84e..3cc7f19716 100644 --- a/Kernel/FileSystem/Ext2FileSystem.h +++ b/Kernel/FileSystem/Ext2FileSystem.h @@ -69,6 +69,8 @@ public: virtual unsigned total_inode_count() const override; virtual unsigned free_inode_count() const override; + virtual KResult prepare_to_unmount() const override; + private: typedef unsigned BlockIndex; typedef unsigned GroupIndex; diff --git a/Kernel/FileSystem/FileSystem.h b/Kernel/FileSystem/FileSystem.h index 1e3df8df0f..0c8bad2860 100644 --- a/Kernel/FileSystem/FileSystem.h +++ b/Kernel/FileSystem/FileSystem.h @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -45,6 +45,8 @@ public: virtual unsigned total_inode_count() const { return 0; } virtual unsigned free_inode_count() const { return 0; } + virtual KResult prepare_to_unmount() const { return KSuccess; } + struct DirectoryEntry { DirectoryEntry(const char* name, InodeIdentifier, u8 file_type); DirectoryEntry(const char* name, int name_length, InodeIdentifier, u8 file_type); diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index 323ae9e106..4053b68c18 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -49,6 +49,7 @@ KResult VFS::mount(NonnullRefPtr&& file_system, Custody& mount_point) KResult VFS::mount(NonnullRefPtr&& file_system, StringView path) { + LOCKER(m_lock); auto result = resolve_path(path, root_custody()); if (result.is_error()) { dbg() << "VFS: mount can't resolve mount point '" << path << "'"; @@ -57,6 +58,28 @@ KResult VFS::mount(NonnullRefPtr&& file_system, StringView path) return mount(move(file_system), result.value()); } +KResult VFS::unmount(NonnullRefPtr&& file_system) +{ + LOCKER(m_lock); + dbg() << "VFS: unmount called with fsid " << file_system.ptr()->fsid(); + + for (auto i = 0; i < m_mounts.size(); i++) { + auto mount = m_mounts.at(i); + if (mount.guest_fs().fsid() == file_system.ptr()->fsid()) { + if (mount.guest_fs().prepare_to_unmount() != KSuccess) { + dbg() << "VFS: Failed to unmount! Device busy"; + return KResult(-EBUSY); + } + dbg() << "VFS: found fs " << file_system.ptr()->fsid() << " at mount " << i << "! Unmounting..."; + m_mounts.remove(i); + return KSuccess; + } + } + + dbg() << "VFS: unmount unable to find fsid in m_mounts!"; + return KResult(-ENODEV); +} + bool VFS::mount_root(NonnullRefPtr&& file_system) { if (m_root_inode) { diff --git a/Kernel/FileSystem/VirtualFileSystem.h b/Kernel/FileSystem/VirtualFileSystem.h index 29064c4a65..8887fba92c 100644 --- a/Kernel/FileSystem/VirtualFileSystem.h +++ b/Kernel/FileSystem/VirtualFileSystem.h @@ -59,6 +59,7 @@ public: bool mount_root(NonnullRefPtr&&); KResult mount(NonnullRefPtr&&, StringView path); KResult mount(NonnullRefPtr&&, Custody& mount_point); + KResult unmount(NonnullRefPtr&&); KResultOr> open(StringView path, int options, mode_t mode, Custody& base); KResultOr> create(StringView path, int options, mode_t mode, Custody& parent_custody); @@ -105,6 +106,8 @@ private: Mount* find_mount_for_host(InodeIdentifier); Mount* find_mount_for_guest(InodeIdentifier); + Lock m_lock { "VFSLock" }; + RefPtr m_root_inode; NonnullOwnPtrVector m_mounts; HashMap m_devices; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 980958d6e1..f51a86a091 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2786,6 +2786,25 @@ int Process::sys$mount(const char* device_path, const char* mountpoint) return result; } +int Process::sys$umount(const char* mountpoint) +{ + if (!is_superuser()) + return -EPERM; + + if (!validate_read_str(mountpoint)) + return -EFAULT; + + auto metadata_or_error = VFS::the().lookup_metadata(mountpoint, current_directory()); + if (metadata_or_error.is_error()) + return metadata_or_error.error(); + + auto fsid = metadata_or_error.value().inode.fsid(); + auto fs = Ext2FS::from_fsid(fsid); + auto ret = VFS::the().unmount(*fs); + + return ret; +} + ProcessTracer& Process::ensure_tracer() { if (!m_tracer) diff --git a/Kernel/Process.h b/Kernel/Process.h index 2b239d68dd..dc268d0cca 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -187,6 +187,7 @@ public: int sys$symlink(const char* target, const char* linkpath); int sys$rmdir(const char* pathname); int sys$mount(const char* device, const char* mountpoint); + int sys$umount(const char* mountpoint); int sys$read_tsc(u32* lsw, u32* msw); int sys$chmod(const char* pathname, mode_t); int sys$fchmod(int fd, mode_t); diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 417f6b1c36..54d8300c5d 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -189,7 +189,7 @@ static u32 handle(RegisterDump& regs, u32 function, u32 arg1, u32 arg2, u32 arg3 case Syscall::SC_sigprocmask: return current->process().sys$sigprocmask((int)arg1, (const sigset_t*)arg2, (sigset_t*)arg3); case Syscall::SC_pipe: - return current->process().sys$pipe((int*)arg1, (int) arg2); + return current->process().sys$pipe((int*)arg1, (int)arg2); case Syscall::SC_killpg: return current->process().sys$killpg((int)arg1, (int)arg2); case Syscall::SC_setuid: @@ -297,6 +297,8 @@ static u32 handle(RegisterDump& regs, u32 function, u32 arg1, u32 arg2, u32 arg3 case Syscall::SC_reboot: { return current->process().sys$reboot(); } + case Syscall::SC_umount: + return current->process().sys$umount((const char*)arg1); case Syscall::SC_dump_backtrace: return current->process().sys$dump_backtrace(); case Syscall::SC_watch_file: diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 4355250a0c..92ebf16675 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -117,6 +117,7 @@ struct timeval; __ENUMERATE_SYSCALL(halt) \ __ENUMERATE_SYSCALL(reboot) \ __ENUMERATE_SYSCALL(mount) \ + __ENUMERATE_SYSCALL(umount) \ __ENUMERATE_SYSCALL(dump_backtrace) \ __ENUMERATE_SYSCALL(dbgputch) \ __ENUMERATE_SYSCALL(dbgputstr) \ diff --git a/Kernel/build-root-filesystem.sh b/Kernel/build-root-filesystem.sh index 903231b569..f75a7c9305 100755 --- a/Kernel/build-root-filesystem.sh +++ b/Kernel/build-root-filesystem.sh @@ -15,7 +15,7 @@ if [ $(id -u) != 0 ]; then fi echo -n "creating initial filesystem structure... " -mkdir -p mnt/{bin,etc,proc,tmp} +mkdir -p mnt/{bin,etc,proc,mnt,tmp} chmod 1777 mnt/tmp echo "done" diff --git a/Libraries/LibC/unistd.cpp b/Libraries/LibC/unistd.cpp index e0f0971e9f..9b983fe900 100644 --- a/Libraries/LibC/unistd.cpp +++ b/Libraries/LibC/unistd.cpp @@ -557,6 +557,12 @@ int mount(const char* device, const char* mountpoint) __RETURN_WITH_ERRNO(rc, rc, -1); } +int umount(const char* mountpoint) +{ + int rc = syscall(SC_umount, mountpoint); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + void dump_backtrace() { syscall(SC_dump_backtrace); diff --git a/Libraries/LibC/unistd.h b/Libraries/LibC/unistd.h index f8f1dce672..71bd365eba 100644 --- a/Libraries/LibC/unistd.h +++ b/Libraries/LibC/unistd.h @@ -101,6 +101,7 @@ int ftruncate(int fd, off_t length); int halt(); int reboot(); int mount(const char* device, const char* mountpoint); +int umount(const char* mountpoint); enum { _PC_NAME_MAX, diff --git a/Userland/umount.cpp b/Userland/umount.cpp new file mode 100644 index 0000000000..31fb472559 --- /dev/null +++ b/Userland/umount.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +int main(int argc, char** argv) +{ + CArgsParser args_parser("umount"); + args_parser.add_arg("mountpoint", "mount point"); + CArgsParserResult args = args_parser.parse(argc, argv); + + if (argc == 2) { + if (umount(argv[1]) < 0) { + perror("umount"); + return 1; + } + } else { + args_parser.print_usage(); + return 0; + } + + return 0; +}