diff --git a/kernel/aster-nix/src/fs/mod.rs b/kernel/aster-nix/src/fs/mod.rs index 32114b732..9c6cefcc4 100644 --- a/kernel/aster-nix/src/fs/mod.rs +++ b/kernel/aster-nix/src/fs/mod.rs @@ -23,6 +23,7 @@ use crate::{ exfat::{ExfatFS, ExfatMountOptions}, ext2::Ext2, fs_resolver::FsPath, + utils::FileSystem, }, prelude::*, thread::kernel_thread::KernelThreadExt, @@ -64,3 +65,26 @@ pub fn lazy_init() { self::rootfs::mount_fs_at(exfat_fs, &target_path).unwrap(); } } + +pub fn get_fs(fs_type: &str) -> Result> { + match fs_type { + "ext2" => { + if let Ok(block_device_ext2) = start_block_device("vext2") { + let ext2_fs = Ext2::open(block_device_ext2).unwrap(); + Ok(ext2_fs) + } else { + return_errno_with_message!(Errno::EINVAL, "Ext2 fs does not exist") + } + } + "exfat" => { + if let Ok(block_device_exfat) = start_block_device("vexfat") { + let exfat_fs = + ExfatFS::open(block_device_exfat, ExfatMountOptions::default()).unwrap(); + Ok(exfat_fs) + } else { + return_errno_with_message!(Errno::EINVAL, "Exfat fs dose not exist") + } + } + _ => return_errno_with_message!(Errno::EINVAL, "Invalid fs type"), + } +} diff --git a/kernel/aster-nix/src/fs/path/dentry.rs b/kernel/aster-nix/src/fs/path/dentry.rs index 9b3f0ed9b..8decb7d6c 100644 --- a/kernel/aster-nix/src/fs/path/dentry.rs +++ b/kernel/aster-nix/src/fs/path/dentry.rs @@ -104,6 +104,18 @@ impl Dentry_ { DentryFlags::from_bits(flags).unwrap() } + /// Check if the Dentry_ is a subdir of the given Dentry_. + pub fn is_subdir(&self, dentry: Arc) -> bool { + let mut parent = self.parent(); + while let Some(p) = parent { + if Arc::ptr_eq(&p, &dentry) { + return true; + } + parent = p.parent(); + } + false + } + pub fn is_mountpoint(&self) -> bool { self.flags().contains(DentryFlags::MOUNTED) } @@ -561,7 +573,7 @@ impl Dentry { /// Make this Dentry' inner to be a mountpoint, /// and set the mountpoint of the child mount to this Dentry's inner. - fn set_mountpoint(&self, child_mount: Arc) { + pub fn set_mountpoint(&self, child_mount: Arc) { child_mount.set_mountpoint_dentry(self.inner.clone()); self.inner.set_mountpoint_dentry(); } @@ -638,6 +650,14 @@ impl Dentry { self.inner.rename(old_name, &new_dir.inner, new_name) } + pub fn do_loopback(&self, recursive: bool) -> Arc { + if recursive { + self.mount_node.copy_mount_tree(self.inner.clone()) + } else { + self.mount_node.clone_mount(self.inner.clone()) + } + } + /// Get the arc reference to self. fn this(&self) -> Arc { self.this.upgrade().unwrap() @@ -669,4 +689,6 @@ impl Dentry { pub fn set_mtime(&self, time: Duration); pub fn key(&self) -> DentryKey; pub fn inode(&self) -> &Arc; + pub fn is_root_of_mount(&self) -> bool; + pub fn is_mountpoint(&self) -> bool; } diff --git a/kernel/aster-nix/src/fs/path/mount.rs b/kernel/aster-nix/src/fs/path/mount.rs index 56403d64a..9f29cf75c 100644 --- a/kernel/aster-nix/src/fs/path/mount.rs +++ b/kernel/aster-nix/src/fs/path/mount.rs @@ -97,6 +97,91 @@ impl MountNode { Ok(child_mount) } + /// Clone a mount node with the an root Dentry_. + /// The new mount node will have the same fs as the original one. + /// The new mount node root Dentry_ will be the given one. + /// The new mount node will have no parent and children. + /// We should set the parent and children manually. + pub fn clone_mount(&self, root_dentry: Arc) -> Arc { + Arc::new_cyclic(|weak_self| Self { + root_dentry, + mountpoint_dentry: RwLock::new(None), + parent: RwLock::new(None), + children: Mutex::new(BTreeMap::new()), + fs: self.fs.clone(), + this: weak_self.clone(), + }) + } + + /// Copy a mount tree with the given root Dentry_. + /// The new mount tree will have the same structure as the original one. + /// But the new mount tree is a subtree which is rooted at the given root Dentry_. + /// The new mount tree is a new tree, which means the original one is not affected. + pub fn copy_mount_tree(&self, root_dentry: Arc) -> Arc { + let new_root_mount = self.clone_mount(root_dentry); + let mut stack = vec![self.this()]; + let mut new_stack = vec![new_root_mount.clone()]; + + let mut dentry = new_root_mount.root_dentry.clone(); + while let Some(old_mount) = stack.pop() { + let new_parent_mount = new_stack.pop().unwrap().clone(); + let old_children = old_mount.children.lock(); + for old_child_mount in old_children.values() { + let mountpoint_dentry = old_child_mount.mountpoint_dentry().unwrap(); + if !dentry.is_subdir(mountpoint_dentry.clone()) { + continue; + } + stack.push(old_child_mount.clone()); + let new_child_mount = + old_child_mount.clone_mount(old_child_mount.root_dentry.clone()); + let key = mountpoint_dentry.key(); + new_parent_mount + .children + .lock() + .insert(key, new_child_mount.clone()); + new_child_mount.set_parent(new_parent_mount.clone()); + new_child_mount + .set_mountpoint_dentry(old_child_mount.mountpoint_dentry().unwrap().clone()); + new_stack.push(new_child_mount.clone()); + dentry = new_child_mount.root_dentry.clone(); + } + } + new_root_mount.clone() + } + + /// Unattach the mount node from the parent mount node. + fn unattach_mount(&self) { + if let Some(parent) = self.parent() { + let parent = parent.upgrade().unwrap(); + parent + .children + .lock() + .remove(&self.mountpoint_dentry().unwrap().key()); + } + } + + /// Attach the mount node to the mountpoint. + fn attach_mount(&self, mountpoint: Arc) { + let key = mountpoint.key(); + mountpoint + .mount_node() + .children + .lock() + .insert(key, self.this()); + self.set_parent(mountpoint.mount_node().clone()); + mountpoint.set_mountpoint(self.this()); + } + + /// Graft the mount tree to the mountpoint. + pub fn graft_mount_tree(&self, mountpoint: Arc) -> Result<()> { + if mountpoint.type_() != InodeType::Dir { + return_errno!(Errno::ENOTDIR); + } + self.unattach_mount(); + self.attach_mount(mountpoint.clone()); + Ok(()) + } + /// Try to get a child mount node from the mountpoint. pub fn get(&self, mountpoint: &Dentry) -> Option> { if !Arc::ptr_eq(mountpoint.mount_node(), &self.this()) { diff --git a/kernel/aster-nix/src/syscall/arch/x86.rs b/kernel/aster-nix/src/syscall/arch/x86.rs index 669b83893..3af2d818f 100644 --- a/kernel/aster-nix/src/syscall/arch/x86.rs +++ b/kernel/aster-nix/src/syscall/arch/x86.rs @@ -53,6 +53,7 @@ use crate::syscall::{ madvise::sys_madvise, mkdir::{sys_mkdir, sys_mkdirat}, mmap::sys_mmap, + mount::sys_mount, mprotect::sys_mprotect, munmap::sys_munmap, nanosleep::{sys_clock_nanosleep, sys_nanosleep}, @@ -225,6 +226,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_ARCH_PRCTL = 158 => sys_arch_prctl(args[..2], &mut context); SYS_CHROOT = 161 => sys_chroot(args[..1]); SYS_SYNC = 162 => sys_sync(args[..0]); + SYS_MOUNT = 165 => sys_mount(args[..5]); SYS_GETTID = 186 => sys_gettid(args[..0]); SYS_TIME = 201 => sys_time(args[..1]); SYS_FUTEX = 202 => sys_futex(args[..6]); diff --git a/kernel/aster-nix/src/syscall/mod.rs b/kernel/aster-nix/src/syscall/mod.rs index 3f28c3853..c6306cc71 100644 --- a/kernel/aster-nix/src/syscall/mod.rs +++ b/kernel/aster-nix/src/syscall/mod.rs @@ -60,6 +60,7 @@ mod lseek; mod madvise; mod mkdir; mod mmap; +mod mount; mod mprotect; mod munmap; mod nanosleep; diff --git a/kernel/aster-nix/src/syscall/mount.rs b/kernel/aster-nix/src/syscall/mount.rs new file mode 100644 index 000000000..af8e340f5 --- /dev/null +++ b/kernel/aster-nix/src/syscall/mount.rs @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: MPL-2.0 + +use super::SyscallReturn; +use crate::{ + fs::{ + fs_resolver::{FsPath, AT_FDCWD}, + path::Dentry, + }, + prelude::*, + syscall::constants::MAX_FILENAME_LEN, + util::read_cstring_from_user, +}; + +pub fn sys_mount( + devname_addr: Vaddr, + dirname_addr: Vaddr, + fstype_addr: Vaddr, + flags: u64, + data: Vaddr, +) -> Result { + let devname = read_cstring_from_user(devname_addr, MAX_FILENAME_LEN)?; + let dirname = read_cstring_from_user(dirname_addr, MAX_FILENAME_LEN)?; + let mount_flags = MountFlags::from_bits_truncate(flags as u32); + debug!( + "devname = {:?}, dirname = {:?}, fstype = 0x{:x}, flags = {:?}, data = 0x{:x}", + devname, dirname, fstype_addr, mount_flags, data, + ); + + let current = current!(); + let target_dentry = { + let dirname = dirname.to_string_lossy(); + if dirname.is_empty() { + return_errno_with_message!(Errno::ENOENT, "dirname is empty"); + } + let fs_path = FsPath::new(AT_FDCWD, dirname.as_ref())?; + current.fs().read().lookup(&fs_path)? + }; + + if mount_flags.contains(MountFlags::MS_REMOUNT) && mount_flags.contains(MountFlags::MS_BIND) { + do_reconfigure_mnt()?; + } else if mount_flags.contains(MountFlags::MS_REMOUNT) { + do_remount()?; + } else if mount_flags.contains(MountFlags::MS_BIND) { + do_loopback( + devname.clone(), + target_dentry.clone(), + mount_flags.contains(MountFlags::MS_REC), + )?; + } else if mount_flags.contains(MountFlags::MS_SHARED) + | mount_flags.contains(MountFlags::MS_PRIVATE) + | mount_flags.contains(MountFlags::MS_SLAVE) + | mount_flags.contains(MountFlags::MS_UNBINDABLE) + { + do_change_type()?; + } else if mount_flags.contains(MountFlags::MS_MOVE) { + do_move_mount_old(devname, target_dentry)?; + } else { + do_new_mount(devname, fstype_addr, target_dentry)?; + } + + Ok(SyscallReturn::Return(0)) +} + +fn do_reconfigure_mnt() -> Result<()> { + todo!() +} + +fn do_remount() -> Result<()> { + todo!() +} + +/// Bind a mount to a new location. +fn do_loopback(old_name: CString, new_dentry: Arc, recursive: bool) -> Result<()> { + let current = current!(); + let old_dentry = { + let old_name = old_name.to_string_lossy(); + if old_name.is_empty() { + return_errno_with_message!(Errno::ENOENT, "old_name is empty"); + } + let fs_path = FsPath::new(AT_FDCWD, old_name.as_ref())?; + current.fs().read().lookup(&fs_path)? + }; + + let new_mount = old_dentry.do_loopback(recursive); + new_mount.graft_mount_tree(new_dentry.clone())?; + Ok(()) +} + +fn do_change_type() -> Result<()> { + todo!() +} + +/// Move a mount from old location to new location. +fn do_move_mount_old(old_name: CString, new_dentry: Arc) -> Result<()> { + let current = current!(); + let old_dentry = { + let old_name = old_name.to_string_lossy(); + if old_name.is_empty() { + return_errno_with_message!(Errno::ENOENT, "old_name is empty"); + } + let fs_path = FsPath::new(AT_FDCWD, old_name.as_ref())?; + current.fs().read().lookup(&fs_path)? + }; + + if !old_dentry.is_root_of_mount() { + return_errno_with_message!(Errno::EINVAL, "old_name can not be moved"); + }; + if old_dentry.mount_node().parent().is_none() { + return_errno_with_message!(Errno::EINVAL, "old_name can not be moved"); + } + + old_dentry + .mount_node() + .graft_mount_tree(new_dentry.clone())?; + + Ok(()) +} + +/// Mount a new filesystem. +fn do_new_mount(devname: CString, fs_type: Vaddr, target_dentry: Arc) -> Result<()> { + let fs_type = read_cstring_from_user(fs_type, MAX_FILENAME_LEN)?; + if fs_type.is_empty() { + return_errno_with_message!(Errno::EINVAL, "fs_type is empty"); + } + let fs_type = fs_type.to_str().unwrap(); + let fs = crate::fs::get_fs(fs_type)?; + target_dentry.mount(fs)?; + Ok(()) +} + +bitflags! { + struct MountFlags: u32 { + const MS_RDONLY = 1; /* Mount read-only */ + const MS_NOSUID = 1<<1; /* Ignore suid and sgid bits */ + const MS_NODEV = 1<<2; /* Disallow access to device special files */ + const MS_NOEXEC = 1<<3; /* Disallow program execution */ + const MS_SYNCHRONOUS = 1<<4; /* Writes are synced at once */ + const MS_REMOUNT = 1<<5; /* Alter flags of a mounted FS */ + const MS_MANDLOCK = 1<<6; /* Allow mandatory locks on an FS */ + const MS_DIRSYNS = 1<<7; /* Directory modifications are synchronous */ + const MS_NOSYMFOLLOW = 1<<8; /* Do not follow symlinks */ + const MS_NOATIME = 1<<10; /* Do not update access times. */ + const MS_NODIRATIME = 1<<11; /* Do not update directory access times. */ + const MS_BIND = 1<<12; /* Bind directory at different place. */ + const MS_MOVE = 1<<13; /* Move mount from old to new. */ + const MS_REC = 1<<14; + const MS_VERBOSE = 1<<15; /* War is peace. Verbosity is silence. MS_VERBOSE is deprecated. */ + + const MS_SILENT = 1<<15; + const MS_POSIXACL = 1<<16; /* VFS does not apply the umask. */ + const MS_UNBINDABLE = 1<<17; /* Change to unbindable. */ + const MS_PRIVATE = 1<<18; /* Change to private. */ + const MS_SLAVE = 1<<19; /* Change to slave. */ + const MS_SHARED = 1<<20; /* Change to shared. */ + const MS_RELATIME = 1<<21; /* Update atime relative to mtime/ctime. */ + const MS_KERNMOUNT = 1<<22; /* This is a kern_mount call. */ + } +} diff --git a/regression/syscall_test/Makefile b/regression/syscall_test/Makefile index 57a4b7391..596332b8f 100644 --- a/regression/syscall_test/Makefile +++ b/regression/syscall_test/Makefile @@ -19,6 +19,7 @@ TESTS ?= \ lseek_test \ mkdir_test \ mmap_test \ + mount_test \ open_create_test \ open_test \ pty_test \ diff --git a/regression/syscall_test/blocklists/mount_test b/regression/syscall_test/blocklists/mount_test new file mode 100644 index 000000000..b88962b74 --- /dev/null +++ b/regression/syscall_test/blocklists/mount_test @@ -0,0 +1,17 @@ +MountTest.MountBadFilesystem +MountTest.MountInvalidTarget +MountTest.MountPermDenied +MountTest.UmountPermDenied +MountTest.MountOverBusy +MountTest.OpenFileBusy +MountTest.UmountDetach +MountTest.ActiveSubmountBusy +MountTest.MountTmpfs +MountTest.MountTmpfsMagicValIgnored +MountTest.NullData +MountTest.MountReadonly +MountTest.MountNoAtime +MountTest.MountNoExec +MountTest.RenameRemoveMountPoint +MountTest.MountFuseFilesystemNoDevice +MountTest.MountFuseFilesystem \ No newline at end of file