Skip to content

Commit f5f85a2

Browse files
committed
fs: add crates/axfs_ramfs and mount to /tmp
1 parent 6e41968 commit f5f85a2

File tree

20 files changed

+517
-14
lines changed

20 files changed

+517
-14
lines changed

.github/workflows/docs.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
name: Deploy docs
22

3-
on:
4-
push:
5-
branches: [main]
3+
on: [push, pull_request]
64

75
jobs:
86
doc:
@@ -20,6 +18,7 @@ jobs:
2018
- name: Build docs
2119
run: make doc_check_missing
2220
- name: Deploy to Github Pages
21+
if: ${{ github.ref == 'refs/heads/main' }}
2322
uses: JamesIves/github-pages-deploy-action@v4
2423
with:
2524
single-commit: true

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
toolchain: nightly
1717
components: rust-src, llvm-tools-preview
1818
- name: Run unit tests
19-
run: make unit_test_no_fail_fast
19+
run: make unittest_no_fail_fast
2020

2121
app-test:
2222
runs-on: ${{ matrix.os }}

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ members = [
1818
"crates/arm_gic",
1919
"crates/axerrno",
2020
"crates/axfs_devfs",
21+
"crates/axfs_ramfs",
2122
"crates/axfs_vfs",
2223
"crates/axio",
2324
"crates/capability",

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ fmt_c:
102102
test:
103103
$(call app_test)
104104

105-
unit_test:
105+
unittest:
106106
$(call unit_test)
107107

108-
unit_test_no_fail_fast:
108+
unittest_no_fail_fast:
109109
$(call unit_test,--no-fail-fast)
110110

111111
disk_img:

crates/axfs_ramfs/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "axfs_ramfs"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["Yuekai Jia <[email protected]>"]
6+
description = "RAM filesystem used by ArceOS"
7+
license = "GPL-3.0-or-later OR Apache-2.0"
8+
homepage = "https://github.com/rcore-os/arceos"
9+
repository = "https://github.com/rcore-os/arceos/tree/main/crates/axfs_ramfs"
10+
documentation = "https://rcore-os.github.io/arceos/axfs_ramfs/index.html"
11+
12+
[dependencies]
13+
axfs_vfs = { path = "../axfs_vfs" }
14+
spin = "0.9"
15+
log = "0.4"

crates/axfs_ramfs/src/dir.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
use alloc::collections::BTreeMap;
2+
use alloc::sync::{Arc, Weak};
3+
use alloc::{string::String, vec::Vec};
4+
5+
use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType};
6+
use axfs_vfs::{VfsError, VfsResult};
7+
use spin::RwLock;
8+
9+
use crate::file::FileNode;
10+
11+
/// The directory node in the RAM filesystem.
12+
///
13+
/// It implements [`axfs_vfs::VfsNodeOps`].
14+
pub struct DirNode {
15+
this: Weak<DirNode>,
16+
parent: RwLock<Weak<dyn VfsNodeOps>>,
17+
children: RwLock<BTreeMap<String, VfsNodeRef>>,
18+
}
19+
20+
impl DirNode {
21+
pub(super) fn new(parent: Option<Weak<dyn VfsNodeOps>>) -> Arc<Self> {
22+
Arc::new_cyclic(|this| Self {
23+
this: this.clone(),
24+
parent: RwLock::new(parent.unwrap_or_else(|| Weak::<Self>::new())),
25+
children: RwLock::new(BTreeMap::new()),
26+
})
27+
}
28+
29+
pub(super) fn set_parent(&self, parent: Option<&VfsNodeRef>) {
30+
*self.parent.write() = parent.map_or(Weak::<Self>::new() as _, Arc::downgrade);
31+
}
32+
33+
/// Returns a string list of all entries in this directory.
34+
pub fn get_entries(&self) -> Vec<String> {
35+
self.children.read().keys().cloned().collect()
36+
}
37+
38+
/// Checks whether a node with the given name exists in this directory.
39+
pub fn exist(&self, name: &str) -> bool {
40+
self.children.read().contains_key(name)
41+
}
42+
43+
/// Creates a new node with the given name and type in this directory.
44+
pub fn create_node(&self, name: &str, ty: VfsNodeType) -> VfsResult {
45+
if self.exist(name) {
46+
log::error!("AlreadyExists {}", name);
47+
return Err(VfsError::AlreadyExists);
48+
}
49+
let node: VfsNodeRef = match ty {
50+
VfsNodeType::File => Arc::new(FileNode::new()),
51+
VfsNodeType::Dir => Self::new(Some(self.this.clone())),
52+
_ => return Err(VfsError::Unsupported),
53+
};
54+
self.children.write().insert(name.into(), node);
55+
Ok(())
56+
}
57+
58+
/// Removes a node by the given name in this directory.
59+
pub fn remove_node(&self, name: &str) -> VfsResult {
60+
let mut children = self.children.write();
61+
let node = children.get(name).ok_or(VfsError::NotFound)?;
62+
if let Some(dir) = node.as_any().downcast_ref::<DirNode>() {
63+
if !dir.children.read().is_empty() {
64+
return Err(VfsError::DirectoryNotEmpty);
65+
}
66+
}
67+
children.remove(name);
68+
Ok(())
69+
}
70+
}
71+
72+
impl VfsNodeOps for DirNode {
73+
fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
74+
Ok(VfsNodeAttr::new_dir(4096, 0))
75+
}
76+
77+
fn parent(&self) -> Option<VfsNodeRef> {
78+
self.parent.read().upgrade()
79+
}
80+
81+
fn lookup(self: Arc<Self>, path: &str) -> VfsResult<VfsNodeRef> {
82+
let (name, rest) = split_path(path);
83+
let node = match name {
84+
"" | "." => Ok(self.clone() as VfsNodeRef),
85+
".." => self.parent().ok_or(VfsError::NotFound),
86+
_ => self
87+
.children
88+
.read()
89+
.get(name)
90+
.cloned()
91+
.ok_or(VfsError::NotFound),
92+
}?;
93+
94+
if let Some(rest) = rest {
95+
node.lookup(rest)
96+
} else {
97+
Ok(node)
98+
}
99+
}
100+
101+
fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult<usize> {
102+
let children = self.children.read();
103+
let mut children = children.iter().skip(start_idx.max(2) - 2);
104+
for (i, ent) in dirents.iter_mut().enumerate() {
105+
match i + start_idx {
106+
0 => *ent = VfsDirEntry::new(".", VfsNodeType::Dir),
107+
1 => *ent = VfsDirEntry::new("..", VfsNodeType::Dir),
108+
_ => {
109+
if let Some((name, node)) = children.next() {
110+
*ent = VfsDirEntry::new(name, node.get_attr().unwrap().file_type());
111+
} else {
112+
return Ok(i);
113+
}
114+
}
115+
}
116+
}
117+
Ok(dirents.len())
118+
}
119+
120+
fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult {
121+
log::debug!("create {:?} at ramfs: {}", ty, path);
122+
let (name, rest) = split_path(path);
123+
if let Some(rest) = rest {
124+
match name {
125+
"" | "." => self.create(rest, ty),
126+
".." => self.parent().ok_or(VfsError::NotFound)?.create(rest, ty),
127+
_ => {
128+
let subdir = self
129+
.children
130+
.read()
131+
.get(name)
132+
.ok_or(VfsError::NotFound)?
133+
.clone();
134+
subdir.create(rest, ty)
135+
}
136+
}
137+
} else if name.is_empty() || name == "." || name == ".." {
138+
Ok(()) // already exists
139+
} else {
140+
self.create_node(name, ty)
141+
}
142+
}
143+
144+
fn remove(&self, path: &str) -> VfsResult {
145+
log::debug!("remove at ramfs: {}", path);
146+
let (name, rest) = split_path(path);
147+
if let Some(rest) = rest {
148+
match name {
149+
"" | "." => self.remove(rest),
150+
".." => self.parent().ok_or(VfsError::NotFound)?.remove(rest),
151+
_ => {
152+
let subdir = self
153+
.children
154+
.read()
155+
.get(name)
156+
.ok_or(VfsError::NotFound)?
157+
.clone();
158+
subdir.remove(rest)
159+
}
160+
}
161+
} else if name.is_empty() || name == "." || name == ".." {
162+
Err(VfsError::InvalidInput) // remove '.' or '..
163+
} else {
164+
self.remove_node(name)
165+
}
166+
}
167+
168+
axfs_vfs::impl_vfs_dir_default! {}
169+
}
170+
171+
fn split_path(path: &str) -> (&str, Option<&str>) {
172+
let trimmed_path = path.trim_start_matches('/');
173+
trimmed_path.find('/').map_or((trimmed_path, None), |n| {
174+
(&trimmed_path[..n], Some(&trimmed_path[n + 1..]))
175+
})
176+
}

