From 709498cac1f2134b2a5e089366ee7136ee029369 Mon Sep 17 00:00:00 2001 From: LoGin Date: Sun, 12 Nov 2023 17:40:45 +0800 Subject: [PATCH] feat: sys_readlink && sys_readlinkat (#436) --- kernel/src/filesystem/vfs/fcntl.rs | 52 ++++++++++++++++++++ kernel/src/filesystem/vfs/syscall.rs | 72 +++++++++++++++++++++++++++- kernel/src/syscall/mod.rs | 20 ++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/kernel/src/filesystem/vfs/fcntl.rs b/kernel/src/filesystem/vfs/fcntl.rs index 94c6e46d..34d11b79 100644 --- a/kernel/src/filesystem/vfs/fcntl.rs +++ b/kernel/src/filesystem/vfs/fcntl.rs @@ -58,5 +58,57 @@ 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, + + /// 跟随符号链接。 + /// 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, +} + +impl Into for AtFlags { + fn into(self) -> i32 { + self as i32 + } +} + /// for F_[GET|SET]FL pub const FD_CLOEXEC: u32 = 1; diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index 80a1cb6a..d4f833b8 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall.rs @@ -14,13 +14,16 @@ use crate::{ libs::rwlock::RwLockWriteGuard, mm::VirtAddr, process::ProcessManager, - syscall::{user_access::UserBufferReader, Syscall, SystemError}, + syscall::{ + user_access::{check_and_clone_cstr, UserBufferReader, UserBufferWriter}, + Syscall, SystemError, + }, time::TimeSpec, }; use super::{ core::{do_mkdir, do_remove_dir, do_unlink_at}, - fcntl::{FcntlCommand, FD_CLOEXEC}, + fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC}, file::{File, FileMode}, utils::rsplit_path, Dirent, FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, @@ -794,7 +797,72 @@ impl Syscall { Self::write(fd, &data) } + + pub fn readlink_at( + dirfd: i32, + path: *const u8, + user_buf: *mut u8, + buf_size: usize, + ) -> Result { + let mut 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 = inode.lookup(path.as_str())?; + if inode.metadata()?.file_type != FileType::SymLink { + return Err(SystemError::EINVAL); + } + + let ubuf = user_buf.buffer::(0).unwrap(); + + let mut file = File::new(inode, FileMode::O_RDONLY)?; + + let len = file.read(buf_size, ubuf)?; + + return Ok(len); + } + + pub fn readlink( + path: *const u8, + user_buf: *mut u8, + buf_size: usize, + ) -> Result { + return Self::readlink_at(AtFlags::AtFdCwd.into(), path, user_buf, buf_size); + } } + #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct IoVec { diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 68886f87..6281984b 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -399,6 +399,8 @@ pub const SYS_CHDIR: usize = 80; pub const SYS_MKDIR: usize = 83; +pub const SYS_READLINK: usize = 89; + pub const SYS_GETTIMEOFDAY: usize = 96; pub const SYS_GETRUSAGE: usize = 98; @@ -437,6 +439,8 @@ pub const SYS_EXIT_GROUP: usize = 231; pub const SYS_UNLINK_AT: usize = 263; +pub const SYS_READLINK_AT: usize = 267; + pub const SYS_ACCEPT4: usize = 288; pub const SYS_PIPE: usize = 293; @@ -1171,6 +1175,22 @@ impl Syscall { Self::get_rusage(who, rusage) } + SYS_READLINK =>{ + let path = args[0] as *const u8; + let buf = args[1] as *mut u8; + let bufsiz = args[2] as usize; + Self::readlink(path, buf, bufsiz) + + } + + SYS_READLINK_AT =>{ + let dirfd = args[0] as i32; + let pathname = args[1] as *const u8; + let buf = args[2] as *mut u8; + let bufsiz = args[3] as usize; + Self::readlink_at(dirfd, pathname, buf, bufsiz) + } + _ => panic!("Unsupported syscall ID: {}", syscall_num), }; return r;