mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-25 02:13:24 +00:00
Add sysfs implementation
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
3a5f270ee9
commit
79b0866259
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" }
|
||||
log = "0.4"
|
||||
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
|
||||
systree = { path = "../systree" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
Reference in New Issue
Block a user