From 24502ac3d4c35ebb6a4af16bec59cfed47d5cc43 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Wed, 5 Nov 2025 23:13:34 +0800 Subject: [PATCH] Add `InodeIo` to simplify `FileIo` and `Inode` --- kernel/src/device/full.rs | 28 +- kernel/src/device/null.rs | 28 +- kernel/src/device/pty/file.rs | 42 ++- kernel/src/device/pty/master.rs | 28 +- kernel/src/device/random.rs | 28 +- kernel/src/device/tdxguest/mod.rs | 34 +- kernel/src/device/tty/n_tty.rs | 32 +- kernel/src/device/urandom.rs | 28 +- kernel/src/device/zero.rs | 28 +- kernel/src/fs/devpts/mod.rs | 24 +- kernel/src/fs/devpts/ptmx.rs | 40 +-- kernel/src/fs/devpts/slave.rs | 46 ++- kernel/src/fs/exfat/inode.rs | 418 ++++++++++++----------- kernel/src/fs/ext2/impl_for_vfs/inode.rs | 55 +-- kernel/src/fs/inode_handle/mod.rs | 106 +++--- kernel/src/fs/overlayfs/fs.rs | 90 ++--- kernel/src/fs/pipe/named_pipe.rs | 28 +- kernel/src/fs/procfs/template/dir.rs | 24 +- kernel/src/fs/procfs/template/file.rs | 44 +-- kernel/src/fs/procfs/template/sym.rs | 44 +-- kernel/src/fs/pseudofs.rs | 55 ++- kernel/src/fs/ramfs/fs.rs | 55 ++- kernel/src/fs/ramfs/memfd.rs | 104 +++--- kernel/src/fs/sysfs/test.rs | 14 +- kernel/src/fs/utils/inode.rs | 77 ++--- kernel/src/fs/utils/mod.rs | 4 +- kernel/src/fs/utils/systree_inode.rs | 92 +++-- 27 files changed, 942 insertions(+), 654 deletions(-) diff --git a/kernel/src/device/full.rs b/kernel/src/device/full.rs index 4ba185b96..f0d2ec7a5 100644 --- a/kernel/src/device/full.rs +++ b/kernel/src/device/full.rs @@ -7,7 +7,7 @@ use crate::{ fs::{ device::{Device, DeviceType}, inode_handle::FileIo, - utils::StatusFlags, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -37,14 +37,34 @@ impl Pollable for Full { } } -impl FileIo for Full { - fn read(&self, writer: &mut VmWriter, _status_flags: StatusFlags) -> Result { +impl InodeIo for Full { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { let len = writer.avail(); writer.fill_zeros(len)?; Ok(len) } - fn write(&self, _reader: &mut VmReader, _status_flags: StatusFlags) -> Result { + fn write_at( + &self, + _offset: usize, + _reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { return_errno_with_message!(Errno::ENOSPC, "no space left on /dev/full") } } + +impl FileIo for Full { + fn check_seekable(&self) -> Result<()> { + Ok(()) + } + + fn is_offset_aware(&self) -> bool { + false + } +} diff --git a/kernel/src/device/null.rs b/kernel/src/device/null.rs index 1ec43081f..1d0c66244 100644 --- a/kernel/src/device/null.rs +++ b/kernel/src/device/null.rs @@ -7,7 +7,7 @@ use crate::{ fs::{ device::{Device, DeviceType}, inode_handle::FileIo, - utils::StatusFlags, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -37,14 +37,34 @@ impl Pollable for Null { } } -impl FileIo for Null { - fn read(&self, _writer: &mut VmWriter, _status_flags: StatusFlags) -> Result { +impl InodeIo for Null { + fn read_at( + &self, + _offset: usize, + _writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { Ok(0) } - fn write(&self, reader: &mut VmReader, _status_flags: StatusFlags) -> Result { + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { let len = reader.remain(); reader.skip(len); Ok(len) } } + +impl FileIo for Null { + fn check_seekable(&self) -> Result<()> { + Ok(()) + } + + fn is_offset_aware(&self) -> bool { + false + } +} diff --git a/kernel/src/device/pty/file.rs b/kernel/src/device/pty/file.rs index 329eac6d6..e30a60bf2 100644 --- a/kernel/src/device/pty/file.rs +++ b/kernel/src/device/pty/file.rs @@ -7,7 +7,7 @@ use crate::{ events::IoEvents, fs::{ inode_handle::FileIo, - utils::{IoctlCmd, StatusFlags}, + utils::{InodeIo, IoctlCmd, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -54,14 +54,40 @@ impl Drop for PtySlaveFile { } } -#[inherit_methods(from = "self.0")] -impl FileIo for PtySlaveFile { - fn read(&self, writer: &mut VmWriter, status_flags: StatusFlags) -> Result; - fn write(&self, reader: &mut VmReader, status_flags: StatusFlags) -> Result; - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result; -} - #[inherit_methods(from = "self.0")] impl Pollable for PtySlaveFile { fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents; } + +impl InodeIo for PtySlaveFile { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result { + self.0.read(writer, status_flags) + } + + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { + self.0.write(reader, status_flags) + } +} + +#[inherit_methods(from = "self.0")] +impl FileIo for PtySlaveFile { + fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result; + + fn check_seekable(&self) -> Result<()> { + return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY"); + } + + fn is_offset_aware(&self) -> bool { + false + } +} diff --git a/kernel/src/device/pty/master.rs b/kernel/src/device/pty/master.rs index fb23ec6b8..2c673e313 100644 --- a/kernel/src/device/pty/master.rs +++ b/kernel/src/device/pty/master.rs @@ -14,7 +14,7 @@ use crate::{ file_table::FdFlags, fs_resolver::FsPath, inode_handle::FileIo, - utils::{mkmod, AccessMode, IoctlCmd, OpenArgs, StatusFlags}, + utils::{mkmod, AccessMode, InodeIo, IoctlCmd, OpenArgs, StatusFlags}, }, prelude::*, process::{ @@ -87,8 +87,13 @@ impl Pollable for PtyMaster { } } -impl FileIo for PtyMaster { - fn read(&self, writer: &mut VmWriter, status_flags: StatusFlags) -> Result { +impl InodeIo for PtyMaster { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result { // TODO: Add support for timeout. let mut buf = vec![0u8; writer.avail().min(IO_CAPACITY)]; let is_nonblocking = status_flags.contains(StatusFlags::O_NONBLOCK); @@ -107,7 +112,12 @@ impl FileIo for PtyMaster { Ok(read_len) } - fn write(&self, reader: &mut VmReader, status_flags: StatusFlags) -> Result { + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { let mut buf = vec![0u8; reader.remain().min(IO_CAPACITY)]; let write_len = reader.read_fallible(&mut buf.as_mut_slice().into())?; @@ -123,6 +133,16 @@ impl FileIo for PtyMaster { self.slave.driver().pollee().invalidate(); Ok(len) } +} + +impl FileIo for PtyMaster { + fn check_seekable(&self) -> Result<()> { + return_errno_with_message!(Errno::ESPIPE, "the inode is a pty"); + } + + fn is_offset_aware(&self) -> bool { + false + } fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { match cmd { diff --git a/kernel/src/device/random.rs b/kernel/src/device/random.rs index 05aba505e..1a52e870e 100644 --- a/kernel/src/device/random.rs +++ b/kernel/src/device/random.rs @@ -8,7 +8,7 @@ use crate::{ fs::{ device::{Device, DeviceType}, inode_handle::FileIo, - utils::StatusFlags, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -45,14 +45,34 @@ impl Pollable for Random { } } -impl FileIo for Random { - fn read(&self, writer: &mut VmWriter, _status_flags: StatusFlags) -> Result { +impl InodeIo for Random { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { Self::getrandom(writer) } - fn write(&self, reader: &mut VmReader, _status_flags: StatusFlags) -> Result { + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { let len = reader.remain(); reader.skip(len); Ok(len) } } + +impl FileIo for Random { + fn check_seekable(&self) -> Result<()> { + Ok(()) + } + + fn is_offset_aware(&self) -> bool { + false + } +} diff --git a/kernel/src/device/tdxguest/mod.rs b/kernel/src/device/tdxguest/mod.rs index 640d9cbc9..2a9282d37 100644 --- a/kernel/src/device/tdxguest/mod.rs +++ b/kernel/src/device/tdxguest/mod.rs @@ -26,7 +26,7 @@ use crate::{ fs::{ device::{Device, DeviceType}, inode_handle::FileIo, - utils::{IoctlCmd, StatusFlags}, + utils::{InodeIo, IoctlCmd, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -99,19 +99,39 @@ impl Pollable for TdxGuest { } } -impl FileIo for TdxGuest { - fn read(&self, _writer: &mut VmWriter, _status_flags: StatusFlags) -> Result { - return_errno_with_message!(Errno::EPERM, "Read operation not supported") +impl InodeIo for TdxGuest { + fn read_at( + &self, + _offset: usize, + _writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { + return_errno_with_message!(Errno::EINVAL, "the file is not valid for reading") } - fn write(&self, _reader: &mut VmReader, _status_flags: StatusFlags) -> Result { - return_errno_with_message!(Errno::EPERM, "Write operation not supported") + fn write_at( + &self, + _offset: usize, + _reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { + return_errno_with_message!(Errno::EINVAL, "the file not valid for writing") + } +} + +impl FileIo for TdxGuest { + fn check_seekable(&self) -> Result<()> { + return_errno_with_message!(Errno::ESPIPE, "seek is not supported") + } + + fn is_offset_aware(&self) -> bool { + false } fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { match cmd { IoctlCmd::TDXGETREPORT => handle_get_report(arg), - _ => return_errno_with_message!(Errno::EPERM, "Unsupported ioctl"), + _ => return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"), } } } diff --git a/kernel/src/device/tty/n_tty.rs b/kernel/src/device/tty/n_tty.rs index f387363a8..f2467820f 100644 --- a/kernel/src/device/tty/n_tty.rs +++ b/kernel/src/device/tty/n_tty.rs @@ -10,7 +10,7 @@ use crate::{ events::IoEvents, fs::{ inode_handle::FileIo, - utils::{IoctlCmd, StatusFlags}, + utils::{InodeIo, IoctlCmd, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -57,11 +57,37 @@ impl Pollable for ConsoleFile { fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents; } +impl InodeIo for ConsoleFile { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result { + self.0.read(writer, status_flags) + } + + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { + self.0.write(reader, status_flags) + } +} + #[inherit_methods(from = "self.0")] impl FileIo for ConsoleFile { - fn read(&self, writer: &mut VmWriter, status_flags: StatusFlags) -> Result; - fn write(&self, reader: &mut VmReader, status_flags: StatusFlags) -> Result; fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result; + + fn check_seekable(&self) -> Result<()> { + return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY"); + } + + fn is_offset_aware(&self) -> bool { + false + } } static N_TTY: Once>]>> = Once::new(); diff --git a/kernel/src/device/urandom.rs b/kernel/src/device/urandom.rs index b3d5a41c6..190f3e00a 100644 --- a/kernel/src/device/urandom.rs +++ b/kernel/src/device/urandom.rs @@ -7,7 +7,7 @@ use crate::{ fs::{ device::{Device, DeviceType}, inode_handle::FileIo, - utils::StatusFlags, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -62,14 +62,34 @@ impl Pollable for Urandom { } } -impl FileIo for Urandom { - fn read(&self, writer: &mut VmWriter, _status_flags: StatusFlags) -> Result { +impl InodeIo for Urandom { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { Self::getrandom(writer) } - fn write(&self, reader: &mut VmReader, _status_flags: StatusFlags) -> Result { + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { let len = reader.remain(); reader.skip(len); Ok(len) } } + +impl FileIo for Urandom { + fn check_seekable(&self) -> Result<()> { + Ok(()) + } + + fn is_offset_aware(&self) -> bool { + false + } +} diff --git a/kernel/src/device/zero.rs b/kernel/src/device/zero.rs index 428f0762d..826d228d0 100644 --- a/kernel/src/device/zero.rs +++ b/kernel/src/device/zero.rs @@ -7,7 +7,7 @@ use crate::{ fs::{ device::{Device, DeviceType}, inode_handle::FileIo, - utils::StatusFlags, + utils::{InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -37,13 +37,33 @@ impl Pollable for Zero { } } -impl FileIo for Zero { - fn read(&self, writer: &mut VmWriter, _status_flags: StatusFlags) -> Result { +impl InodeIo for Zero { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { let read_len = writer.fill_zeros(writer.avail())?; Ok(read_len) } - fn write(&self, reader: &mut VmReader, _status_flags: StatusFlags) -> Result { + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { Ok(reader.remain()) } } + +impl FileIo for Zero { + fn check_seekable(&self) -> Result<()> { + Ok(()) + } + + fn is_offset_aware(&self) -> bool { + false + } +} diff --git a/kernel/src/fs/devpts/mod.rs b/kernel/src/fs/devpts/mod.rs index 5f2e9fcbf..daf0895e1 100644 --- a/kernel/src/fs/devpts/mod.rs +++ b/kernel/src/fs/devpts/mod.rs @@ -9,7 +9,7 @@ use id_alloc::IdAlloc; pub use self::ptmx::Ptmx; use self::slave::PtySlaveInode; -use super::utils::MknodType; +use super::utils::{InodeIo, MknodType, StatusFlags}; use crate::{ device::PtyMaster, fs::{ @@ -17,7 +17,7 @@ use crate::{ registry::{FsProperties, FsType}, utils::{ mkmod, DirEntryVecExt, DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, - IoctlCmd, Metadata, SuperBlock, NAME_MAX, + Metadata, SuperBlock, NAME_MAX, }, }, prelude::*, @@ -158,6 +158,26 @@ impl RootInode { } } +impl InodeIo for RootInode { + fn read_at( + &self, + _offset: usize, + _writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { + Err(Error::new(Errno::EISDIR)) + } + + fn write_at( + &self, + _offset: usize, + _reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { + Err(Error::new(Errno::EISDIR)) + } +} + impl Inode for RootInode { fn size(&self) -> usize { self.metadata.read().size diff --git a/kernel/src/fs/devpts/ptmx.rs b/kernel/src/fs/devpts/ptmx.rs index 617eff4cc..43560a20b 100644 --- a/kernel/src/fs/devpts/ptmx.rs +++ b/kernel/src/fs/devpts/ptmx.rs @@ -46,6 +46,26 @@ impl Ptmx { // Many methods are left to do nothing because every time the ptmx is being opened, // it returns the pty master. So the ptmx can not be used at upper layer. +impl InodeIo for Ptmx { + fn read_at( + &self, + _offset: usize, + _writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { + Ok(0) + } + + fn write_at( + &self, + _offset: usize, + _reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { + Ok(0) + } +} + impl Inode for Ptmx { fn size(&self) -> usize { self.metadata.read().size @@ -118,26 +138,6 @@ impl Inode for Ptmx { self.metadata.write().ctime = time; } - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - Ok(0) - } - - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - Ok(0) - } - - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { - Ok(0) - } - - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { - Ok(0) - } - - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - Ok(0) - } - fn fs(&self) -> Arc { // FIXME: The below code may panic if the devpts is dropped. self.devpts().unwrap() diff --git a/kernel/src/fs/devpts/slave.rs b/kernel/src/fs/devpts/slave.rs index a9e53cc83..088e3a006 100644 --- a/kernel/src/fs/devpts/slave.rs +++ b/kernel/src/fs/devpts/slave.rs @@ -6,12 +6,10 @@ use super::*; use crate::{ device::PtySlave, - events::IoEvents, fs::{ inode_handle::FileIo, utils::{AccessMode, StatusFlags}, }, - process::signal::{PollHandle, Pollable}, }; /// Same major number with Linux, the minor number is the index of slave. @@ -39,6 +37,26 @@ impl PtySlaveInode { } } +impl InodeIo for PtySlaveInode { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result { + self.device.read(writer, status_flags) + } + + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { + self.device.write(reader, status_flags) + } +} + impl Inode for PtySlaveInode { /// Do not cache dentry in DCACHE. /// @@ -119,30 +137,6 @@ impl Inode for PtySlaveInode { self.metadata.write().ctime = time; } - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - self.device.read(writer, StatusFlags::empty()) - } - - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - self.device.read(writer, StatusFlags::empty()) - } - - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { - self.device.write(reader, StatusFlags::empty()) - } - - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { - self.device.write(reader, StatusFlags::empty()) - } - - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - self.device.ioctl(cmd, arg) - } - - fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents { - self.device.poll(mask, poller) - } - fn fs(&self) -> Arc { self.fs.upgrade().unwrap() } diff --git a/kernel/src/fs/exfat/inode.rs b/kernel/src/fs/exfat/inode.rs index 44c2e2c46..fb9afe6d5 100644 --- a/kernel/src/fs/exfat/inode.rs +++ b/kernel/src/fs/exfat/inode.rs @@ -25,17 +25,16 @@ use super::{ utils::{make_hash_index, DosTimestamp}, }; use crate::{ - events::IoEvents, fs::{ exfat::{dentry::ExfatDentryIterator, fat::ExfatChain, fs::ExfatFs}, path::{is_dot, is_dot_or_dotdot, is_dotdot}, utils::{ - mkmod, CachePage, DirentVisitor, Extension, Inode, InodeMode, InodeType, IoctlCmd, - Metadata, MknodType, PageCache, PageCacheBackend, SymbolicLink, + mkmod, CachePage, DirentVisitor, Extension, Inode, InodeIo, InodeMode, InodeType, + Metadata, MknodType, PageCache, PageCacheBackend, StatusFlags, SymbolicLink, }, }, prelude::*, - process::{signal::PollHandle, Gid, Uid}, + process::{Gid, Uid}, vm::vmo::Vmo, }; @@ -615,6 +614,193 @@ impl ExfatInodeInner { } impl ExfatInode { + fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { + let inner = self.inner.upread(); + if inner.inode_type.is_directory() { + return_errno!(Errno::EISDIR) + } + let (read_off, read_len) = { + let file_size = inner.size; + let start = file_size.min(offset); + let end = file_size.min(offset + writer.avail()); + (start, end - start) + }; + inner.page_cache.pages().read(read_off, writer)?; + + inner.upgrade().update_atime()?; + Ok(read_len) + } + + // The offset and the length of buffer must be multiples of the block size. + fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { + let inner = self.inner.upread(); + if inner.inode_type.is_directory() { + return_errno!(Errno::EISDIR) + } + if !is_block_aligned(offset) || !is_block_aligned(writer.avail()) { + return_errno_with_message!(Errno::EINVAL, "not block-aligned"); + } + + let sector_size = inner.fs().sector_size(); + + let (read_off, read_len) = { + let file_size = inner.size; + let start = file_size.min(offset).align_down(sector_size); + let end = file_size + .min(offset + writer.avail()) + .align_down(sector_size); + (start, end - start) + }; + + inner + .page_cache + .discard_range(read_off..read_off + read_len); + + let mut buf_offset = 0; + let bio_segment = BioSegment::alloc(1, BioDirection::FromDevice); + + let start_pos = inner.start_chain.walk_to_cluster_at_offset(read_off)?; + let cluster_size = inner.fs().cluster_size(); + let mut cur_cluster = start_pos.0.clone(); + let mut cur_offset = start_pos.1; + for _ in Bid::from_offset(read_off)..Bid::from_offset(read_off + read_len) { + let physical_bid = + Bid::from_offset(cur_cluster.cluster_id() as usize * cluster_size + cur_offset); + inner + .fs() + .block_device() + .read_blocks(physical_bid, bio_segment.clone())?; + bio_segment.reader().unwrap().read_fallible(writer)?; + buf_offset += BLOCK_SIZE; + + cur_offset += BLOCK_SIZE; + if cur_offset >= cluster_size { + cur_cluster = cur_cluster.walk(1)?; + cur_offset %= BLOCK_SIZE; + } + } + + inner.upgrade().update_atime()?; + Ok(read_len) + } + + fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { + let write_len = reader.remain(); + // We need to obtain the fs lock to resize the file. + let new_size = { + let mut inner = self.inner.write(); + if inner.inode_type.is_directory() { + return_errno!(Errno::EISDIR) + } + + let file_size = inner.size; + let file_allocated_size = inner.size_allocated; + let new_size = offset + write_len; + let fs = inner.fs(); + let fs_guard = fs.lock(); + if new_size > file_size { + if new_size > file_allocated_size { + inner.resize(new_size, &fs_guard)?; + } + inner.page_cache.resize(new_size)?; + } + new_size.max(file_size) + }; + + // Locks released here, so that file write can be parallelized. + let inner = self.inner.upread(); + inner.page_cache.pages().write(offset, reader)?; + + // Update timestamps and size. + { + let mut inner = inner.upgrade(); + + inner.update_atime_and_mtime()?; + inner.size = new_size; + } + + let inner = self.inner.read(); + + // Write data back. + if inner.is_sync() { + let fs = inner.fs(); + let fs_guard = fs.lock(); + inner.sync_all(&fs_guard)?; + } + + Ok(write_len) + } + + fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { + let write_len = reader.remain(); + let inner = self.inner.upread(); + if inner.inode_type.is_directory() { + return_errno!(Errno::EISDIR) + } + if !is_block_aligned(offset) || !is_block_aligned(write_len) { + return_errno_with_message!(Errno::EINVAL, "not block-aligned"); + } + + let file_size = inner.size; + let file_allocated_size = inner.size_allocated; + let end_offset = offset + write_len; + + let start = offset.min(file_size); + let end = end_offset.min(file_size); + inner.page_cache.discard_range(start..end); + + let new_size = { + let mut inner = inner.upgrade(); + if end_offset > file_size { + let fs = inner.fs(); + let fs_guard = fs.lock(); + if end_offset > file_allocated_size { + inner.resize(end_offset, &fs_guard)?; + } + inner.page_cache.resize(end_offset)?; + } + file_size.max(end_offset) + }; + + let inner = self.inner.upread(); + + let bio_segment = BioSegment::alloc(1, BioDirection::ToDevice); + let start_pos = inner.start_chain.walk_to_cluster_at_offset(offset)?; + let cluster_size = inner.fs().cluster_size(); + let mut cur_cluster = start_pos.0.clone(); + let mut cur_offset = start_pos.1; + for _ in Bid::from_offset(offset)..Bid::from_offset(end_offset) { + bio_segment.writer().unwrap().write_fallible(reader)?; + let physical_bid = + Bid::from_offset(cur_cluster.cluster_id() as usize * cluster_size + cur_offset); + let fs = inner.fs(); + fs.block_device() + .write_blocks(physical_bid, bio_segment.clone())?; + + cur_offset += BLOCK_SIZE; + if cur_offset >= cluster_size { + cur_cluster = cur_cluster.walk(1)?; + cur_offset %= BLOCK_SIZE; + } + } + + { + let mut inner = inner.upgrade(); + inner.update_atime_and_mtime()?; + inner.size = new_size; + } + + let inner = self.inner.read(); + // Sync this inode since size has changed. + if inner.is_sync() { + let fs = inner.fs(); + let fs_guard = fs.lock(); + inner.sync_metadata(&fs_guard)?; + } + + Ok(write_len) + } + // TODO: Should be called when inode is evicted from fs. pub(super) fn reclaim_space(&self) -> Result<()> { let inner = self.inner.write(); @@ -1093,6 +1279,34 @@ fn check_corner_cases_for_rename( Ok(()) } +impl InodeIo for ExfatInode { + fn read_at( + &self, + offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result { + if status_flags.contains(StatusFlags::O_DIRECT) { + self.read_direct_at(offset, writer) + } else { + self.read_at(offset, writer) + } + } + + fn write_at( + &self, + offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { + if status_flags.contains(StatusFlags::O_DIRECT) { + self.write_direct_at(offset, reader) + } else { + self.write_at(offset, reader) + } + } +} + impl Inode for ExfatInode { fn ino(&self) -> u64 { self.inner.read().ino @@ -1231,193 +1445,6 @@ impl Inode for ExfatInode { Some(self.inner.read().page_cache.pages().clone()) } - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - let inner = self.inner.upread(); - if inner.inode_type.is_directory() { - return_errno!(Errno::EISDIR) - } - let (read_off, read_len) = { - let file_size = inner.size; - let start = file_size.min(offset); - let end = file_size.min(offset + writer.avail()); - (start, end - start) - }; - inner.page_cache.pages().read(read_off, writer)?; - - inner.upgrade().update_atime()?; - Ok(read_len) - } - - // The offset and the length of buffer must be multiples of the block size. - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - let inner = self.inner.upread(); - if inner.inode_type.is_directory() { - return_errno!(Errno::EISDIR) - } - if !is_block_aligned(offset) || !is_block_aligned(writer.avail()) { - return_errno_with_message!(Errno::EINVAL, "not block-aligned"); - } - - let sector_size = inner.fs().sector_size(); - - let (read_off, read_len) = { - let file_size = inner.size; - let start = file_size.min(offset).align_down(sector_size); - let end = file_size - .min(offset + writer.avail()) - .align_down(sector_size); - (start, end - start) - }; - - inner - .page_cache - .discard_range(read_off..read_off + read_len); - - let mut buf_offset = 0; - let bio_segment = BioSegment::alloc(1, BioDirection::FromDevice); - - let start_pos = inner.start_chain.walk_to_cluster_at_offset(read_off)?; - let cluster_size = inner.fs().cluster_size(); - let mut cur_cluster = start_pos.0.clone(); - let mut cur_offset = start_pos.1; - for _ in Bid::from_offset(read_off)..Bid::from_offset(read_off + read_len) { - let physical_bid = - Bid::from_offset(cur_cluster.cluster_id() as usize * cluster_size + cur_offset); - inner - .fs() - .block_device() - .read_blocks(physical_bid, bio_segment.clone())?; - bio_segment.reader().unwrap().read_fallible(writer)?; - buf_offset += BLOCK_SIZE; - - cur_offset += BLOCK_SIZE; - if cur_offset >= cluster_size { - cur_cluster = cur_cluster.walk(1)?; - cur_offset %= BLOCK_SIZE; - } - } - - inner.upgrade().update_atime()?; - Ok(read_len) - } - - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { - let write_len = reader.remain(); - // We need to obtain the fs lock to resize the file. - let new_size = { - let mut inner = self.inner.write(); - if inner.inode_type.is_directory() { - return_errno!(Errno::EISDIR) - } - - let file_size = inner.size; - let file_allocated_size = inner.size_allocated; - let new_size = offset + write_len; - let fs = inner.fs(); - let fs_guard = fs.lock(); - if new_size > file_size { - if new_size > file_allocated_size { - inner.resize(new_size, &fs_guard)?; - } - inner.page_cache.resize(new_size)?; - } - new_size.max(file_size) - }; - - // Locks released here, so that file write can be parallelized. - let inner = self.inner.upread(); - inner.page_cache.pages().write(offset, reader)?; - - // Update timestamps and size. - { - let mut inner = inner.upgrade(); - - inner.update_atime_and_mtime()?; - inner.size = new_size; - } - - let inner = self.inner.read(); - - // Write data back. - if inner.is_sync() { - let fs = inner.fs(); - let fs_guard = fs.lock(); - inner.sync_all(&fs_guard)?; - } - - Ok(write_len) - } - - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { - let write_len = reader.remain(); - let inner = self.inner.upread(); - if inner.inode_type.is_directory() { - return_errno!(Errno::EISDIR) - } - if !is_block_aligned(offset) || !is_block_aligned(write_len) { - return_errno_with_message!(Errno::EINVAL, "not block-aligned"); - } - - let file_size = inner.size; - let file_allocated_size = inner.size_allocated; - let end_offset = offset + write_len; - - let start = offset.min(file_size); - let end = end_offset.min(file_size); - inner.page_cache.discard_range(start..end); - - let new_size = { - let mut inner = inner.upgrade(); - if end_offset > file_size { - let fs = inner.fs(); - let fs_guard = fs.lock(); - if end_offset > file_allocated_size { - inner.resize(end_offset, &fs_guard)?; - } - inner.page_cache.resize(end_offset)?; - } - file_size.max(end_offset) - }; - - let inner = self.inner.upread(); - - let bio_segment = BioSegment::alloc(1, BioDirection::ToDevice); - let start_pos = inner.start_chain.walk_to_cluster_at_offset(offset)?; - let cluster_size = inner.fs().cluster_size(); - let mut cur_cluster = start_pos.0.clone(); - let mut cur_offset = start_pos.1; - for _ in Bid::from_offset(offset)..Bid::from_offset(end_offset) { - bio_segment.writer().unwrap().write_fallible(reader)?; - let physical_bid = - Bid::from_offset(cur_cluster.cluster_id() as usize * cluster_size + cur_offset); - let fs = inner.fs(); - fs.block_device() - .write_blocks(physical_bid, bio_segment.clone())?; - - cur_offset += BLOCK_SIZE; - if cur_offset >= cluster_size { - cur_cluster = cur_cluster.walk(1)?; - cur_offset %= BLOCK_SIZE; - } - } - - { - let mut inner = inner.upgrade(); - inner.update_atime_and_mtime()?; - inner.size = new_size; - } - - let inner = self.inner.read(); - // Sync this inode since size has changed. - if inner.is_sync() { - let fs = inner.fs(); - let fs_guard = fs.lock(); - inner.sync_metadata(&fs_guard)?; - } - - Ok(write_len) - } - fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result> { let fs = self.inner.read().fs(); let fs_guard = fs.lock(); @@ -1679,10 +1706,6 @@ impl Inode for ExfatInode { return_errno_with_message!(Errno::EINVAL, "unsupported operation") } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - return_errno_with_message!(Errno::EINVAL, "unsupported operation") - } - fn sync_all(&self) -> Result<()> { let inner = self.inner.read(); let fs = inner.fs(); @@ -1705,11 +1728,6 @@ impl Inode for ExfatInode { Ok(()) } - fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents { - let events = IoEvents::IN | IoEvents::OUT; - events & mask - } - fn is_dentry_cacheable(&self) -> bool { true } diff --git a/kernel/src/fs/ext2/impl_for_vfs/inode.rs b/kernel/src/fs/ext2/impl_for_vfs/inode.rs index 27cca69e3..a66b861f6 100644 --- a/kernel/src/fs/ext2/impl_for_vfs/inode.rs +++ b/kernel/src/fs/ext2/impl_for_vfs/inode.rs @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MPL-2.0 -#![expect(unused_variables)] - use core::time::Duration; use crate::{ fs::{ ext2::{FilePerm, Inode as Ext2Inode}, utils::{ - DirentVisitor, Extension, FallocMode, FileSystem, Inode, InodeMode, InodeType, - IoctlCmd, Metadata, MknodType, SymbolicLink, XattrName, XattrNamespace, XattrSetFlags, + DirentVisitor, Extension, FallocMode, FileSystem, Inode, InodeIo, InodeMode, InodeType, + Metadata, MknodType, StatusFlags, SymbolicLink, XattrName, XattrNamespace, + XattrSetFlags, }, }, prelude::*, @@ -17,6 +16,34 @@ use crate::{ vm::vmo::Vmo, }; +impl InodeIo for Ext2Inode { + fn read_at( + &self, + offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result { + if status_flags.contains(StatusFlags::O_DIRECT) { + self.read_direct_at(offset, writer) + } else { + self.read_at(offset, writer) + } + } + + fn write_at( + &self, + offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { + if status_flags.contains(StatusFlags::O_DIRECT) { + self.write_direct_at(offset, reader) + } else { + self.write_at(offset, reader) + } + } +} + impl Inode for Ext2Inode { fn size(&self) -> usize { self.file_size() as _ @@ -93,22 +120,6 @@ impl Inode for Ext2Inode { Some(self.page_cache()) } - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - self.read_at(offset, writer) - } - - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - self.read_direct_at(offset, writer) - } - - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { - self.write_at(offset, reader) - } - - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { - self.write_direct_at(offset, reader) - } - fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result> { Ok(self.create(name, type_, mode.into())?) } @@ -169,10 +180,6 @@ impl Inode for Ext2Inode { self.fallocate(mode, offset, len) } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - Err(Error::new(Errno::EINVAL)) - } - fn sync_all(&self) -> Result<()> { self.sync_all()?; self.fs().block_device().sync()?; diff --git a/kernel/src/fs/inode_handle/mod.rs b/kernel/src/fs/inode_handle/mod.rs index ed8e44e86..15dc128f4 100644 --- a/kernel/src/fs/inode_handle/mod.rs +++ b/kernel/src/fs/inode_handle/mod.rs @@ -8,6 +8,7 @@ use core::sync::atomic::{AtomicU32, Ordering}; pub use dyn_cap::InodeHandle; +use super::utils::InodeIo; use crate::{ events::IoEvents, fs::{ @@ -34,71 +35,91 @@ struct HandleInner { impl HandleInner { pub(self) fn read(&self, writer: &mut VmWriter) -> Result { - if let Some(ref file_io) = self.file_io { - return file_io.read(writer, self.status_flags()); - } + let (inode_io, is_offset_aware) = self.inode_io_and_is_offset_aware(); + let status_flags = self.status_flags(); - if !self.path.inode().is_seekable() { - return self.read_at(0, writer); + if !is_offset_aware { + return inode_io.read_at(0, writer, status_flags); } let mut offset = self.offset.lock(); - let len = self.read_at(*offset, writer)?; - + let len = inode_io.read_at(*offset, writer, status_flags)?; *offset += len; + Ok(len) } pub(self) fn write(&self, reader: &mut VmReader) -> Result { - if let Some(ref file_io) = self.file_io { - return file_io.write(reader, self.status_flags()); - } + let (inode_io, is_offset_aware) = self.inode_io_and_is_offset_aware(); + let status_flags = self.status_flags(); - if !self.path.inode().is_seekable() { - return self.write_at(0, reader); + if !is_offset_aware { + return inode_io.write_at(0, reader, status_flags); } let mut offset = self.offset.lock(); - if self.status_flags().contains(StatusFlags::O_APPEND) { + // FIXME: How can we deal with the `O_APPEND` flag if `file_io` is set? + if status_flags.contains(StatusFlags::O_APPEND) && self.file_io.is_none() { + // FIXME: `O_APPEND` should ensure that new content is appended even if another process + // is writing to the file concurrently. *offset = self.path.size(); } - let len = self.write_at(*offset, reader)?; - + let len = inode_io.write_at(*offset, reader, status_flags)?; *offset += len; + Ok(len) } - pub(self) fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - if let Some(ref _file_io) = self.file_io { - todo!("support read_at for FileIo"); + fn inode_io_and_is_offset_aware(&self) -> (&dyn InodeIo, bool) { + if let Some(ref file_io) = self.file_io { + let is_offset_aware = file_io.is_offset_aware(); + return (file_io.as_ref(), is_offset_aware); } - if self.status_flags().contains(StatusFlags::O_DIRECT) { - self.path.inode().read_direct_at(offset, writer) - } else { - self.path.inode().read_at(offset, writer) - } + let inode = self.path.inode(); + let is_offset_aware = inode.type_().is_seekable(); + (inode.as_ref(), is_offset_aware) + } + + pub(self) fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { + let inode_io = self.inode_io_and_check_seekable()?; + let status_flags = self.status_flags(); + + inode_io.read_at(offset, writer, status_flags) } pub(self) fn write_at(&self, mut offset: usize, reader: &mut VmReader) -> Result { - if let Some(ref _file_io) = self.file_io { - todo!("support write_at for FileIo"); - } - + let inode_io = self.inode_io_and_check_seekable()?; let status_flags = self.status_flags(); - if status_flags.contains(StatusFlags::O_APPEND) { - // If the file has the O_APPEND flag, the offset is ignored + + // FIXME: How can we deal with the `O_APPEND` flag if `file_io` is set? + if status_flags.contains(StatusFlags::O_APPEND) && self.file_io.is_none() { + // If the file has the `O_APPEND` flag, the offset is ignored. + // FIXME: `O_APPEND` should ensure that new content is appended even if another process + // is writing to the file concurrently. offset = self.path.size(); } - if status_flags.contains(StatusFlags::O_DIRECT) { - self.path.inode().write_direct_at(offset, reader) - } else { - self.path.inode().write_at(offset, reader) + inode_io.write_at(offset, reader, status_flags) + } + + fn inode_io_and_check_seekable(&self) -> Result<&dyn InodeIo> { + if let Some(ref file_io) = self.file_io { + file_io.check_seekable()?; + return Ok(file_io.as_ref()); } + + let inode = self.path.inode(); + if !inode.type_().is_seekable() { + return_errno_with_message!( + Errno::ESPIPE, + "the inode cannot be read or written at a specific offset" + ); + } + Ok(inode.as_ref()) } pub(self) fn seek(&self, pos: SeekFrom) -> Result { @@ -136,7 +157,8 @@ impl HandleInner { return file_io.poll(mask, poller); } - self.path.inode().poll(mask, poller) + let events = IoEvents::IN | IoEvents::OUT; + events & mask } pub(self) fn fallocate(&self, mode: FallocMode, offset: usize, len: usize) -> Result<()> { @@ -154,7 +176,7 @@ impl HandleInner { return file_io.ioctl(cmd, arg); } - self.path.inode().ioctl(cmd, arg) + return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); } pub(self) fn mappable(&self) -> Result { @@ -265,16 +287,12 @@ impl Debug for HandleInner { /// /// This trait is typically implemented for special files like devices or /// named pipes (FIFOs), which have behaviors different from regular on-disk files. -// -// TODO: The `status_flags` parameter in `read` and `write` may need to be stored directly -// in the `FileIo`. We need further refactoring to find an appropriate way to enable `FileIo` -// to utilize the information in the `HandleInner`. -pub trait FileIo: Pollable + Send + Sync + 'static { - /// Reads data from the file into the given `VmWriter`. - fn read(&self, writer: &mut VmWriter, status_flags: StatusFlags) -> Result; +pub trait FileIo: Pollable + InodeIo + Send + Sync + 'static { + /// Checks whether the `seek()` operation should fail. + fn check_seekable(&self) -> Result<()>; - /// Writes data from the given `VmReader` into the file. - fn write(&self, reader: &mut VmReader, status_flags: StatusFlags) -> Result; + /// Returns whether the `read()`/`write()` operation should use and advance the offset. + fn is_offset_aware(&self) -> bool; // See `FileLike::mappable`. fn mappable(&self) -> Result { diff --git a/kernel/src/fs/overlayfs/fs.rs b/kernel/src/fs/overlayfs/fs.rs index d15a407df..34e1f709f 100644 --- a/kernel/src/fs/overlayfs/fs.rs +++ b/kernel/src/fs/overlayfs/fs.rs @@ -25,7 +25,7 @@ use crate::{ registry::{FsProperties, FsType}, utils::{ mkmod, AccessMode, DirentCounter, DirentVisitor, FallocMode, FileSystem, FsFlags, - Inode, InodeMode, InodeType, IoctlCmd, Metadata, MknodType, StatusFlags, SuperBlock, + Inode, InodeIo, InodeMode, InodeType, Metadata, MknodType, StatusFlags, SuperBlock, SymbolicLink, XattrName, XattrNamespace, XattrSetFlags, NAME_MAX, XATTR_VALUE_MAX_LEN, }, }, @@ -280,34 +280,30 @@ impl OverlayInode { /// Writes data to the target inode, if it resides in the lower layer, /// it will be copied up to the upper layer. /// The corresponding parent directories will be created also if they do not exist. - pub fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { + pub fn write_at( + &self, + offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { if self.type_ == InodeType::Dir { return_errno!(Errno::EISDIR); } let upper = self.build_upper_recursively_if_needed()?; - upper.write_at(offset, reader) + upper.write_at(offset, reader, status_flags) } - pub fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { + pub fn read_at( + &self, + offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result { if self.type_ == InodeType::Dir { return_errno!(Errno::EISDIR); } - let upper = self.build_upper_recursively_if_needed()?; - upper.write_direct_at(offset, reader) - } - - pub fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - if self.type_ == InodeType::Dir { - return_errno!(Errno::EISDIR); - } - self.get_top_valid_inode().read_at(offset, writer) - } - - pub fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - if self.type_ == InodeType::Dir { - return_errno!(Errno::EISDIR); - } - self.get_top_valid_inode().read_direct_at(offset, writer) + self.get_top_valid_inode() + .read_at(offset, writer, status_flags) } /// Returns the children objects in a unified view. @@ -497,13 +493,6 @@ impl OverlayInode { pub fn sync_data(&self) -> Result<()> { self.upper().map_or(Ok(()), |upper| upper.sync_data()) } - - pub fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - self.upper().map_or_else( - || Err(Error::with_message(Errno::ENOTTY, "ioctl is not supported")), - |upper| upper.ioctl(cmd, arg), - ) - } } #[inherit_methods(from = "self.get_top_valid_inode()")] @@ -828,10 +817,10 @@ impl OverlayInode { .alloc_segment(lower_size.align_up(BLOCK_SIZE) / BLOCK_SIZE)?; let mut writer = data_buf.writer().to_fallible(); - let read_len = lower.read_at(0, &mut writer)?; + let read_len = lower.read_at(0, &mut writer, StatusFlags::empty())?; let mut reader = data_buf.reader().to_fallible(); - let _ = upper.write_at(0, reader.limit(read_len))?; + let _ = upper.write_at(0, reader.limit(read_len), StatusFlags::empty())?; Ok(()) } @@ -906,6 +895,22 @@ fn is_opaque_dir(inode: &Arc) -> Result { Ok(value == WHITEOUT_AND_OPAQUE_XATTR_VALUE) } +#[inherit_methods(from = "self")] +impl InodeIo for OverlayInode { + fn read_at( + &self, + offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result; + fn write_at( + &self, + offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result; +} + #[inherit_methods(from = "self")] impl Inode for OverlayInode { fn size(&self) -> usize; @@ -926,10 +931,6 @@ impl Inode for OverlayInode { fn ctime(&self) -> Duration; fn set_ctime(&self, time: Duration); fn page_cache(&self) -> Option>; - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result; - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result; - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result; - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result; fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result>; fn mknod(&self, name: &str, mode: InodeMode, type_: MknodType) -> Result>; fn open( @@ -945,7 +946,6 @@ impl Inode for OverlayInode { fn rename(&self, old_name: &str, target: &Arc, new_name: &str) -> Result<()>; fn read_link(&self) -> Result; fn write_link(&self, target: &str) -> Result<()>; - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result; fn sync_all(&self) -> Result<()>; fn sync_data(&self) -> Result<()>; fn fallocate(&self, mode: FallocMode, offset: usize, len: usize) -> Result<()>; @@ -1219,7 +1219,11 @@ mod tests { let f2 = l2.new_fs_child("f2", InodeType::File, mode).unwrap(); let f2_inode = f2.inode(); f2_inode - .write_at(0, &mut VmReader::from([8u8; 4].as_slice()).to_fallible()) + .write_at( + 0, + &mut VmReader::from([8u8; 4].as_slice()).to_fallible(), + StatusFlags::empty(), + ) .unwrap(); f2_inode.set_group(Gid::new(77)).unwrap(); f2_inode @@ -1361,8 +1365,12 @@ mod tests { let mut data = [0u8; 4]; let f2 = root.lookup("f2").unwrap(); - f2.read_at(0, &mut VmWriter::from(data.as_mut_slice()).to_fallible()) - .unwrap(); + f2.read_at( + 0, + &mut VmWriter::from(data.as_mut_slice()).to_fallible(), + StatusFlags::empty(), + ) + .unwrap(); assert_eq!(data, [8u8; 4]); let d1 = root.lookup("d1").unwrap(); @@ -1444,8 +1452,12 @@ mod tests { f1.set_atime(Duration::default()); f1.sync_data().unwrap(); let mut data = [0u8; 1]; - f1.read_at(0, &mut VmWriter::from(data.as_mut_slice()).to_fallible()) - .unwrap(); + f1.read_at( + 0, + &mut VmWriter::from(data.as_mut_slice()).to_fallible(), + StatusFlags::empty(), + ) + .unwrap(); assert_eq!(data, [3u8; 1]); let d1 = root.lookup("d1").unwrap(); diff --git a/kernel/src/fs/pipe/named_pipe.rs b/kernel/src/fs/pipe/named_pipe.rs index cc36b5a54..9cb39f7bb 100644 --- a/kernel/src/fs/pipe/named_pipe.rs +++ b/kernel/src/fs/pipe/named_pipe.rs @@ -12,7 +12,7 @@ use crate::{ events::IoEvents, fs::{ inode_handle::FileIo, - utils::{AccessMode, StatusFlags}, + utils::{AccessMode, InodeIo, StatusFlags}, }, prelude::*, process::signal::{PollHandle, Pollable}, @@ -81,8 +81,13 @@ impl Drop for NamedPipeHandle { } } -impl FileIo for NamedPipeHandle { - fn read(&self, writer: &mut VmWriter, status_flags: StatusFlags) -> Result { +impl InodeIo for NamedPipeHandle { + fn read_at( + &self, + _offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result { if status_flags.contains(StatusFlags::O_NONBLOCK) { self.try_read(writer) } else { @@ -90,7 +95,12 @@ impl FileIo for NamedPipeHandle { } } - fn write(&self, reader: &mut VmReader, status_flags: StatusFlags) -> Result { + fn write_at( + &self, + _offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { if status_flags.contains(StatusFlags::O_NONBLOCK) { self.try_write(reader) } else { @@ -99,6 +109,16 @@ impl FileIo for NamedPipeHandle { } } +impl FileIo for NamedPipeHandle { + fn check_seekable(&self) -> Result<()> { + return_errno_with_message!(Errno::ESPIPE, "the inode is a FIFO file") + } + + fn is_offset_aware(&self) -> bool { + false + } +} + /// A named pipe (FIFO) that provides inter-process communication. /// /// Named pipes are special files that appear in the filesystem and provide diff --git a/kernel/src/fs/procfs/template/dir.rs b/kernel/src/fs/procfs/template/dir.rs index 0ecff0006..a70967483 100644 --- a/kernel/src/fs/procfs/template/dir.rs +++ b/kernel/src/fs/procfs/template/dir.rs @@ -11,8 +11,8 @@ use crate::{ fs::{ path::{is_dot, is_dotdot}, utils::{ - DirEntryVecExt, DirentVisitor, FileSystem, Inode, InodeMode, InodeType, Metadata, - MknodType, + DirEntryVecExt, DirentVisitor, FileSystem, Inode, InodeIo, InodeMode, InodeType, + Metadata, MknodType, StatusFlags, }, }, prelude::*, @@ -71,6 +71,26 @@ impl ProcDir { } } +impl InodeIo for ProcDir { + fn read_at( + &self, + _offset: usize, + _writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { + Err(Error::new(Errno::EISDIR)) + } + + fn write_at( + &self, + _offset: usize, + _reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { + Err(Error::new(Errno::EISDIR)) + } +} + #[inherit_methods(from = "self.common")] impl Inode for ProcDir { fn size(&self) -> usize; diff --git a/kernel/src/fs/procfs/template/file.rs b/kernel/src/fs/procfs/template/file.rs index aa3495a36..fd587ab14 100644 --- a/kernel/src/fs/procfs/template/file.rs +++ b/kernel/src/fs/procfs/template/file.rs @@ -6,7 +6,9 @@ use inherit_methods_macro::inherit_methods; use super::{Common, ProcFs}; use crate::{ - fs::utils::{FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata, SymbolicLink}, + fs::utils::{ + FileSystem, Inode, InodeIo, InodeMode, InodeType, Metadata, StatusFlags, SymbolicLink, + }, prelude::*, process::{Gid, Uid}, }; @@ -36,6 +38,26 @@ impl ProcFile { } } +impl InodeIo for ProcFile { + fn read_at( + &self, + offset: usize, + writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { + self.inner.read_at(offset, writer) + } + + fn write_at( + &self, + offset: usize, + reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { + self.inner.write_at(offset, reader) + } +} + #[inherit_methods(from = "self.common")] impl Inode for ProcFile { fn size(&self) -> usize; @@ -63,22 +85,6 @@ impl Inode for ProcFile { InodeType::File } - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - self.inner.read_at(offset, writer) - } - - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - self.read_at(offset, writer) - } - - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { - self.inner.write_at(offset, reader) - } - - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { - self.write_at(offset, reader) - } - fn read_link(&self) -> Result { Err(Error::new(Errno::EINVAL)) } @@ -87,10 +93,6 @@ impl Inode for ProcFile { Err(Error::new(Errno::EINVAL)) } - fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { - Err(Error::new(Errno::EPERM)) - } - fn is_dentry_cacheable(&self) -> bool { !self.common.is_volatile() } diff --git a/kernel/src/fs/procfs/template/sym.rs b/kernel/src/fs/procfs/template/sym.rs index cc933560e..c5deeffac 100644 --- a/kernel/src/fs/procfs/template/sym.rs +++ b/kernel/src/fs/procfs/template/sym.rs @@ -6,7 +6,9 @@ use inherit_methods_macro::inherit_methods; use super::{Common, ProcFs}; use crate::{ - fs::utils::{FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata, SymbolicLink}, + fs::utils::{ + FileSystem, Inode, InodeIo, InodeMode, InodeType, Metadata, StatusFlags, SymbolicLink, + }, prelude::*, process::{Gid, Uid}, }; @@ -37,6 +39,26 @@ impl ProcSym { } } +impl InodeIo for ProcSym { + fn read_at( + &self, + _offset: usize, + _writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { + Err(Error::new(Errno::EPERM)) + } + + fn write_at( + &self, + _offset: usize, + _reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { + Err(Error::new(Errno::EPERM)) + } +} + #[inherit_methods(from = "self.common")] impl Inode for ProcSym { fn size(&self) -> usize; @@ -64,22 +86,6 @@ impl Inode for ProcSym { InodeType::SymLink } - fn read_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result { - Err(Error::new(Errno::EPERM)) - } - - fn read_direct_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result { - Err(Error::new(Errno::EPERM)) - } - - fn write_at(&self, _offset: usize, _reader: &mut VmReader) -> Result { - Err(Error::new(Errno::EPERM)) - } - - fn write_direct_at(&self, _offset: usize, _reader: &mut VmReader) -> Result { - Err(Error::new(Errno::EPERM)) - } - fn read_link(&self) -> Result { self.inner.read_link() } @@ -88,10 +94,6 @@ impl Inode for ProcSym { Err(Error::new(Errno::EPERM)) } - fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { - Err(Error::new(Errno::EPERM)) - } - fn is_dentry_cacheable(&self) -> bool { !self.common.is_volatile() } diff --git a/kernel/src/fs/pseudofs.rs b/kernel/src/fs/pseudofs.rs index 5fc779892..f6f0bd26d 100644 --- a/kernel/src/fs/pseudofs.rs +++ b/kernel/src/fs/pseudofs.rs @@ -4,6 +4,7 @@ use core::time::Duration; use spin::Once; +use super::utils::{InodeIo, StatusFlags}; use crate::{ fs::{ registry::{FsProperties, FsType}, @@ -210,6 +211,32 @@ impl PseudoInode { } } +impl InodeIo for PseudoInode { + fn read_at( + &self, + _offset: usize, + _writer: &mut VmWriter, + _status: StatusFlags, + ) -> Result { + return_errno_with_message!( + Errno::ESPIPE, + "pseudo inodes cannot be read at a specific offset" + ); + } + + fn write_at( + &self, + _offset: usize, + _reader: &mut VmReader, + _status: StatusFlags, + ) -> Result { + return_errno_with_message!( + Errno::ESPIPE, + "pseudo inodes cannot be written at a specific offset" + ); + } +} + impl Inode for PseudoInode { fn size(&self) -> usize { self.metadata.lock().size @@ -288,34 +315,6 @@ impl Inode for PseudoInode { self.metadata.lock().ctime = time; } - fn read_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result { - return_errno_with_message!( - Errno::ESPIPE, - "pseudo inodes cannot be read at a specific offset" - ); - } - - fn read_direct_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result { - return_errno_with_message!( - Errno::ESPIPE, - "pseudo inodes cannot be read at a specific offset" - ); - } - - fn write_at(&self, _offset: usize, _reader: &mut VmReader) -> Result { - return_errno_with_message!( - Errno::ESPIPE, - "pseudo inodes cannot be written at a specific offset" - ); - } - - fn write_direct_at(&self, _offset: usize, _reader: &mut VmReader) -> Result { - return_errno_with_message!( - Errno::ESPIPE, - "pseudo inodes cannot be written at a specific offset" - ); - } - fn fs(&self) -> Arc { self.fs.upgrade().unwrap() } diff --git a/kernel/src/fs/ramfs/fs.rs b/kernel/src/fs/ramfs/fs.rs index 2af980b81..606c11dd4 100644 --- a/kernel/src/fs/ramfs/fs.rs +++ b/kernel/src/fs/ramfs/fs.rs @@ -16,7 +16,6 @@ use ostd::{ use super::{memfd::MemfdInode, xattr::RamXattr, *}; use crate::{ - events::IoEvents, fs::{ device::Device, inode_handle::FileIo, @@ -25,13 +24,13 @@ use crate::{ registry::{FsProperties, FsType}, utils::{ mkmod, AccessMode, CStr256, CachePage, DirentVisitor, Extension, FallocMode, - FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata, MknodType, + FileSystem, FsFlags, Inode, InodeIo, InodeMode, InodeType, Metadata, MknodType, PageCache, PageCacheBackend, Permission, StatusFlags, SuperBlock, SymbolicLink, XattrName, XattrNamespace, XattrSetFlags, }, }, prelude::*, - process::{signal::PollHandle, Gid, Uid}, + process::{Gid, Uid}, time::clocks::RealTimeCoarseClock, vm::vmo::Vmo, }; @@ -539,14 +538,13 @@ impl PageCacheBackend for RamInode { } } -impl Inode for RamInode { - fn page_cache(&self) -> Option> { - self.inner - .as_file() - .map(|page_cache| page_cache.pages().clone()) - } - - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { +impl InodeIo for RamInode { + fn read_at( + &self, + offset: usize, + writer: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { let read_len = match &self.inner { Inner::File(page_cache) => { let (offset, read_len) = { @@ -567,11 +565,12 @@ impl Inode for RamInode { Ok(read_len) } - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - self.read_at(offset, writer) - } - - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { + fn write_at( + &self, + offset: usize, + reader: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { let written_len = match self.typ { InodeType::File => { let page_cache = self.inner.as_file().unwrap(); @@ -600,9 +599,13 @@ impl Inode for RamInode { }; Ok(written_len) } +} - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { - self.write_at(offset, reader) +impl Inode for RamInode { + fn page_cache(&self) -> Option> { + self.inner + .as_file() + .map(|page_cache| page_cache.pages().clone()) } fn size(&self) -> usize { @@ -1139,11 +1142,6 @@ impl Inode for RamInode { } } - fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents { - let events = IoEvents::IN | IoEvents::OUT; - events & mask - } - fn fs(&self) -> Arc { Weak::upgrade(&self.fs).unwrap() } @@ -1180,17 +1178,6 @@ impl Inode for RamInode { } } - fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { - return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); - } - - fn is_seekable(&self) -> bool { - !matches!( - self.typ, - InodeType::NamedPipe | InodeType::CharDevice | InodeType::Dir | InodeType::Socket - ) - } - fn extension(&self) -> Option<&Extension> { Some(&self.extension) } diff --git a/kernel/src/fs/ramfs/memfd.rs b/kernel/src/fs/ramfs/memfd.rs index 4992921f4..406e65438 100644 --- a/kernel/src/fs/ramfs/memfd.rs +++ b/kernel/src/fs/ramfs/memfd.rs @@ -24,8 +24,8 @@ use crate::{ tmpfs::TmpFs, utils::{ chmod, mkmod, AccessMode, CachePage, CreationFlags, Extension, FallocMode, FileSystem, - Inode, InodeMode, InodeType, IoctlCmd, Metadata, OpenArgs, PageCacheBackend, SeekFrom, - StatusFlags, XattrName, XattrNamespace, XattrSetFlags, + Inode, InodeIo, InodeMode, InodeType, IoctlCmd, Metadata, OpenArgs, PageCacheBackend, + SeekFrom, StatusFlags, XattrName, XattrNamespace, XattrSetFlags, }, }, prelude::*, @@ -107,40 +107,20 @@ impl PageCacheBackend for MemfdInode { } #[inherit_methods(from = "self.inode")] -impl Inode for MemfdInode { - fn metadata(&self) -> Metadata; - fn size(&self) -> usize; - fn atime(&self) -> Duration; - fn set_atime(&self, time: Duration); - fn mtime(&self) -> Duration; - fn set_mtime(&self, time: Duration); - fn ctime(&self) -> Duration; - fn set_ctime(&self, time: Duration); - fn ino(&self) -> u64; - fn type_(&self) -> InodeType; - fn mode(&self) -> Result; - fn owner(&self) -> Result; - fn set_owner(&self, uid: Uid) -> Result<()>; - fn group(&self) -> Result; - fn set_group(&self, gid: Gid) -> Result<()>; - fn page_cache(&self) -> Option>; - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result; - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result; - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result; - fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents; - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result; - fn extension(&self) -> Option<&Extension>; - fn set_xattr( +impl InodeIo for MemfdInode { + fn read_at( &self, - name: XattrName, - value_reader: &mut VmReader, - flags: XattrSetFlags, - ) -> Result<()>; - fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result; - fn list_xattr(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result; - fn remove_xattr(&self, name: XattrName) -> Result<()>; + offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result; - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { + fn write_at( + &self, + offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result { if !reader.has_remain() { return Ok(0); } @@ -173,8 +153,38 @@ impl Inode for MemfdInode { } } - self.inode.write_at(offset, reader) + self.inode.write_at(offset, reader, status_flags) } +} + +#[inherit_methods(from = "self.inode")] +impl Inode for MemfdInode { + fn metadata(&self) -> Metadata; + fn size(&self) -> usize; + fn atime(&self) -> Duration; + fn set_atime(&self, time: Duration); + fn mtime(&self) -> Duration; + fn set_mtime(&self, time: Duration); + fn ctime(&self) -> Duration; + fn set_ctime(&self, time: Duration); + fn ino(&self) -> u64; + fn type_(&self) -> InodeType; + fn mode(&self) -> Result; + fn owner(&self) -> Result; + fn set_owner(&self, uid: Uid) -> Result<()>; + fn group(&self) -> Result; + fn set_group(&self, gid: Gid) -> Result<()>; + fn page_cache(&self) -> Option>; + fn extension(&self) -> Option<&Extension>; + fn set_xattr( + &self, + name: XattrName, + value_reader: &mut VmReader, + flags: XattrSetFlags, + ) -> Result<()>; + fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result; + fn list_xattr(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result; + fn remove_xattr(&self, name: XattrName) -> Result<()>; fn resize(&self, new_size: usize) -> Result<()> { let seals = self.seals.lock(); @@ -337,8 +347,9 @@ impl MemfdFile { } impl Pollable for MemfdFile { - fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents { - self.memfd_inode.poll(mask, poller) + fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents { + let events = IoEvents::IN | IoEvents::OUT; + events & mask } } @@ -357,13 +368,16 @@ impl FileLike for MemfdFile { return_errno_with_message!(Errno::EBADF, "the file is not opened readable"); } - self.memfd_inode.read_at(offset, writer) + self.memfd_inode + .read_at(offset, writer, self.status_flags()) } fn write(&self, reader: &mut VmReader) -> Result { let mut offset = self.offset.lock(); if self.status_flags().contains(StatusFlags::O_APPEND) { + // FIXME: `O_APPEND` should ensure that new content is appended even if another process + // is writing to the file concurrently. *offset = self.memfd_inode.size(); } @@ -378,12 +392,14 @@ impl FileLike for MemfdFile { return_errno_with_message!(Errno::EBADF, "the file is not opened writable"); } - if self.status_flags().contains(StatusFlags::O_APPEND) { - // If the file has the O_APPEND flag, the offset is ignored + let status_flags = self.status_flags(); + if status_flags.contains(StatusFlags::O_APPEND) { + // If the file has the `O_APPEND` flag, the offset is ignored. + // FIXME: `O_APPEND` should ensure that new content is appended even if another process + // is writing to the file concurrently. offset = self.memfd_inode.size(); } - - self.memfd_inode.write_at(offset, reader) + self.memfd_inode.write_at(offset, reader, status_flags) } fn resize(&self, new_size: usize) -> Result<()> { @@ -442,12 +458,12 @@ impl FileLike for MemfdFile { Ok(Mappable::Inode(self.memfd_inode.clone())) } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { + fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { if self.rights.is_empty() { return_errno_with_message!(Errno::EBADF, "the file is opened as a path"); } - self.memfd_inode.ioctl(cmd, arg) + return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); } fn inode(&self) -> &Arc { diff --git a/kernel/src/fs/sysfs/test.rs b/kernel/src/fs/sysfs/test.rs index e6ec8b820..3664c840e 100644 --- a/kernel/src/fs/sysfs/test.rs +++ b/kernel/src/fs/sysfs/test.rs @@ -26,7 +26,7 @@ use ostd::{ use crate::{ fs::{ sysfs::{self, fs::SysFs}, - utils::{mkmod, DirentVisitor, FileSystem, InodeType}, + utils::{mkmod, DirentVisitor, FileSystem, InodeType, StatusFlags}, }, prelude::*, time::clocks::init_for_ktest as time_init_for_ktest, @@ -367,7 +367,7 @@ fn test_sysfs_read_attr() { let mut buf = [0u8; 64]; let mut writer = VmWriter::from(&mut buf[..]).to_fallible(); let bytes_read = r_attr_inode - .read_at(0, &mut writer) + .read_at(0, &mut writer, StatusFlags::empty()) .expect("read_at failed"); assert!(bytes_read > 0); @@ -376,7 +376,7 @@ fn test_sysfs_read_attr() { // Reading a directory should fail (expect EINVAL as per inode.rs) let mut writer = VmWriter::from(&mut buf[..]).to_fallible(); // Reset writer - let result = leaf1_dir_inode.read_at(0, &mut writer); + let result = leaf1_dir_inode.read_at(0, &mut writer, StatusFlags::empty()); assert!(result.is_err()); } @@ -401,7 +401,7 @@ fn test_sysfs_write_attr() { let new_val = "new_value"; let mut reader = VmReader::from(new_val.as_bytes()).to_fallible(); let bytes_written = rw_attr_inode - .write_at(0, &mut reader) + .write_at(0, &mut reader, StatusFlags::empty()) .expect("write_at failed"); assert_eq!(bytes_written, new_val.len()); @@ -409,19 +409,19 @@ fn test_sysfs_write_attr() { let mut buf = [0u8; 64]; let mut writer = VmWriter::from(&mut buf[..]).to_fallible(); let bytes_read = rw_attr_inode - .read_at(0, &mut writer) + .read_at(0, &mut writer, StatusFlags::empty()) .expect("read_at failed"); let content = core::str::from_utf8(&buf[..bytes_read]).unwrap(); assert_eq!(content, new_val); // Write to r_attr1 (should fail - EIO expected from underlying PermissionDenied) let mut reader = VmReader::from("attempt_write".as_bytes()).to_fallible(); - let result = r_attr_inode.write_at(0, &mut reader); + let result = r_attr_inode.write_at(0, &mut reader, StatusFlags::empty()); assert!(result.is_err()); // Writing to a directory should fail (expect EINVAL as per inode.rs) let mut reader = VmReader::from("attempt_write".as_bytes()).to_fallible(); - let result = leaf1_dir_inode.write_at(0, &mut reader); + let result = leaf1_dir_inode.write_at(0, &mut reader, StatusFlags::empty()); assert!(result.is_err()); } diff --git a/kernel/src/fs/utils/inode.rs b/kernel/src/fs/utils/inode.rs index 3ade0e394..089e1c29d 100644 --- a/kernel/src/fs/utils/inode.rs +++ b/kernel/src/fs/utils/inode.rs @@ -8,11 +8,10 @@ use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, use ostd::task::Task; use super::{ - AccessMode, DirentVisitor, FallocMode, FileSystem, InodeMode, IoctlCmd, XattrName, - XattrNamespace, XattrSetFlags, + AccessMode, DirentVisitor, FallocMode, FileSystem, InodeMode, XattrName, XattrNamespace, + XattrSetFlags, }; use crate::{ - events::IoEvents, fs::{ device::{Device, DeviceType}, inode_handle::FileIo, @@ -21,10 +20,7 @@ use crate::{ utils::StatusFlags, }, prelude::*, - process::{ - credentials::capabilities::CapSet, posix_thread::AsPosixThread, signal::PollHandle, Gid, - Uid, - }, + process::{credentials::capabilities::CapSet, posix_thread::AsPosixThread, Gid, Uid}, time::clocks::RealTimeCoarseClock, vm::vmo::Vmo, }; @@ -56,6 +52,10 @@ impl InodeType { *self == InodeType::BlockDevice || *self == InodeType::CharDevice } + pub fn is_seekable(&self) -> bool { + *self != InodeType::NamedPipe && *self != Self::Socket + } + /// Parse the inode type in the `mode` from syscall, and convert it into `InodeType`. pub fn from_raw_mode(mut mode: u16) -> Result { const TYPE_MASK: u16 = 0o170000; @@ -243,7 +243,29 @@ impl From> for MknodType { } } -pub trait Inode: Any + Sync + Send { +/// I/O operations in an [`Inode`]. +/// +/// This abstracts the common I/O operations used by both [`Inode`] (for regular files) and +/// [`FileIo`] (for special files). +pub trait InodeIo { + /// Reads data from the file into the given `VmWriter`. + fn read_at( + &self, + offset: usize, + writer: &mut VmWriter, + status_flags: StatusFlags, + ) -> Result; + + /// Writes data from the given `VmReader` into the file. + fn write_at( + &self, + offset: usize, + reader: &mut VmReader, + status_flags: StatusFlags, + ) -> Result; +} + +pub trait Inode: Any + InodeIo + Send + Sync { fn size(&self) -> usize; fn resize(&self, new_size: usize) -> Result<()>; @@ -282,22 +304,6 @@ pub trait Inode: Any + Sync + Send { None } - fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - Err(Error::new(Errno::EISDIR)) - } - - fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result { - Err(Error::new(Errno::EISDIR)) - } - - fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result { - Err(Error::new(Errno::EISDIR)) - } - - fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result { - Err(Error::new(Errno::EISDIR)) - } - fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result> { Err(Error::new(Errno::ENOTDIR)) } @@ -346,10 +352,6 @@ pub trait Inode: Any + Sync + Send { Err(Error::new(Errno::EISDIR)) } - fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - Err(Error::new(Errno::EISDIR)) - } - fn sync_all(&self) -> Result<()> { Ok(()) } @@ -364,11 +366,6 @@ pub trait Inode: Any + Sync + Send { return_errno!(Errno::EOPNOTSUPP); } - fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents { - let events = IoEvents::IN | IoEvents::OUT; - events & mask - } - fn fs(&self) -> Arc; /// Returns whether a VFS dentry for this inode should be put into the dentry cache. @@ -392,10 +389,6 @@ pub trait Inode: Any + Sync + Send { true } - fn is_seekable(&self) -> bool { - true - } - /// Get the extension of this inode fn extension(&self) -> Option<&Extension> { None @@ -503,22 +496,22 @@ impl dyn Inode { pub fn read_bytes_at(&self, offset: usize, buf: &mut [u8]) -> Result { let mut writer = VmWriter::from(buf).to_fallible(); - self.read_at(offset, &mut writer) + self.read_at(offset, &mut writer, StatusFlags::empty()) } pub fn write_bytes_at(&self, offset: usize, buf: &[u8]) -> Result { let mut reader = VmReader::from(buf).to_fallible(); - self.write_at(offset, &mut reader) + self.write_at(offset, &mut reader, StatusFlags::empty()) } pub fn read_bytes_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result { let mut writer = VmWriter::from(buf).to_fallible(); - self.read_direct_at(offset, &mut writer) + self.read_at(offset, &mut writer, StatusFlags::O_DIRECT) } pub fn write_bytes_direct_at(&self, offset: usize, buf: &[u8]) -> Result { let mut reader = VmReader::from(buf).to_fallible(); - self.write_direct_at(offset, &mut reader) + self.write_at(offset, &mut reader, StatusFlags::O_DIRECT) } } @@ -533,7 +526,7 @@ impl Write for InodeWriter<'_> { let mut reader = VmReader::from(buf).to_fallible(); let write_len = self .inner - .write_at(self.offset, &mut reader) + .write_at(self.offset, &mut reader, StatusFlags::empty()) .map_err(|_| IoError::new(IoErrorKind::WriteZero, "failed to write buffer"))?; self.offset += write_len; Ok(write_len) diff --git a/kernel/src/fs/utils/mod.rs b/kernel/src/fs/utils/mod.rs index 7918e1abf..59b37ef4a 100644 --- a/kernel/src/fs/utils/mod.rs +++ b/kernel/src/fs/utils/mod.rs @@ -11,7 +11,9 @@ pub use falloc_mode::FallocMode; pub use file_creation_mask::{AtomicFileCreationMask, FileCreationMask}; pub use flock::{FlockItem, FlockList, FlockType}; pub use fs::{FileSystem, FsFlags, SuperBlock}; -pub use inode::{Extension, Inode, InodeType, Metadata, MknodType, Permission, SymbolicLink}; +pub use inode::{ + Extension, Inode, InodeIo, InodeType, Metadata, MknodType, Permission, SymbolicLink, +}; pub use inode_mode::InodeMode; pub(crate) use inode_mode::{chmod, mkmod, perms_to_mask, who_and_perms_to_mask, who_to_mask}; pub use ioctl::IoctlCmd; diff --git a/kernel/src/fs/utils/systree_inode.rs b/kernel/src/fs/utils/systree_inode.rs index 7a4677852..ef293e7d3 100644 --- a/kernel/src/fs/utils/systree_inode.rs +++ b/kernel/src/fs/utils/systree_inode.rs @@ -13,18 +13,17 @@ use aster_systree::{ SysAttr, SysBranchNode, SysNode, SysNodeId, SysNodeType, SysObj, SysStr, SysSymlink, }; +use super::InodeIo; use crate::{ - events::IoEvents, fs::{ inode_handle::FileIo, utils::{ mkmod, AccessMode, DirentVisitor, FallocMode, FileSystem, Inode, InodeMode, InodeType, - IoctlCmd, Metadata, MknodType, StatusFlags, SymbolicLink, + Metadata, MknodType, StatusFlags, SymbolicLink, }, }, prelude::*, - process::{signal::PollHandle, Gid, Uid}, - return_errno, return_errno_with_message, + process::{Gid, Uid}, time::{clocks::RealTimeCoarseClock, Clock}, }; @@ -279,6 +278,42 @@ pub(in crate::fs) enum SysTreeNodeKind { Symlink(Arc), } +impl InodeIo for KInode { + default fn read_at( + &self, + offset: usize, + buf: &mut VmWriter, + _status_flags: StatusFlags, + ) -> Result { + let SysTreeNodeKind::Attr(attr, leaf) = &self.node_kind() else { + return Err(Error::new(Errno::EINVAL)); + }; + + let len = leaf.read_attr_at(attr.name(), offset, buf)?; + + Ok(len) + } + + default fn write_at( + &self, + offset: usize, + buf: &mut VmReader, + _status_flags: StatusFlags, + ) -> Result { + let SysTreeNodeKind::Attr(attr, leaf) = &self.node_kind() else { + return Err(Error::new(Errno::EINVAL)); + }; + + let len = if offset == 0 { + leaf.write_attr(attr.name(), buf)? + } else { + leaf.write_attr_at(attr.name(), offset, buf)? + }; + + Ok(len) + } +} + impl Inode for KInode { default fn type_(&self) -> InodeType { self.metadata().type_ @@ -354,38 +389,6 @@ impl Inode for KInode { None } - default fn read_at(&self, offset: usize, buf: &mut VmWriter) -> Result { - self.read_direct_at(offset, buf) - } - - default fn read_direct_at(&self, offset: usize, buf: &mut VmWriter) -> Result { - let SysTreeNodeKind::Attr(attr, leaf) = &self.node_kind() else { - return Err(Error::new(Errno::EINVAL)); - }; - - let len = leaf.read_attr_at(attr.name(), offset, buf)?; - - Ok(len) - } - - default fn write_at(&self, offset: usize, buf: &mut VmReader) -> Result { - self.write_direct_at(offset, buf) - } - - default fn write_direct_at(&self, offset: usize, buf: &mut VmReader) -> Result { - let SysTreeNodeKind::Attr(attr, leaf) = &self.node_kind() else { - return Err(Error::new(Errno::EINVAL)); - }; - - let len = if offset == 0 { - leaf.write_attr(attr.name(), buf)? - } else { - leaf.write_attr_at(attr.name(), offset, buf)? - }; - - Ok(len) - } - default fn create( &self, name: &str, @@ -561,10 +564,6 @@ impl Inode for KInode { None } - default fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { - Err(Error::new(Errno::ENOTTY)) - } - default fn sync_all(&self) -> Result<()> { Ok(()) } @@ -577,19 +576,6 @@ impl Inode for KInode { Err(Error::new(Errno::EOPNOTSUPP)) } - default fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents { - let mut events = IoEvents::empty(); - if let SysTreeNodeKind::Attr(attr, _) = &self.node_kind() { - if attr.perms().can_read() { - events |= IoEvents::IN; - } - if attr.perms().can_write() { - events |= IoEvents::OUT; - } - } - events & mask - } - default fn is_dentry_cacheable(&self) -> bool { true }