Add `InodeIo` to simplify `FileIo` and `Inode`

This commit is contained in:
Ruihan Li 2025-11-05 23:13:34 +08:00 committed by Jianfeng Jiang
parent 08dfe533c4
commit 24502ac3d4
27 changed files with 942 additions and 654 deletions

View File

@ -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<usize> {
impl InodeIo for Full {
fn read_at(
&self,
_offset: usize,
writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
let len = writer.avail();
writer.fill_zeros(len)?;
Ok(len)
}
fn write(&self, _reader: &mut VmReader, _status_flags: StatusFlags) -> Result<usize> {
fn write_at(
&self,
_offset: usize,
_reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
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
}
}

View File

@ -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<usize> {
impl InodeIo for Null {
fn read_at(
&self,
_offset: usize,
_writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
Ok(0)
}
fn write(&self, reader: &mut VmReader, _status_flags: StatusFlags) -> Result<usize> {
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
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
}
}

View File

@ -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<usize>;
fn write(&self, reader: &mut VmReader, status_flags: StatusFlags) -> Result<usize>;
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
}
#[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<usize> {
self.0.read(writer, status_flags)
}
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize> {
self.0.write(reader, status_flags)
}
}
#[inherit_methods(from = "self.0")]
impl FileIo for PtySlaveFile {
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
fn check_seekable(&self) -> Result<()> {
return_errno_with_message!(Errno::ESPIPE, "the inode is a TTY");
}
fn is_offset_aware(&self) -> bool {
false
}
}

View File

