1#![no_std]
7
8extern crate alloc;
9
10use alloc::{
11 collections::BTreeMap,
12 string::{String, ToString},
13 sync::Arc,
14 vec::Vec,
15};
16use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
17use spin::{Mutex, RwLock};
18use strate_fs_abstraction::{
19 FsCapabilities, FsError, FsResult, RenameFlags, VfsDirEntry, VfsFileInfo, VfsFileSystem,
20 VfsFileType, VfsTimestamp, VfsVolumeInfo,
21};
22
23enum RamNode {
25 File {
26 data: Vec<u8>,
27 mode: u32,
28 },
29 Directory {
30 entries: BTreeMap<String, u64>, mode: u32,
32 },
33}
34
35struct RamInode {
36 node: Mutex<RamNode>,
37 open_count: AtomicU32,
38}
39
40impl RamInode {
41 fn new(node: RamNode) -> Self {
43 Self {
44 node: Mutex::new(node),
45 open_count: AtomicU32::new(0),
46 }
47 }
48}
49
50pub struct RamFileSystem {
51 inodes: RwLock<BTreeMap<u64, Arc<RamInode>>>,
52 next_inode: AtomicU64,
53 capabilities: FsCapabilities,
54}
55
56impl RamFileSystem {
57 pub fn new() -> Self {
59 let root = Arc::new(RamInode::new(RamNode::Directory {
60 entries: BTreeMap::new(),
61 mode: 0o040755,
62 }));
63 let mut inodes = BTreeMap::new();
64 inodes.insert(2, root); Self {
67 inodes: RwLock::new(inodes),
68 next_inode: AtomicU64::new(10), capabilities: FsCapabilities::writable_linux(),
70 }
71 }
72
73 fn get_node(&self, ino: u64) -> FsResult<Arc<RamInode>> {
75 self.inodes
76 .read()
77 .get(&ino)
78 .cloned()
79 .ok_or(FsError::InodeNotFound)
80 }
81
82 fn allocate_inode(&self, node: RamNode) -> u64 {
84 let id = self.next_inode.fetch_add(1, Ordering::SeqCst);
85 self.inodes
86 .write()
87 .insert(id, Arc::new(RamInode::new(node)));
88 id
89 }
90
91 fn lookup_child_inode(&self, parent_ino: u64, name: &str) -> FsResult<u64> {
93 let parent = self.get_node(parent_ino)?;
94 let guard = parent.node.lock();
95 match &*guard {
96 RamNode::Directory { entries, .. } => {
97 entries.get(name).copied().ok_or(FsError::NotFound)
98 }
99 _ => Err(FsError::NotADirectory),
100 }
101 }
102
103 pub fn resolve_path_internal(&self, path: &str) -> FsResult<u64> {
105 let mut current_ino = self.root_inode();
106 for part in path.split('/').filter(|s| !s.is_empty()) {
107 current_ino = self.lookup_child_inode(current_ino, part)?;
108 }
109 Ok(current_ino)
110 }
111
112 fn to_file_mode(mode: u32) -> u32 {
114 0o100000 | (mode & 0o7777)
115 }
116
117 fn to_dir_mode(mode: u32) -> u32 {
119 0o040000 | (mode & 0o7777)
120 }
121
122 pub fn register_open(&self, ino: u64) -> FsResult<()> {
124 let inode = self.get_node(ino)?;
125 let mut current = inode.open_count.load(Ordering::Acquire);
126 loop {
127 if current == u32::MAX {
128 return Err(FsError::TooManyOpenFiles);
129 }
130 match inode.open_count.compare_exchange_weak(
131 current,
132 current + 1,
133 Ordering::AcqRel,
134 Ordering::Acquire,
135 ) {
136 Ok(_) => return Ok(()),
137 Err(actual) => current = actual,
138 }
139 }
140 }
141
142 pub fn unregister_open(&self, ino: u64) -> FsResult<()> {
144 let inode = self.get_node(ino)?;
145 let mut current = inode.open_count.load(Ordering::Acquire);
146 let removed_last = loop {
147 if current == 0 {
148 return Err(FsError::InodeNotFound);
149 }
150 let next = current - 1;
151 match inode.open_count.compare_exchange_weak(
152 current,
153 next,
154 Ordering::AcqRel,
155 Ordering::Acquire,
156 ) {
157 Ok(_) => break next == 0,
158 Err(actual) => current = actual,
159 }
160 };
161
162 if removed_last {
163 self.collect_if_detached(ino)?;
164 }
165 Ok(())
166 }
167
168 fn is_open(&self, ino: u64) -> bool {
170 self.get_node(ino)
171 .map(|inode| inode.open_count.load(Ordering::Acquire) > 0)
172 .unwrap_or(false)
173 }
174
175 fn directory_contains_inode(&self, root_dir_ino: u64, target_ino: u64) -> FsResult<bool> {
177 let mut stack = Vec::new();
178 stack.push(root_dir_ino);
179
180 while let Some(current) = stack.pop() {
181 if current == target_ino {
182 return Ok(true);
183 }
184
185 let node = self.get_node(current)?;
186 let guard = node.node.lock();
187 if let RamNode::Directory { entries, .. } = &*guard {
188 for &child in entries.values() {
189 stack.push(child);
190 }
191 }
192 }
193
194 Ok(false)
195 }
196
197 fn collect_if_detached(&self, ino: u64) -> FsResult<()> {
199 if ino == self.root_inode() || self.is_open(ino) {
200 return Ok(());
201 }
202 if self.directory_contains_inode(self.root_inode(), ino)? {
203 return Ok(());
204 }
205 self.collect_detached_subtree(ino)
206 }
207
208 fn collect_detached_subtree(&self, ino: u64) -> FsResult<()> {
210 if self.is_open(ino) {
211 return Ok(());
212 }
213
214 let children = match self.get_node(ino) {
215 Ok(node) => {
216 let guard = node.node.lock();
217 match &*guard {
218 RamNode::Directory { entries, .. } => {
219 let mut out = Vec::new();
220 for &child in entries.values() {
221 out.push(child);
222 }
223 out
224 }
225 RamNode::File { .. } => Vec::new(),
226 }
227 }
228 Err(_) => return Ok(()),
229 };
230
231 for child in children {
232 self.collect_detached_subtree(child)?;
233 }
234
235 if !self.is_open(ino) {
236 self.inodes.write().remove(&ino);
237 }
238 Ok(())
239 }
240}
241
242impl VfsFileSystem for RamFileSystem {
243 fn fs_type(&self) -> &'static str {
245 "ramfs"
246 }
247
248 fn capabilities(&self) -> &FsCapabilities {
250 &self.capabilities
251 }
252
253 fn root_inode(&self) -> u64 {
255 2
256 }
257
258 fn get_volume_info(&self) -> FsResult<VfsVolumeInfo> {
260 Ok(VfsVolumeInfo {
261 fs_type: String::from("ramfs"),
262 block_size: 4096,
263 ..VfsVolumeInfo::default()
264 })
265 }
266
267 fn stat(&self, ino: u64) -> FsResult<VfsFileInfo> {
269 let node = self.get_node(ino)?;
270 let guard = node.node.lock();
271 let mut info = VfsFileInfo::default();
272 info.ino = ino;
273 match &*guard {
274 RamNode::File { data, mode } => {
275 info.size = data.len() as u64;
276 info.file_type = VfsFileType::RegularFile;
277 info.mode = *mode;
278 }
279 RamNode::Directory { mode, .. } => {
280 info.size = 0;
281 info.file_type = VfsFileType::Directory;
282 info.mode = *mode;
283 }
284 }
285 Ok(info)
286 }
287
288 fn lookup(&self, parent_ino: u64, name: &str) -> FsResult<VfsFileInfo> {
290 let ino = self.lookup_child_inode(parent_ino, name)?;
291 self.stat(ino)
292 }
293
294 fn resolve_path(&self, path: &str) -> FsResult<u64> {
296 self.resolve_path_internal(path)
297 }
298
299 fn read(&self, ino: u64, offset: u64, buf: &mut [u8]) -> FsResult<usize> {
301 let node = self.get_node(ino)?;
302 let guard = node.node.lock();
303 match &*guard {
304 RamNode::File { data, .. } => {
305 let start = usize::try_from(offset).map_err(|_| FsError::InvalidArgument)?;
306 if offset >= data.len() as u64 {
307 return Ok(0);
308 }
309 let end = start
310 .checked_add(buf.len())
311 .ok_or(FsError::FileTooLarge)?
312 .min(data.len());
313 let count = end - start;
314 buf[..count].copy_from_slice(&data[start..end]);
315 Ok(count)
316 }
317 _ => Err(FsError::IsADirectory),
318 }
319 }
320
321 fn write(&self, ino: u64, offset: u64, data: &[u8]) -> FsResult<usize> {
323 let node = self.get_node(ino)?;
324 let mut guard = node.node.lock();
325 match &mut *guard {
326 RamNode::File {
327 data: file_data, ..
328 } => {
329 let start = usize::try_from(offset).map_err(|_| FsError::InvalidArgument)?;
330 let end = start.checked_add(data.len()).ok_or(FsError::FileTooLarge)?;
331 if end > file_data.len() {
332 file_data.resize(end, 0);
333 }
334 file_data[start..end].copy_from_slice(data);
335 Ok(data.len())
336 }
337 _ => Err(FsError::IsADirectory),
338 }
339 }
340
341 fn readdir(&self, ino: u64) -> FsResult<Vec<VfsDirEntry>> {
343 let node = self.get_node(ino)?;
344 let guard = node.node.lock();
345 match &*guard {
346 RamNode::Directory { entries, .. } => {
347 let children: Vec<(String, u64)> = entries
348 .iter()
349 .map(|(name, &ino)| (name.clone(), ino))
350 .collect();
351 drop(guard);
352
353 let mut result = Vec::new();
354 for (name, child_ino) in children {
355 let child_node = self.get_node(child_ino)?;
356 let child_guard = child_node.node.lock();
357 let file_type = match &*child_guard {
358 RamNode::File { .. } => VfsFileType::RegularFile,
359 RamNode::Directory { .. } => VfsFileType::Directory,
360 };
361 result.push(VfsDirEntry {
362 name,
363 ino: child_ino,
364 file_type,
365 offset: 0,
366 });
367 }
368 Ok(result)
369 }
370 _ => Err(FsError::NotADirectory),
371 }
372 }
373
374 fn create_file(&self, parent_ino: u64, name: &str, mode: u32) -> FsResult<VfsFileInfo> {
376 let parent = self.get_node(parent_ino)?;
377 {
378 let guard = parent.node.lock();
379 match &*guard {
380 RamNode::Directory { entries, .. } => {
381 if entries.contains_key(name) {
382 return Err(FsError::AlreadyExists);
383 }
384 }
385 _ => return Err(FsError::NotADirectory),
386 }
387 }
388
389 let new_ino = self.allocate_inode(RamNode::File {
390 data: Vec::new(),
391 mode: Self::to_file_mode(mode),
392 });
393
394 let mut guard = parent.node.lock();
395 match &mut *guard {
396 RamNode::Directory { entries, .. } => {
397 if entries.contains_key(name) {
398 self.inodes.write().remove(&new_ino);
399 return Err(FsError::AlreadyExists);
400 }
401 entries.insert(name.to_string(), new_ino);
402 drop(guard);
403 self.stat(new_ino)
404 }
405 _ => {
406 self.inodes.write().remove(&new_ino);
407 Err(FsError::NotADirectory)
408 }
409 }
410 }
411
412 fn create_directory(&self, parent_ino: u64, name: &str, mode: u32) -> FsResult<VfsFileInfo> {
414 let parent = self.get_node(parent_ino)?;
415 {
416 let guard = parent.node.lock();
417 match &*guard {
418 RamNode::Directory { entries, .. } => {
419 if entries.contains_key(name) {
420 return Err(FsError::AlreadyExists);
421 }
422 }
423 _ => return Err(FsError::NotADirectory),
424 }
425 }
426
427 let new_ino = self.allocate_inode(RamNode::Directory {
428 entries: BTreeMap::new(),
429 mode: Self::to_dir_mode(mode),
430 });
431
432 let mut guard = parent.node.lock();
433 match &mut *guard {
434 RamNode::Directory { entries, .. } => {
435 if entries.contains_key(name) {
436 self.inodes.write().remove(&new_ino);
437 return Err(FsError::AlreadyExists);
438 }
439 entries.insert(name.to_string(), new_ino);
440 drop(guard);
441 self.stat(new_ino)
442 }
443 _ => {
444 self.inodes.write().remove(&new_ino);
445 Err(FsError::NotADirectory)
446 }
447 }
448 }
449
450 fn unlink(&self, parent_ino: u64, name: &str, target_ino: u64) -> FsResult<()> {
452 let parent = self.get_node(parent_ino)?;
453 let child_ino = {
454 let guard = parent.node.lock();
455 match &*guard {
456 RamNode::Directory { entries, .. } => {
457 *entries.get(name).ok_or(FsError::NotFound)?
458 }
459 _ => return Err(FsError::NotADirectory),
460 }
461 };
462
463 if child_ino != target_ino {
464 return Err(FsError::InvalidArgument);
465 }
466
467 let node = self.get_node(child_ino)?;
468 {
469 let node_guard = node.node.lock();
470 if let RamNode::Directory {
471 entries: child_entries,
472 ..
473 } = &*node_guard
474 {
475 if !child_entries.is_empty() {
476 return Err(FsError::NotEmpty);
477 }
478 }
479 }
480
481 let mut guard = parent.node.lock();
482 match &mut *guard {
483 RamNode::Directory { entries, .. } => {
484 let current = *entries.get(name).ok_or(FsError::NotFound)?;
485 if current != child_ino {
486 return Err(FsError::InvalidArgument);
487 }
488 entries.remove(name);
489 drop(guard);
490 self.collect_if_detached(child_ino)?;
491 Ok(())
492 }
493 _ => Err(FsError::NotADirectory),
494 }
495 }
496
497 fn rename(
499 &self,
500 old_parent: u64,
501 old_name: &str,
502 new_parent: u64,
503 new_name: &str,
504 flags: RenameFlags,
505 ) -> FsResult<()> {
506 if old_parent == new_parent && old_name == new_name {
507 return Ok(());
508 }
509 if flags.exchange {
510 return Err(FsError::NotSupported);
511 }
512 if flags.no_replace && flags.replace_if_exists {
513 return Err(FsError::InvalidArgument);
514 }
515
516 let old_parent_node = self.get_node(old_parent)?;
517 let moved_ino = {
518 let guard = old_parent_node.node.lock();
519 match &*guard {
520 RamNode::Directory { entries, .. } => {
521 *entries.get(old_name).ok_or(FsError::NotFound)?
522 }
523 _ => return Err(FsError::NotADirectory),
524 }
525 };
526
527 {
528 let moved_node = self.get_node(moved_ino)?;
529 let moved_guard = moved_node.node.lock();
530 if let RamNode::Directory { .. } = &*moved_guard {
531 if self.directory_contains_inode(moved_ino, new_parent)? {
532 return Err(FsError::InvalidArgument);
533 }
534 }
535 }
536
537 let new_parent_node = self.get_node(new_parent)?;
538 let mut new_guard = new_parent_node.node.lock();
539 let replaced_ino = match &mut *new_guard {
540 RamNode::Directory { entries, .. } => {
541 if let Some(&existing) = entries.get(new_name) {
542 if flags.no_replace {
543 return Err(FsError::AlreadyExists);
544 }
545 if !flags.replace_if_exists && existing != moved_ino {
546 return Err(FsError::AlreadyExists);
547 }
548 Some(existing)
549 } else {
550 None
551 }
552 }
553 _ => return Err(FsError::NotADirectory),
554 };
555 drop(new_guard);
556
557 if let Some(existing_ino) = replaced_ino {
558 let existing_node = self.get_node(existing_ino)?;
559 let existing_guard = existing_node.node.lock();
560 if let RamNode::Directory {
561 entries: child_entries,
562 ..
563 } = &*existing_guard
564 {
565 if !child_entries.is_empty() {
566 return Err(FsError::NotEmpty);
567 }
568 }
569 }
570
571 {
572 let mut guard = old_parent_node.node.lock();
573 match &mut *guard {
574 RamNode::Directory { entries, .. } => {
575 let current = *entries.get(old_name).ok_or(FsError::NotFound)?;
576 if current != moved_ino {
577 return Err(FsError::InvalidArgument);
578 }
579 entries.remove(old_name);
580 }
581 _ => return Err(FsError::NotADirectory),
582 }
583 }
584
585 let mut guard = new_parent_node.node.lock();
586 match &mut *guard {
587 RamNode::Directory { entries, .. } => {
588 let mut replaced_for_gc: Option<u64> = None;
589 if let Some(existing_ino) = entries.insert(new_name.to_string(), moved_ino) {
590 if existing_ino != moved_ino {
591 replaced_for_gc = Some(existing_ino);
592 }
593 }
594 drop(guard);
595 if let Some(existing_ino) = replaced_for_gc {
596 self.collect_if_detached(existing_ino)?;
597 }
598 Ok(())
599 }
600 _ => Err(FsError::NotADirectory),
601 }
602 }
603
604 fn set_size(&self, ino: u64, size: u64) -> FsResult<()> {
606 let node = self.get_node(ino)?;
607 let mut guard = node.node.lock();
608 match &mut *guard {
609 RamNode::File { data, .. } => {
610 let new_size = usize::try_from(size).map_err(|_| FsError::FileTooLarge)?;
611 data.resize(new_size, 0);
612 Ok(())
613 }
614 _ => Err(FsError::IsADirectory),
615 }
616 }
617
618 fn set_times(
620 &self,
621 ino: u64,
622 _atime: Option<VfsTimestamp>,
623 _mtime: Option<VfsTimestamp>,
624 ) -> FsResult<()> {
625 let _ = self.get_node(ino)?;
626 Ok(())
627 }
628
629 fn readlink(&self, _ino: u64) -> FsResult<String> {
631 Err(FsError::NotSupported)
632 }
633
634 fn invalidate_inode(&self, _ino: u64) {}
636 fn invalidate_all_caches(&self) {}
638}
639
640pub fn split_path(path: &str) -> (&str, &str) {
642 let path = path.trim_end_matches('/');
643 if let Some(idx) = path.rfind('/') {
644 if idx == 0 {
645 ("/", &path[1..])
646 } else {
647 (&path[..idx], &path[idx + 1..])
648 }
649 } else {
650 ("/", path)
651 }
652}