1#[cfg(not(feature = "std"))]
19use alloc::collections::BTreeMap;
20
21#[cfg(feature = "thread-safe")]
22use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
23#[cfg(feature = "thread-safe")]
24use std::sync::{Arc, RwLock};
25
26#[cfg(feature = "thread-safe")]
27use dashmap::DashMap;
28
29#[cfg(all(feature = "std", not(feature = "thread-safe")))]
30use std::cell::{Cell, RefCell};
31#[cfg(all(feature = "std", not(feature = "thread-safe")))]
32use std::collections::HashMap;
33#[cfg(all(feature = "std", not(feature = "thread-safe")))]
34use std::rc::Rc;
35
36#[cfg(not(feature = "std"))]
37use alloc::rc::Rc;
38#[cfg(not(feature = "std"))]
39use core::cell::{Cell, RefCell};
40
41use crate::error::FsError;
42use crate::handle::FileHandle;
43use crate::inode::{FileContent, Inode, Metadata};
44use crate::time::{MonotonicCounter, TimeProvider};
45use crate::types::*;
46
47#[cfg(feature = "logging")]
49use crate::{debug, error, trace};
50
51#[cfg(feature = "thread-safe")]
60pub type InodeRef = Arc<RwLock<Inode>>;
61#[cfg(not(feature = "thread-safe"))]
62pub type InodeRef = Rc<RefCell<Inode>>;
63
64#[cfg(feature = "thread-safe")]
67type FdTable = DashMap<Fd, FileHandle>;
68#[cfg(all(feature = "std", not(feature = "thread-safe")))]
69type FdTable = RefCell<HashMap<Fd, FileHandle>>;
70#[cfg(not(feature = "std"))]
71type FdTable = RefCell<BTreeMap<Fd, FileHandle>>;
72
73#[cfg(feature = "thread-safe")]
75type InodeTable = DashMap<InodeId, InodeRef>;
76#[cfg(all(feature = "std", not(feature = "thread-safe")))]
77type InodeTable = RefCell<HashMap<InodeId, InodeRef>>;
78#[cfg(not(feature = "std"))]
79type InodeTable = RefCell<BTreeMap<InodeId, InodeRef>>;
80
81#[cfg(feature = "thread-safe")]
83type NextInodeCounter = AtomicU64;
84#[cfg(not(feature = "thread-safe"))]
85type NextInodeCounter = Cell<InodeId>;
86
87#[cfg(feature = "thread-safe")]
89type NextFdCounter = AtomicU32;
90#[cfg(not(feature = "thread-safe"))]
91type NextFdCounter = Cell<Fd>;
92
93macro_rules! inode_read {
99 ($inode:expr) => {{
100 #[cfg(feature = "thread-safe")]
101 {
102 $inode.read().unwrap()
103 }
104 #[cfg(not(feature = "thread-safe"))]
105 {
106 $inode.borrow()
107 }
108 }};
109}
110
111macro_rules! inode_write {
113 ($inode:expr) => {{
114 #[cfg(feature = "thread-safe")]
115 {
116 $inode.write().unwrap()
117 }
118 #[cfg(not(feature = "thread-safe"))]
119 {
120 $inode.borrow_mut()
121 }
122 }};
123}
124
125pub struct Fs<T: TimeProvider = MonotonicCounter> {
132 pub(crate) next_inode: NextInodeCounter,
133 pub(crate) next_fd: NextFdCounter,
134 pub(crate) fd_table: FdTable,
135 pub(crate) inode_table: InodeTable,
136 pub(crate) root_inode: InodeId,
137 pub(crate) time_provider: T,
138}
139
140impl<T: TimeProvider> Fs<T> {
141 pub(crate) fn new_inode_ref(inode: Inode) -> InodeRef {
147 #[cfg(feature = "thread-safe")]
148 {
149 Arc::new(RwLock::new(inode))
150 }
151 #[cfg(not(feature = "thread-safe"))]
152 {
153 Rc::new(RefCell::new(inode))
154 }
155 }
156
157 pub fn with_time_provider(time_provider: T) -> Self {
161 let timestamp = time_provider.now();
162 let mut root_inode = Inode::new_dir(0);
163 root_inode.metadata.created = timestamp;
164 root_inode.metadata.modified = timestamp;
165
166 let fs = Self::empty_with(time_provider, 0, 1);
167 fs.inode_insert(0, Self::new_inode_ref(root_inode));
168 fs
169 }
170
171 pub(crate) fn empty_with(
175 time_provider: T,
176 root_inode: InodeId,
177 next_inode_seed: InodeId,
178 ) -> Self {
179 Self {
180 next_inode: new_inode_counter(next_inode_seed),
181 next_fd: new_fd_counter(3), fd_table: new_fd_table(),
183 inode_table: new_inode_table(),
184 root_inode,
185 time_provider,
186 }
187 }
188
189 fn allocate_inode(&self) -> InodeId {
190 #[cfg(feature = "thread-safe")]
191 {
192 self.next_inode.fetch_add(1, Ordering::Relaxed) as InodeId
193 }
194 #[cfg(not(feature = "thread-safe"))]
195 {
196 let id = self.next_inode.get();
197 self.next_inode.set(id + 1);
198 id
199 }
200 }
201
202 fn allocate_fd(&self) -> Fd {
203 #[cfg(feature = "thread-safe")]
204 {
205 self.next_fd.fetch_add(1, Ordering::Relaxed)
206 }
207 #[cfg(not(feature = "thread-safe"))]
208 {
209 let fd = self.next_fd.get();
210 self.next_fd.set(fd + 1);
211 fd
212 }
213 }
214
215 fn fd_with<R>(&self, fd: Fd, f: impl FnOnce(&FileHandle) -> R) -> Result<R, FsError> {
227 #[cfg(feature = "thread-safe")]
228 {
229 let handle = self.fd_table.get(&fd).ok_or(FsError::BadFileDescriptor)?;
230 Ok(f(&handle))
231 }
232 #[cfg(not(feature = "thread-safe"))]
233 {
234 let map = self.fd_table.borrow();
235 let handle = map.get(&fd).ok_or(FsError::BadFileDescriptor)?;
236 Ok(f(handle))
237 }
238 }
239
240 fn fd_try_with<R>(&self, fd: Fd, f: impl FnOnce(&FileHandle) -> R) -> Option<R> {
243 #[cfg(feature = "thread-safe")]
244 {
245 self.fd_table.get(&fd).map(|h| f(&h))
246 }
247 #[cfg(not(feature = "thread-safe"))]
248 {
249 self.fd_table.borrow().get(&fd).map(f)
250 }
251 }
252
253 fn fd_insert(&self, fd: Fd, handle: FileHandle) {
254 #[cfg(feature = "thread-safe")]
255 {
256 self.fd_table.insert(fd, handle);
257 }
258 #[cfg(not(feature = "thread-safe"))]
259 {
260 self.fd_table.borrow_mut().insert(fd, handle);
261 }
262 }
263
264 fn fd_remove(&self, fd: Fd) -> Option<FileHandle> {
265 #[cfg(feature = "thread-safe")]
266 {
267 self.fd_table.remove(&fd).map(|(_, v)| v)
268 }
269 #[cfg(not(feature = "thread-safe"))]
270 {
271 self.fd_table.borrow_mut().remove(&fd)
272 }
273 }
274
275 pub(crate) fn inode_get(&self, id: InodeId) -> Option<InodeRef> {
283 #[cfg(feature = "thread-safe")]
284 {
285 self.inode_table.get(&id).map(|r| r.clone())
286 }
287 #[cfg(not(feature = "thread-safe"))]
288 {
289 self.inode_table.borrow().get(&id).cloned()
290 }
291 }
292
293 pub(crate) fn inode_insert(&self, id: InodeId, inode: InodeRef) {
294 #[cfg(feature = "thread-safe")]
295 {
296 self.inode_table.insert(id, inode);
297 }
298 #[cfg(not(feature = "thread-safe"))]
299 {
300 self.inode_table.borrow_mut().insert(id, inode);
301 }
302 }
303
304 pub(crate) fn inode_for_each<F>(&self, mut f: F)
307 where
308 F: FnMut(InodeId, &InodeRef),
309 {
310 #[cfg(feature = "thread-safe")]
311 {
312 for entry in self.inode_table.iter() {
313 f(*entry.key(), entry.value());
314 }
315 }
316 #[cfg(not(feature = "thread-safe"))]
317 {
318 for (&id, inode_rc) in self.inode_table.borrow().iter() {
319 f(id, inode_rc);
320 }
321 }
322 }
323
324 pub(crate) fn next_inode_value(&self) -> InodeId {
326 #[cfg(feature = "thread-safe")]
327 {
328 self.next_inode.load(Ordering::Relaxed)
329 }
330 #[cfg(not(feature = "thread-safe"))]
331 {
332 self.next_inode.get()
333 }
334 }
335
336 fn find_inode(&self, parent_inode: &InodeRef, name: &str) -> Result<InodeRef, FsError> {
341 let parent = inode_read!(parent_inode);
342
343 match &parent.content {
344 FileContent::Dir(entries) => {
345 if let Some(&inode_id) = entries.get(name) {
346 self.inode_get(inode_id).ok_or(FsError::NotFound)
347 } else {
348 Err(FsError::NotFound)
349 }
350 }
351 FileContent::File(_) => Err(FsError::NotADirectory),
352 }
353 }
354
355 fn create_inode(
356 &self,
357 parent_inode: &InodeRef,
358 name: &str,
359 is_dir: bool,
360 ) -> Result<InodeRef, FsError> {
361 let mut parent = inode_write!(parent_inode);
362
363 match &mut parent.content {
364 FileContent::Dir(entries) => {
365 if entries.contains_key(name) {
366 return Err(FsError::AlreadyExists);
367 }
368
369 let new_inode_id = self.allocate_inode();
370 let timestamp = self.time_provider.now();
371 let mut new_inode = if is_dir {
372 Inode::new_dir(new_inode_id)
373 } else {
374 Inode::new_file(new_inode_id)
375 };
376
377 new_inode.metadata.created = timestamp;
378 new_inode.metadata.modified = timestamp;
379
380 let new_inode_ref = Self::new_inode_ref(new_inode);
381 entries.insert(name.into(), new_inode_id);
382 self.inode_insert(new_inode_id, new_inode_ref.clone());
383 parent.metadata.modified = timestamp;
384
385 Ok(new_inode_ref)
386 }
387 FileContent::File(_) => Err(FsError::NotADirectory),
388 }
389 }
390
391 pub fn open_path(&self, path: &str) -> Result<Fd, FsError> {
396 self.open_path_with_flags(path, O_RDWR | O_CREAT)
397 }
398
399 pub fn open_at(&self, dir_fd: Fd, path: &str, flags: u32) -> Result<Fd, FsError> {
400 debug!(
401 "open_at: dir_fd={}, path={}, flags={:#x}",
402 dir_fd, path, flags
403 );
404
405 if path.starts_with('/') {
406 error!("open_at: absolute path not allowed");
407 return Err(FsError::InvalidArgument);
408 }
409
410 let dir_inode_id = self.fd_with(dir_fd, |h| h.inode_id).map_err(|_| {
411 error!("open_at: bad directory file descriptor {}", dir_fd);
412 FsError::BadFileDescriptor
413 })?;
414
415 let dir_inode = self.inode_get(dir_inode_id).ok_or(FsError::NotFound)?;
416
417 {
419 let inode = inode_read!(dir_inode);
420 if matches!(inode.content, FileContent::File(_)) {
421 error!("open_at: dir_fd {} is not a directory", dir_fd);
422 return Err(FsError::NotADirectory);
423 }
424 }
425
426 if path.is_empty() {
427 error!("open_at: empty path not supported yet");
428 return Err(FsError::InvalidArgument);
429 }
430
431 let comps: alloc::vec::Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
432
433 if comps.is_empty() {
434 return Err(FsError::InvalidArgument);
435 }
436
437 let mut current_inode = dir_inode;
438
439 for comp in comps.iter().take(comps.len() - 1) {
440 current_inode = self.find_inode(¤t_inode, comp)?;
441 let inode = inode_read!(current_inode);
442 if matches!(inode.content, FileContent::File(_)) {
443 return Err(FsError::NotADirectory);
444 }
445 }
446
447 let filename = comps[comps.len() - 1];
448 let file_inode = match self.find_inode(¤t_inode, filename) {
449 Ok(inode) => inode,
450 Err(FsError::NotFound) if flags & O_CREAT != 0 => {
451 self.create_inode(¤t_inode, filename, false)?
452 }
453 Err(e) => return Err(e),
454 };
455
456 {
458 let inode = inode_read!(file_inode);
459 if matches!(inode.content, FileContent::Dir(_)) {
460 let access_mode = flags & 0x3;
461 if access_mode == O_WRONLY || access_mode == O_RDWR {
462 return Err(FsError::IsADirectory);
463 }
464 if flags & O_TRUNC != 0 {
465 return Err(FsError::IsADirectory);
466 }
467 }
468 }
469
470 if flags & O_TRUNC != 0 {
472 let access_mode = flags & 0x3;
473 if access_mode == O_RDONLY {
474 return Err(FsError::InvalidArgument);
475 }
476 let mut inode = inode_write!(file_inode);
477 if let FileContent::File(storage) = &mut inode.content {
478 storage.truncate(0);
479 inode.metadata.size = 0;
480 inode.metadata.modified = self.time_provider.now();
481 }
482 }
483
484 let inode_id = inode_read!(file_inode).id;
485 let handle = FileHandle::new(inode_id, 0, flags);
486
487 let fd = self.allocate_fd();
488 self.fd_insert(fd, handle);
489 debug!("open_at: allocated fd={} for inode={}", fd, inode_id);
490 Ok(fd)
491 }
492
493 pub fn open_path_with_flags(&self, path: &str, flags: u32) -> Result<Fd, FsError> {
494 debug!("open_path_with_flags: path={}, flags={:#x}", path, flags);
495
496 if path.is_empty() {
497 error!("open_path_with_flags: empty path");
498 return Err(FsError::InvalidArgument);
499 }
500
501 let comps: alloc::vec::Vec<&str> = path
502 .trim_start_matches('/')
503 .split('/')
504 .filter(|s| !s.is_empty())
505 .collect();
506
507 if comps.is_empty() {
509 debug!("open_path_with_flags: opening root directory");
510 let handle = FileHandle::new(self.root_inode, 0, flags);
511 let fd = self.allocate_fd();
512 self.fd_insert(fd, handle);
513 debug!("open_path_with_flags: allocated fd={} for root", fd);
514 return Ok(fd);
515 }
516
517 let root_inode = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
518 let mut current_inode = root_inode;
519
520 for comp in comps.iter().take(comps.len() - 1) {
521 current_inode = self.find_inode(¤t_inode, comp)?;
522 let inode = inode_read!(current_inode);
523 if matches!(inode.content, FileContent::File(_)) {
524 return Err(FsError::NotADirectory);
525 }
526 }
527
528 let filename = comps[comps.len() - 1];
529 let file_inode = match self.find_inode(¤t_inode, filename) {
530 Ok(inode) => inode,
531 Err(FsError::NotFound) if flags & O_CREAT != 0 => {
532 self.create_inode(¤t_inode, filename, false)?
533 }
534 Err(e) => return Err(e),
535 };
536
537 {
538 let inode = inode_read!(file_inode);
539 if matches!(inode.content, FileContent::Dir(_)) {
540 let access_mode = flags & 0x3;
541 if access_mode == O_WRONLY || access_mode == O_RDWR {
542 return Err(FsError::IsADirectory);
543 }
544 if flags & O_TRUNC != 0 {
545 return Err(FsError::IsADirectory);
546 }
547 }
548 }
549
550 if flags & O_TRUNC != 0 {
551 let access_mode = flags & 0x3;
552 if access_mode == O_RDONLY {
553 return Err(FsError::InvalidArgument);
554 }
555 let mut inode = inode_write!(file_inode);
556 if let FileContent::File(storage) = &mut inode.content {
557 storage.truncate(0);
558 inode.metadata.size = 0;
559 inode.metadata.modified = self.time_provider.now();
560 }
561 }
562
563 let inode_id = inode_read!(file_inode).id;
564 let handle = FileHandle::new(inode_id, 0, flags);
565
566 let fd = self.allocate_fd();
567 self.fd_insert(fd, handle);
568 debug!(
569 "open_path_with_flags: allocated fd={} for inode={}",
570 fd, inode_id
571 );
572 Ok(fd)
573 }
574
575 pub fn write(&self, fd: Fd, buf: &[u8]) -> Result<usize, FsError> {
576 trace!("write: fd={}, len={}", fd, buf.len());
577
578 let (inode_id, flags, position) = self
579 .fd_with(fd, |h| (h.inode_id, h.flags, h.get_position()))
580 .map_err(|_| {
581 error!("write: bad file descriptor {}", fd);
582 FsError::BadFileDescriptor
583 })?;
584
585 let access_mode = flags & 0x3;
586 if access_mode == O_RDONLY {
587 return Err(FsError::PermissionDenied);
588 }
589
590 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
591 let mut inode_ref = inode_write!(inode);
592
593 match &mut inode_ref.content {
594 FileContent::File(storage) => {
595 let pos = if flags & O_APPEND != 0 {
597 storage.size()
598 } else {
599 position as usize
600 };
601
602 let n = storage.write(pos, buf);
603 inode_ref.metadata.size = storage.size() as u64;
604 inode_ref.metadata.modified = self.time_provider.now();
605
606 drop(inode_ref);
608 self.fd_try_with(fd, |h| h.set_position((pos + n) as u64));
609
610 debug!("write: fd={}, wrote {} bytes at pos {}", fd, n, pos);
611 Ok(n)
612 }
613 FileContent::Dir(_) => {
614 error!("write: fd={} is a directory", fd);
615 Err(FsError::BadFileDescriptor)
616 }
617 }
618 }
619
620 pub fn write_at(&self, fd: Fd, offset: u64, buf: &[u8]) -> Result<usize, FsError> {
623 trace!("write_at: fd={}, offset={}, len={}", fd, offset, buf.len());
624
625 let (inode_id, flags) = self.fd_with(fd, |h| (h.inode_id, h.flags)).map_err(|_| {
626 error!("write_at: bad file descriptor {}", fd);
627 FsError::BadFileDescriptor
628 })?;
629
630 let access_mode = flags & 0x3;
631 if access_mode == O_RDONLY {
632 return Err(FsError::PermissionDenied);
633 }
634
635 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
636 let mut inode_ref = inode_write!(inode);
637
638 match &mut inode_ref.content {
639 FileContent::File(storage) => {
640 let n = storage.write(offset as usize, buf);
641 inode_ref.metadata.size = storage.size() as u64;
642 inode_ref.metadata.modified = self.time_provider.now();
643
644 drop(inode_ref);
645 self.fd_try_with(fd, |h| h.set_position(offset + n as u64));
646
647 debug!(
648 "write_at: fd={}, wrote {} bytes at offset {}",
649 fd, n, offset
650 );
651 Ok(n)
652 }
653 FileContent::Dir(_) => {
654 error!("write_at: fd={} is a directory", fd);
655 Err(FsError::BadFileDescriptor)
656 }
657 }
658 }
659
660 pub fn append_write(&self, fd: Fd, buf: &[u8]) -> Result<usize, FsError> {
662 trace!("append_write: fd={}, len={}", fd, buf.len());
663
664 let (inode_id, flags) = self.fd_with(fd, |h| (h.inode_id, h.flags)).map_err(|_| {
665 error!("append_write: bad file descriptor {}", fd);
666 FsError::BadFileDescriptor
667 })?;
668
669 let access_mode = flags & 0x3;
670 if access_mode == O_RDONLY {
671 return Err(FsError::PermissionDenied);
672 }
673
674 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
675 let mut inode_ref = inode_write!(inode);
676
677 match &mut inode_ref.content {
678 FileContent::File(storage) => {
679 let pos = storage.size();
680 let n = storage.write(pos, buf);
681 inode_ref.metadata.size = storage.size() as u64;
682 inode_ref.metadata.modified = self.time_provider.now();
683
684 drop(inode_ref);
685 self.fd_try_with(fd, |h| h.set_position((pos + n) as u64));
686
687 debug!(
688 "append_write: fd={}, appended {} bytes at pos {}",
689 fd, n, pos
690 );
691 Ok(n)
692 }
693 FileContent::Dir(_) => {
694 error!("append_write: fd={} is a directory", fd);
695 Err(FsError::BadFileDescriptor)
696 }
697 }
698 }
699
700 pub fn read(&self, fd: Fd, out: &mut [u8]) -> Result<usize, FsError> {
701 trace!("read: fd={}, buf_len={}", fd, out.len());
702
703 let (inode_id, flags, position) = self
704 .fd_with(fd, |h| (h.inode_id, h.flags, h.get_position()))
705 .map_err(|_| {
706 error!("read: bad file descriptor {}", fd);
707 FsError::BadFileDescriptor
708 })?;
709
710 let access_mode = flags & 0x3;
711 if access_mode == O_WRONLY {
712 return Err(FsError::PermissionDenied);
713 }
714
715 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
716 let inode_ref = inode_read!(inode);
717
718 match &inode_ref.content {
719 FileContent::File(storage) => {
720 let pos = position as usize;
721 let n = storage.read(pos, out);
722 drop(inode_ref);
723 self.fd_try_with(fd, |h| h.set_position(position + n as u64));
724 debug!("read: fd={}, read {} bytes at pos {}", fd, n, pos);
725 Ok(n)
726 }
727 FileContent::Dir(_) => {
728 error!("read: fd={} is a directory", fd);
729 Err(FsError::BadFileDescriptor)
730 }
731 }
732 }
733
734 pub fn read_at(&self, fd: Fd, offset: u64, out: &mut [u8]) -> Result<usize, FsError> {
737 trace!(
738 "read_at: fd={}, offset={}, buf_len={}",
739 fd,
740 offset,
741 out.len()
742 );
743
744 let (inode_id, flags) = self.fd_with(fd, |h| (h.inode_id, h.flags)).map_err(|_| {
745 error!("read_at: bad file descriptor {}", fd);
746 FsError::BadFileDescriptor
747 })?;
748
749 let access_mode = flags & 0x3;
750 if access_mode == O_WRONLY {
751 return Err(FsError::PermissionDenied);
752 }
753
754 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
755 let inode_ref = inode_read!(inode);
756
757 match &inode_ref.content {
758 FileContent::File(storage) => {
759 let n = storage.read(offset as usize, out);
760 drop(inode_ref);
761 self.fd_try_with(fd, |h| h.set_position(offset + n as u64));
762 debug!("read_at: fd={}, read {} bytes at offset {}", fd, n, offset);
763 Ok(n)
764 }
765 FileContent::Dir(_) => {
766 error!("read_at: fd={} is a directory", fd);
767 Err(FsError::BadFileDescriptor)
768 }
769 }
770 }
771
772 pub fn ftruncate(&self, fd: Fd, size: u64) -> Result<(), FsError> {
773 let (inode_id, flags) = self.fd_with(fd, |h| (h.inode_id, h.flags))?;
774
775 let access_mode = flags & 0x3;
776 if access_mode == O_RDONLY {
777 return Err(FsError::PermissionDenied);
778 }
779
780 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
781 let mut inode_ref = inode_write!(inode);
782
783 match &mut inode_ref.content {
784 FileContent::File(storage) => {
785 storage.truncate(size as usize);
786 inode_ref.metadata.size = size;
787 inode_ref.metadata.modified = self.time_provider.now();
788 Ok(())
789 }
790 FileContent::Dir(_) => Err(FsError::BadFileDescriptor),
791 }
792 }
793
794 pub fn close(&self, fd: Fd) -> Result<(), FsError> {
795 trace!("close: fd={}", fd);
796
797 self.fd_remove(fd).ok_or_else(|| {
798 error!("close: bad file descriptor {}", fd);
799 FsError::BadFileDescriptor
800 })?;
801
802 debug!("close: fd={} closed successfully", fd);
803 Ok(())
804 }
805
806 pub fn stat(&self, path: &str) -> Result<Metadata, FsError> {
807 if path.is_empty() {
808 return Err(FsError::InvalidArgument);
809 }
810
811 let comps: alloc::vec::Vec<&str> = path
812 .trim_start_matches('/')
813 .split('/')
814 .filter(|s| !s.is_empty())
815 .collect();
816
817 if comps.is_empty() {
818 let root = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
819 return Ok(inode_read!(root).metadata);
820 }
821
822 let mut current_inode = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
823
824 for comp in comps.iter() {
825 current_inode = self.find_inode(¤t_inode, comp)?;
826 }
827
828 Ok(inode_read!(current_inode).metadata)
829 }
830
831 pub fn fstat(&self, fd: Fd) -> Result<Metadata, FsError> {
832 let inode_id = self.fd_with(fd, |h| h.inode_id)?;
833 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
834 Ok(inode_read!(inode).metadata)
835 }
836
837 pub fn seek(&self, fd: Fd, offset: i64, whence: i32) -> Result<u64, FsError> {
838 trace!("seek: fd={}, offset={}, whence={}", fd, offset, whence);
839
840 const SEEK_SET: i32 = 0;
841 const SEEK_CUR: i32 = 1;
842 const SEEK_END: i32 = 2;
843
844 let (inode_id, current) = self
845 .fd_with(fd, |h| (h.inode_id, h.get_position()))
846 .map_err(|_| {
847 error!("seek: bad file descriptor {}", fd);
848 FsError::BadFileDescriptor
849 })?;
850 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
851 let inode_ref = inode_read!(inode);
852
853 match &inode_ref.content {
854 FileContent::File(storage) => {
855 let new_pos = match whence {
856 SEEK_SET => {
857 if offset < 0 {
858 return Err(FsError::InvalidArgument);
859 }
860 offset as u64
861 }
862 SEEK_CUR => {
863 let new = current as i64 + offset;
864 if new < 0 {
865 return Err(FsError::InvalidArgument);
866 }
867 new as u64
868 }
869 SEEK_END => {
870 let size = storage.size() as i64;
871 let new = size + offset;
872 if new < 0 {
873 return Err(FsError::InvalidArgument);
874 }
875 new as u64
876 }
877 _ => return Err(FsError::InvalidArgument),
878 };
879
880 drop(inode_ref);
881 self.fd_try_with(fd, |h| h.set_position(new_pos));
882 debug!("seek: fd={}, new_pos={}", fd, new_pos);
883 Ok(new_pos)
884 }
885 FileContent::Dir(_) => Err(FsError::BadFileDescriptor),
886 }
887 }
888
889 pub fn mkdir(&self, path: &str) -> Result<(), FsError> {
890 debug!("mkdir: path={}", path);
891
892 if path.is_empty() {
893 error!("mkdir: empty path");
894 return Err(FsError::InvalidArgument);
895 }
896
897 let comps: alloc::vec::Vec<&str> = path
898 .trim_start_matches('/')
899 .split('/')
900 .filter(|s| !s.is_empty())
901 .collect();
902
903 if comps.is_empty() {
904 return Err(FsError::InvalidArgument);
905 }
906
907 let mut current_inode = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
908
909 for comp in comps.iter().take(comps.len() - 1) {
910 current_inode = self.find_inode(¤t_inode, comp)?;
911 let inode = inode_read!(current_inode);
912 if matches!(inode.content, FileContent::File(_)) {
913 return Err(FsError::NotADirectory);
914 }
915 }
916
917 let dirname = comps[comps.len() - 1];
918 self.create_inode(¤t_inode, dirname, true)?;
919 debug!("mkdir: created directory {}", path);
920 Ok(())
921 }
922
923 pub fn mkdir_p(&self, path: &str) -> Result<(), FsError> {
924 if path.is_empty() {
925 return Err(FsError::InvalidArgument);
926 }
927
928 let comps: alloc::vec::Vec<&str> = path
929 .trim_start_matches('/')
930 .split('/')
931 .filter(|s| !s.is_empty())
932 .collect();
933
934 if comps.is_empty() {
935 return Ok(()); }
937
938 let mut current_inode = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
939
940 for comp in comps.iter() {
941 current_inode = match self.find_inode(¤t_inode, comp) {
942 Ok(inode) => {
943 {
944 let borrowed = inode_read!(inode);
945 if matches!(borrowed.content, FileContent::File(_)) {
946 return Err(FsError::NotADirectory);
947 }
948 }
949 inode
950 }
951 Err(FsError::NotFound) => self.create_inode(¤t_inode, comp, true)?,
952 Err(e) => return Err(e),
953 };
954 }
955
956 Ok(())
957 }
958
959 pub fn unlink(&self, path: &str) -> Result<(), FsError> {
960 debug!("unlink: path={}", path);
961
962 if path.is_empty() {
963 error!("unlink: empty path");
964 return Err(FsError::InvalidArgument);
965 }
966
967 let comps: alloc::vec::Vec<&str> = path
968 .trim_start_matches('/')
969 .split('/')
970 .filter(|s| !s.is_empty())
971 .collect();
972
973 if comps.is_empty() {
974 return Err(FsError::InvalidArgument);
975 }
976
977 let mut current_inode = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
978
979 for comp in comps.iter().take(comps.len() - 1) {
980 current_inode = self.find_inode(¤t_inode, comp)?;
981 let inode = inode_read!(current_inode);
982 if matches!(inode.content, FileContent::File(_)) {
983 return Err(FsError::NotADirectory);
984 }
985 }
986
987 let filename = comps[comps.len() - 1];
988
989 let target_inode = self.find_inode(¤t_inode, filename)?;
991 {
992 let target = inode_read!(target_inode);
993 if matches!(target.content, FileContent::Dir(_)) {
994 return Err(FsError::IsADirectory);
995 }
996 }
997
998 let mut parent = inode_write!(current_inode);
1000 match &mut parent.content {
1001 FileContent::Dir(entries) => {
1002 entries.remove(filename);
1003 let timestamp = self.time_provider.now();
1004 parent.metadata.modified = timestamp;
1005 debug!("unlink: removed {}", path);
1006 Ok(())
1007 }
1008 FileContent::File(_) => Err(FsError::NotADirectory),
1009 }
1010 }
1012
1013 pub fn readdir(&self, path: &str) -> Result<alloc::vec::Vec<alloc::string::String>, FsError> {
1014 if path.is_empty() {
1015 return Err(FsError::InvalidArgument);
1016 }
1017
1018 let comps: alloc::vec::Vec<&str> = path
1019 .trim_start_matches('/')
1020 .split('/')
1021 .filter(|s| !s.is_empty())
1022 .collect();
1023
1024 let mut current_inode = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
1025
1026 for comp in comps.iter() {
1027 current_inode = self.find_inode(¤t_inode, comp)?;
1028 }
1029
1030 let inode = inode_read!(current_inode);
1031 match &inode.content {
1032 FileContent::Dir(entries) => {
1033 let mut result = alloc::vec::Vec::new();
1034 for name in entries.keys() {
1035 result.push(name.clone());
1036 }
1037 Ok(result)
1038 }
1039 FileContent::File(_) => Err(FsError::NotADirectory),
1040 }
1041 }
1042
1043 pub fn readdir_fd(
1046 &self,
1047 fd: Fd,
1048 ) -> Result<alloc::vec::Vec<(alloc::string::String, bool)>, FsError> {
1049 let inode_id = self.fd_with(fd, |h| h.inode_id)?;
1050 let inode = self.inode_get(inode_id).ok_or(FsError::NotFound)?;
1051
1052 let entries: alloc::vec::Vec<(alloc::string::String, InodeId)> = {
1056 let inode_ref = inode_read!(inode);
1057 match &inode_ref.content {
1058 FileContent::Dir(map) => map.iter().map(|(n, &id)| (n.clone(), id)).collect(),
1059 FileContent::File(_) => return Err(FsError::NotADirectory),
1060 }
1061 };
1062
1063 let mut result = alloc::vec::Vec::with_capacity(entries.len());
1064 for (name, child_id) in entries {
1065 if let Some(child_inode) = self.inode_get(child_id) {
1066 let is_dir = inode_read!(child_inode).metadata.is_dir;
1067 result.push((name, is_dir));
1068 }
1069 }
1070 Ok(result)
1071 }
1072
1073 pub fn rmdir(&self, path: &str) -> Result<(), FsError> {
1075 debug!("rmdir: path={}", path);
1076
1077 if path.is_empty() {
1078 error!("rmdir: empty path");
1079 return Err(FsError::InvalidArgument);
1080 }
1081
1082 let comps: alloc::vec::Vec<&str> = path
1083 .trim_start_matches('/')
1084 .split('/')
1085 .filter(|s| !s.is_empty())
1086 .collect();
1087
1088 if comps.is_empty() {
1089 return Err(FsError::InvalidArgument);
1090 }
1091
1092 let mut current_inode = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
1093
1094 for comp in comps.iter().take(comps.len() - 1) {
1095 current_inode = self.find_inode(¤t_inode, comp)?;
1096 let inode = inode_read!(current_inode);
1097 if matches!(inode.content, FileContent::File(_)) {
1098 return Err(FsError::NotADirectory);
1099 }
1100 }
1101
1102 let dirname = comps[comps.len() - 1];
1103 let target_inode = self.find_inode(¤t_inode, dirname)?;
1104
1105 {
1106 let target = inode_read!(target_inode);
1107 match &target.content {
1108 FileContent::File(_) => return Err(FsError::NotADirectory),
1109 FileContent::Dir(entries) => {
1110 if !entries.is_empty() {
1111 error!("rmdir: directory not empty");
1112 return Err(FsError::NotEmpty);
1113 }
1114 }
1115 }
1116 }
1117
1118 let mut parent = inode_write!(current_inode);
1119 match &mut parent.content {
1120 FileContent::Dir(entries) => {
1121 entries.remove(dirname);
1122 let timestamp = self.time_provider.now();
1123 parent.metadata.modified = timestamp;
1124 debug!("rmdir: removed {}", path);
1125 Ok(())
1126 }
1127 FileContent::File(_) => Err(FsError::NotADirectory),
1128 }
1129 }
1130
1131 pub fn rename(&self, old_path: &str, new_path: &str) -> Result<(), FsError> {
1133 debug!("rename: old_path={}, new_path={}", old_path, new_path);
1134
1135 if old_path.is_empty() || new_path.is_empty() {
1136 error!("rename: empty path");
1137 return Err(FsError::InvalidArgument);
1138 }
1139
1140 let old_comps: alloc::vec::Vec<&str> = old_path
1141 .trim_start_matches('/')
1142 .split('/')
1143 .filter(|s| !s.is_empty())
1144 .collect();
1145
1146 let new_comps: alloc::vec::Vec<&str> = new_path
1147 .trim_start_matches('/')
1148 .split('/')
1149 .filter(|s| !s.is_empty())
1150 .collect();
1151
1152 if old_comps.is_empty() || new_comps.is_empty() {
1153 return Err(FsError::InvalidArgument);
1154 }
1155
1156 if old_comps == new_comps {
1157 return Ok(());
1158 }
1159
1160 let root_inode = self.inode_get(self.root_inode).ok_or(FsError::NotFound)?;
1162 let mut old_parent = root_inode.clone();
1163
1164 for comp in old_comps.iter().take(old_comps.len() - 1) {
1165 old_parent = self.find_inode(&old_parent, comp)?;
1166 let inode = inode_read!(old_parent);
1167 if matches!(inode.content, FileContent::File(_)) {
1168 return Err(FsError::NotADirectory);
1169 }
1170 }
1171
1172 let old_name = old_comps[old_comps.len() - 1];
1173
1174 let source_inode = self.find_inode(&old_parent, old_name)?;
1176 let source_is_dir = inode_read!(source_inode).metadata.is_dir;
1177
1178 let mut new_parent = root_inode;
1180
1181 for comp in new_comps.iter().take(new_comps.len() - 1) {
1182 new_parent = self.find_inode(&new_parent, comp)?;
1183 let inode = inode_read!(new_parent);
1184 if matches!(inode.content, FileContent::File(_)) {
1185 return Err(FsError::NotADirectory);
1186 }
1187 }
1188
1189 let new_name = new_comps[new_comps.len() - 1];
1190
1191 if let Ok(dest_inode) = self.find_inode(&new_parent, new_name) {
1193 let dest = inode_read!(dest_inode);
1194 let dest_is_dir = dest.metadata.is_dir;
1195
1196 if source_is_dir && !dest_is_dir {
1197 return Err(FsError::NotADirectory);
1198 }
1199 if !source_is_dir && dest_is_dir {
1200 return Err(FsError::IsADirectory);
1201 }
1202 if dest_is_dir
1203 && matches!(&dest.content, FileContent::Dir(entries) if !entries.is_empty())
1204 {
1205 return Err(FsError::NotEmpty);
1206 }
1207 }
1208
1209 let old_parent_id = inode_read!(old_parent).id;
1210 let new_parent_id = inode_read!(new_parent).id;
1211
1212 let timestamp = self.time_provider.now();
1213
1214 if old_parent_id == new_parent_id {
1215 let mut parent = inode_write!(old_parent);
1217 if let FileContent::Dir(entries) = &mut parent.content {
1218 if let Some(inode_id) = entries.remove(old_name) {
1219 entries.insert(alloc::string::String::from(new_name), inode_id);
1220 parent.metadata.modified = timestamp;
1221 } else {
1222 return Err(FsError::NotFound);
1223 }
1224 } else {
1225 return Err(FsError::NotADirectory);
1226 }
1227 } else {
1228 if old_parent_id < new_parent_id {
1231 let mut old_p = inode_write!(old_parent);
1232 let mut new_p = inode_write!(new_parent);
1233 let old_entries = match &mut old_p.content {
1234 FileContent::Dir(e) => e,
1235 _ => return Err(FsError::NotADirectory),
1236 };
1237 let inode_id = old_entries.remove(old_name).ok_or(FsError::NotFound)?;
1238 old_p.metadata.modified = timestamp;
1239 let new_entries = match &mut new_p.content {
1240 FileContent::Dir(e) => e,
1241 _ => return Err(FsError::NotADirectory),
1242 };
1243 new_entries.insert(alloc::string::String::from(new_name), inode_id);
1244 new_p.metadata.modified = timestamp;
1245 } else {
1246 let mut new_p = inode_write!(new_parent);
1247 let mut old_p = inode_write!(old_parent);
1248 let old_entries = match &mut old_p.content {
1249 FileContent::Dir(e) => e,
1250 _ => return Err(FsError::NotADirectory),
1251 };
1252 let inode_id = old_entries.remove(old_name).ok_or(FsError::NotFound)?;
1253 old_p.metadata.modified = timestamp;
1254 let new_entries = match &mut new_p.content {
1255 FileContent::Dir(e) => e,
1256 _ => return Err(FsError::NotADirectory),
1257 };
1258 new_entries.insert(alloc::string::String::from(new_name), inode_id);
1259 new_p.metadata.modified = timestamp;
1260 }
1261 }
1262
1263 debug!("rename: {} -> {}", old_path, new_path);
1264 Ok(())
1265 }
1266}
1267
1268impl Default for Fs<MonotonicCounter> {
1269 fn default() -> Self {
1270 Self::new()
1271 }
1272}
1273
1274impl Fs<MonotonicCounter> {
1275 pub fn new() -> Self {
1276 Self::with_time_provider(MonotonicCounter::new())
1277 }
1278}
1279
1280#[cfg(feature = "thread-safe")]
1285fn new_fd_table() -> FdTable {
1286 DashMap::new()
1287}
1288#[cfg(all(feature = "std", not(feature = "thread-safe")))]
1289fn new_fd_table() -> FdTable {
1290 RefCell::new(HashMap::new())
1291}
1292#[cfg(not(feature = "std"))]
1293fn new_fd_table() -> FdTable {
1294 RefCell::new(BTreeMap::new())
1295}
1296
1297#[cfg(feature = "thread-safe")]
1298fn new_inode_table() -> InodeTable {
1299 DashMap::new()
1300}
1301#[cfg(all(feature = "std", not(feature = "thread-safe")))]
1302fn new_inode_table() -> InodeTable {
1303 RefCell::new(HashMap::new())
1304}
1305#[cfg(not(feature = "std"))]
1306fn new_inode_table() -> InodeTable {
1307 RefCell::new(BTreeMap::new())
1308}
1309
1310#[cfg(feature = "thread-safe")]
1311fn new_inode_counter(initial: InodeId) -> NextInodeCounter {
1312 AtomicU64::new(initial)
1313}
1314#[cfg(not(feature = "thread-safe"))]
1315fn new_inode_counter(initial: InodeId) -> NextInodeCounter {
1316 Cell::new(initial)
1317}
1318
1319#[cfg(feature = "thread-safe")]
1320fn new_fd_counter(initial: Fd) -> NextFdCounter {
1321 AtomicU32::new(initial)
1322}
1323#[cfg(not(feature = "thread-safe"))]
1324fn new_fd_counter(initial: Fd) -> NextFdCounter {
1325 Cell::new(initial)
1326}