@ -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<usize> {
impl InodeIo for PtyMaster {
fn read_at(
&self,
_offset: usize,
writer: &mut VmWriter,
status_flags: StatusFlags,
) -> Result<usize> {
// 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<usize> {
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize> {
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<i32> {
match cmd {

View File

@ -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<usize> {
impl InodeIo for Random {
fn read_at(
&self,
_offset: usize,
writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
Self::getrandom(writer)
}
fn write(&self, reader: &mut VmReader, _status_flags: StatusFlags) -> Result<usize> {
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
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
}
}

View File

@ -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<usize> {
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<usize> {
return_errno_with_message!(Errno::EINVAL, "the file is not valid for reading")
}
fn write(&self, _reader: &mut VmReader, _status_flags: StatusFlags) -> Result<usize> {
return_errno_with_message!(Errno::EPERM, "Write operation not supported")
fn write_at(
&self,
_offset: usize,
_reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
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<i32> {
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"),
}
}
}

View File

@ -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<usize> {
self.0.read(writer, status_flags)
}
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize> {
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<usize>;
fn write(&self, reader: &mut VmReader, status_flags: StatusFlags) -> Result<usize>;
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
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<Box<[Arc<Tty<ConsoleDriver>>]>> = Once::new();

View File

@ -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<usize> {
impl InodeIo for Urandom {
fn read_at(
&self,
_offset: usize,
writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
Self::getrandom(writer)
}
fn write(&self, reader: &mut VmReader, _status_flags: StatusFlags) -> Result<usize> {
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
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
}
}

View File

@ -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<usize> {
impl InodeIo for Zero {
fn read_at(
&self,
_offset: usize,
writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
let read_len = writer.fill_zeros(writer.avail())?;
Ok(read_len)
}
fn write(&self, reader: &mut VmReader, _status_flags: StatusFlags) -> Result<usize> {
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
Ok(reader.remain())
}
}
impl FileIo for Zero {
fn check_seekable(&self) -> Result<()> {
Ok(())
}
fn is_offset_aware(&self) -> bool {
false
}
}

View File

@ -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<usize> {
Err(Error::new(Errno::EISDIR))
}
fn write_at(
&self,
_offset: usize,
_reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
}
impl Inode for RootInode {
fn size(&self) -> usize {
self.metadata.read().size

View File

@ -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<usize> {
Ok(0)
}
fn write_at(
&self,
_offset: usize,
_reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
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<usize> {
Ok(0)
}
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
Ok(0)
}
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
Ok(0)
}
fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
Ok(0)
}
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
Ok(0)
}
fn fs(&self) -> Arc<dyn FileSystem> {
// FIXME: The below code may panic if the devpts is dropped.
self.devpts().unwrap()

View File

@ -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<usize> {
self.device.read(writer, status_flags)
}
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize> {
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<usize> {
self.device.read(writer, StatusFlags::empty())
}
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
self.device.read(writer, StatusFlags::empty())
}
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
self.device.write(reader, StatusFlags::empty())
}
fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
self.device.write(reader, StatusFlags::empty())
}
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
self.device.ioctl(cmd, arg)
}
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
self.device.poll(mask, poller)
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.fs.upgrade().unwrap()
}

View File

@ -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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<Arc<dyn Inode>> {
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<i32> {
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
}

View File

@ -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<usize> {
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<usize> {
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<usize> {
self.read_at(offset, writer)
}
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
self.read_direct_at(offset, writer)
}
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
self.write_at(offset, reader)
}
fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
self.write_direct_at(offset, reader)
}
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
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<i32> {
Err(Error::new(Errno::EINVAL))
}
fn sync_all(&self) -> Result<()> {
self.sync_all()?;
self.fs().block_device().sync()?;

View File

@ -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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
@ -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<Mappable> {
@ -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<usize>;
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<usize>;
/// 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<Mappable> {

View File

@ -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<usize> {
pub fn write_at(
&self,
offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize> {
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<usize> {
pub fn read_at(
&self,
offset: usize,
writer: &mut VmWriter,
status_flags: StatusFlags,
) -> Result<usize> {
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<usize> {
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<usize> {
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<i32> {
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<dyn Inode>) -> Result<bool> {
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<usize>;
fn write_at(
&self,
offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize>;
}
#[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<Arc<Vmo>>;
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize>;
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize>;
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize>;
fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize>;
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>>;
fn mknod(&self, name: &str, mode: InodeMode, type_: MknodType) -> Result<Arc<dyn Inode>>;
fn open(
@ -945,7 +946,6 @@ impl Inode for OverlayInode {
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()>;
fn read_link(&self) -> Result<SymbolicLink>;
fn write_link(&self, target: &str) -> Result<()>;
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
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();

View File

@ -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<usize> {
impl InodeIo for NamedPipeHandle {
fn read_at(
&self,
_offset: usize,
writer: &mut VmWriter,
status_flags: StatusFlags,
) -> Result<usize> {
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<usize> {
fn write_at(
&self,
_offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize> {
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

View File

@ -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<D: DirOps> ProcDir<D> {
}
}
impl<D: DirOps + 'static> InodeIo for ProcDir<D> {
fn read_at(
&self,
_offset: usize,
_writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn write_at(
&self,
_offset: usize,
_reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
}
#[inherit_methods(from = "self.common")]
impl<D: DirOps + 'static> Inode for ProcDir<D> {
fn size(&self) -> usize;

View File

@ -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<F: FileOps> ProcFile<F> {
}
}
impl<F: FileOps + 'static> InodeIo for ProcFile<F> {
fn read_at(
&self,
offset: usize,
writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
self.inner.read_at(offset, writer)
}
fn write_at(
&self,
offset: usize,
reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
self.inner.write_at(offset, reader)
}
}
#[inherit_methods(from = "self.common")]
impl<F: FileOps + 'static> Inode for ProcFile<F> {
fn size(&self) -> usize;
@ -63,22 +85,6 @@ impl<F: FileOps + 'static> Inode for ProcFile<F> {
InodeType::File
}
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
self.inner.read_at(offset, writer)
}
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
self.read_at(offset, writer)
}
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
self.inner.write_at(offset, reader)
}
fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
self.write_at(offset, reader)
}
fn read_link(&self) -> Result<SymbolicLink> {
Err(Error::new(Errno::EINVAL))
}
@ -87,10 +93,6 @@ impl<F: FileOps + 'static> Inode for ProcFile<F> {
Err(Error::new(Errno::EINVAL))
}
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
Err(Error::new(Errno::EPERM))
}
fn is_dentry_cacheable(&self) -> bool {
!self.common.is_volatile()
}

View File

@ -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<S: SymOps> ProcSym<S> {
}
}
impl<S: SymOps + 'static> InodeIo for ProcSym<S> {
fn read_at(
&self,
_offset: usize,
_writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_at(
&self,
_offset: usize,
_reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
}
#[inherit_methods(from = "self.common")]
impl<S: SymOps + 'static> Inode for ProcSym<S> {
fn size(&self) -> usize;
@ -64,22 +86,6 @@ impl<S: SymOps + 'static> Inode for ProcSym<S> {
InodeType::SymLink
}
fn read_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn read_direct_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_at(&self, _offset: usize, _reader: &mut VmReader) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_direct_at(&self, _offset: usize, _reader: &mut VmReader) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn read_link(&self) -> Result<SymbolicLink> {
self.inner.read_link()
}
@ -88,10 +94,6 @@ impl<S: SymOps + 'static> Inode for ProcSym<S> {
Err(Error::new(Errno::EPERM))
}
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
Err(Error::new(Errno::EPERM))
}
fn is_dentry_cacheable(&self) -> bool {
!self.common.is_volatile()
}

View File

@ -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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
return_errno_with_message!(
Errno::ESPIPE,
"pseudo inodes cannot be written at a specific offset"
);
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.fs.upgrade().unwrap()
}

View File

@ -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<Arc<Vmo>> {
self.inner
.as_file()
.map(|page_cache| page_cache.pages().clone())
}
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
impl InodeIo for RamInode {
fn read_at(
&self,
offset: usize,
writer: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
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<usize> {
self.read_at(offset, writer)
}
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
fn write_at(
&self,
offset: usize,
reader: &mut VmReader,
_status_flags: StatusFlags,
) -> Result<usize> {
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<usize> {
self.write_at(offset, reader)
impl Inode for RamInode {
fn page_cache(&self) -> Option<Arc<Vmo>> {
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<dyn FileSystem> {
Weak::upgrade(&self.fs).unwrap()
}
@ -1180,17 +1178,6 @@ impl Inode for RamInode {
}
}
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
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)
}

View File

@ -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<InodeMode>;
fn owner(&self) -> Result<Uid>;
fn set_owner(&self, uid: Uid) -> Result<()>;
fn group(&self) -> Result<Gid>;
fn set_group(&self, gid: Gid) -> Result<()>;
fn page_cache(&self) -> Option<Arc<Vmo>>;
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize>;
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize>;
fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize>;
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents;
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32>;
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<usize>;
fn list_xattr(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result<usize>;
fn remove_xattr(&self, name: XattrName) -> Result<()>;
offset: usize,
writer: &mut VmWriter,
status_flags: StatusFlags,
) -> Result<usize>;
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
fn write_at(
&self,
offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize> {
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<InodeMode>;
fn owner(&self) -> Result<Uid>;
fn set_owner(&self, uid: Uid) -> Result<()>;
fn group(&self) -> Result<Gid>;
fn set_group(&self, gid: Gid) -> Result<()>;
fn page_cache(&self) -> Option<Arc<Vmo>>;
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<usize>;
fn list_xattr(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result<usize>;
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<usize> {
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<i32> {
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
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<dyn Inode> {

View File

@ -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());
}

View File

@ -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<Self> {
const TYPE_MASK: u16 = 0o170000;
@ -243,7 +243,29 @@ impl From<Arc<dyn Device>> 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<usize>;
/// Writes data from the given `VmReader` into the file.
fn write_at(
&self,
offset: usize,
reader: &mut VmReader,
status_flags: StatusFlags,
) -> Result<usize>;
}
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<usize> {
Err(Error::new(Errno::EISDIR))
}
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn write_direct_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
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<i32> {
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<dyn FileSystem>;
/// 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<usize> {
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<usize> {
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<usize> {
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<usize> {
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)

View File

@ -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;

View File

@ -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<dyn SysSymlink>),
}
impl<KInode: SysTreeInodeTy + Send + Sync + 'static> InodeIo for KInode {
default fn read_at(
&self,
offset: usize,
buf: &mut VmWriter,
_status_flags: StatusFlags,
) -> Result<usize> {
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<usize> {
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<KInode: SysTreeInodeTy + Send + Sync + 'static> Inode for KInode {
default fn type_(&self) -> InodeType {
self.metadata().type_
@ -354,38 +389,6 @@ impl<KInode: SysTreeInodeTy + Send + Sync + 'static> Inode for KInode {
None
}
default fn read_at(&self, offset: usize, buf: &mut VmWriter) -> Result<usize> {
self.read_direct_at(offset, buf)
}
default fn read_direct_at(&self, offset: usize, buf: &mut VmWriter) -> Result<usize> {
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<usize> {
self.write_direct_at(offset, buf)
}
default fn write_direct_at(&self, offset: usize, buf: &mut VmReader) -> Result<usize> {
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<KInode: SysTreeInodeTy + Send + Sync + 'static> Inode for KInode {
None
}
default fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
Err(Error::new(Errno::ENOTTY))
}
default fn sync_all(&self) -> Result<()> {
Ok(())
}
@ -577,19 +576,6 @@ impl<KInode: SysTreeInodeTy + Send + Sync + 'static> 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
}