From 0fb515b011967be01006cf88d788793dbbce2967 Mon Sep 17 00:00:00 2001 From: LoGin Date: Wed, 15 Nov 2023 15:39:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84pipe=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E4=BB=A5=E5=8F=8Aopenat=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E8=B0=83=E7=94=A8=20(#441)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/syscall.rs | 1 + kernel/src/filesystem/vfs/core.rs | 4 +- kernel/src/filesystem/vfs/file.rs | 80 ++++++------ kernel/src/filesystem/vfs/open.rs | 104 ++++++++++++++- kernel/src/filesystem/vfs/syscall.rs | 189 ++++++++++++++++----------- kernel/src/filesystem/vfs/utils.rs | 12 +- kernel/src/syscall/mod.rs | 39 +++++- 7 files changed, 303 insertions(+), 126 deletions(-) diff --git a/kernel/src/arch/x86_64/syscall.rs b/kernel/src/arch/x86_64/syscall.rs index e1943c21..6ef96d90 100644 --- a/kernel/src/arch/x86_64/syscall.rs +++ b/kernel/src/arch/x86_64/syscall.rs @@ -23,6 +23,7 @@ pub const SYS_FCHMOD: usize = 91; pub const SYS_UMASK: usize = 95; pub const SYS_SYSINFO: usize = 99; pub const SYS_CLOCK_GETTIME: usize = 228; +pub const SYS_OPENAT: usize = 257; pub const SYS_FCHMODAT: usize = 268; pub const SYS_FACCESSAT: usize = 269; pub const SYS_PRLIMIT64: usize = 302; diff --git a/kernel/src/filesystem/vfs/core.rs b/kernel/src/filesystem/vfs/core.rs index 2225d45e..667b9294 100644 --- a/kernel/src/filesystem/vfs/core.rs +++ b/kernel/src/filesystem/vfs/core.rs @@ -218,7 +218,7 @@ pub fn do_remove_dir(dirfd: i32, path: &str) -> Result { } let pcb = ProcessManager::current_pcb(); - let (inode_begin, remain_path) = user_path_at(&pcb, dirfd, path.to_string())?; + let (inode_begin, remain_path) = user_path_at(&pcb, dirfd, path)?; let inode: Result, SystemError> = inode_begin.lookup_follow_symlink(remain_path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES); @@ -258,7 +258,7 @@ pub fn do_unlink_at(dirfd: i32, path: &str) -> Result { return Err(SystemError::ENAMETOOLONG); } let pcb = ProcessManager::current_pcb(); - let (inode_begin, remain_path) = user_path_at(&pcb, dirfd, path.to_string())?; + let (inode_begin, remain_path) = user_path_at(&pcb, dirfd, path)?; let inode: Result, SystemError> = inode_begin.lookup_follow_symlink(&remain_path, VFS_MAX_FOLLOW_SYMLINK_TIMES); diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index ceb27bc9..01f7a361 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -46,45 +46,49 @@ bitflags! { /// 与Linux 5.19.10的uapi/asm-generic/fcntl.h相同 /// https://opengrok.ringotek.cn/xref/linux-5.19.10/tools/include/uapi/asm-generic/fcntl.h#19 pub struct FileMode: u32{ - /* File access modes for `open' and `fcntl'. */ - /// Open Read-only - const O_RDONLY = 0o0; - /// Open Write-only - const O_WRONLY = 0o1; - /// Open read/write - const O_RDWR = 0o2; - /// Mask for file access modes - const O_ACCMODE = 0o00000003; + /* File access modes for `open' and `fcntl'. */ + /// Open Read-only + const O_RDONLY = 0o0; + /// Open Write-only + const O_WRONLY = 0o1; + /// Open read/write + const O_RDWR = 0o2; + /// Mask for file access modes + const O_ACCMODE = 0o00000003; - /* Bits OR'd into the second argument to open. */ - /// Create file if it does not exist - const O_CREAT = 0o00000100; - /// Fail if file already exists - const O_EXCL = 0o00000200; - /// Do not assign controlling terminal - const O_NOCTTY = 0o00000400; - /// 文件存在且是普通文件,并以O_RDWR或O_WRONLY打开,则它会被清空 - const O_TRUNC = 0o00001000; - /// 文件指针会被移动到文件末尾 - const O_APPEND = 0o00002000; - /// 非阻塞式IO模式 - const O_NONBLOCK = 0o00004000; - /// 每次write都等待物理I/O完成,但是如果写操作不影响读取刚写入的数据,则不等待文件属性更新 - const O_DSYNC = 0o00010000; - /// fcntl, for BSD compatibility - const FASYNC = 0o00020000; - /* direct disk access hint */ - const O_DIRECT = 0o00040000; - const O_LARGEFILE = 0o00100000; - /// 打开的必须是一个目录 - const O_DIRECTORY = 0o00200000; - /// Do not follow symbolic links - const O_NOFOLLOW = 0o00400000; - const O_NOATIME = 0o01000000; - /// set close_on_exec - const O_CLOEXEC = 0o02000000; - /// 每次write都等到物理I/O完成,包括write引起的文件属性的更新 - const O_SYNC = 0o04000000; + /* Bits OR'd into the second argument to open. */ + /// Create file if it does not exist + const O_CREAT = 0o00000100; + /// Fail if file already exists + const O_EXCL = 0o00000200; + /// Do not assign controlling terminal + const O_NOCTTY = 0o00000400; + /// 文件存在且是普通文件,并以O_RDWR或O_WRONLY打开,则它会被清空 + const O_TRUNC = 0o00001000; + /// 文件指针会被移动到文件末尾 + const O_APPEND = 0o00002000; + /// 非阻塞式IO模式 + const O_NONBLOCK = 0o00004000; + /// 每次write都等待物理I/O完成,但是如果写操作不影响读取刚写入的数据,则不等待文件属性更新 + const O_DSYNC = 0o00010000; + /// fcntl, for BSD compatibility + const FASYNC = 0o00020000; + /* direct disk access hint */ + const O_DIRECT = 0o00040000; + const O_LARGEFILE = 0o00100000; + /// 打开的必须是一个目录 + const O_DIRECTORY = 0o00200000; + /// Do not follow symbolic links + const O_NOFOLLOW = 0o00400000; + const O_NOATIME = 0o01000000; + /// set close_on_exec + const O_CLOEXEC = 0o02000000; + /// 每次write都等到物理I/O完成,包括write引起的文件属性的更新 + const O_SYNC = 0o04000000; + + const O_PATH = 0o10000000; + + const O_PATH_FLAGS = Self::O_DIRECTORY.bits|Self::O_NOFOLLOW.bits|Self::O_CLOEXEC.bits|Self::O_PATH.bits; } } diff --git a/kernel/src/filesystem/vfs/open.rs b/kernel/src/filesystem/vfs/open.rs index c918ea92..3bee41cd 100644 --- a/kernel/src/filesystem/vfs/open.rs +++ b/kernel/src/filesystem/vfs/open.rs @@ -1,11 +1,17 @@ +use alloc::sync::Arc; + use crate::{ + driver::base::block::SeekFrom, process::ProcessManager, syscall::{user_access::check_and_clone_cstr, SystemError}, }; use super::{ - fcntl::AtFlags, syscall::ModeType, utils::user_path_at, MAX_PATHLEN, - VFS_MAX_FOLLOW_SYMLINK_TIMES, + fcntl::AtFlags, + file::{File, FileMode}, + syscall::{ModeType, OpenHow, OpenHowResolve}, + utils::{rsplit_path, user_path_at}, + FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; pub(super) fn do_faccessat( @@ -34,7 +40,7 @@ pub(super) fn do_faccessat( return Err(SystemError::EINVAL); } - let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; + let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, &path)?; // 如果找不到文件,则返回错误码ENOENT let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; @@ -50,7 +56,7 @@ pub fn do_fchmodat(dirfd: i32, path: *const u8, _mode: ModeType) -> Result Result Result { + let how = OpenHow::new(o_flags, mode, OpenHowResolve::empty()); + return do_sys_openat2(dfd, path, how, follow_symlink); +} + +fn do_sys_openat2( + dirfd: i32, + path: &str, + how: OpenHow, + follow_symlink: bool, +) -> Result { + // kdebug!("open: path: {}, mode: {:?}", path, mode); + // 文件名过长 + if path.len() > MAX_PATHLEN as usize { + return Err(SystemError::ENAMETOOLONG); + } + let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; + let inode: Result, SystemError> = inode_begin.lookup_follow_symlink( + &path, + if follow_symlink { + VFS_MAX_FOLLOW_SYMLINK_TIMES + } else { + 0 + }, + ); + + let inode: Arc = if inode.is_err() { + let errno = inode.unwrap_err(); + // 文件不存在,且需要创建 + if how.o_flags.contains(FileMode::O_CREAT) + && !how.o_flags.contains(FileMode::O_DIRECTORY) + && errno == SystemError::ENOENT + { + let (filename, parent_path) = rsplit_path(&path); + // 查找父目录 + let parent_inode: Arc = + ROOT_INODE().lookup(parent_path.unwrap_or("/"))?; + // 创建文件 + let inode: Arc = parent_inode.create( + filename, + FileType::File, + ModeType::from_bits_truncate(0o755), + )?; + inode + } else { + // 不需要创建文件,因此返回错误码 + return Err(errno); + } + } else { + inode.unwrap() + }; + + let file_type: FileType = inode.metadata()?.file_type; + // 如果要打开的是文件夹,而目标不是文件夹 + if how.o_flags.contains(FileMode::O_DIRECTORY) && file_type != FileType::Dir { + return Err(SystemError::ENOTDIR); + } + + // 创建文件对象 + + let mut file: File = File::new(inode, how.o_flags)?; + + // 打开模式为“追加” + if how.o_flags.contains(FileMode::O_APPEND) { + file.lseek(SeekFrom::SeekEnd(0))?; + } + + // 如果O_TRUNC,并且,打开模式包含O_RDWR或O_WRONLY,清空文件 + if how.o_flags.contains(FileMode::O_TRUNC) + && (how.o_flags.contains(FileMode::O_RDWR) || how.o_flags.contains(FileMode::O_WRONLY)) + && file_type == FileType::File + { + file.ftruncate(0)?; + } + // 把文件对象存入pcb + let r = ProcessManager::current_pcb() + .fd_table() + .write() + .alloc_fd(file, None) + .map(|fd| fd as usize); + + return r; +} diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index 58b6cdda..7de4ae74 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall.rs @@ -25,7 +25,7 @@ use super::{ core::{do_mkdir, do_remove_dir, do_unlink_at}, fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC}, file::{File, FileMode}, - open::{do_faccessat, do_fchmodat}, + open::{do_faccessat, do_fchmodat, do_sys_open}, utils::{rsplit_path, user_path_at}, Dirent, FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; @@ -146,6 +146,100 @@ impl PosixKstat { } } } + +/// +/// Arguments for how openat2(2) should open the target path. If only @flags and +/// @mode are non-zero, then openat2(2) operates very similarly to openat(2). +/// +/// However, unlike openat(2), unknown or invalid bits in @flags result in +/// -EINVAL rather than being silently ignored. @mode must be zero unless one of +/// {O_CREAT, O_TMPFILE} are set. +/// +/// ## 成员变量 +/// +/// - flags: O_* flags. +/// - mode: O_CREAT/O_TMPFILE file mode. +/// - resolve: RESOLVE_* flags. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct PosixOpenHow { + pub flags: u64, + pub mode: u64, + pub resolve: u64, +} + +impl PosixOpenHow { + #[allow(dead_code)] + pub fn new(flags: u64, mode: u64, resolve: u64) -> Self { + Self { + flags, + mode, + resolve, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct OpenHow { + pub o_flags: FileMode, + pub mode: ModeType, + pub resolve: OpenHowResolve, +} + +impl OpenHow { + pub fn new(mut o_flags: FileMode, mut mode: ModeType, resolve: OpenHowResolve) -> Self { + if !o_flags.contains(FileMode::O_CREAT) { + mode = ModeType::empty(); + } + + if o_flags.contains(FileMode::O_PATH) { + o_flags = o_flags.intersection(FileMode::O_PATH_FLAGS); + } + + Self { + o_flags, + mode, + resolve, + } + } +} + +impl From for OpenHow { + fn from(posix_open_how: PosixOpenHow) -> Self { + let o_flags = FileMode::from_bits_truncate(posix_open_how.flags as u32); + let mode = ModeType::from_bits_truncate(posix_open_how.mode as u32); + let resolve = OpenHowResolve::from_bits_truncate(posix_open_how.resolve as u64); + return Self::new(o_flags, mode, resolve); + } +} + +bitflags! { + pub struct OpenHowResolve: u64{ + /// Block mount-point crossings + /// (including bind-mounts). + const RESOLVE_NO_XDEV = 0x01; + + /// Block traversal through procfs-style + /// "magic-links" + const RESOLVE_NO_MAGICLINKS = 0x02; + + /// Block traversal through all symlinks + /// (implies OEXT_NO_MAGICLINKS) + const RESOLVE_NO_SYMLINKS = 0x04; + /// Block "lexical" trickery like + /// "..", symlinks, and absolute + const RESOLVE_BENEATH = 0x08; + /// Make all jumps to "/" and ".." + /// be scoped inside the dirfd + /// (similar to chroot(2)). + const RESOLVE_IN_ROOT = 0x10; + // Only complete if resolution can be + // completed through cached lookup. May + // return -EAGAIN if that's not + // possible. + const RESOLVE_CACHED = 0x20; + } +} impl Syscall { /// @brief 为当前进程打开一个文件 /// @@ -153,78 +247,23 @@ impl Syscall { /// @param o_flags 打开文件的标志位 /// /// @return 文件描述符编号,或者是错误码 - pub fn open(path: &str, mode: FileMode, follow_symlink: bool) -> Result { - // kdebug!("open: path: {}, mode: {:?}", path, mode); - // 文件名过长 - if path.len() > MAX_PATHLEN as usize { - return Err(SystemError::ENAMETOOLONG); - } + pub fn open( + path: &str, + flags: FileMode, + mode: ModeType, + follow_symlink: bool, + ) -> Result { + return do_sys_open(AtFlags::AT_FDCWD.bits(), path, flags, mode, follow_symlink); + } - let inode: Result, SystemError> = ROOT_INODE().lookup_follow_symlink( - path, - if follow_symlink { - VFS_MAX_FOLLOW_SYMLINK_TIMES - } else { - 0 - }, - ); - - let inode: Arc = if inode.is_err() { - let errno = inode.unwrap_err(); - // 文件不存在,且需要创建 - if mode.contains(FileMode::O_CREAT) - && !mode.contains(FileMode::O_DIRECTORY) - && errno == SystemError::ENOENT - { - let (filename, parent_path) = rsplit_path(path); - // 查找父目录 - let parent_inode: Arc = - ROOT_INODE().lookup(parent_path.unwrap_or("/"))?; - // 创建文件 - let inode: Arc = parent_inode.create( - filename, - FileType::File, - ModeType::from_bits_truncate(0o755), - )?; - inode - } else { - // 不需要创建文件,因此返回错误码 - return Err(errno); - } - } else { - inode.unwrap() - }; - - let file_type: FileType = inode.metadata()?.file_type; - // 如果要打开的是文件夹,而目标不是文件夹 - if mode.contains(FileMode::O_DIRECTORY) && file_type != FileType::Dir { - return Err(SystemError::ENOTDIR); - } - - // 创建文件对象 - - let mut file: File = File::new(inode, mode)?; - - // 打开模式为“追加” - if mode.contains(FileMode::O_APPEND) { - file.lseek(SeekFrom::SeekEnd(0))?; - } - - // 如果O_TRUNC,并且,打开模式包含O_RDWR或O_WRONLY,清空文件 - if mode.contains(FileMode::O_TRUNC) - && (mode.contains(FileMode::O_RDWR) || mode.contains(FileMode::O_WRONLY)) - && file_type == FileType::File - { - file.ftruncate(0)?; - } - // 把文件对象存入pcb - let r = ProcessManager::current_pcb() - .fd_table() - .write() - .alloc_fd(file, None) - .map(|fd| fd as usize); - - return r; + pub fn openat( + dirfd: i32, + path: &str, + o_flags: FileMode, + mode: ModeType, + follow_symlink: bool, + ) -> Result { + return do_sys_open(dirfd, path, o_flags, mode, follow_symlink); } /// @brief 关闭文件 @@ -764,14 +803,14 @@ impl Syscall { } pub fn stat(path: &str, user_kstat: *mut PosixKstat) -> Result { - let fd = Self::open(path, FileMode::O_RDONLY, true)?; + let fd = Self::open(path, FileMode::O_RDONLY, ModeType::empty(), true)?; let r = Self::fstat(fd as i32, user_kstat); Self::close(fd).ok(); return r; } pub fn lstat(path: &str, user_kstat: *mut PosixKstat) -> Result { - let fd = Self::open(path, FileMode::O_RDONLY, false)?; + let fd = Self::open(path, FileMode::O_RDONLY, ModeType::empty(), false)?; let r = Self::fstat(fd as i32, user_kstat); Self::close(fd).ok(); return r; @@ -847,7 +886,7 @@ impl Syscall { return Err(SystemError::EINVAL); } - let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; + 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 { diff --git a/kernel/src/filesystem/vfs/utils.rs b/kernel/src/filesystem/vfs/utils.rs index c8171a65..73b3fb7e 100644 --- a/kernel/src/filesystem/vfs/utils.rs +++ b/kernel/src/filesystem/vfs/utils.rs @@ -35,9 +35,10 @@ pub fn rsplit_path(path: &str) -> (&str, Option<&str>) { pub fn user_path_at( pcb: &Arc, dirfd: i32, - mut path: String, + path: &str, ) -> Result<(Arc, String), SystemError> { let mut inode = ROOT_INODE(); + let ret_path; // 如果path不是绝对路径,则需要拼接 if path.as_bytes()[0] != b'/' { // 如果dirfd不是AT_FDCWD,则需要检查dirfd是否是目录 @@ -58,13 +59,16 @@ pub fn user_path_at( } inode = file_guard.inode(); + ret_path = String::from(path); } else { let mut cwd = pcb.basic().cwd(); cwd.push('/'); - cwd.push_str(path.as_str()); - path = cwd; + cwd.push_str(path); + ret_path = cwd; } + } else { + ret_path = String::from(path); } - return Ok((inode, path)); + return Ok((inode, ret_path)); } diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 0987c42f..1f7a777b 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -6,7 +6,8 @@ use core::{ use crate::{ arch::syscall::{ SYS_ACCESS, SYS_CHMOD, SYS_CLOCK_GETTIME, SYS_FACCESSAT, SYS_FACCESSAT2, SYS_FCHMOD, - SYS_FCHMODAT, SYS_LSTAT, SYS_PRLIMIT64, SYS_READV, SYS_SYSINFO, SYS_UMASK, SYS_UNLINK, + SYS_FCHMODAT, SYS_LSTAT, SYS_OPENAT, SYS_PRLIMIT64, SYS_READV, SYS_SYSINFO, SYS_UMASK, + SYS_UNLINK, }, libs::{futex::constant::FutexFlag, rand::GRandFlags}, process::{ @@ -367,6 +368,7 @@ pub const SYS_RT_SIGRETURN: usize = 15; pub const SYS_IOCTL: usize = 16; pub const SYS_WRITEV: usize = 20; +pub const SYS_PIPE: usize = 22; pub const SYS_MADVISE: usize = 28; @@ -456,7 +458,7 @@ pub const SYS_READLINK_AT: usize = 267; pub const SYS_ACCEPT4: usize = 288; -pub const SYS_PIPE: usize = 293; +pub const SYS_PIPE2: usize = 293; #[allow(dead_code)] pub const SYS_GET_RANDOM: usize = 318; @@ -515,8 +517,31 @@ impl Syscall { let path: &str = path.unwrap(); let flags = args[1]; + let mode = args[2]; + let open_flags: FileMode = FileMode::from_bits_truncate(flags as u32); - Self::open(path, open_flags, true) + let mode = ModeType::from_bits(mode as u32).ok_or(SystemError::EINVAL)?; + Self::open(path, open_flags, mode, true) + }; + res + } + + SYS_OPENAT => { + let dirfd = args[0] as i32; + let path: &CStr = unsafe { CStr::from_ptr(args[1] as *const c_char) }; + let flags = args[2]; + let mode = args[3]; + + let path: Result<&str, core::str::Utf8Error> = path.to_str(); + let res = if path.is_err() { + Err(SystemError::EINVAL) + } else { + let path: &str = path.unwrap(); + + let open_flags: FileMode = + FileMode::from_bits(flags as u32).ok_or(SystemError::EINVAL)?; + let mode = ModeType::from_bits(mode as u32).ok_or(SystemError::EINVAL)?; + Self::openat(dirfd, path, open_flags, mode, true) }; res } @@ -725,6 +750,14 @@ impl Syscall { SYS_CLOCK => Self::clock(), SYS_PIPE => { + let pipefd: *mut i32 = args[0] as *mut c_int; + if pipefd.is_null() { + Err(SystemError::EFAULT) + } else { + Self::pipe2(pipefd, FileMode::empty()) + } + } + SYS_PIPE2 => { let pipefd: *mut i32 = args[0] as *mut c_int; let arg1 = args[1]; if pipefd.is_null() {