1use std::sync::Arc;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use rand::RngCore;
5
6use crate::allocator::{BitmapAllocator, SlotAllocator};
7use crate::block_store::BlockStore;
8use crate::codec::{
9 read_encrypted_object, read_encrypted_raw, write_encrypted_object, write_encrypted_raw,
10 ObjectCodec, PostcardCodec,
11};
12use crate::crypto::CryptoEngine;
13use crate::error::{FsError, FsResult};
14use crate::model::*;
15use crate::transaction::TransactionManager;
16
17pub struct FilesystemCore {
24 store: Arc<dyn BlockStore>,
25 crypto: Arc<dyn CryptoEngine>,
26 codec: PostcardCodec,
27 allocator: BitmapAllocator,
28 txn: TransactionManager,
29 superblock: Option<Superblock>,
31 next_inode_id: InodeId,
33}
34
35struct AncestorEntry {
38 inode: Inode,
39 dir_page: DirectoryPage,
40 child_index: usize,
41}
42
43fn max_chunk_payload(block_size: usize) -> usize {
47 if block_size > 200 {
50 block_size - 200
51 } else {
52 0
53 }
54}
55
56fn now_secs() -> u64 {
57 SystemTime::now()
58 .duration_since(UNIX_EPOCH)
59 .unwrap_or_default()
60 .as_secs()
61}
62
63impl FilesystemCore {
64 pub fn new(store: Arc<dyn BlockStore>, crypto: Arc<dyn CryptoEngine>) -> Self {
66 let total_blocks = store.total_blocks();
67 Self {
68 store,
69 crypto,
70 codec: PostcardCodec,
71 allocator: BitmapAllocator::new(total_blocks),
72 txn: TransactionManager::new(),
73 superblock: None,
74 next_inode_id: 1,
75 }
76 }
77
78 pub fn init_filesystem(&mut self) -> FsResult<()> {
83 let block_size = self.store.block_size() as u32;
84 let total_blocks = self.store.total_blocks();
85
86 let header = StorageHeader::new(block_size, total_blocks);
88 let header_bytes = self.codec.serialize_object(&header)?;
89 let bs = self.store.block_size();
90 let mut block = vec![0u8; bs];
91 rand::thread_rng().fill_bytes(&mut block);
92 let len = header_bytes.len() as u32;
93 block[..4].copy_from_slice(&len.to_le_bytes());
94 block[4..4 + header_bytes.len()].copy_from_slice(&header_bytes);
95 self.store.write_block(BLOCK_STORAGE_HEADER, &block)?;
96
97 let root_inode_id = self.alloc_inode_id();
99 let dir_page = DirectoryPage::new();
100 let dir_page_block = self.allocator.allocate()?;
101 write_encrypted_object(
102 self.store.as_ref(),
103 self.crypto.as_ref(),
104 &self.codec,
105 dir_page_block,
106 ObjectKind::DirectoryPage,
107 &dir_page,
108 )?;
109
110 let ts = now_secs();
111 let root_inode = Inode {
112 id: root_inode_id,
113 kind: InodeKind::Directory,
114 size: 0,
115 directory_page_ref: ObjectRef::new(dir_page_block),
116 extent_map_ref: ObjectRef::null(),
117 created_at: ts,
118 modified_at: ts,
119 };
120 let root_inode_block = self.allocator.allocate()?;
121 write_encrypted_object(
122 self.store.as_ref(),
123 self.crypto.as_ref(),
124 &self.codec,
125 root_inode_block,
126 ObjectKind::Inode,
127 &root_inode,
128 )?;
129
130 let sb = Superblock {
132 generation: 1,
133 root_inode_ref: ObjectRef::new(root_inode_block),
134 };
135 self.superblock = Some(sb.clone());
136
137 self.txn.commit(
139 self.store.as_ref(),
140 self.crypto.as_ref(),
141 &self.codec,
142 &self.allocator,
143 &sb,
144 )?;
145
146 Ok(())
147 }
148
149 pub fn open(&mut self) -> FsResult<()> {
151 let header = self.read_storage_header()?;
153 if !header.is_valid() {
154 return Err(FsError::InvalidSuperblock);
155 }
156
157 let (rp, was_b) = TransactionManager::recover_latest(self.store.as_ref(), &self.codec)?
159 .ok_or(FsError::InvalidRootPointer)?;
160
161 let sb: Superblock = read_encrypted_object(
163 self.store.as_ref(),
164 self.crypto.as_ref(),
165 &self.codec,
166 rp.superblock_ref.block_id,
167 )?;
168
169 let sb_bytes = self.codec.serialize_object(&sb)?;
171 let checksum = blake3::hash(&sb_bytes);
172 if *checksum.as_bytes() != rp.checksum {
173 return Err(FsError::InvalidSuperblock);
174 }
175
176 self.txn = TransactionManager::from_recovered(rp.generation, was_b);
177 self.superblock = Some(sb.clone());
178
179 self.rebuild_allocator(&sb)?;
181
182 Ok(())
183 }
184
185 fn split_path(path: &str) -> FsResult<(Vec<&str>, &str)> {
192 let trimmed = path.trim_matches('/');
193 if trimmed.is_empty() {
194 return Err(FsError::Internal("empty path".into()));
195 }
196 let parts: Vec<&str> = trimmed.split('/').collect();
197 let (dirs, leaf) = parts.split_at(parts.len() - 1);
198 Ok((dirs.to_vec(), leaf[0]))
199 }
200
201 fn split_dir_path(path: &str) -> Vec<&str> {
203 let trimmed = path.trim_matches('/');
204 if trimmed.is_empty() {
205 return Vec::new();
206 }
207 trimmed.split('/').collect()
208 }
209
210 fn resolve_dir_chain(
217 &self,
218 components: &[&str],
219 root_inode: &Inode,
220 ) -> FsResult<(Vec<AncestorEntry>, Inode, DirectoryPage)> {
221 let mut ancestors: Vec<AncestorEntry> = Vec::new();
222 let mut current_inode = root_inode.clone();
223 let mut current_dir_page: DirectoryPage =
224 self.read_obj(current_inode.directory_page_ref.block_id)?;
225
226 for component in components {
227 let idx = current_dir_page
228 .entries
229 .iter()
230 .position(|e| e.name == *component)
231 .ok_or_else(|| FsError::DirectoryNotFound(component.to_string()))?;
232
233 let entry = ¤t_dir_page.entries[idx];
234 if entry.kind != InodeKind::Directory {
235 return Err(FsError::NotADirectory(component.to_string()));
236 }
237
238 let child_inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
239 let child_dir_page: DirectoryPage =
240 self.read_obj(child_inode.directory_page_ref.block_id)?;
241
242 ancestors.push(AncestorEntry {
243 inode: current_inode,
244 dir_page: current_dir_page,
245 child_index: idx,
246 });
247
248 current_inode = child_inode;
249 current_dir_page = child_dir_page;
250 }
251
252 Ok((ancestors, current_inode, current_dir_page))
253 }
254
255 fn commit_cow_chain(
262 &mut self,
263 sb: &Superblock,
264 ancestors: &[AncestorEntry],
265 target_inode: &Inode,
266 new_dir_page: &DirectoryPage,
267 ) -> FsResult<()> {
268 let mut new_dp_block = self.allocator.allocate()?;
270 self.write_obj(new_dp_block, ObjectKind::DirectoryPage, new_dir_page)?;
271
272 let mut new_inode = target_inode.clone();
274 new_inode.directory_page_ref = ObjectRef::new(new_dp_block);
275 new_inode.modified_at = now_secs();
276 let mut new_inode_block = self.allocator.allocate()?;
277 self.write_obj(new_inode_block, ObjectKind::Inode, &new_inode)?;
278
279 for ancestor in ancestors.iter().rev() {
281 let mut parent_dp = ancestor.dir_page.clone();
282 parent_dp.entries[ancestor.child_index].inode_ref = ObjectRef::new(new_inode_block);
283
284 new_dp_block = self.allocator.allocate()?;
285 self.write_obj(new_dp_block, ObjectKind::DirectoryPage, &parent_dp)?;
286
287 let mut parent_inode = ancestor.inode.clone();
288 parent_inode.directory_page_ref = ObjectRef::new(new_dp_block);
289 parent_inode.modified_at = now_secs();
290 new_inode_block = self.allocator.allocate()?;
291 self.write_obj(new_inode_block, ObjectKind::Inode, &parent_inode)?;
292 }
293
294 let new_sb = Superblock {
296 generation: sb.generation + 1,
297 root_inode_ref: ObjectRef::new(new_inode_block),
298 };
299 self.commit_superblock(new_sb)?;
300 Ok(())
301 }
302
303 pub fn create_file(&mut self, path: &str) -> FsResult<()> {
310 let (dir_parts, leaf) = Self::split_path(path)?;
311 self.validate_name(leaf)?;
312 let sb = self
313 .superblock
314 .as_ref()
315 .ok_or(FsError::NotInitialized)?
316 .clone();
317
318 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
319 let (ancestors, target_inode, mut dir_page) =
320 self.resolve_dir_chain(&dir_parts, &root_inode)?;
321
322 if dir_page.entries.iter().any(|e| e.name == leaf) {
323 return Err(FsError::FileAlreadyExists(leaf.to_string()));
324 }
325
326 let extent_map = ExtentMap::new();
328 let em_block = self.allocator.allocate()?;
329 self.write_obj(em_block, ObjectKind::ExtentMap, &extent_map)?;
330
331 let inode_id = self.alloc_inode_id();
333 let ts = now_secs();
334 let file_inode = Inode {
335 id: inode_id,
336 kind: InodeKind::File,
337 size: 0,
338 directory_page_ref: ObjectRef::null(),
339 extent_map_ref: ObjectRef::new(em_block),
340 created_at: ts,
341 modified_at: ts,
342 };
343 let inode_block = self.allocator.allocate()?;
344 self.write_obj(inode_block, ObjectKind::Inode, &file_inode)?;
345
346 dir_page.entries.push(DirectoryEntry {
347 name: leaf.to_string(),
348 inode_ref: ObjectRef::new(inode_block),
349 inode_id,
350 kind: InodeKind::File,
351 });
352
353 self.commit_cow_chain(&sb, &ancestors, &target_inode, &dir_page)?;
354 Ok(())
355 }
356
357 pub fn write_file(&mut self, path: &str, offset: u64, data: &[u8]) -> FsResult<()> {
363 let (dir_parts, leaf) = Self::split_path(path)?;
364 let sb = self
365 .superblock
366 .as_ref()
367 .ok_or(FsError::NotInitialized)?
368 .clone();
369 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
370 let (ancestors, target_inode, dir_page) =
371 self.resolve_dir_chain(&dir_parts, &root_inode)?;
372
373 let entry = dir_page
374 .entries
375 .iter()
376 .find(|e| e.name == leaf)
377 .ok_or_else(|| FsError::FileNotFound(leaf.to_string()))?;
378
379 if entry.kind != InodeKind::File {
380 return Err(FsError::NotAFile(leaf.to_string()));
381 }
382
383 let file_inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
384 let mut extent_map: ExtentMap = self.read_obj(file_inode.extent_map_ref.block_id)?;
385
386 let chunk_size = max_chunk_payload(self.store.block_size());
387 if chunk_size == 0 {
388 return Err(FsError::DataTooLarge(data.len()));
389 }
390
391 if data.is_empty() {
392 return Ok(());
393 }
394
395 let old_size = file_inode.size as usize;
396 let write_start = offset as usize;
397 let write_end = write_start + data.len();
398 let new_size = std::cmp::max(old_size, write_end);
399
400 extent_map.entries.sort_by_key(|e| e.chunk_index);
402 let sorted_len = extent_map.entries.len();
405
406 let first_chunk = if write_start >= old_size {
410 old_size / chunk_size
411 } else {
412 write_start / chunk_size
413 };
414 let last_chunk = (new_size - 1) / chunk_size;
415
416 for chunk_idx in first_chunk..=last_chunk {
417 let chunk_file_start = chunk_idx * chunk_size;
418 let chunk_file_end = std::cmp::min(chunk_file_start + chunk_size, new_size);
419 let chunk_len = chunk_file_end - chunk_file_start;
420
421 let mut chunk_buf = vec![0u8; chunk_len];
422
423 let chunk_idx_u64 = chunk_idx as u64;
425 if chunk_file_start < old_size {
426 if let Ok(pos) = extent_map.entries[..sorted_len]
427 .binary_search_by_key(&chunk_idx_u64, |e| e.chunk_index)
428 {
429 let existing = &extent_map.entries[pos];
430 let raw = read_encrypted_raw(
431 self.store.as_ref(),
432 self.crypto.as_ref(),
433 &self.codec,
434 existing.data_ref.block_id,
435 )?;
436 let copy_len = std::cmp::min(existing.plaintext_len as usize, chunk_len);
437 let src_len = std::cmp::min(copy_len, raw.len());
438 chunk_buf[..src_len].copy_from_slice(&raw[..src_len]);
439 }
440 }
441
442 let overlap_start = std::cmp::max(chunk_file_start, write_start);
444 let overlap_end = std::cmp::min(chunk_file_end, write_end);
445 if overlap_start < overlap_end {
446 let data_off = overlap_start - write_start;
447 let chunk_off = overlap_start - chunk_file_start;
448 let len = overlap_end - overlap_start;
449 chunk_buf[chunk_off..chunk_off + len]
450 .copy_from_slice(&data[data_off..data_off + len]);
451 }
452
453 let data_block = self.allocator.allocate()?;
455 write_encrypted_raw(
456 self.store.as_ref(),
457 self.crypto.as_ref(),
458 &self.codec,
459 data_block,
460 ObjectKind::FileDataChunk,
461 &chunk_buf,
462 )?;
463
464 if let Ok(pos) = extent_map.entries[..sorted_len]
466 .binary_search_by_key(&chunk_idx_u64, |e| e.chunk_index)
467 {
468 extent_map.entries[pos].data_ref = ObjectRef::new(data_block);
469 extent_map.entries[pos].plaintext_len = chunk_buf.len() as u32;
470 } else {
471 extent_map.entries.push(ExtentEntry {
472 chunk_index: chunk_idx_u64,
473 data_ref: ObjectRef::new(data_block),
474 plaintext_len: chunk_buf.len() as u32,
475 });
476 }
477 }
478
479 extent_map.entries.sort_by_key(|e| e.chunk_index);
481
482 let new_em_block = self.allocator.allocate()?;
484 self.write_obj(new_em_block, ObjectKind::ExtentMap, &extent_map)?;
485
486 let mut new_file_inode = file_inode.clone();
488 new_file_inode.size = new_size as u64;
489 new_file_inode.extent_map_ref = ObjectRef::new(new_em_block);
490 new_file_inode.modified_at = now_secs();
491 let new_inode_block = self.allocator.allocate()?;
492 self.write_obj(new_inode_block, ObjectKind::Inode, &new_file_inode)?;
493
494 let mut new_dir_page = dir_page.clone();
496 for e in &mut new_dir_page.entries {
497 if e.name == leaf {
498 e.inode_ref = ObjectRef::new(new_inode_block);
499 }
500 }
501
502 self.commit_cow_chain(&sb, &ancestors, &target_inode, &new_dir_page)?;
503 Ok(())
504 }
505
506 pub fn read_file(&self, path: &str, offset: u64, len: usize) -> FsResult<Vec<u8>> {
508 let (dir_parts, leaf) = Self::split_path(path)?;
509 let sb = self.superblock.as_ref().ok_or(FsError::NotInitialized)?;
510 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
511 let (_, _, dir_page) = self.resolve_dir_chain(&dir_parts, &root_inode)?;
512
513 let entry = dir_page
514 .entries
515 .iter()
516 .find(|e| e.name == leaf)
517 .ok_or_else(|| FsError::FileNotFound(leaf.to_string()))?;
518
519 if entry.kind != InodeKind::File {
520 return Err(FsError::NotAFile(leaf.to_string()));
521 }
522
523 let file_inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
524 let extent_map: ExtentMap = self.read_obj(file_inode.extent_map_ref.block_id)?;
525
526 let full_data = self.read_all_chunks(&extent_map)?;
527
528 let start = offset as usize;
529 if start >= full_data.len() {
530 return Ok(Vec::new());
531 }
532 let end = std::cmp::min(start + len, full_data.len());
533 Ok(full_data[start..end].to_vec())
534 }
535
536 pub fn list_directory(&self, path: &str) -> FsResult<Vec<DirListEntry>> {
540 let sb = self.superblock.as_ref().ok_or(FsError::NotInitialized)?;
541 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
542
543 let components = Self::split_dir_path(path);
544 let (_, _, dir_page) = self.resolve_dir_chain(&components, &root_inode)?;
545
546 let mut result = Vec::new();
547 for entry in &dir_page.entries {
548 let inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
549 result.push(DirListEntry {
550 name: entry.name.clone(),
551 kind: entry.kind,
552 size: inode.size,
553 inode_id: entry.inode_id,
554 });
555 }
556 Ok(result)
557 }
558
559 pub fn create_directory(&mut self, path: &str) -> FsResult<()> {
563 let (dir_parts, leaf) = Self::split_path(path)?;
564 self.validate_name(leaf)?;
565 let sb = self
566 .superblock
567 .as_ref()
568 .ok_or(FsError::NotInitialized)?
569 .clone();
570 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
571 let (ancestors, target_inode, mut dir_page) =
572 self.resolve_dir_chain(&dir_parts, &root_inode)?;
573
574 if dir_page.entries.iter().any(|e| e.name == leaf) {
575 return Err(FsError::DirectoryAlreadyExists(leaf.to_string()));
576 }
577
578 let sub_dp = DirectoryPage::new();
580 let sub_dp_block = self.allocator.allocate()?;
581 self.write_obj(sub_dp_block, ObjectKind::DirectoryPage, &sub_dp)?;
582
583 let inode_id = self.alloc_inode_id();
584 let ts = now_secs();
585 let dir_inode = Inode {
586 id: inode_id,
587 kind: InodeKind::Directory,
588 size: 0,
589 directory_page_ref: ObjectRef::new(sub_dp_block),
590 extent_map_ref: ObjectRef::null(),
591 created_at: ts,
592 modified_at: ts,
593 };
594 let inode_block = self.allocator.allocate()?;
595 self.write_obj(inode_block, ObjectKind::Inode, &dir_inode)?;
596
597 dir_page.entries.push(DirectoryEntry {
598 name: leaf.to_string(),
599 inode_ref: ObjectRef::new(inode_block),
600 inode_id,
601 kind: InodeKind::Directory,
602 });
603
604 self.commit_cow_chain(&sb, &ancestors, &target_inode, &dir_page)?;
605 Ok(())
606 }
607
608 pub fn remove_file(&mut self, path: &str) -> FsResult<()> {
610 let (dir_parts, leaf) = Self::split_path(path)?;
611 let sb = self
612 .superblock
613 .as_ref()
614 .ok_or(FsError::NotInitialized)?
615 .clone();
616 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
617 let (ancestors, target_inode, mut dir_page) =
618 self.resolve_dir_chain(&dir_parts, &root_inode)?;
619
620 let idx = dir_page
621 .entries
622 .iter()
623 .position(|e| e.name == leaf)
624 .ok_or_else(|| FsError::FileNotFound(leaf.to_string()))?;
625
626 let entry = &dir_page.entries[idx];
627 if entry.kind == InodeKind::Directory {
628 let dir_inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
629 let sub_page: DirectoryPage = self.read_obj(dir_inode.directory_page_ref.block_id)?;
630 if !sub_page.entries.is_empty() {
631 return Err(FsError::DirectoryNotEmpty(leaf.to_string()));
632 }
633 }
634
635 dir_page.entries.remove(idx);
636 self.commit_cow_chain(&sb, &ancestors, &target_inode, &dir_page)?;
637 Ok(())
638 }
639
640 pub fn rename(&mut self, old_path: &str, new_path: &str) -> FsResult<()> {
643 let (old_dir, old_leaf) = Self::split_path(old_path)?;
644 let (new_dir, new_leaf) = Self::split_path(new_path)?;
645 self.validate_name(new_leaf)?;
646
647 if old_dir != new_dir {
648 return Err(FsError::Internal(
649 "rename across directories is not supported".into(),
650 ));
651 }
652
653 let sb = self
654 .superblock
655 .as_ref()
656 .ok_or(FsError::NotInitialized)?
657 .clone();
658 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
659 let (ancestors, target_inode, mut dir_page) =
660 self.resolve_dir_chain(&old_dir, &root_inode)?;
661
662 if dir_page.entries.iter().any(|e| e.name == new_leaf) {
663 return Err(FsError::FileAlreadyExists(new_leaf.to_string()));
664 }
665
666 let entry = dir_page
667 .entries
668 .iter_mut()
669 .find(|e| e.name == old_leaf)
670 .ok_or_else(|| FsError::FileNotFound(old_leaf.to_string()))?;
671
672 entry.name = new_leaf.to_string();
673
674 self.commit_cow_chain(&sb, &ancestors, &target_inode, &dir_page)?;
675 Ok(())
676 }
677
678 pub fn sync(&self) -> FsResult<()> {
680 self.store.sync()
681 }
682
683 fn alloc_inode_id(&mut self) -> InodeId {
686 let id = self.next_inode_id;
687 self.next_inode_id += 1;
688 id
689 }
690
691 fn validate_name(&self, name: &str) -> FsResult<()> {
692 if name.is_empty() || name.contains('/') || name.contains('\0') {
693 return Err(FsError::Internal("invalid name".into()));
694 }
695 if name.len() > MAX_NAME_LEN {
696 return Err(FsError::NameTooLong(name.len(), MAX_NAME_LEN));
697 }
698 Ok(())
699 }
700
701 fn read_obj<T: serde::de::DeserializeOwned>(&self, block_id: u64) -> FsResult<T> {
702 read_encrypted_object(
703 self.store.as_ref(),
704 self.crypto.as_ref(),
705 &self.codec,
706 block_id,
707 )
708 }
709
710 fn write_obj<T: serde::Serialize>(
711 &self,
712 block_id: u64,
713 kind: ObjectKind,
714 obj: &T,
715 ) -> FsResult<()> {
716 write_encrypted_object(
717 self.store.as_ref(),
718 self.crypto.as_ref(),
719 &self.codec,
720 block_id,
721 kind,
722 obj,
723 )
724 }
725
726 fn read_all_chunks(&self, extent_map: &ExtentMap) -> FsResult<Vec<u8>> {
727 let mut entries = extent_map.entries.clone();
728 entries.sort_by_key(|e| e.chunk_index);
729
730 let mut buf = Vec::new();
731 for entry in &entries {
732 let chunk = read_encrypted_raw(
733 self.store.as_ref(),
734 self.crypto.as_ref(),
735 &self.codec,
736 entry.data_ref.block_id,
737 )?;
738 let len = entry.plaintext_len as usize;
740 if len <= chunk.len() {
741 buf.extend_from_slice(&chunk[..len]);
742 } else {
743 buf.extend_from_slice(&chunk);
744 }
745 }
746 Ok(buf)
747 }
748
749 fn read_storage_header(&self) -> FsResult<StorageHeader> {
750 let block = self.store.read_block(BLOCK_STORAGE_HEADER)?;
751 if block.len() < 4 {
752 return Err(FsError::InvalidSuperblock);
753 }
754 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
755 if len == 0 || 4 + len > block.len() {
756 return Err(FsError::InvalidSuperblock);
757 }
758 self.codec
759 .deserialize_object::<StorageHeader>(&block[4..4 + len])
760 }
761
762 fn commit_superblock(&mut self, sb: Superblock) -> FsResult<()> {
763 self.txn.commit(
764 self.store.as_ref(),
765 self.crypto.as_ref(),
766 &self.codec,
767 &self.allocator,
768 &sb,
769 )?;
770 self.superblock = Some(sb);
771 Ok(())
772 }
773
774 fn rebuild_allocator(&mut self, sb: &Superblock) -> FsResult<()> {
777 let (rp, _) = TransactionManager::recover_latest(self.store.as_ref(), &self.codec)?
784 .ok_or(FsError::InvalidRootPointer)?;
785 self.allocator.mark_allocated(rp.superblock_ref.block_id)?;
786
787 self.mark_inode_tree(sb.root_inode_ref.block_id)?;
789
790 Ok(())
794 }
795
796 fn mark_inode_tree(&mut self, inode_block: u64) -> FsResult<()> {
797 self.allocator.mark_allocated(inode_block)?;
798 let inode: Inode = self.read_obj(inode_block)?;
799
800 if inode.id >= self.next_inode_id {
801 self.next_inode_id = inode.id + 1;
802 }
803
804 match inode.kind {
805 InodeKind::Directory => {
806 if !inode.directory_page_ref.is_null() {
807 self.allocator
808 .mark_allocated(inode.directory_page_ref.block_id)?;
809 let dir_page: DirectoryPage =
810 self.read_obj(inode.directory_page_ref.block_id)?;
811 for entry in &dir_page.entries {
812 self.mark_inode_tree(entry.inode_ref.block_id)?;
813 }
814 }
815 }
816 InodeKind::File => {
817 if !inode.extent_map_ref.is_null() {
818 self.allocator
819 .mark_allocated(inode.extent_map_ref.block_id)?;
820 let extent_map: ExtentMap = self.read_obj(inode.extent_map_ref.block_id)?;
821 for entry in &extent_map.entries {
822 self.allocator.mark_allocated(entry.data_ref.block_id)?;
823 }
824 }
825 }
826 }
827 Ok(())
828 }
829}
830
831#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
833pub struct DirListEntry {
834 pub name: String,
835 pub kind: InodeKind,
836 pub size: u64,
837 pub inode_id: InodeId,
838}