diff --git a/kernel/src/arch/x86_64/syscall.rs b/kernel/src/arch/x86_64/syscall.rs index 9f60733b..0e915a1f 100644 --- a/kernel/src/arch/x86_64/syscall.rs +++ b/kernel/src/arch/x86_64/syscall.rs @@ -13,7 +13,10 @@ use alloc::string::String; use super::{interrupt::TrapFrame, mm::barrier::mfence}; +pub const SYS_ACCESS: usize = 21; pub const SYS_PRLIMIT64: usize = 302; +pub const SYS_FACCESSAT: usize = 269; +pub const SYS_FACCESSAT2: usize = 439; /// ### 存储PCB系统调用栈以及在syscall过程中暂存用户态rsp的结构体 /// diff --git a/kernel/src/filesystem/vfs/fcntl.rs b/kernel/src/filesystem/vfs/fcntl.rs index 34d11b79..f5f05721 100644 --- a/kernel/src/filesystem/vfs/fcntl.rs +++ b/kernel/src/filesystem/vfs/fcntl.rs @@ -58,55 +58,48 @@ pub enum FcntlCommand { SetFileRwHint = F_LINUX_SPECIFIC_BASE + 14, } -/// The constants AT_REMOVEDIR and AT_EACCESS have the same value. AT_EACCESS is -/// meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to -/// unlinkat. The two functions do completely different things and therefore, -/// the flags can be allowed to overlap. For example, passing AT_REMOVEDIR to -/// faccessat would be undefined behavior and thus treating it equivalent to -/// AT_EACCESS is valid undefined behavior. -#[derive(Debug, Copy, Clone, Eq, PartialEq, FromPrimitive, ToPrimitive)] -#[allow(non_camel_case_types)] -pub enum AtFlags { - /// 特殊值,用于指示openat应使用当前工作目录。 - AtFdCwd = -100, - /// 不要跟随符号链接。 - /// AT_SYMLINK_NOFOLLOW: 0x100 - AtSymlinkNoFollow = 0x100, - /// AtEAccess: 使用有效ID进行访问测试,而不是实际ID。 - /// AtRemoveDir: 删除目录而不是取消链接文件。 - /// AT_EACCESS: 0x200 - /// AT_REMOVEDIR: 0x200 - AtEAccess_OR_AtRemoveDir = 0x200, +bitflags! { - /// 跟随符号链接。 - /// AT_SYMLINK_FOLLOW: 0x400 - AtSymlinkFollow = 0x400, - /// 禁止终端自动挂载遍历。 - /// AT_NO_AUTOMOUNT: 0x800 - AtNoAutomount = 0x800, - /// 允许空的相对路径名。 - /// AT_EMPTY_PATH: 0x1000 - AtEmptyPath = 0x1000, - /// statx()所需的同步类型。 - /// AT_STATX_SYNC_TYPE: 0x6000 - AtStatxSyncType = 0x6000, - /// 执行与stat()相同的操作。 - /// AT_STATX_SYNC_AS_STAT: 0x0000 - AtStatxSyncAsStat = 0x0000, - /// 强制将属性与服务器同步。 - /// AT_STATX_FORCE_SYNC: 0x2000 - AtStatxForceSync = 0x2000, - /// 不要将属性与服务器同步。 - /// AT_STATX_DONT_SYNC: 0x4000 - AtStatxDontSync = 0x4000, - /// 应用于整个子树。 - /// AT_RECURSIVE: 0x8000 - AtRecursive = 0x8000, -} + /// The constants AT_REMOVEDIR and AT_EACCESS have the same value. AT_EACCESS is + /// meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to + /// unlinkat. The two functions do completely different things and therefore, + /// the flags can be allowed to overlap. For example, passing AT_REMOVEDIR to + /// faccessat would be undefined behavior and thus treating it equivalent to + /// AT_EACCESS is valid undefined behavior. + pub struct AtFlags: i32 { + /// 特殊值,用于指示openat应使用当前工作目录。 + const AT_FDCWD = -100; + /// 不要跟随符号链接。 + const AT_SYMLINK_NOFOLLOW = 0x100; + /// AtEAccess: 使用有效ID进行访问测试,而不是实际ID。 + const AT_EACCESS = 0x200; + /// AtRemoveDir: 删除目录而不是取消链接文件。 + const AT_REMOVEDIR = 0x200; -impl Into for AtFlags { - fn into(self) -> i32 { - self as i32 + /// 跟随符号链接。 + /// AT_SYMLINK_FOLLOW: 0x400 + const AT_SYMLINK_FOLLOW = 0x400; + /// 禁止终端自动挂载遍历。 + /// AT_NO_AUTOMOUNT: 0x800 + const AT_NO_AUTOMOUNT = 0x800; + /// 允许空的相对路径名。 + /// AT_EMPTY_PATH: 0x1000 + const AT_EMPTY_PATH = 0x1000; + /// statx()所需的同步类型。 + /// AT_STATX_SYNC_TYPE: 0x6000 + const AT_STATX_SYNC_TYPE = 0x6000; + /// 执行与stat()相同的操作。 + /// AT_STATX_SYNC_AS_STAT: 0x0000 + const AT_STATX_SYNC_AS_STAT = 0x0000; + /// 强制将属性与服务器同步。 + /// AT_STATX_FORCE_SYNC: 0x2000 + const AT_STATX_FORCE_SYNC = 0x2000; + /// 不要将属性与服务器同步。 + /// AT_STATX_DONT_SYNC: 0x4000 + const AT_STATX_DONT_SYNC = 0x4000; + /// 应用于整个子树。 + /// AT_RECURSIVE: 0x8000 + const AT_RECURSIVE = 0x8000; } } diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 0197a6b8..61756024 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -4,6 +4,7 @@ pub mod core; pub mod fcntl; pub mod file; pub mod mount; +pub mod open; pub mod syscall; mod utils; diff --git a/kernel/src/filesystem/vfs/open.rs b/kernel/src/filesystem/vfs/open.rs new file mode 100644 index 00000000..2cbfa123 --- /dev/null +++ b/kernel/src/filesystem/vfs/open.rs @@ -0,0 +1,27 @@ +use crate::syscall::SystemError; + +use super::{fcntl::AtFlags, syscall::ModeType}; + +pub(super) fn do_faccessat( + _dirfd: i32, + _pathname: *const u8, + mode: ModeType, + flags: u32, +) -> Result { + if (mode.bits() & (!ModeType::S_IXUGO.bits())) != 0 { + return Err(SystemError::EINVAL); + } + + if (flags + & (!((AtFlags::AT_EACCESS | AtFlags::AT_SYMLINK_NOFOLLOW | AtFlags::AT_EMPTY_PATH).bits() + as u32))) + != 0 + { + return Err(SystemError::EINVAL); + } + + // let follow_symlink = flags & AtFlags::AT_SYMLINK_NOFOLLOW.bits() as u32 == 0; + + // todo: 接着完善(可以借鉴linux 6.1.9的do_faccessat) + return Ok(0); +} diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index d4f833b8..40e4a52f 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall.rs @@ -25,7 +25,8 @@ use super::{ core::{do_mkdir, do_remove_dir, do_unlink_at}, fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC}, file::{File, FileMode}, - utils::rsplit_path, + open::do_faccessat, + utils::{rsplit_path, user_path_at}, Dirent, FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; // use crate::kdebug; @@ -804,41 +805,14 @@ impl Syscall { user_buf: *mut u8, buf_size: usize, ) -> Result { - let mut path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; let mut user_buf = UserBufferWriter::new(user_buf, buf_size, true)?; if path.len() == 0 { return Err(SystemError::EINVAL); } - let mut inode = ROOT_INODE(); - // 如果path不是绝对路径,则需要拼接 - if path.as_bytes()[0] != b'/' { - // 如果dirfd不是AT_FDCWD,则需要检查dirfd是否是目录 - if dirfd != AtFlags::AtFdCwd.into() { - let binding = ProcessManager::current_pcb().fd_table(); - let fd_table_guard = binding.read(); - let file = fd_table_guard - .get_file_by_fd(dirfd) - .ok_or(SystemError::EBADF)?; - - // drop guard 以避免无法调度的问题 - drop(fd_table_guard); - - let file_guard = file.lock(); - // 如果dirfd不是目录,则返回错误码ENOTDIR - if file_guard.file_type() != FileType::Dir { - return Err(SystemError::ENOTDIR); - } - - inode = file_guard.inode(); - } else { - let mut cwd = ProcessManager::current_pcb().basic().cwd(); - cwd.push('/'); - cwd.push_str(path.as_str()); - path = cwd; - } - } + let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; let inode = inode.lookup(path.as_str())?; if inode.metadata()?.file_type != FileType::SymLink { @@ -859,7 +833,25 @@ impl Syscall { user_buf: *mut u8, buf_size: usize, ) -> Result { - return Self::readlink_at(AtFlags::AtFdCwd.into(), path, user_buf, buf_size); + return Self::readlink_at(AtFlags::AT_FDCWD.bits(), path, user_buf, buf_size); + } + + pub fn access(pathname: *const u8, mode: u32) -> Result { + return do_faccessat( + AtFlags::AT_FDCWD.bits(), + pathname, + ModeType::from_bits_truncate(mode), + 0, + ); + } + + pub fn faccessat2( + dirfd: i32, + pathname: *const u8, + mode: u32, + flags: u32, + ) -> Result { + return do_faccessat(dirfd, pathname, ModeType::from_bits_truncate(mode), flags); } } diff --git a/kernel/src/filesystem/vfs/utils.rs b/kernel/src/filesystem/vfs/utils.rs index dd9db916..237b4fc0 100644 --- a/kernel/src/filesystem/vfs/utils.rs +++ b/kernel/src/filesystem/vfs/utils.rs @@ -1,3 +1,9 @@ +use alloc::{string::String, sync::Arc}; + +use crate::{process::ProcessControlBlock, syscall::SystemError}; + +use super::{fcntl::AtFlags, FileType, IndexNode, ROOT_INODE}; + /// @brief 切分路径字符串,返回最左侧那一级的目录名和剩余的部分。 /// /// 举例:对于 /123/456/789/ 本函数返回的第一个值为123, 第二个值为456/789 @@ -19,3 +25,45 @@ pub fn rsplit_path(path: &str) -> (&str, Option<&str>) { return (comp, rest_opt); } + +/// 根据dirfd和path,计算接下来开始lookup的inode和剩余的path +/// +/// ## 返回值 +/// +/// 返回值为(需要lookup的inode, 剩余的path) +pub fn user_path_at( + pcb: &Arc, + dirfd: i32, + mut path: String, +) -> Result<(Arc, String), SystemError> { + let mut inode = ROOT_INODE(); + // 如果path不是绝对路径,则需要拼接 + if path.as_bytes()[0] != b'/' { + // 如果dirfd不是AT_FDCWD,则需要检查dirfd是否是目录 + if dirfd != AtFlags::AT_FDCWD.bits() { + let binding = pcb.fd_table(); + let fd_table_guard = binding.read(); + let file = fd_table_guard + .get_file_by_fd(dirfd) + .ok_or(SystemError::EBADF)?; + + // drop guard 以避免无法调度的问题 + drop(fd_table_guard); + + let file_guard = file.lock(); + // 如果dirfd不是目录,则返回错误码ENOTDIR + if file_guard.file_type() != FileType::Dir { + return Err(SystemError::ENOTDIR); + } + + inode = file_guard.inode(); + } else { + let mut cwd = pcb.basic().cwd(); + cwd.push('/'); + cwd.push_str(path.as_str()); + path = cwd; + } + } + + return Ok((inode, path)); +} diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 6b9c5851..92a4fd74 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -4,7 +4,7 @@ use core::{ }; use crate::{ - arch::syscall::SYS_PRLIMIT64, + arch::syscall::{SYS_ACCESS, SYS_FACCESSAT, SYS_FACCESSAT2, SYS_PRLIMIT64}, libs::{futex::constant::FutexFlag, rand::GRandFlags}, process::{ fork::KernelCloneArgs, @@ -1204,6 +1204,27 @@ impl Syscall { Self::prlimit64(pid, resource, new_limit, old_limit) } + SYS_ACCESS => { + let pathname = args[0] as *const u8; + let mode = args[1] as u32; + Self::access(pathname, mode) + } + + SYS_FACCESSAT => { + let dirfd = args[0] as i32; + let pathname = args[1] as *const u8; + let mode = args[2] as u32; + Self::faccessat2(dirfd, pathname, mode, 0) + } + + SYS_FACCESSAT2 => { + let dirfd = args[0] as i32; + let pathname = args[1] as *const u8; + let mode = args[2] as u32; + let flags = args[3] as u32; + Self::faccessat2(dirfd, pathname, mode, flags) + } + _ => panic!("Unsupported syscall ID: {}", syscall_num), }; return r;