mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 12:56:48 +00:00
Add sysfs implementation
This commit is contained in:
parent
3a5f270ee9
commit
79b0866259
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -221,6 +221,7 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
"riscv",
|
"riscv",
|
||||||
"spin",
|
"spin",
|
||||||
|
"systree",
|
||||||
"takeable",
|
"takeable",
|
||||||
"tdx-guest",
|
"tdx-guest",
|
||||||
"time",
|
"time",
|
||||||
@ -301,6 +302,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"ostd",
|
"ostd",
|
||||||
"spin",
|
"spin",
|
||||||
|
"systree",
|
||||||
"typeflags-util",
|
"typeflags-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1684,6 +1686,16 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "systree"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"component",
|
||||||
|
"ostd",
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "takeable"
|
name = "takeable"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -19,6 +19,7 @@ members = [
|
|||||||
"kernel/comps/input",
|
"kernel/comps/input",
|
||||||
"kernel/comps/network",
|
"kernel/comps/network",
|
||||||
"kernel/comps/softirq",
|
"kernel/comps/softirq",
|
||||||
|
"kernel/comps/systree",
|
||||||
"kernel/comps/logger",
|
"kernel/comps/logger",
|
||||||
"kernel/comps/mlsdisk",
|
"kernel/comps/mlsdisk",
|
||||||
"kernel/comps/time",
|
"kernel/comps/time",
|
||||||
|
@ -11,6 +11,7 @@ time = { name = "aster-time" }
|
|||||||
framebuffer = { name = "aster-framebuffer" }
|
framebuffer = { name = "aster-framebuffer" }
|
||||||
network = { name = "aster-network" }
|
network = { name = "aster-network" }
|
||||||
mlsdisk = { name = "aster-mlsdisk" }
|
mlsdisk = { name = "aster-mlsdisk" }
|
||||||
|
systree = { name = "systree" }
|
||||||
|
|
||||||
[whitelist]
|
[whitelist]
|
||||||
[whitelist.nix.main]
|
[whitelist.nix.main]
|
||||||
|
1
Makefile
1
Makefile
@ -171,6 +171,7 @@ OSDK_CRATES := \
|
|||||||
kernel/comps/input \
|
kernel/comps/input \
|
||||||
kernel/comps/network \
|
kernel/comps/network \
|
||||||
kernel/comps/softirq \
|
kernel/comps/softirq \
|
||||||
|
kernel/comps/systree \
|
||||||
kernel/comps/logger \
|
kernel/comps/logger \
|
||||||
kernel/comps/mlsdisk \
|
kernel/comps/mlsdisk \
|
||||||
kernel/comps/time \
|
kernel/comps/time \
|
||||||
|
@ -58,6 +58,7 @@ inherit-methods-macro = { git = "https://github.com/asterinas/inherit-methods-ma
|
|||||||
getset = "0.1.2"
|
getset = "0.1.2"
|
||||||
takeable = "0.2.2"
|
takeable = "0.2.2"
|
||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
|
systree = { path = "comps/systree" }
|
||||||
# Fixed point numbers
|
# Fixed point numbers
|
||||||
# TODO: fork this crate to rewrite all the (unnecessary) unsafe usage
|
# TODO: fork this crate to rewrite all the (unnecessary) unsafe usage
|
||||||
fixed = "1.28.0"
|
fixed = "1.28.0"
|
||||||
|
10
kernel/comps/systree/Cargo.toml
Normal file
10
kernel/comps/systree/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "systree"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "2.5"
|
||||||
|
ostd = { path = "../../../ostd" }
|
||||||
|
component = { path = "../../libs/comp-sys/component" }
|
||||||
|
spin = "0.9"
|
151
kernel/comps/systree/src/attr.rs
Normal file
151
kernel/comps/systree/src/attr.rs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::collections::BTreeMap;
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use super::{Error, Result, SysStr};
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Flags defining the properties and permissions of a `SysAttr`.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SysAttrFlags: u32 {
|
||||||
|
/// Indicates whether the attribute can be read.
|
||||||
|
const CAN_READ = 1 << 0;
|
||||||
|
/// Indicates whether the attribute can be written to.
|
||||||
|
const CAN_WRITE = 1 << 1;
|
||||||
|
/// Indicates whether an attribute is a binary one
|
||||||
|
/// (rather than a textual one).
|
||||||
|
const IS_BINARY = 1 << 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SysAttrFlags {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::CAN_READ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An attribute may be fetched or updated via the methods of `SysNode`
|
||||||
|
/// such as `SysNode::read_attr` and `SysNode::write_attr`.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SysAttr {
|
||||||
|
/// Local ID within the node's `SysAttrSet`. Unique within the set.
|
||||||
|
id: u8,
|
||||||
|
/// The name of the attribute. Used to look up the attribute in a `SysAttrSet`.
|
||||||
|
name: SysStr,
|
||||||
|
/// Flags defining the behavior and permissions of the attribute.
|
||||||
|
flags: SysAttrFlags,
|
||||||
|
// Potentially add read/write handler functions or trait objects later
|
||||||
|
// read_handler: fn(...) -> Result<usize>,
|
||||||
|
// write_handler: fn(...) -> Result<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysAttr {
|
||||||
|
/// Creates a new attribute.
|
||||||
|
pub fn new(id: u8, name: SysStr, flags: SysAttrFlags) -> Self {
|
||||||
|
Self { id, name, flags }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the unique ID of the attribute within its set.
|
||||||
|
pub fn id(&self) -> u8 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the name of the attribute.
|
||||||
|
pub fn name(&self) -> &SysStr {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the flags associated with the attribute.
|
||||||
|
pub fn flags(&self) -> SysAttrFlags {
|
||||||
|
self.flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A collection of `SysAttr` for a `SysNode`.
|
||||||
|
/// Manages the attributes associated with a specific node in the `SysTree`.
|
||||||
|
///
|
||||||
|
/// This is an immutable collection - use `SysAttrSetBuilder` to create non-empty sets.
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct SysAttrSet {
|
||||||
|
/// Stores attributes keyed by their name.
|
||||||
|
attrs: BTreeMap<SysStr, SysAttr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysAttrSet {
|
||||||
|
/// Maximum number of attributes allowed per node (limited by u8 ID space).
|
||||||
|
pub const CAPACITY: usize = 1 << u8::BITS;
|
||||||
|
|
||||||
|
/// Creates a new, empty attribute set.
|
||||||
|
///
|
||||||
|
/// To create a non-empty attribute set, use `SysAttrSetBuilder`.
|
||||||
|
pub fn new_empty() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves an attribute by its name.
|
||||||
|
pub fn get(&self, name: &str) -> Option<&SysAttr> {
|
||||||
|
self.attrs.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the attributes in the set.
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &SysAttr> {
|
||||||
|
self.attrs.values()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of attributes in the set.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.attrs.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the attribute set is empty.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.attrs.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if an attribute with the given name exists in the set.
|
||||||
|
pub fn contains(&self, attr_name: &str) -> bool {
|
||||||
|
self.attrs.contains_key(attr_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct SysAttrSetBuilder {
|
||||||
|
attrs: BTreeMap<SysStr, SysAttr>,
|
||||||
|
next_id: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysAttrSetBuilder {
|
||||||
|
/// Creates a new builder.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an attribute definition to the builder.
|
||||||
|
///
|
||||||
|
/// If an attribute with the same name already exists, this is a no-op.
|
||||||
|
pub fn add(&mut self, name: SysStr, flags: SysAttrFlags) -> &mut Self {
|
||||||
|
if self.attrs.contains_key(&name) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = self.next_id;
|
||||||
|
self.next_id += 1;
|
||||||
|
let new_attr = SysAttr::new(id, name.clone(), flags);
|
||||||
|
self.attrs.insert(name, new_attr);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the builder and returns the constructed `SysAttrSet`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns `Err` if the capacity limit is reached.
|
||||||
|
pub fn build(self) -> Result<SysAttrSet> {
|
||||||
|
if self.attrs.len() > SysAttrSet::CAPACITY {
|
||||||
|
return Err(Error::PermissionDenied);
|
||||||
|
}
|
||||||
|
Ok(SysAttrSet { attrs: self.attrs })
|
||||||
|
}
|
||||||
|
}
|
93
kernel/comps/systree/src/lib.rs
Normal file
93
kernel/comps/systree/src/lib.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! This crate organizes the kernel information
|
||||||
|
//! about the entire system in a tree structure called `SysTree`.
|
||||||
|
//!
|
||||||
|
//! This crate provides a singleton of `SysTree`,
|
||||||
|
//! which is the "model" part of Asterinas's
|
||||||
|
//! model-view-controller (MVC) architecture
|
||||||
|
//! for organizing and managing device and kernel information.
|
||||||
|
//! The "view" part is sysfs,
|
||||||
|
//! a file system that exposes the system information
|
||||||
|
//! of the in-kernel `SysTree` to the user space.
|
||||||
|
//! The "controller" part consists of
|
||||||
|
//! various subsystems, buses, drivers, and kernel modules.
|
||||||
|
//! The "view" part has read-only access to the "model",
|
||||||
|
//! whereas the "controller" part can make changes to the "model".
|
||||||
|
//! This MVC architecture achieves separation of concerns,
|
||||||
|
//! making the code more modular, maintainable, and easier to understand.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
mod attr;
|
||||||
|
mod node;
|
||||||
|
#[cfg(ktest)]
|
||||||
|
mod test;
|
||||||
|
mod tree;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use alloc::{borrow::Cow, sync::Arc};
|
||||||
|
|
||||||
|
use component::{init_component, ComponentInitError};
|
||||||
|
use spin::Once;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
attr::{SysAttr, SysAttrFlags, SysAttrSet, SysAttrSetBuilder},
|
||||||
|
node::{SysBranchNode, SysNode, SysNodeId, SysNodeType, SysObj, SysSymlink},
|
||||||
|
tree::SysTree,
|
||||||
|
utils::{SymlinkNodeFields, SysBranchNodeFields, SysNormalNodeFields, SysObjFields},
|
||||||
|
};
|
||||||
|
|
||||||
|
static SINGLETON: Once<Arc<SysTree>> = Once::new();
|
||||||
|
|
||||||
|
#[init_component]
|
||||||
|
fn init() -> core::result::Result<(), ComponentInitError> {
|
||||||
|
SINGLETON.call_once(|| Arc::new(SysTree::new()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(ktest)]
|
||||||
|
pub fn init_for_ktest() {
|
||||||
|
SINGLETON.call_once(|| Arc::new(SysTree::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the global SysTree instance. Panics if not initialized. (Asterinas specific)
|
||||||
|
pub fn singleton() -> &'static Arc<SysTree> {
|
||||||
|
SINGLETON.get().expect("SysTree not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An owned string or a static reference to string.
|
||||||
|
pub type SysStr = Cow<'static, str>;
|
||||||
|
|
||||||
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Attempted to access a non-existent node
|
||||||
|
NodeNotFound(SysNodeId),
|
||||||
|
/// Invalid operation for node type
|
||||||
|
InvalidNodeOperation(SysNodeType),
|
||||||
|
/// Attribute operation failed
|
||||||
|
AttributeError,
|
||||||
|
/// Permission denied for operation
|
||||||
|
PermissionDenied,
|
||||||
|
/// Other internal error
|
||||||
|
InternalError(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::NodeNotFound(id) => write!(f, "Node not found: {:?}", id),
|
||||||
|
Error::InvalidNodeOperation(ty) => {
|
||||||
|
write!(f, "Invalid operation for node type: {:?}", ty)
|
||||||
|
}
|
||||||
|
Error::AttributeError => write!(f, "Attribute error"),
|
||||||
|
Error::PermissionDenied => write!(f, "Permission denied for operation"),
|
||||||
|
Error::InternalError(msg) => write!(f, "Internal error: {}", msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
232
kernel/comps/systree/src/node.rs
Normal file
232
kernel/comps/systree/src/node.rs
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::{string::String, sync::Arc, vec, vec::Vec};
|
||||||
|
use core::{
|
||||||
|
any::Any,
|
||||||
|
fmt::Debug,
|
||||||
|
sync::atomic::{AtomicU64, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use ostd::mm::{VmReader, VmWriter};
|
||||||
|
|
||||||
|
use super::{Error, Result, SysAttrSet, SysStr};
|
||||||
|
|
||||||
|
pub const MAX_ATTR_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
/// The three types of nodes in a `SysTree`.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum SysNodeType {
|
||||||
|
/// A branching node is one that may contain child nodes.
|
||||||
|
Branch,
|
||||||
|
/// A leaf node is one that may not contain child nodes.
|
||||||
|
Leaf,
|
||||||
|
/// A symlink node,
|
||||||
|
/// which ia a special kind of leaf node that points to another node,
|
||||||
|
/// similar to a symbolic link in file systems.
|
||||||
|
Symlink,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait that represents a branching node in a `SysTree`.
|
||||||
|
pub trait SysBranchNode: SysNode {
|
||||||
|
/// Visits a child node with the given name using a closure.
|
||||||
|
///
|
||||||
|
/// If the child with the given name exists,
|
||||||
|
/// a reference to the child will be provided to the closure.
|
||||||
|
/// Otherwise, the closure will be given a `None`.
|
||||||
|
///
|
||||||
|
/// # Efficiency
|
||||||
|
///
|
||||||
|
/// This method is a more efficient, but less convenient version
|
||||||
|
/// of the `child` method.
|
||||||
|
/// The method does not require taking the ownership of the child node.
|
||||||
|
/// So use this method when efficiency is a primary concern,
|
||||||
|
/// while using the `child` method for the sake of convenience.
|
||||||
|
///
|
||||||
|
/// # Deadlock
|
||||||
|
///
|
||||||
|
/// The implementation of this method depends on the concrete type
|
||||||
|
/// and probably will hold an internal lock.
|
||||||
|
/// So the caller should do as little as possible inside the closure.
|
||||||
|
/// In particular, the caller should _not_ invoke other methods
|
||||||
|
/// on this object as this might cause deadlock.
|
||||||
|
fn visit_child_with(&self, name: &str, f: &mut dyn FnMut(Option<&dyn SysNode>));
|
||||||
|
|
||||||
|
/// Visits child nodes with a minimum ID using a closure.
|
||||||
|
///
|
||||||
|
/// This method iterates over the child nodes
|
||||||
|
/// whose IDs are no less than a specified minimum value.
|
||||||
|
/// and provide them to the given closure one at a time.
|
||||||
|
///
|
||||||
|
/// The iteration terminates until there are no unvisited children
|
||||||
|
/// or the closure returns a `None`.
|
||||||
|
///
|
||||||
|
/// # Efficiency
|
||||||
|
///
|
||||||
|
/// This method is a more efficient, but less convenient version
|
||||||
|
/// of the `children` method.
|
||||||
|
/// The method require neither taking the ownership of the child nodes
|
||||||
|
/// nor doing heap allocations.
|
||||||
|
/// So use this method when efficiency is a primary concern,
|
||||||
|
/// while using the `children` method for the sake of convenience.
|
||||||
|
///
|
||||||
|
/// # Deadlock
|
||||||
|
///
|
||||||
|
/// Same as the `visit_child_with` method.
|
||||||
|
fn visit_children_with(
|
||||||
|
&self,
|
||||||
|
min_id: u64,
|
||||||
|
f: &mut dyn for<'a> FnMut(&'a Arc<(dyn SysObj + 'static)>) -> Option<()>,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Returns a child with a specified name.
|
||||||
|
fn child(&self, name: &str) -> Option<Arc<dyn SysObj>>;
|
||||||
|
|
||||||
|
/// Collects all children into a `Vec`.
|
||||||
|
fn children(&self) -> Vec<Arc<dyn SysObj>> {
|
||||||
|
let mut children: Vec<Arc<dyn SysObj>> = Vec::new();
|
||||||
|
self.visit_children_with(0, &mut |child_arc| {
|
||||||
|
children.push(child_arc.clone());
|
||||||
|
Some(())
|
||||||
|
});
|
||||||
|
children
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Counts the number of children.
|
||||||
|
fn count_children(&self) -> usize {
|
||||||
|
let mut count = 0;
|
||||||
|
self.visit_children_with(0, &mut |_| {
|
||||||
|
count += 1;
|
||||||
|
Some(())
|
||||||
|
});
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The trait that abstracts a "normal" node in a `SysTree`.
|
||||||
|
///
|
||||||
|
/// The branching and leaf nodes are considered "normal",
|
||||||
|
/// whereas the symlink nodes are considered "special".
|
||||||
|
/// This trait abstracts the common interface of "normal" nodes.
|
||||||
|
/// In particular, every "normal" node may have associated attributes.
|
||||||
|
pub trait SysNode: SysObj {
|
||||||
|
/// Returns the attribute set of a `SysNode`.
|
||||||
|
fn node_attrs(&self) -> &SysAttrSet;
|
||||||
|
|
||||||
|
/// Reads the value of an attribute.
|
||||||
|
fn read_attr(&self, name: &str, writer: &mut VmWriter) -> Result<usize>;
|
||||||
|
|
||||||
|
/// Writes the value of an attribute.
|
||||||
|
fn write_attr(&self, name: &str, reader: &mut VmReader) -> Result<usize>;
|
||||||
|
|
||||||
|
/// Shows the string value of an attribute.
|
||||||
|
///
|
||||||
|
/// Most attributes are textual, rather binary (see `SysAttrFlags::IS_BINARY`).
|
||||||
|
/// So using this `show_attr` method is more convenient than
|
||||||
|
/// the `read_attr` method.
|
||||||
|
fn show_attr(&self, name: &str) -> Result<String> {
|
||||||
|
let mut buf: Vec<u8> = vec![0; MAX_ATTR_SIZE];
|
||||||
|
let mut writer = VmWriter::from(buf.as_mut_slice()).to_fallible();
|
||||||
|
let read_len = self.read_attr(name, &mut writer)?;
|
||||||
|
// Use from_utf8_lossy or handle error properly if strict UTF-8 is needed
|
||||||
|
let attr_val =
|
||||||
|
String::from_utf8(buf[..read_len].to_vec()).map_err(|_| Error::AttributeError)?;
|
||||||
|
Ok(attr_val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores the string value of an attribute.
|
||||||
|
///
|
||||||
|
/// Most attributes are textual, rather binary (see `SysAttrFlags::IS_BINARY`).
|
||||||
|
/// So using this `store_attr` method is more convenient than
|
||||||
|
/// the `write_attr` method.
|
||||||
|
fn store_attr(&self, name: &str, new_val: &str) -> Result<usize> {
|
||||||
|
let mut reader = VmReader::from(new_val.as_bytes()).to_fallible();
|
||||||
|
self.write_attr(name, &mut reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait that abstracts any symlink node in a `SysTree`.
|
||||||
|
pub trait SysSymlink: SysObj {
|
||||||
|
/// A path that represents the target node of this symlink node.
|
||||||
|
fn target_path(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The base trait for any node in a `SysTree`.
|
||||||
|
pub trait SysObj: Any + Send + Sync + Debug + 'static {
|
||||||
|
/// Returns a reference to this object as `Any` for downcasting.
|
||||||
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
||||||
|
/// Attempts to get an Arc to this object as a `SysSymlink`.
|
||||||
|
fn arc_as_symlink(&self) -> Option<Arc<dyn SysSymlink>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to get an Arc to this object as a `SysNode`.
|
||||||
|
fn arc_as_node(&self) -> Option<Arc<dyn SysNode>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to get an Arc to this object as a `SysBranchNode`.
|
||||||
|
fn arc_as_branch(&self) -> Option<Arc<dyn SysBranchNode>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the unique and immutable ID of a node.
|
||||||
|
fn id(&self) -> &SysNodeId;
|
||||||
|
|
||||||
|
/// Returns the type of a node.
|
||||||
|
fn type_(&self) -> SysNodeType;
|
||||||
|
|
||||||
|
/// Returns the name of a node.
|
||||||
|
///
|
||||||
|
/// The name is guaranteed _not_ to contain two special characters:
|
||||||
|
/// `'/'` and `'\0'`.
|
||||||
|
///
|
||||||
|
/// The root node of a `SysTree` has an empty name.
|
||||||
|
/// All other inodes must have an non-empty name.
|
||||||
|
fn name(&self) -> SysStr;
|
||||||
|
|
||||||
|
/// Returns whether a node is the root of a `SysTree`.
|
||||||
|
fn is_root(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path from the root to this node.
|
||||||
|
///
|
||||||
|
/// The path of a node is the names of all the ancestors concatenated
|
||||||
|
/// with `/` as the separator.
|
||||||
|
///
|
||||||
|
/// If the node has been attached to a `SysTree`,
|
||||||
|
/// then the returned path begins with `/`.
|
||||||
|
/// Otherwise, the returned path does _not_ begin with `/`.
|
||||||
|
fn path(&self) -> SysStr {
|
||||||
|
todo!("implement with the parent and name methods")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The unique ID of a `SysNode`.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct SysNodeId(u64);
|
||||||
|
|
||||||
|
impl SysNodeId {
|
||||||
|
/// Creates a new ID.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
let next_id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
|
// Guard against integer overflow
|
||||||
|
assert!(next_id <= u64::MAX / 2);
|
||||||
|
|
||||||
|
Self(next_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the value of the ID.
|
||||||
|
pub fn as_u64(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SysNodeId {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
165
kernel/comps/systree/src/tree.rs
Normal file
165
kernel/comps/systree/src/tree.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! Defines the main `SysTree` structure and its root node implementation.
|
||||||
|
|
||||||
|
use alloc::{
|
||||||
|
borrow::Cow,
|
||||||
|
collections::BTreeMap,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use core::any::Any;
|
||||||
|
|
||||||
|
use ostd::{
|
||||||
|
mm::{VmReader, VmWriter},
|
||||||
|
sync::RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
attr::SysAttrSet,
|
||||||
|
node::{SysBranchNode, SysNode, SysNodeId, SysNodeType, SysObj, SysSymlink},
|
||||||
|
Error, Result, SysStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SysTree {
|
||||||
|
root: Arc<RootNode>,
|
||||||
|
// event_hub: SysEventHub,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysTree {
|
||||||
|
/// Creates a new `SysTree` instance with a default root node
|
||||||
|
/// and standard subdirectories like "devices", "block", "kernel".
|
||||||
|
/// This is intended to be called once for the singleton.
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
let root_node = Arc::new_cyclic(|weak_self| RootNode {
|
||||||
|
id: SysNodeId::new(),
|
||||||
|
name: "".into(),
|
||||||
|
attrs: SysAttrSet::new_empty(),
|
||||||
|
children: RwLock::new(BTreeMap::new()),
|
||||||
|
self_ref: weak_self.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Self { root: root_node }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the root node of the tree.
|
||||||
|
pub fn root(&self) -> &Arc<RootNode> {
|
||||||
|
&self.root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RootNode {
|
||||||
|
id: SysNodeId,
|
||||||
|
name: SysStr,
|
||||||
|
attrs: SysAttrSet,
|
||||||
|
children: RwLock<BTreeMap<SysStr, Arc<dyn SysObj>>>,
|
||||||
|
self_ref: Weak<Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RootNode {
|
||||||
|
/// Adds a child node. This was part of the concrete RootNode impl in lib.rs.
|
||||||
|
/// It's not part of the SysBranchNode trait definition.
|
||||||
|
pub fn add_child(&self, new_child: Arc<dyn SysObj>) -> Result<()> {
|
||||||
|
let name = new_child.name();
|
||||||
|
let mut children_guard = self.children.write();
|
||||||
|
if children_guard.contains_key(&name) {
|
||||||
|
return Err(Error::PermissionDenied);
|
||||||
|
}
|
||||||
|
children_guard.insert(name.clone(), new_child);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysObj for RootNode {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arc_as_symlink(&self) -> Option<Arc<dyn SysSymlink>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arc_as_node(&self) -> Option<Arc<dyn SysNode>> {
|
||||||
|
self.self_ref
|
||||||
|
.upgrade()
|
||||||
|
.map(|arc_self| arc_self as Arc<dyn SysNode>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arc_as_branch(&self) -> Option<Arc<dyn SysBranchNode>> {
|
||||||
|
self.self_ref
|
||||||
|
.upgrade()
|
||||||
|
.map(|arc_self| arc_self as Arc<dyn SysBranchNode>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> &SysNodeId {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_(&self) -> SysNodeType {
|
||||||
|
if self.children.read().is_empty() {
|
||||||
|
return SysNodeType::Leaf;
|
||||||
|
}
|
||||||
|
SysNodeType::Branch
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> SysStr {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_root(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self) -> SysStr {
|
||||||
|
Cow::from("/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysNode for RootNode {
|
||||||
|
fn node_attrs(&self) -> &SysAttrSet {
|
||||||
|
&self.attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_attr(&self, _name: &str, _writer: &mut VmWriter) -> Result<usize> {
|
||||||
|
Err(Error::AttributeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_attr(&self, _name: &str, _reader: &mut VmReader) -> Result<usize> {
|
||||||
|
Err(Error::AttributeError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysBranchNode for RootNode {
|
||||||
|
fn visit_child_with(&self, name: &str, f: &mut dyn FnMut(Option<&dyn SysNode>)) {
|
||||||
|
let children_guard = self.children.read();
|
||||||
|
children_guard
|
||||||
|
.get(name)
|
||||||
|
.map(|child| {
|
||||||
|
if let Some(node_ref) = child.arc_as_node().as_deref() {
|
||||||
|
f(Some(node_ref));
|
||||||
|
} else {
|
||||||
|
f(None);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| f(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_children_with(&self, _min_id: u64, f: &mut dyn FnMut(&Arc<dyn SysObj>) -> Option<()>) {
|
||||||
|
let children_guard = self.children.read();
|
||||||
|
for child_arc in children_guard.values() {
|
||||||
|
if f(child_arc).is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn child(&self, name: &str) -> Option<Arc<dyn SysObj>> {
|
||||||
|
self.children.read().get(name).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn children(&self) -> Vec<Arc<dyn SysObj>> {
|
||||||
|
self.children.read().values().cloned().collect()
|
||||||
|
}
|
||||||
|
}
|
138
kernel/comps/systree/src/utils.rs
Normal file
138
kernel/comps/systree/src/utils.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! Utility definitions and helper structs for implementing `SysTree` nodes.
|
||||||
|
|
||||||
|
use alloc::{collections::BTreeMap, string::String, sync::Arc};
|
||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
|
use ostd::sync::RwLock;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
attr::SysAttrSet,
|
||||||
|
node::{SysNodeId, SysObj},
|
||||||
|
Error, Result, SysStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SysObjFields {
|
||||||
|
id: SysNodeId,
|
||||||
|
name: SysStr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysObjFields {
|
||||||
|
pub fn new(name: SysStr) -> Self {
|
||||||
|
Self {
|
||||||
|
id: SysNodeId::new(),
|
||||||
|
name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> &SysNodeId {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
self.name.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SysNormalNodeFields {
|
||||||
|
base: SysObjFields,
|
||||||
|
attr_set: SysAttrSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysNormalNodeFields {
|
||||||
|
pub fn new(name: SysStr, attr_set: SysAttrSet) -> Self {
|
||||||
|
Self {
|
||||||
|
base: SysObjFields::new(name),
|
||||||
|
attr_set,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> &SysNodeId {
|
||||||
|
self.base.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
self.base.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attr_set(&self) -> &SysAttrSet {
|
||||||
|
&self.attr_set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SysBranchNodeFields<C: SysObj + ?Sized> {
|
||||||
|
base: SysNormalNodeFields,
|
||||||
|
pub children: RwLock<BTreeMap<SysStr, Arc<C>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: SysObj + ?Sized> SysBranchNodeFields<C> {
|
||||||
|
pub fn new(name: SysStr, attr_set: SysAttrSet) -> Self {
|
||||||
|
Self {
|
||||||
|
base: SysNormalNodeFields::new(name, attr_set),
|
||||||
|
children: RwLock::new(BTreeMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> &SysNodeId {
|
||||||
|
self.base.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
self.base.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attr_set(&self) -> &SysAttrSet {
|
||||||
|
self.base.attr_set()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, child_name: &str) -> bool {
|
||||||
|
let children = self.children.read();
|
||||||
|
children.contains_key(child_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_child(&self, new_child: Arc<C>) -> Result<()> {
|
||||||
|
let mut children = self.children.write();
|
||||||
|
let name = new_child.name();
|
||||||
|
if children.contains_key(name.deref()) {
|
||||||
|
return Err(Error::PermissionDenied);
|
||||||
|
}
|
||||||
|
children.insert(name.clone(), new_child);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_child(&self, child_name: &str) -> Option<Arc<C>> {
|
||||||
|
let mut children = self.children.write();
|
||||||
|
children.remove(child_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SymlinkNodeFields {
|
||||||
|
base: SysObjFields,
|
||||||
|
target_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymlinkNodeFields {
|
||||||
|
pub fn new(name: SysStr, target_path: String) -> Self {
|
||||||
|
Self {
|
||||||
|
base: SysObjFields::new(name),
|
||||||
|
target_path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> &SysNodeId {
|
||||||
|
self.base.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
self.base.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target_path(&self) -> &str {
|
||||||
|
&self.target_path
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ ostd = { path = "../../../ostd" }
|
|||||||
component = { path = "../../libs/comp-sys/component" }
|
component = { path = "../../libs/comp-sys/component" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
|
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
|
||||||
|
systree = { path = "../systree" }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
@ -330,6 +330,19 @@ impl From<int_to_c_enum::TryFromIntError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<systree::Error> for Error {
|
||||||
|
fn from(err: systree::Error) -> Self {
|
||||||
|
use systree::Error::*;
|
||||||
|
match err {
|
||||||
|
NodeNotFound(_) => Error::new(Errno::ENOENT),
|
||||||
|
InvalidNodeOperation(_) => Error::new(Errno::EINVAL),
|
||||||
|
AttributeError => Error::new(Errno::EIO),
|
||||||
|
PermissionDenied => Error::new(Errno::EACCES),
|
||||||
|
InternalError(msg) => Error::with_message(Errno::EIO, msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! return_errno {
|
macro_rules! return_errno {
|
||||||
($errno: expr) => {
|
($errno: expr) => {
|
||||||
|
@ -15,6 +15,7 @@ pub mod pipe;
|
|||||||
pub mod procfs;
|
pub mod procfs;
|
||||||
pub mod ramfs;
|
pub mod ramfs;
|
||||||
pub mod rootfs;
|
pub mod rootfs;
|
||||||
|
pub mod sysfs;
|
||||||
pub mod thread_info;
|
pub mod thread_info;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ use super::{
|
|||||||
path::MountNode,
|
path::MountNode,
|
||||||
procfs::{self, ProcFS},
|
procfs::{self, ProcFS},
|
||||||
ramfs::RamFS,
|
ramfs::RamFS,
|
||||||
|
sysfs::{init as sysfs_init, singleton as sysfs_singleton},
|
||||||
utils::{FileSystem, InodeMode, InodeType},
|
utils::{FileSystem, InodeMode, InodeType},
|
||||||
};
|
};
|
||||||
use crate::{fs::path::is_dot, prelude::*};
|
use crate::{fs::path::is_dot, prelude::*};
|
||||||
@ -113,7 +114,11 @@ pub fn init(initramfs_buf: &[u8]) -> Result<()> {
|
|||||||
// Mount DevFS
|
// Mount DevFS
|
||||||
let dev_dentry = fs.lookup(&FsPath::try_from("/dev")?)?;
|
let dev_dentry = fs.lookup(&FsPath::try_from("/dev")?)?;
|
||||||
dev_dentry.mount(RamFS::new())?;
|
dev_dentry.mount(RamFS::new())?;
|
||||||
|
// Mount SysFS
|
||||||
|
let sys_dentry = fs.lookup(&FsPath::try_from("/sys")?)?;
|
||||||
|
sysfs_init();
|
||||||
|
let sysfs: Arc<dyn FileSystem> = sysfs_singleton().clone();
|
||||||
|
sys_dentry.mount(sysfs)?;
|
||||||
println!("[kernel] rootfs is ready");
|
println!("[kernel] rootfs is ready");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
54
kernel/src/fs/sysfs/fs.rs
Normal file
54
kernel/src/fs/sysfs/fs.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
|
||||||
|
use systree::singleton as systree_singleton;
|
||||||
|
|
||||||
|
use crate::fs::{
|
||||||
|
sysfs::inode::SysFsInode,
|
||||||
|
utils::{FileSystem, FsFlags, Inode, SuperBlock},
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A file system for exposing kernel information to the user space.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SysFs {
|
||||||
|
sb: SuperBlock,
|
||||||
|
root: Arc<dyn Inode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAGIC_NUMBER: u64 = 0x62656572; // SYSFS_MAGIC
|
||||||
|
const BLOCK_SIZE: usize = 4096;
|
||||||
|
const NAME_MAX: usize = 255;
|
||||||
|
|
||||||
|
impl SysFs {
|
||||||
|
pub(crate) fn new() -> Arc<Self> {
|
||||||
|
let sb = SuperBlock::new(MAGIC_NUMBER, BLOCK_SIZE, NAME_MAX);
|
||||||
|
let systree_ref = systree_singleton();
|
||||||
|
let root_inode = SysFsInode::new_root(systree_ref);
|
||||||
|
|
||||||
|
Arc::new(Self {
|
||||||
|
sb,
|
||||||
|
root: root_inode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileSystem for SysFs {
|
||||||
|
fn sync(&self) -> Result<()> {
|
||||||
|
// Sysfs is volatile, sync is a no-op
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_inode(&self) -> Arc<dyn Inode> {
|
||||||
|
self.root.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sb(&self) -> SuperBlock {
|
||||||
|
self.sb.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flags(&self) -> FsFlags {
|
||||||
|
FsFlags::empty()
|
||||||
|
}
|
||||||
|
}
|
731
kernel/src/fs/sysfs/inode.rs
Normal file
731
kernel/src/fs/sysfs/inode.rs
Normal file
@ -0,0 +1,731 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use alloc::{
|
||||||
|
borrow::Cow,
|
||||||
|
string::{String, ToString},
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
|
use ostd::sync::RwLock;
|
||||||
|
use systree::{
|
||||||
|
SysAttr, SysAttrFlags, SysBranchNode, SysNode, SysNodeId, SysNodeType, SysObj, SysStr,
|
||||||
|
SysSymlink, SysTree,
|
||||||
|
};
|
||||||
|
|
||||||
|
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<SysTree>,
|
||||||
|
/// 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<InodeMode>,
|
||||||
|
/// Weak reference to the parent inode.
|
||||||
|
parent: Weak<SysFsInode>,
|
||||||
|
/// Weak self-reference for cyclic data structures.
|
||||||
|
this: Weak<SysFsInode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum InnerNode {
|
||||||
|
Branch(Arc<dyn SysBranchNode>),
|
||||||
|
Leaf(Arc<dyn SysNode>),
|
||||||
|
Attr(SysAttr, Arc<dyn SysNode>),
|
||||||
|
Symlink(Arc<dyn SysSymlink>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SysFsInode {
|
||||||
|
pub(crate) fn new_root(systree: &'static Arc<SysTree>) -> Arc<Self> {
|
||||||
|
let root_node = systree.root().clone(); // systree.root() returns Arc<dyn SysBranchNode>
|
||||||
|
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<SysTree>,
|
||||||
|
attr: SysAttr,
|
||||||
|
node: Arc<dyn SysNode>,
|
||||||
|
parent: Weak<SysFsInode>,
|
||||||
|
) -> Arc<Self> {
|
||||||
|
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<SysTree>,
|
||||||
|
symlink: Arc<dyn SysSymlink>,
|
||||||
|
parent: Weak<SysFsInode>,
|
||||||
|
) -> Arc<Self> {
|
||||||
|
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<SysTree>,
|
||||||
|
inner_node: InnerNode, // Must be InnerNode::Branch
|
||||||
|
parent: Weak<SysFsInode>,
|
||||||
|
) -> Arc<Self> {
|
||||||
|
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<SysTree>,
|
||||||
|
inner_node: InnerNode, // Must be InnerNode::Leaf
|
||||||
|
parent: Weak<SysFsInode>,
|
||||||
|
) -> Arc<Self> {
|
||||||
|
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<SysFsInode> {
|
||||||
|
self.this.upgrade().expect("Weak ref invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_node_or_attr(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
sysnode: &dyn SysBranchNode,
|
||||||
|
) -> Result<Arc<dyn Inode>> {
|
||||||
|
// 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()),
|
||||||
|
);
|
||||||
|
return 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()),
|
||||||
|
);
|
||||||
|
return 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()),
|
||||||
|
);
|
||||||
|
return 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<dyn SysNode> = 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()),
|
||||||
|
);
|
||||||
|
return Ok(inode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_attr(&self, name: &str, sysnode: &dyn SysNode) -> Result<Arc<dyn Inode>> {
|
||||||
|
// 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<dyn SysNode> = 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<Item = Dentry> + '_ {
|
||||||
|
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 {
|
||||||
|
self.metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ino(&self) -> u64 {
|
||||||
|
self.metadata.ino
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mode(&self) -> Result<InodeMode> {
|
||||||
|
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<Uid> {
|
||||||
|
Ok(self.metadata.uid)
|
||||||
|
}
|
||||||
|
fn set_owner(&self, _uid: Uid) -> Result<()> {
|
||||||
|
Err(Error::new(Errno::EPERM))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn group(&self) -> Result<Gid> {
|
||||||
|
Ok(self.metadata.gid)
|
||||||
|
}
|
||||||
|
fn set_group(&self, _gid: Gid) -> Result<()> {
|
||||||
|
Err(Error::new(Errno::EPERM))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||||
|
crate::fs::sysfs::singleton().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_cache(&self) -> Option<crate::vm::vmo::Vmo<aster_rights::Full>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_at(&self, offset: usize, buf: &mut VmWriter) -> Result<usize> {
|
||||||
|
self.read_direct_at(offset, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_direct_at(&self, _offset: usize, buf: &mut VmWriter) -> Result<usize> {
|
||||||
|
// TODO: it is unclear whether we should simply ignore the offset
|
||||||
|
// or report errors if it is non-zero.
|
||||||
|
|
||||||
|
let InnerNode::Attr(attr, leaf) = &self.inner_node else {
|
||||||
|
return Err(Error::new(Errno::EINVAL));
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: check read permission
|
||||||
|
Ok(leaf.read_attr(attr.name(), buf)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_at(&self, offset: usize, buf: &mut VmReader) -> Result<usize> {
|
||||||
|
self.write_direct_at(offset, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_direct_at(&self, _offset: usize, buf: &mut VmReader) -> Result<usize> {
|
||||||
|
let InnerNode::Attr(attr, leaf) = &self.inner_node else {
|
||||||
|
return Err(Error::new(Errno::EINVAL));
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: check write permission
|
||||||
|
Ok(leaf.write_attr(attr.name(), buf)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
|
||||||
|
Err(Error::new(Errno::EPERM))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mknod(&self, _name: &str, _mode: InodeMode, _dev: MknodType) -> Result<Arc<dyn Inode>> {
|
||||||
|
Err(Error::new(Errno::EPERM))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn link(&self, _old: &Arc<dyn Inode>, _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<dyn Inode>, _new_name: &str) -> Result<()> {
|
||||||
|
Err(Error::new(Errno::EPERM))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
|
||||||
|
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<usize> {
|
||||||
|
// 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 mut iter = self.new_dentry_iter(start_ino + 1);
|
||||||
|
|
||||||
|
while let Some(dentry) = iter.next() {
|
||||||
|
// The offset reported back to the caller should be the absolute position
|
||||||
|
let next_offset = (dentry.ino + 1) as usize;
|
||||||
|
let res = visitor.visit(&dentry.name, dentry.ino, dentry.type_, next_offset);
|
||||||
|
|
||||||
|
if res.is_err() {
|
||||||
|
if count == 0 {
|
||||||
|
return Err(Error::new(Errno::EINVAL));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
last_ino = dentry.ino;
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
Ok(0)
|
||||||
|
} else {
|
||||||
|
// Return absolute offset instead of an increment
|
||||||
|
Ok((last_ino + 1) as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_link(&self) -> Result<String> {
|
||||||
|
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<Arc<dyn Device>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||||
|
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<SysAttr>,
|
||||||
|
dir_ino: Ino,
|
||||||
|
min_ino: Ino,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttrDentryIter {
|
||||||
|
fn new(attrs: Vec<SysAttr>, 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<Dentry> {
|
||||||
|
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<Arc<dyn SysObj>>,
|
||||||
|
min_ino: Ino,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeDentryIter {
|
||||||
|
fn new(nodes: Vec<Arc<dyn SysObj>>, min_ino: Ino) -> Self {
|
||||||
|
Self {
|
||||||
|
nodes,
|
||||||
|
min_ino,
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for NodeDentryIter {
|
||||||
|
type Item = Dentry;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Dentry> {
|
||||||
|
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<'a> Iterator for ThisAndParentDentryIter<'a> {
|
||||||
|
type Item = Dentry;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Dentry> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
kernel/src/fs/sysfs/mod.rs
Normal file
27
kernel/src/fs/sysfs/mod.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
mod fs;
|
||||||
|
mod inode;
|
||||||
|
#[cfg(ktest)]
|
||||||
|
mod test;
|
||||||
|
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
|
||||||
|
use spin::Once;
|
||||||
|
|
||||||
|
pub use self::{fs::SysFs, inode::SysFsInode};
|
||||||
|
|
||||||
|
static SYSFS_SINGLETON: Once<Arc<SysFs>> = Once::new();
|
||||||
|
|
||||||
|
/// Returns a reference to the global SysFs instance. Panics if not initialized.
|
||||||
|
pub fn singleton() -> &'static Arc<SysFs> {
|
||||||
|
SYSFS_SINGLETON.get().expect("SysFs not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the SysFs singleton.
|
||||||
|
/// Ensures that the singleton is created by calling it.
|
||||||
|
/// Should be called during kernel filesystem initialization, *after* systree::init().
|
||||||
|
pub fn init() {
|
||||||
|
// Ensure systree is initialized first. This should be handled by the kernel's init order.
|
||||||
|
SYSFS_SINGLETON.call_once(|| SysFs::new());
|
||||||
|
}
|
@ -23,6 +23,7 @@ INITRAMFS_EMPTY_DIRS := \
|
|||||||
$(INITRAMFS)/opt \
|
$(INITRAMFS)/opt \
|
||||||
$(INITRAMFS)/proc \
|
$(INITRAMFS)/proc \
|
||||||
$(INITRAMFS)/dev \
|
$(INITRAMFS)/dev \
|
||||||
|
$(INITRAMFS)/sys \
|
||||||
$(INITRAMFS)/ext2 \
|
$(INITRAMFS)/ext2 \
|
||||||
$(INITRAMFS)/exfat
|
$(INITRAMFS)/exfat
|
||||||
INITRAMFS_ALL_DIRS := \
|
INITRAMFS_ALL_DIRS := \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user