From 401c87a0ccb7bb72d40622ae326889cacaaf5f7a Mon Sep 17 00:00:00 2001 From: Jesse Date: Fri, 2 Aug 2019 23:18:47 +1000 Subject: [PATCH] Kernel: mount system call (#396) It is now possible to mount ext2 `DiskDevice` devices under Serenity on any folder in the root filesystem. Currently any user can do this with any permissions. There's a fair amount of assumptions made here too, that might not be too good, but can be worked on in the future. This is a good start to allow more dynamic operation under the OS itself. It is also currently impossible to unmount and such, and devices will fail to mount in Linux as the FS 'needs to be cleaned'. I'll work on getting `umount` done ASAP to rectify this (as well as working on less assumption-making in the mount syscall. We don't want to just be able to mount DiskDevices!). This could probably be fixed with some `-t` flag or something similar. --- Kernel/Devices/Device.h | 1 + Kernel/Devices/DiskDevice.cpp | 3 +- Kernel/Devices/DiskDevice.h | 7 +++-- Kernel/Devices/DiskPartition.cpp | 3 +- Kernel/Devices/DiskPartition.h | 6 ++++ Kernel/Devices/FloppyDiskDevice.cpp | 1 + Kernel/Devices/FloppyDiskDevice.h | 8 +++++- Kernel/Devices/PATAChannel.cpp | 5 ++-- Kernel/Devices/PATADiskDevice.cpp | 9 +++--- Kernel/Devices/PATADiskDevice.h | 10 +++++-- Kernel/Process.cpp | 44 ++++++++++++++++++++++++++++- Kernel/Process.h | 1 + Kernel/Syscall.cpp | 2 ++ Kernel/Syscall.h | 1 + Libraries/LibC/unistd.cpp | 6 ++++ Libraries/LibC/unistd.h | 1 + Userland/mount.cpp | 29 +++++++++++++++++++ 17 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 Userland/mount.cpp diff --git a/Kernel/Devices/Device.h b/Kernel/Devices/Device.h index fbe899b55c..467a81e5ad 100644 --- a/Kernel/Devices/Device.h +++ b/Kernel/Devices/Device.h @@ -25,6 +25,7 @@ public: uid_t gid() const { return m_gid; } virtual bool is_device() const override { return true; } + virtual bool is_disk_device() const { return false; } protected: Device(unsigned major, unsigned minor); diff --git a/Kernel/Devices/DiskDevice.cpp b/Kernel/Devices/DiskDevice.cpp index 7541e5f01e..9fc7851497 100644 --- a/Kernel/Devices/DiskDevice.cpp +++ b/Kernel/Devices/DiskDevice.cpp @@ -1,6 +1,7 @@ #include -DiskDevice::DiskDevice() +DiskDevice::DiskDevice(int major, int minor) + : BlockDevice(major, minor) { } diff --git a/Kernel/Devices/DiskDevice.h b/Kernel/Devices/DiskDevice.h index 9912d80b89..873a91c5d7 100644 --- a/Kernel/Devices/DiskDevice.h +++ b/Kernel/Devices/DiskDevice.h @@ -2,11 +2,12 @@ #include #include +#include // FIXME: Support 64-bit DiskOffset typedef u32 DiskOffset; -class DiskDevice : public RefCounted { +class DiskDevice : public BlockDevice { public: virtual ~DiskDevice(); @@ -20,6 +21,8 @@ public: virtual bool read_blocks(unsigned index, u16 count, u8*) = 0; virtual bool write_blocks(unsigned index, u16 count, const u8*) = 0; + virtual bool is_disk_device() const override { return true; }; + protected: - DiskDevice(); + DiskDevice(int major, int minor); }; diff --git a/Kernel/Devices/DiskPartition.cpp b/Kernel/Devices/DiskPartition.cpp index 94921c8407..562ca39e6e 100644 --- a/Kernel/Devices/DiskPartition.cpp +++ b/Kernel/Devices/DiskPartition.cpp @@ -8,7 +8,8 @@ NonnullRefPtr DiskPartition::create(NonnullRefPtr dev } DiskPartition::DiskPartition(NonnullRefPtr device, unsigned block_offset) - : m_device(move(device)) + : DiskDevice(100, 0) + , m_device(move(device)) , m_block_offset(block_offset) { } diff --git a/Kernel/Devices/DiskPartition.h b/Kernel/Devices/DiskPartition.h index f4e83697c5..dfdeab4c74 100644 --- a/Kernel/Devices/DiskPartition.h +++ b/Kernel/Devices/DiskPartition.h @@ -14,6 +14,12 @@ public: virtual bool read_blocks(unsigned index, u16 count, u8*) override; virtual bool write_blocks(unsigned index, u16 count, const u8*) override; + // ^BlockDevice + virtual ssize_t read(FileDescription&, u8*, ssize_t) override { return 0; } + virtual bool can_read(FileDescription&) const override { return true; } + virtual ssize_t write(FileDescription&, const u8*, ssize_t) override { return 0; } + virtual bool can_write(FileDescription&) const override { return true; } + private: virtual const char* class_name() const override; diff --git a/Kernel/Devices/FloppyDiskDevice.cpp b/Kernel/Devices/FloppyDiskDevice.cpp index a895359950..de8f03ab87 100644 --- a/Kernel/Devices/FloppyDiskDevice.cpp +++ b/Kernel/Devices/FloppyDiskDevice.cpp @@ -84,6 +84,7 @@ const char* FloppyDiskDevice::class_name() const FloppyDiskDevice::FloppyDiskDevice(FloppyDiskDevice::DriveType type) : IRQHandler(IRQ_FLOPPY_DRIVE) + , DiskDevice(89, (type == FloppyDiskDevice::DriveType::Master) ? 0 : 1) , m_io_base_addr((type == FloppyDiskDevice::DriveType::Master) ? 0x3F0 : 0x370) { initialize(); diff --git a/Kernel/Devices/FloppyDiskDevice.h b/Kernel/Devices/FloppyDiskDevice.h index 8120415d68..441bf3a5be 100644 --- a/Kernel/Devices/FloppyDiskDevice.h +++ b/Kernel/Devices/FloppyDiskDevice.h @@ -132,7 +132,7 @@ private: }; public: - static NonnullRefPtr create(DriveType type); + static NonnullRefPtr create(DriveType); virtual ~FloppyDiskDevice() override; // ^DiskDevice @@ -142,6 +142,12 @@ public: virtual bool read_blocks(unsigned index, u16 count, u8*) override; virtual bool write_blocks(unsigned index, u16 count, const u8*) override; + // ^BlockDevice + virtual ssize_t read(FileDescription&, u8*, ssize_t) override { return 0; } + virtual bool can_read(FileDescription&) const override { return true; } + virtual ssize_t write(FileDescription&, const u8*, ssize_t) override { return 0; } + virtual bool can_write(FileDescription&) const override { return true; } + protected: explicit FloppyDiskDevice(DriveType); diff --git a/Kernel/Devices/PATAChannel.cpp b/Kernel/Devices/PATAChannel.cpp index 8847505636..ece7e0619a 100644 --- a/Kernel/Devices/PATAChannel.cpp +++ b/Kernel/Devices/PATAChannel.cpp @@ -236,11 +236,12 @@ void PATAChannel::detect_disks() heads, spt); + int major = (m_channel_number == 0) ? 3 : 4; if (i == 0) { - m_master = PATADiskDevice::create(*this, PATADiskDevice::DriveType::Master); + m_master = PATADiskDevice::create(*this, PATADiskDevice::DriveType::Master, major, 0); m_master->set_drive_geometry(cyls, heads, spt); } else { - m_slave = PATADiskDevice::create(*this, PATADiskDevice::DriveType::Slave); + m_slave = PATADiskDevice::create(*this, PATADiskDevice::DriveType::Slave, major, 1); m_slave->set_drive_geometry(cyls, heads, spt); } } diff --git a/Kernel/Devices/PATADiskDevice.cpp b/Kernel/Devices/PATADiskDevice.cpp index 69b50e08c2..627b952180 100644 --- a/Kernel/Devices/PATADiskDevice.cpp +++ b/Kernel/Devices/PATADiskDevice.cpp @@ -78,13 +78,14 @@ #define ATA_REG_ALTSTATUS 0x0C #define ATA_REG_DEVADDRESS 0x0D -NonnullRefPtr PATADiskDevice::create(PATAChannel& channel, DriveType type) +NonnullRefPtr PATADiskDevice::create(PATAChannel& channel, DriveType type, int major, int minor) { - return adopt(*new PATADiskDevice(channel, type)); + return adopt(*new PATADiskDevice(channel, type, major, minor)); } -PATADiskDevice::PATADiskDevice(PATAChannel& channel, DriveType type) - : m_drive_type(type) +PATADiskDevice::PATADiskDevice(PATAChannel& channel, DriveType type, int major, int minor) + : DiskDevice(major, minor) + , m_drive_type(type) , m_channel(channel) { } diff --git a/Kernel/Devices/PATADiskDevice.h b/Kernel/Devices/PATADiskDevice.h index 3d3c4928d4..cd23f705e1 100644 --- a/Kernel/Devices/PATADiskDevice.h +++ b/Kernel/Devices/PATADiskDevice.h @@ -26,7 +26,7 @@ public: }; public: - static NonnullRefPtr create(PATAChannel&, DriveType); + static NonnullRefPtr create(PATAChannel&, DriveType, int major, int minor); virtual ~PATADiskDevice() override; // ^DiskDevice @@ -38,8 +38,14 @@ public: void set_drive_geometry(u16, u16, u16); + // ^BlockDevice + virtual ssize_t read(FileDescription&, u8*, ssize_t) override { return 0; } + virtual bool can_read(FileDescription&) const override { return true; } + virtual ssize_t write(FileDescription&, const u8*, ssize_t) override { return 0; } + virtual bool can_write(FileDescription&) const override { return true; } + protected: - explicit PATADiskDevice(PATAChannel&, DriveType); + explicit PATADiskDevice(PATAChannel&, DriveType, int, int); private: // ^DiskDevice diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index f0d58004cb..bbbe442013 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1900,7 +1901,7 @@ int Process::sys$poll(pollfd* fds, int nfds, int timeout) dbgprintf("%s<%u> polling on (read:%u, write:%u), timeout=%d\n", name().characters(), pid(), rfds.size(), wfds.size(), timeout); #endif - if (has_timeout|| timeout < 0) { + if (has_timeout || timeout < 0) { if (current->block(actual_timeout, has_timeout, rfds, wfds, Thread::SelectBlocker::FDVector()) == Thread::BlockResult::InterruptedBySignal) return -EINTR; } @@ -2726,6 +2727,47 @@ int Process::sys$reboot() return ESUCCESS; } +int Process::sys$mount(const char* device, const char* mountpoint) +{ + dbgprintf("mount: device = %s mountpoint = %s\n", device, mountpoint); + if (!validate_read_str(device) || !validate_read_str(mountpoint)) + return -EFAULT; + + // Let's do a stat to get some information about the device.. + // Let's use lstat so we can convert devname into a major/minor device pair! + struct stat st; + int rc = sys$stat(device, &st); + if (rc < 0) { + dbgprintf("mount: call to sys$stat failed!\n"); + return -ENODEV; + } + + int major = (st.st_rdev & 0xfff00u) >> 8u; + int minor = (st.st_rdev & 0xffu) | ((st.st_rdev >> 12u) & 0xfff00u); + + auto* dev = VFS::the().get_device(major, minor); + auto* disk_device = static_cast(dev); + + dbgprintf("mount: attempting to mount %d,%d to %s...\n", major, minor, mountpoint); + + // Do a quick check to make sure we're not passing nullptr to Ext2FS::create! + if (dev == nullptr) + return -ENODEV; + + // We currently only support ext2. Sorry :^) + auto ext2fs = Ext2FS::create(*disk_device); + if (!ext2fs->initialize()) { + dbgprintf("mount: failed to create ext2fs!\n"); + return -ENODEV; // Hmmm, this doesn't seem quite right.... + } + + // Let's mount the volume now + VFS::the().mount(ext2fs, mountpoint); + dbgprintf("mount: successfully mounted ext2 volume on %s to %s!\n", device, mountpoint); + + return ESUCCESS; +} + ProcessTracer& Process::ensure_tracer() { if (!m_tracer) diff --git a/Kernel/Process.h b/Kernel/Process.h index e114bc0cbf..dfbbfc4771 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -183,6 +183,7 @@ public: int sys$unlink(const char* pathname); 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$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 432a593bba..b378367ea2 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -292,6 +292,8 @@ static u32 handle(RegisterDump& regs, u32 function, u32 arg1, u32 arg2, u32 arg3 return current->process().sys$halt(); break; } + case Syscall::SC_mount: + return current->process().sys$mount((const char*)arg1, (const char*)arg2); case Syscall::SC_reboot: { return current->process().sys$reboot(); } diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 44ba85cd88..0451c415da 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -116,6 +116,7 @@ struct timeval; __ENUMERATE_SYSCALL(fchown) \ __ENUMERATE_SYSCALL(halt) \ __ENUMERATE_SYSCALL(reboot) \ + __ENUMERATE_SYSCALL(mount) \ __ENUMERATE_SYSCALL(dump_backtrace) \ __ENUMERATE_SYSCALL(dbgputch) \ __ENUMERATE_SYSCALL(dbgputstr) \ diff --git a/Libraries/LibC/unistd.cpp b/Libraries/LibC/unistd.cpp index 922039c09f..5d9e9bb97d 100644 --- a/Libraries/LibC/unistd.cpp +++ b/Libraries/LibC/unistd.cpp @@ -546,6 +546,12 @@ int reboot() __RETURN_WITH_ERRNO(rc, rc, -1); } +int mount(const char* device, const char* mountpoint) +{ + int rc = syscall(SC_mount, device, 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 b4b8928e9c..567f098c82 100644 --- a/Libraries/LibC/unistd.h +++ b/Libraries/LibC/unistd.h @@ -98,6 +98,7 @@ int fchown(int fd, uid_t, gid_t); int ftruncate(int fd, off_t length); int halt(); int reboot(); +int mount(const char* device, const char* mountpoint); enum { _PC_NAME_MAX, diff --git a/Userland/mount.cpp b/Userland/mount.cpp new file mode 100644 index 0000000000..751063799e --- /dev/null +++ b/Userland/mount.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +// This version of 'mount' must have the following arguments +// 'mount +// It can currently only mount _physical_ devices found in /dev +// +// Currently, it is only possible to mount ext2 devices. Sorry! :^) +int main(int argc, char** argv) +{ + CArgsParser args_parser("mount"); + args_parser.add_arg("devname", "device path"); + args_parser.add_arg("mountpoint", "mount point"); + CArgsParserResult args = args_parser.parse(argc, argv); + + if (argc == 3) { + // Let's use lstat so we can convert devname into a major/minor device pair! + if (mount(argv[1], argv[2]) < 0) { + perror("mount"); + return 1; + } + } else { + args_parser.print_usage(); + return 0; + } + + return 0; +}