// SPDX-License-Identifier: MPL-2.0 use alloc::{ borrow::Cow, string::{String, ToString}, sync::{Arc, Weak}, vec::Vec, }; use core::time::Duration; use aster_systree::{ SysAttr, SysAttrFlags, SysBranchNode, SysNode, SysNodeId, SysNodeType, SysObj, SysStr, SysSymlink, SysTree, }; use ostd::sync::RwLock; use crate::{ events::IoEvents, fs::{ device::Device, utils::{ DirentVisitor, FallocMode, FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata, MknodType, }, }, prelude::{VmReader, VmWriter}, process::{signal::PollHandle, Gid, Uid}, return_errno_with_message, time::{clocks::RealTimeCoarseClock, Clock}, Errno, Error, Result, }; type Ino = u64; pub struct SysFsInode { /// The global SysTree reference, representing the kernel's exported system information tree. systree: &'static Arc, /// The corresponding node in the SysTree. inner_node: InnerNode, /// The metadata of this inode. /// /// Most of the metadata (e.g., file size, timestamps) /// can be determined upon the creation of an inode, /// and are thus kept intact inside the immutable `metadata` field. /// Currently, the only mutable metadata is `mode`, /// which allows user space to `chmod` an inode on sysfs. metadata: Metadata, /// The file mode (permissions) of this inode, protected by a lock. mode: RwLock, /// Weak reference to the parent inode. parent: Weak, /// Weak self-reference for cyclic data structures. this: Weak, } #[derive(Debug)] enum InnerNode { Branch(Arc), Leaf(Arc), Attr(SysAttr, Arc), Symlink(Arc), } impl SysFsInode { pub(crate) fn new_root(systree: &'static Arc) -> Arc { let root_node = systree.root().clone(); // systree.root() returns Arc let inner_node = InnerNode::Branch(root_node); let parent = Weak::new(); Self::new_branch_dir(systree, inner_node, parent) } fn new_attr( systree: &'static Arc, attr: SysAttr, node: Arc, parent: Weak, ) -> Arc { let inner_node = InnerNode::Attr(attr.clone(), node); let ino = ino::from_inner_node(&inner_node); let metadata = Self::new_metadata(ino, InodeType::File); let mode = RwLock::new(Self::flags_to_inode_mode(attr.flags())); Arc::new_cyclic(|this| Self { systree, inner_node, metadata, mode, parent, this: this.clone(), }) } fn new_symlink( systree: &'static Arc, symlink: Arc, parent: Weak, ) -> Arc { let inner_node = InnerNode::Symlink(symlink); let ino = ino::from_inner_node(&inner_node); let metadata = Self::new_metadata(ino, InodeType::SymLink); let mode = RwLock::new(InodeMode::from_bits_truncate(0o777)); Arc::new_cyclic(|this| Self { systree, inner_node, metadata, mode, parent, this: this.clone(), }) } fn new_metadata(ino: u64, type_: InodeType) -> Metadata { let now = RealTimeCoarseClock::get().read_time(); Metadata { dev: 0, ino, size: 0, blk_size: 1024, blocks: 0, atime: now, mtime: now, ctime: now, type_, mode: InodeMode::from_bits_truncate(0o555), nlinks: 1, uid: Uid::new_root(), gid: Gid::new_root(), rdev: 0, } } fn flags_to_inode_mode(flags: SysAttrFlags) -> InodeMode { let mut bits = 0o000; // Start with no permissions if flags.contains(SysAttrFlags::CAN_READ) { bits |= 0o444; // Add read permissions if flag is set } if flags.contains(SysAttrFlags::CAN_WRITE) { bits |= 0o222; // Add write permissions if flag is set } // Note: Execute permissions (0o111) are typically granted for directories, handled elsewhere. InodeMode::from_bits_truncate(bits) } fn new_branch_dir( systree: &'static Arc, inner_node: InnerNode, // Must be InnerNode::Branch parent: Weak, ) -> Arc { let ino = ino::from_inner_node(&inner_node); let metadata = Self::new_metadata(ino, InodeType::Dir); let mode = RwLock::new(InodeMode::from_bits_truncate(0o555)); Arc::new_cyclic(|this| Self { systree, inner_node, metadata, mode, parent, this: this.clone(), }) } fn new_leaf_dir( systree: &'static Arc, inner_node: InnerNode, // Must be InnerNode::Leaf parent: Weak, ) -> Arc { let ino = ino::from_inner_node(&inner_node); let metadata = Self::new_metadata(ino, InodeType::Dir); // Leaf nodes are represented as Dirs let mode = RwLock::new(InodeMode::from_bits_truncate(0o555)); // Read/execute for all Arc::new_cyclic(|this| Self { systree, inner_node, metadata, mode, parent, this: this.clone(), }) } pub fn this(&self) -> Arc { self.this.upgrade().expect("Weak ref invalid") } fn lookup_node_or_attr( &self, name: &str, sysnode: &dyn SysBranchNode, ) -> Result> { // Try finding a child node (Branch, Leaf, Symlink) first if let Some(child_sysnode) = sysnode.child(name) { let child_type = child_sysnode.type_(); match child_type { SysNodeType::Branch => { let child_branch = child_sysnode .arc_as_branch() .ok_or(Error::new(Errno::EIO))?; let inode = Self::new_branch_dir( self.systree, InnerNode::Branch(child_branch), Arc::downgrade(&self.this()), ); Ok(inode) } SysNodeType::Leaf => { let child_leaf_node = child_sysnode.arc_as_node().ok_or(Error::new(Errno::EIO))?; let inode = Self::new_leaf_dir( self.systree, InnerNode::Leaf(child_leaf_node), Arc::downgrade(&self.this()), ); Ok(inode) } SysNodeType::Symlink => { let child_symlink = child_sysnode .arc_as_symlink() .ok_or(Error::new(Errno::EIO))?; let inode = Self::new_symlink( self.systree, child_symlink, Arc::downgrade(&self.this()), ); Ok(inode) } } } else { // If no child node found, try finding an attribute of the current branch node let Some(attr) = sysnode.node_attrs().get(name) else { return_errno_with_message!(Errno::ENOENT, "child node or attribute not found"); }; let parent_node_arc: Arc = match &self.inner_node { InnerNode::Branch(branch_arc) => branch_arc.clone(), // This case shouldn't happen if lookup_node_or_attr is called correctly _ => { return Err(Error::with_message( Errno::EIO, "lookup_node_or_attr called on non-branch inode", )) } }; let inode = Self::new_attr( self.systree, attr.clone(), parent_node_arc, Arc::downgrade(&self.this()), ); Ok(inode) } } fn lookup_attr(&self, name: &str, sysnode: &dyn SysNode) -> Result> { // This function is called when the current inode is a Leaf directory let Some(attr) = sysnode.node_attrs().get(name) else { return Err(Error::new(Errno::ENOENT)); }; let leaf_node_arc: Arc = match &self.inner_node { InnerNode::Leaf(leaf_arc) => leaf_arc.clone(), // This case shouldn't happen if lookup_attr is called correctly _ => { return Err(Error::with_message( Errno::EIO, "lookup_attr called on non-leaf inode", )) } }; let inode = Self::new_attr( self.systree, attr.clone(), leaf_node_arc, Arc::downgrade(&self.this()), ); Ok(inode) } fn new_dentry_iter(&self, min_ino: Ino) -> impl Iterator + '_ { match &self.inner_node { InnerNode::Branch(branch_node) => { let attrs = branch_node.node_attrs().iter().cloned().collect(); let attr_iter = AttrDentryIter::new(attrs, self.ino(), min_ino); let child_objs = branch_node.children(); let node_iter = NodeDentryIter::new(child_objs, min_ino); let special_iter = ThisAndParentDentryIter::new(self, min_ino); attr_iter.chain(node_iter).chain(special_iter) } InnerNode::Leaf(leaf_node) => { let attrs = leaf_node.node_attrs().iter().cloned().collect(); let attr_iter = AttrDentryIter::new(attrs, self.ino(), min_ino); let node_iter = NodeDentryIter::new(Vec::new(), min_ino); let special_iter = ThisAndParentDentryIter::new(self, min_ino); attr_iter.chain(node_iter).chain(special_iter) } InnerNode::Attr(_, _) | InnerNode::Symlink(_) => { let attr_iter = AttrDentryIter::new(Vec::new(), self.ino(), min_ino); let node_iter = NodeDentryIter::new(Vec::new(), min_ino); let special_iter = ThisAndParentDentryIter::new(self, min_ino); attr_iter.chain(node_iter).chain(special_iter) } } } } impl Inode for SysFsInode { fn type_(&self) -> InodeType { self.metadata.type_ } fn metadata(&self) -> Metadata { let mut metadata = self.metadata; metadata.mode = *self.mode.read(); metadata } fn ino(&self) -> u64 { self.metadata.ino } fn mode(&self) -> Result { Ok(*self.mode.read()) } fn set_mode(&self, mode: InodeMode) -> Result<()> { *self.mode.write() = mode; Ok(()) } fn size(&self) -> usize { self.metadata.size } fn resize(&self, _new_size: usize) -> Result<()> { Err(Error::new(Errno::EPERM)) } fn atime(&self) -> Duration { self.metadata.atime } fn set_atime(&self, _time: Duration) {} fn mtime(&self) -> Duration { self.metadata.mtime } fn set_mtime(&self, _time: Duration) {} fn ctime(&self) -> Duration { self.metadata.ctime } fn set_ctime(&self, _time: Duration) {} fn owner(&self) -> Result { Ok(self.metadata.uid) } fn set_owner(&self, _uid: Uid) -> Result<()> { Err(Error::new(Errno::EPERM)) } fn group(&self) -> Result { Ok(self.metadata.gid) } fn set_group(&self, _gid: Gid) -> Result<()> { Err(Error::new(Errno::EPERM)) } fn fs(&self) -> Arc { crate::fs::sysfs::singleton().clone() } fn page_cache(&self) -> Option> { None } fn read_at(&self, offset: usize, buf: &mut VmWriter) -> Result { self.read_direct_at(offset, buf) } fn read_direct_at(&self, offset: usize, buf: &mut VmWriter) -> Result { let InnerNode::Attr(attr, leaf) = &self.inner_node else { return Err(Error::new(Errno::EINVAL)); }; let len = if offset == 0 { leaf.read_attr(attr.name(), buf)? } else { // The `read_attr_at` method is more general than `read_attr`, // but it could be less efficient. So we only use the more general form when necessary. leaf.read_attr_at(attr.name(), offset, buf)? }; Ok(len) } fn write_at(&self, offset: usize, buf: &mut VmReader) -> Result { self.write_direct_at(offset, buf) } fn write_direct_at(&self, offset: usize, buf: &mut VmReader) -> Result { let InnerNode::Attr(attr, leaf) = &self.inner_node 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) } fn create(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result> { Err(Error::new(Errno::EPERM)) } fn mknod(&self, _name: &str, _mode: InodeMode, _dev: MknodType) -> Result> { Err(Error::new(Errno::EPERM)) } fn link(&self, _old: &Arc, _name: &str) -> Result<()> { Err(Error::new(Errno::EPERM)) } fn unlink(&self, _name: &str) -> Result<()> { Err(Error::new(Errno::EPERM)) } fn rename(&self, _old_name: &str, _target: &Arc, _new_name: &str) -> Result<()> { Err(Error::new(Errno::EPERM)) } fn lookup(&self, name: &str) -> Result> { if self.type_() != InodeType::Dir { return Err(Error::new(Errno::ENOTDIR)); } if name == "." { return Ok(self.this()); } else if name == ".." { return Ok(self.parent.upgrade().unwrap_or_else(|| self.this())); } // Dispatch based on the concrete type of the directory inode match &self.inner_node { InnerNode::Branch(branch_node) => { // Current inode is a directory corresponding to a SysBranchNode self.lookup_node_or_attr(name, branch_node.as_ref()) } InnerNode::Leaf(leaf_node) => { // Current inode is a directory corresponding to a SysNode (Leaf) // Leaf directories only contain attributes, not other nodes. self.lookup_attr(name, leaf_node.as_ref()) } // Attr and Symlink nodes are not directories InnerNode::Attr(_, _) | InnerNode::Symlink(_) => Err(Error::new(Errno::ENOTDIR)), } } fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result { // Why interpreting the `offset` argument as an inode number? // // It may take multiple `getdents` system calls // -- and thus multiple calls to this method -- // to list a large directory when the syscall is provided a small buffer. // Between these calls, // the directory may have new entries added or existing ones removed // by some concurrent users that are working on the directory. // In such situations, // missing some of the concurrently-added entries is inevitable, // but reporting the same entry multiple times would be // very confusing to the user. // // To address this issue, // the `readdir_at` method reports entries starting from a user-given `offset` // and returns an increment that the next call should be put on the `offset` argument // to avoid getting duplicated entries. // // Different file systems may interpret the meaning of // the `offset` argument differently: // one may take it as a _byte_ offset, // while the other may treat it as an _index_. // This freedom is guaranteed by Linux as documented in // [the man page of getdents](https://man7.org/linux/man-pages/man2/getdents.2.html). // // Our implementation of sysfs interprets the `offset` // as an _inode number_. // By inode numbers, directory entries will have a _stable_ order // across different calls to `readdir_at`. // The `new_dentry_iter` is responsible for filtering out entries // with inode numbers less than `start_ino`. let start_ino = offset as Ino; let mut count = 0; let mut last_ino = start_ino; let dentries = { let mut dentries: Vec<_> = self.new_dentry_iter(start_ino).collect(); dentries.sort_by_key(|d| d.ino); dentries }; for dentry in dentries { let res = visitor.visit(&dentry.name, dentry.ino, dentry.type_, dentry.ino as usize); if res.is_err() { if count == 0 { return Err(Error::new(Errno::EINVAL)); } else { break; } } count += 1; last_ino = dentry.ino; } if count == 0 { return Ok(0); } let next_ino = last_ino + 1; Ok((next_ino - start_ino) as usize) } fn read_link(&self) -> Result { match &self.inner_node { InnerNode::Symlink(s) => Ok(s.target_path().to_string()), _ => Err(Error::new(Errno::EINVAL)), } } fn write_link(&self, _target: &str) -> Result<()> { Err(Error::new(Errno::EPERM)) } fn as_device(&self) -> Option> { None } fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result { Err(Error::new(Errno::ENOTTY)) } fn sync_all(&self) -> Result<()> { Ok(()) } fn sync_data(&self) -> Result<()> { Ok(()) } fn fallocate(&self, _mode: FallocMode, _offset: usize, _len: usize) -> Result<()> { Err(Error::new(Errno::EOPNOTSUPP)) } fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents { let mut events = IoEvents::empty(); if let InnerNode::Attr(attr, _) = &self.inner_node { if attr.flags().contains(SysAttrFlags::CAN_READ) { events |= IoEvents::IN; } if attr.flags().contains(SysAttrFlags::CAN_WRITE) { events |= IoEvents::OUT; } } events & mask } fn is_dentry_cacheable(&self) -> bool { true } } // Update AttrDentryIter to filter by min_ino struct AttrDentryIter { attrs: Vec, dir_ino: Ino, min_ino: Ino, index: usize, } impl AttrDentryIter { fn new(attrs: Vec, dir_ino: Ino, min_ino: Ino) -> Self { Self { attrs, dir_ino, min_ino, index: 0, } } } impl Iterator for AttrDentryIter { type Item = Dentry; fn next(&mut self) -> Option { while self.index < self.attrs.len() { let attr = &self.attrs[self.index]; self.index += 1; let attr_ino = ino::from_dir_ino_and_attr_id(self.dir_ino, attr.id()); if attr_ino >= self.min_ino { // Filter by min_ino return Some(Dentry { ino: attr_ino, name: attr.name().clone(), type_: InodeType::File, }); } } None } } struct NodeDentryIter { nodes: Vec>, min_ino: Ino, index: usize, } impl NodeDentryIter { fn new(nodes: Vec>, min_ino: Ino) -> Self { Self { nodes, min_ino, index: 0, } } } impl Iterator for NodeDentryIter { type Item = Dentry; fn next(&mut self) -> Option { while self.index < self.nodes.len() { let obj = &self.nodes[self.index]; self.index += 1; let obj_ino = ino::from_sysnode_id(obj.id()); if obj_ino >= self.min_ino { // Filter by min_ino here let type_ = match obj.type_() { SysNodeType::Branch => InodeType::Dir, // Leaf nodes are presented as directories containing their attributes SysNodeType::Leaf => InodeType::Dir, SysNodeType::Symlink => InodeType::SymLink, }; return Some(Dentry { ino: obj_ino, name: obj.name(), type_, }); } } None } } struct ThisAndParentDentryIter<'a> { inode: &'a SysFsInode, min_ino: Ino, state: u8, // 0 = self, 1 = parent, 2 = done } impl<'a> ThisAndParentDentryIter<'a> { fn new(inode: &'a SysFsInode, min_ino: Ino) -> Self { Self { inode, min_ino, state: 0, } } } impl Iterator for ThisAndParentDentryIter<'_> { type Item = Dentry; fn next(&mut self) -> Option { match self.state { 0 => { self.state = 1; if self.inode.ino() >= self.min_ino { Some(Dentry { ino: self.inode.ino(), name: Cow::from("."), type_: InodeType::Dir, }) } else { self.next() } } 1 => { self.state = 2; let parent_ino = self .inode .parent .upgrade() .map_or(self.inode.ino(), |p| p.ino()); if parent_ino >= self.min_ino { Some(Dentry { ino: parent_ino, name: Cow::from(".."), type_: InodeType::Dir, }) } else { None } } _ => None, } } } /// A directory entry of sysfs. struct Dentry { pub ino: Ino, pub name: SysStr, pub type_: InodeType, } mod ino { use super::{InnerNode, Ino, SysNodeId}; const ATTR_ID_BITS: u8 = 8; pub fn from_sysnode_id(node_id: &SysNodeId) -> Ino { node_id.as_u64() << ATTR_ID_BITS } pub fn from_dir_ino_and_attr_id(dir_ino: Ino, attr_id: u8) -> Ino { dir_ino + (attr_id as Ino) } pub fn from_inner_node(inner: &InnerNode) -> Ino { match inner { InnerNode::Branch(branch_node) => from_sysnode_id(branch_node.id()), InnerNode::Leaf(leaf_node) => from_sysnode_id(leaf_node.id()), InnerNode::Symlink(symlink_node) => from_sysnode_id(symlink_node.id()), InnerNode::Attr(attr, node) => { // node here is the parent (Branch or Leaf) let dir_ino = from_sysnode_id(node.id()); // Get parent dir ino from_dir_ino_and_attr_id(dir_ino, attr.id()) } } } }