crates/axfs_ramfs/src/file.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use alloc::vec::Vec;
2+
use axfs_vfs::{impl_vfs_non_dir_default, VfsNodeAttr, VfsNodeOps, VfsResult};
3+
use spin::RwLock;
4+
5+
/// The file node in the RAM filesystem.
6+
///
7+
/// It implements [`axfs_vfs::VfsNodeOps`].
8+
pub struct FileNode {
9+
content: RwLock<Vec<u8>>,
10+
}
11+
12+
impl FileNode {
13+
pub(super) const fn new() -> Self {
14+
Self {
15+
content: RwLock::new(Vec::new()),
16+
}
17+
}
18+
}
19+
20+
impl VfsNodeOps for FileNode {
21+
fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
22+
Ok(VfsNodeAttr::new_file(self.content.read().len() as _, 0))
23+
}
24+
25+
fn truncate(&self, size: u64) -> VfsResult {
26+
let mut content = self.content.write();
27+
if size < content.len() as u64 {
28+
content.truncate(size as _);
29+
} else {
30+
content.resize(size as _, 0);
31+
}
32+
Ok(())
33+
}
34+
35+
fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult<usize> {
36+
let content = self.content.read();
37+
let start = content.len().min(offset as usize);
38+
let end = content.len().min(offset as usize + buf.len());
39+
let src = &content[start..end];
40+
buf[..src.len()].copy_from_slice(src);
41+
Ok(src.len())
42+
}
43+
44+
fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult<usize> {
45+
let offset = offset as usize;
46+
let mut content = self.content.write();
47+
if offset + buf.len() > content.len() {
48+
content.resize(offset + buf.len(), 0);
49+
}
50+
let dst = &mut content[offset..offset + buf.len()];
51+
dst.copy_from_slice(&buf[..dst.len()]);
52+
Ok(buf.len())
53+
}
54+
55+
impl_vfs_non_dir_default! {}
56+
}

