From 782301c2399f18865035fcb966777c164d915afa Mon Sep 17 00:00:00 2001 From: Chen Chengjun Date: Thu, 16 Oct 2025 09:50:28 +0000 Subject: [PATCH] Remove open-related APIs from FsResolver --- kernel/src/device/pty/master.rs | 7 ++-- kernel/src/fs/mod.rs | 29 +++++++++---- kernel/src/fs/open_args.rs | 47 +++++++++++++++++++++ kernel/src/fs/path/mod.rs | 47 ++++++++++++++++++++- kernel/src/syscall/open.rs | 74 ++++++++++++++++++++++++++++----- 5 files changed, 180 insertions(+), 24 deletions(-) create mode 100644 kernel/src/fs/open_args.rs diff --git a/kernel/src/device/pty/master.rs b/kernel/src/device/pty/master.rs index ded16b61b..f1f223744 100644 --- a/kernel/src/device/pty/master.rs +++ b/kernel/src/device/pty/master.rs @@ -13,6 +13,7 @@ use crate::{ file_table::FdFlags, fs_resolver::FsPath, inode_handle::FileIo, + open_args::OpenArgs, utils::{mkmod, AccessMode, Inode, IoctlCmd}, }, prelude::*, @@ -127,13 +128,13 @@ impl FileIo for PtyMaster { let fs_path = FsPath::try_from(slave_name.as_str())?; let inode_handle = { - let flags = AccessMode::O_RDWR as u32; - let mode = mkmod!(u+rw).bits(); + let open_args = OpenArgs::from_modes(AccessMode::O_RDWR, mkmod!(u+rw)); thread_local .borrow_fs() .resolver() .read() - .open(&fs_path, flags, mode)? + .lookup(&fs_path)? + .open(open_args)? }; Arc::new(inode_handle) }; diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index bcb580834..e10810884 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -12,6 +12,7 @@ pub mod file_table; pub mod fs_resolver; pub mod inode_handle; pub mod named_pipe; +pub mod open_args; pub mod overlayfs; pub mod path; pub mod pipe; @@ -33,6 +34,7 @@ use crate::{ ext2::Ext2, file_table::FdFlags, fs_resolver::{FsPath, FsResolver}, + open_args::OpenArgs, utils::{mkmod, AccessMode}, }, prelude::*, @@ -104,19 +106,28 @@ pub fn init_in_first_process(ctx: &Context) { // Initialize the file table for the first process. let tty_path = FsPath::new(fs_resolver::AT_FDCWD, "/dev/console").expect("cannot find tty"); let stdin = { - let flags = AccessMode::O_RDONLY as u32; - let mode = mkmod!(u+r); - fs_resolver.open(&tty_path, flags, mode.bits()).unwrap() + let open_args = OpenArgs::from_modes(AccessMode::O_RDONLY, mkmod!(u+r)); + fs_resolver + .lookup(&tty_path) + .unwrap() + .open(open_args) + .unwrap() }; let stdout = { - let flags = AccessMode::O_WRONLY as u32; - let mode = mkmod!(u+w); - fs_resolver.open(&tty_path, flags, mode.bits()).unwrap() + let open_args = OpenArgs::from_modes(AccessMode::O_WRONLY, mkmod!(u+w)); + fs_resolver + .lookup(&tty_path) + .unwrap() + .open(open_args) + .unwrap() }; let stderr = { - let flags = AccessMode::O_WRONLY as u32; - let mode = mkmod!(u+w); - fs_resolver.open(&tty_path, flags, mode.bits()).unwrap() + let open_args = OpenArgs::from_modes(AccessMode::O_WRONLY, mkmod!(u+w)); + fs_resolver + .lookup(&tty_path) + .unwrap() + .open(open_args) + .unwrap() }; let mut file_table_ref = ctx.thread_local.borrow_file_table_mut(); diff --git a/kernel/src/fs/open_args.rs b/kernel/src/fs/open_args.rs new file mode 100644 index 000000000..8f50b7afd --- /dev/null +++ b/kernel/src/fs/open_args.rs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MPL-2.0 + +use crate::{ + fs::utils::{AccessMode, CreationFlags, InodeMode, StatusFlags}, + prelude::*, +}; + +/// Arguments for an open request. +#[derive(Debug)] +pub struct OpenArgs { + pub creation_flags: CreationFlags, + pub status_flags: StatusFlags, + pub access_mode: AccessMode, + pub inode_mode: InodeMode, +} + +impl OpenArgs { + /// Create `OpenArgs` from the given flags and mode. + pub fn from_flags_and_mode(flags: u32, inode_mode: InodeMode) -> Result { + let creation_flags = CreationFlags::from_bits_truncate(flags); + let status_flags = StatusFlags::from_bits_truncate(flags); + let access_mode = AccessMode::from_u32(flags)?; + Ok(Self { + creation_flags, + status_flags, + access_mode, + inode_mode, + }) + } + + /// Create `OpenArgs` from the given access mode and inode mode. + pub fn from_modes(access_mode: AccessMode, inode_mode: InodeMode) -> Self { + Self { + creation_flags: CreationFlags::empty(), + status_flags: StatusFlags::empty(), + access_mode, + inode_mode, + } + } + + /// Returns whether to follow the tail link when resolving the path. + pub fn follow_tail_link(&self) -> bool { + !(self.creation_flags.contains(CreationFlags::O_NOFOLLOW) + || self.creation_flags.contains(CreationFlags::O_CREAT) + && self.creation_flags.contains(CreationFlags::O_EXCL)) + } +} diff --git a/kernel/src/fs/path/mod.rs b/kernel/src/fs/path/mod.rs index 13c2f1810..4c4039bf6 100644 --- a/kernel/src/fs/path/mod.rs +++ b/kernel/src/fs/path/mod.rs @@ -10,10 +10,12 @@ pub use mount_namespace::MountNamespace; use crate::{ fs::{ + inode_handle::InodeHandle, + open_args::OpenArgs, path::dentry::{Dentry, DentryKey}, utils::{ - FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType, Permission, XattrName, - XattrNamespace, XattrSetFlags, NAME_MAX, + CreationFlags, FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType, + Permission, StatusFlags, XattrName, XattrNamespace, XattrSetFlags, NAME_MAX, }, }, prelude::*, @@ -366,6 +368,47 @@ impl Path { self.dentry.rename(old_name, &new_dir.dentry, new_name) } + + /// Opens the `Path` with the given `OpenArgs`. + /// + /// Returns an `InodeHandle` on success. + pub fn open(&self, open_args: OpenArgs) -> Result { + let inode = self.inode(); + let inode_type = inode.type_(); + let creation_flags = &open_args.creation_flags; + + match inode_type { + InodeType::NamedPipe => { + warn!("NamedPipe doesn't support additional operation when opening."); + debug!("Open NamedPipe with args: {open_args:?}."); + } + InodeType::SymLink => { + if creation_flags.contains(CreationFlags::O_NOFOLLOW) + && !open_args.status_flags.contains(StatusFlags::O_PATH) + { + return_errno_with_message!(Errno::ELOOP, "file is a symlink"); + } + } + _ => {} + } + + if creation_flags.contains(CreationFlags::O_CREAT) + && creation_flags.contains(CreationFlags::O_EXCL) + { + return_errno_with_message!(Errno::EEXIST, "file exists"); + } + if creation_flags.contains(CreationFlags::O_DIRECTORY) && inode_type != InodeType::Dir { + return_errno_with_message!( + Errno::ENOTDIR, + "O_DIRECTORY is specified but file is not a directory" + ); + } + + if inode_type.is_regular_file() && creation_flags.contains(CreationFlags::O_TRUNC) { + self.resize(0)?; + } + InodeHandle::new(self.clone(), open_args.access_mode, open_args.status_flags) + } } #[inherit_methods(from = "self.dentry")] diff --git a/kernel/src/syscall/open.rs b/kernel/src/syscall/open.rs index 13fd3bd0c..b529a5cad 100644 --- a/kernel/src/syscall/open.rs +++ b/kernel/src/syscall/open.rs @@ -4,8 +4,10 @@ use super::SyscallReturn; use crate::{ fs::{ file_table::{FdFlags, FileDesc}, - fs_resolver::{FsPath, AT_FDCWD}, - utils::{AccessMode, CreationFlags}, + fs_resolver::{FsPath, FsResolver, LookupResult, AT_FDCWD}, + inode_handle::InodeHandle, + open_args::OpenArgs, + utils::{AccessMode, CreationFlags, InodeMode, InodeType}, }, prelude::*, syscall::constants::MAX_FILENAME_LEN, @@ -31,16 +33,22 @@ pub fn sys_openat( let file_handle = { let path = path.to_string_lossy(); let fs_path = FsPath::new(dirfd, path.as_ref())?; + let fs_ref = ctx.thread_local.borrow_fs(); let mask_mode = mode & !fs_ref.umask().get(); - let inode_handle = fs_ref - .resolver() - .read() - .open(&fs_path, flags, mask_mode) - .map_err(|err| match err.error() { - Errno::EINTR => Error::new(Errno::ERESTARTSYS), - _ => err, - })?; + + let fs_resolver = fs_ref.resolver().read(); + let inode_handle = do_open( + &fs_resolver, + &fs_path, + flags, + InodeMode::from_bits_truncate(mask_mode), + ) + .map_err(|err| match err.error() { + Errno::EINTR => Error::new(Errno::ERESTARTSYS), + _ => err, + })?; + Arc::new(inode_handle) }; @@ -68,3 +76,49 @@ pub fn sys_creat(path_addr: Vaddr, mode: u16, ctx: &Context) -> Result Result { + let open_args = OpenArgs::from_flags_and_mode(flags, mode)?; + + let lookup_res = if open_args.follow_tail_link() { + fs_resolver.lookup_unresolved(path)? + } else { + fs_resolver.lookup_unresolved_no_follow(path)? + }; + + let inode_handle = match lookup_res { + LookupResult::Resolved(target_path) => target_path.open(open_args)?, + LookupResult::AtParent(result) => { + if !open_args.creation_flags.contains(CreationFlags::O_CREAT) { + return_errno_with_message!(Errno::ENOENT, "file does not exist"); + } + if open_args + .creation_flags + .contains(CreationFlags::O_DIRECTORY) + { + return_errno_with_message!(Errno::ENOTDIR, "cannot create directory"); + } + if result.target_is_dir() { + return_errno_with_message!(Errno::EISDIR, "cannot create directory"); + } + + let (parent, tail_name) = result.into_parent_and_tail_name(); + let new_path = + parent.new_fs_child(&tail_name, InodeType::File, open_args.inode_mode)?; + + // Don't check access mode for newly created file. + InodeHandle::new_unchecked_access( + new_path, + open_args.access_mode, + open_args.status_flags, + )? + } + }; + + Ok(inode_handle) +}