Add syscall mount

Signed-off-by: Zhenchen Wang <m202372036@hust.edu.cn>
This commit is contained in:
Zhenchen Wang
2024-05-17 11:19:36 +08:00
committed by Tate, Hongliang Tian
parent ff83ce7667
commit a893ceca4a
8 changed files with 311 additions and 1 deletions

View File

@ -23,6 +23,7 @@ use crate::{
exfat::{ExfatFS, ExfatMountOptions}, exfat::{ExfatFS, ExfatMountOptions},
ext2::Ext2, ext2::Ext2,
fs_resolver::FsPath, fs_resolver::FsPath,
utils::FileSystem,
}, },
prelude::*, prelude::*,
thread::kernel_thread::KernelThreadExt, thread::kernel_thread::KernelThreadExt,
@ -64,3 +65,26 @@ pub fn lazy_init() {
self::rootfs::mount_fs_at(exfat_fs, &target_path).unwrap(); self::rootfs::mount_fs_at(exfat_fs, &target_path).unwrap();
} }
} }
pub fn get_fs(fs_type: &str) -> Result<Arc<dyn FileSystem>> {
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"),
}
}

View File

@ -104,6 +104,18 @@ impl Dentry_ {
DentryFlags::from_bits(flags).unwrap() DentryFlags::from_bits(flags).unwrap()
} }
/// Check if the Dentry_ is a subdir of the given Dentry_.
pub fn is_subdir(&self, dentry: Arc<Self>) -> 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 { pub fn is_mountpoint(&self) -> bool {
self.flags().contains(DentryFlags::MOUNTED) self.flags().contains(DentryFlags::MOUNTED)
} }
@ -561,7 +573,7 @@ impl Dentry {
/// Make this Dentry' inner to be a mountpoint, /// Make this Dentry' inner to be a mountpoint,
/// and set the mountpoint of the child mount to this Dentry's inner. /// and set the mountpoint of the child mount to this Dentry's inner.
fn set_mountpoint(&self, child_mount: Arc<MountNode>) { pub fn set_mountpoint(&self, child_mount: Arc<MountNode>) {
child_mount.set_mountpoint_dentry(self.inner.clone()); child_mount.set_mountpoint_dentry(self.inner.clone());
self.inner.set_mountpoint_dentry(); self.inner.set_mountpoint_dentry();
} }
@ -638,6 +650,14 @@ impl Dentry {
self.inner.rename(old_name, &new_dir.inner, new_name) self.inner.rename(old_name, &new_dir.inner, new_name)
} }
pub fn do_loopback(&self, recursive: bool) -> Arc<MountNode> {
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. /// Get the arc reference to self.
fn this(&self) -> Arc<Self> { fn this(&self) -> Arc<Self> {
self.this.upgrade().unwrap() self.this.upgrade().unwrap()
@ -669,4 +689,6 @@ impl Dentry {
pub fn set_mtime(&self, time: Duration); pub fn set_mtime(&self, time: Duration);
pub fn key(&self) -> DentryKey; pub fn key(&self) -> DentryKey;
pub fn inode(&self) -> &Arc<dyn Inode>; pub fn inode(&self) -> &Arc<dyn Inode>;
pub fn is_root_of_mount(&self) -> bool;
pub fn is_mountpoint(&self) -> bool;
} }

View File

@ -97,6 +97,91 @@ impl MountNode {
Ok(child_mount) 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<Dentry_>) -> Arc<Self> {
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<Dentry_>) -> Arc<Self> {
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<Dentry>) {
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<Dentry>) -> 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. /// Try to get a child mount node from the mountpoint.
pub fn get(&self, mountpoint: &Dentry) -> Option<Arc<Self>> { pub fn get(&self, mountpoint: &Dentry) -> Option<Arc<Self>> {
if !Arc::ptr_eq(mountpoint.mount_node(), &self.this()) { if !Arc::ptr_eq(mountpoint.mount_node(), &self.this()) {

View File

@ -53,6 +53,7 @@ use crate::syscall::{
madvise::sys_madvise, madvise::sys_madvise,
mkdir::{sys_mkdir, sys_mkdirat}, mkdir::{sys_mkdir, sys_mkdirat},
mmap::sys_mmap, mmap::sys_mmap,
mount::sys_mount,
mprotect::sys_mprotect, mprotect::sys_mprotect,
munmap::sys_munmap, munmap::sys_munmap,
nanosleep::{sys_clock_nanosleep, sys_nanosleep}, 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_ARCH_PRCTL = 158 => sys_arch_prctl(args[..2], &mut context);
SYS_CHROOT = 161 => sys_chroot(args[..1]); SYS_CHROOT = 161 => sys_chroot(args[..1]);
SYS_SYNC = 162 => sys_sync(args[..0]); SYS_SYNC = 162 => sys_sync(args[..0]);
SYS_MOUNT = 165 => sys_mount(args[..5]);
SYS_GETTID = 186 => sys_gettid(args[..0]); SYS_GETTID = 186 => sys_gettid(args[..0]);
SYS_TIME = 201 => sys_time(args[..1]); SYS_TIME = 201 => sys_time(args[..1]);
SYS_FUTEX = 202 => sys_futex(args[..6]); SYS_FUTEX = 202 => sys_futex(args[..6]);

View File

@ -60,6 +60,7 @@ mod lseek;
mod madvise; mod madvise;
mod mkdir; mod mkdir;
mod mmap; mod mmap;
mod mount;
mod mprotect; mod mprotect;
mod munmap; mod munmap;
mod nanosleep; mod nanosleep;

View File

@ -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<SyscallReturn> {
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<Dentry>, 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<Dentry>) -> 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<Dentry>) -> 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. */
}
}

View File

@ -19,6 +19,7 @@ TESTS ?= \
lseek_test \ lseek_test \
mkdir_test \ mkdir_test \
mmap_test \ mmap_test \
mount_test \
open_create_test \ open_create_test \
open_test \ open_test \
pty_test \ pty_test \

View File

@ -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