crates/axfs_ramfs/src/lib.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! RAM filesystem used by [ArceOS](https://github.com/rcore-os/arceos).
2+
//!
3+
//! The implementation is based on [`axfs_vfs`].
4+
5+
#![cfg_attr(not(test), no_std)]
6+
7+
extern crate alloc;
8+
9+
mod dir;
10+
mod file;
11+
12+
#[cfg(test)]
13+
mod tests;
14+
15+
pub use self::dir::DirNode;
16+
pub use self::file::FileNode;
17+
18+
use alloc::sync::Arc;
19+
use axfs_vfs::{VfsNodeRef, VfsOps, VfsResult};
20+
use spin::once::Once;
21+
22+
/// A RAM filesystem that implements [`axfs_vfs::VfsOps`].
23+
pub struct RamFileSystem {
24+
parent: Once<VfsNodeRef>,
25+
root: Arc<DirNode>,
26+
}
27+
28+
impl RamFileSystem {
29+
/// Create a new instance.
30+
pub fn new() -> Self {
31+
Self {
32+
parent: Once::new(),
33+
root: DirNode::new(None),
34+
}
35+
}
36+
37+
/// Returns the root directory node in [`Arc<DirNode>`](DirNode).
38+
pub fn root_dir_node(&self) -> Arc<DirNode> {
39+
self.root.clone()
40+
}
41+
}
42+
43+
impl VfsOps for RamFileSystem {
44+
fn mount(&self, _path: &str, mount_point: VfsNodeRef) -> VfsResult {
45+
if let Some(parent) = mount_point.parent() {
46+
self.root.set_parent(Some(self.parent.call_once(|| parent)));
47+
} else {
48+
self.root.set_parent(None);
49+
}
50+
Ok(())
51+
}
52+
53+
fn root_dir(&self) -> VfsNodeRef {
54+
self.root.clone()
55+
}
56+
}
57+
58+
impl Default for RamFileSystem {
59+
fn default() -> Self {
60+
Self::new()
61+
}
62+
}

0 commit comments

Comments
 (0)