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 {
20 store: Arc<dyn BlockStore>,
21 crypto: Arc<dyn CryptoEngine>,
22 codec: PostcardCodec,
23 allocator: BitmapAllocator,
24 txn: TransactionManager,
25 superblock: Option<Superblock>,
27 next_inode_id: InodeId,
29}
30
31fn max_chunk_payload(block_size: usize) -> usize {
35 if block_size > 200 {
38 block_size - 200
39 } else {
40 0
41 }
42}
43
44fn now_secs() -> u64 {
45 SystemTime::now()
46 .duration_since(UNIX_EPOCH)
47 .unwrap_or_default()
48 .as_secs()
49}
50
51impl FilesystemCore {
52 pub fn new(store: Arc<dyn BlockStore>, crypto: Arc<dyn CryptoEngine>) -> Self {
54 let total_blocks = store.total_blocks();
55 Self {
56 store,
57 crypto,
58 codec: PostcardCodec,
59 allocator: BitmapAllocator::new(total_blocks),
60 txn: TransactionManager::new(),
61 superblock: None,
62 next_inode_id: 1,
63 }
64 }
65
66 pub fn init_filesystem(&mut self) -> FsResult<()> {
71 let block_size = self.store.block_size() as u32;
72 let total_blocks = self.store.total_blocks();
73
74 let header = StorageHeader::new(block_size, total_blocks);
76 let header_bytes = self.codec.serialize_object(&header)?;
77 let bs = self.store.block_size();
78 let mut block = vec![0u8; bs];
79 rand::thread_rng().fill_bytes(&mut block);
80 let len = header_bytes.len() as u32;
81 block[..4].copy_from_slice(&len.to_le_bytes());
82 block[4..4 + header_bytes.len()].copy_from_slice(&header_bytes);
83 self.store.write_block(BLOCK_STORAGE_HEADER, &block)?;
84
85 let root_inode_id = self.alloc_inode_id();
87 let dir_page = DirectoryPage::new();
88 let dir_page_block = self.allocator.allocate()?;
89 write_encrypted_object(
90 self.store.as_ref(),
91 self.crypto.as_ref(),
92 &self.codec,
93 dir_page_block,
94 ObjectKind::DirectoryPage,
95 &dir_page,
96 )?;
97
98 let ts = now_secs();
99 let root_inode = Inode {
100 id: root_inode_id,
101 kind: InodeKind::Directory,
102 size: 0,
103 directory_page_ref: ObjectRef::new(dir_page_block),
104 extent_map_ref: ObjectRef::null(),
105 created_at: ts,
106 modified_at: ts,
107 };
108 let root_inode_block = self.allocator.allocate()?;
109 write_encrypted_object(
110 self.store.as_ref(),
111 self.crypto.as_ref(),
112 &self.codec,
113 root_inode_block,
114 ObjectKind::Inode,
115 &root_inode,
116 )?;
117
118 let sb = Superblock {
120 generation: 1,
121 root_inode_ref: ObjectRef::new(root_inode_block),
122 };
123 self.superblock = Some(sb.clone());
124
125 self.txn.commit(
127 self.store.as_ref(),
128 self.crypto.as_ref(),
129 &self.codec,
130 &self.allocator,
131 &sb,
132 )?;
133
134 Ok(())
135 }
136
137 pub fn open(&mut self) -> FsResult<()> {
139 let header = self.read_storage_header()?;
141 if !header.is_valid() {
142 return Err(FsError::InvalidSuperblock);
143 }
144
145 let (rp, was_b) = TransactionManager::recover_latest(self.store.as_ref(), &self.codec)?
147 .ok_or(FsError::InvalidRootPointer)?;
148
149 let sb: Superblock = read_encrypted_object(
151 self.store.as_ref(),
152 self.crypto.as_ref(),
153 &self.codec,
154 rp.superblock_ref.block_id,
155 )?;
156
157 let sb_bytes = self.codec.serialize_object(&sb)?;
159 let checksum = blake3::hash(&sb_bytes);
160 if *checksum.as_bytes() != rp.checksum {
161 return Err(FsError::InvalidSuperblock);
162 }
163
164 self.txn = TransactionManager::from_recovered(rp.generation, was_b);
165 self.superblock = Some(sb.clone());
166
167 self.rebuild_allocator(&sb)?;
169
170 Ok(())
171 }
172
173 pub fn create_file(&mut self, name: &str) -> FsResult<()> {
178 self.validate_name(name)?;
179 let sb = self
180 .superblock
181 .as_ref()
182 .ok_or(FsError::NotInitialized)?
183 .clone();
184
185 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
187 let mut dir_page: DirectoryPage = self.read_obj(root_inode.directory_page_ref.block_id)?;
188
189 if dir_page.entries.iter().any(|e| e.name == name) {
191 return Err(FsError::FileAlreadyExists(name.to_string()));
192 }
193
194 let extent_map = ExtentMap::new();
196 let em_block = self.allocator.allocate()?;
197 self.write_obj(em_block, ObjectKind::ExtentMap, &extent_map)?;
198
199 let inode_id = self.alloc_inode_id();
201 let ts = now_secs();
202 let file_inode = Inode {
203 id: inode_id,
204 kind: InodeKind::File,
205 size: 0,
206 directory_page_ref: ObjectRef::null(),
207 extent_map_ref: ObjectRef::new(em_block),
208 created_at: ts,
209 modified_at: ts,
210 };
211 let inode_block = self.allocator.allocate()?;
212 self.write_obj(inode_block, ObjectKind::Inode, &file_inode)?;
213
214 dir_page.entries.push(DirectoryEntry {
216 name: name.to_string(),
217 inode_ref: ObjectRef::new(inode_block),
218 inode_id,
219 kind: InodeKind::File,
220 });
221
222 let new_dp_block = self.allocator.allocate()?;
224 self.write_obj(new_dp_block, ObjectKind::DirectoryPage, &dir_page)?;
225
226 let mut new_root = root_inode.clone();
228 new_root.directory_page_ref = ObjectRef::new(new_dp_block);
229 new_root.modified_at = now_secs();
230 let new_root_block = self.allocator.allocate()?;
231 self.write_obj(new_root_block, ObjectKind::Inode, &new_root)?;
232
233 let new_sb = Superblock {
235 generation: sb.generation + 1,
236 root_inode_ref: ObjectRef::new(new_root_block),
237 };
238 self.commit_superblock(new_sb)?;
239
240 Ok(())
241 }
242
243 pub fn write_file(&mut self, name: &str, offset: u64, data: &[u8]) -> FsResult<()> {
246 let sb = self
247 .superblock
248 .as_ref()
249 .ok_or(FsError::NotInitialized)?
250 .clone();
251 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
252 let dir_page: DirectoryPage = self.read_obj(root_inode.directory_page_ref.block_id)?;
253
254 let entry = dir_page
255 .entries
256 .iter()
257 .find(|e| e.name == name)
258 .ok_or_else(|| FsError::FileNotFound(name.to_string()))?;
259
260 if entry.kind != InodeKind::File {
261 return Err(FsError::NotAFile(name.to_string()));
262 }
263
264 let file_inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
265 let mut extent_map: ExtentMap = self.read_obj(file_inode.extent_map_ref.block_id)?;
266
267 let mut buf = self.read_all_chunks(&extent_map)?;
270
271 let end = offset as usize + data.len();
272 if end > buf.len() {
273 buf.resize(end, 0);
274 }
275 buf[offset as usize..end].copy_from_slice(data);
276
277 let total_size = buf.len();
278
279 let chunk_size = max_chunk_payload(self.store.block_size());
281 if chunk_size == 0 {
282 return Err(FsError::DataTooLarge(total_size));
283 }
284
285 let mut new_entries = Vec::new();
286 for (i, chunk_data) in buf.chunks(chunk_size).enumerate() {
287 let data_block = self.allocator.allocate()?;
288 write_encrypted_raw(
289 self.store.as_ref(),
290 self.crypto.as_ref(),
291 &self.codec,
292 data_block,
293 ObjectKind::FileDataChunk,
294 chunk_data,
295 )?;
296 new_entries.push(ExtentEntry {
297 chunk_index: i as u64,
298 data_ref: ObjectRef::new(data_block),
299 plaintext_len: chunk_data.len() as u32,
300 });
301 }
302
303 extent_map.entries = new_entries;
305
306 let new_em_block = self.allocator.allocate()?;
308 self.write_obj(new_em_block, ObjectKind::ExtentMap, &extent_map)?;
309
310 let mut new_file_inode = file_inode.clone();
312 new_file_inode.size = total_size as u64;
313 new_file_inode.extent_map_ref = ObjectRef::new(new_em_block);
314 new_file_inode.modified_at = now_secs();
315 let new_inode_block = self.allocator.allocate()?;
316 self.write_obj(new_inode_block, ObjectKind::Inode, &new_file_inode)?;
317
318 let mut new_dir_page = dir_page.clone();
320 for e in &mut new_dir_page.entries {
321 if e.name == name {
322 e.inode_ref = ObjectRef::new(new_inode_block);
323 }
324 }
325 let new_dp_block = self.allocator.allocate()?;
326 self.write_obj(new_dp_block, ObjectKind::DirectoryPage, &new_dir_page)?;
327
328 let mut new_root = root_inode.clone();
330 new_root.directory_page_ref = ObjectRef::new(new_dp_block);
331 new_root.modified_at = now_secs();
332 let new_root_block = self.allocator.allocate()?;
333 self.write_obj(new_root_block, ObjectKind::Inode, &new_root)?;
334
335 let new_sb = Superblock {
336 generation: sb.generation + 1,
337 root_inode_ref: ObjectRef::new(new_root_block),
338 };
339 self.commit_superblock(new_sb)?;
340
341 Ok(())
342 }
343
344 pub fn read_file(&self, name: &str, offset: u64, len: usize) -> FsResult<Vec<u8>> {
346 let sb = self.superblock.as_ref().ok_or(FsError::NotInitialized)?;
347 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
348 let dir_page: DirectoryPage = self.read_obj(root_inode.directory_page_ref.block_id)?;
349
350 let entry = dir_page
351 .entries
352 .iter()
353 .find(|e| e.name == name)
354 .ok_or_else(|| FsError::FileNotFound(name.to_string()))?;
355
356 if entry.kind != InodeKind::File {
357 return Err(FsError::NotAFile(name.to_string()));
358 }
359
360 let file_inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
361 let extent_map: ExtentMap = self.read_obj(file_inode.extent_map_ref.block_id)?;
362
363 let full_data = self.read_all_chunks(&extent_map)?;
364
365 let start = offset as usize;
366 if start >= full_data.len() {
367 return Ok(Vec::new());
368 }
369 let end = std::cmp::min(start + len, full_data.len());
370 Ok(full_data[start..end].to_vec())
371 }
372
373 pub fn list_directory(&self) -> FsResult<Vec<DirListEntry>> {
375 let sb = self.superblock.as_ref().ok_or(FsError::NotInitialized)?;
376 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
377 let dir_page: DirectoryPage = self.read_obj(root_inode.directory_page_ref.block_id)?;
378
379 let mut result = Vec::new();
380 for entry in &dir_page.entries {
381 let inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
382 result.push(DirListEntry {
383 name: entry.name.clone(),
384 kind: entry.kind,
385 size: inode.size,
386 inode_id: entry.inode_id,
387 });
388 }
389 Ok(result)
390 }
391
392 pub fn create_directory(&mut self, name: &str) -> FsResult<()> {
394 self.validate_name(name)?;
395 let sb = self
396 .superblock
397 .as_ref()
398 .ok_or(FsError::NotInitialized)?
399 .clone();
400 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
401 let mut dir_page: DirectoryPage = self.read_obj(root_inode.directory_page_ref.block_id)?;
402
403 if dir_page.entries.iter().any(|e| e.name == name) {
404 return Err(FsError::DirectoryAlreadyExists(name.to_string()));
405 }
406
407 let sub_dp = DirectoryPage::new();
409 let sub_dp_block = self.allocator.allocate()?;
410 self.write_obj(sub_dp_block, ObjectKind::DirectoryPage, &sub_dp)?;
411
412 let inode_id = self.alloc_inode_id();
414 let ts = now_secs();
415 let dir_inode = Inode {
416 id: inode_id,
417 kind: InodeKind::Directory,
418 size: 0,
419 directory_page_ref: ObjectRef::new(sub_dp_block),
420 extent_map_ref: ObjectRef::null(),
421 created_at: ts,
422 modified_at: ts,
423 };
424 let inode_block = self.allocator.allocate()?;
425 self.write_obj(inode_block, ObjectKind::Inode, &dir_inode)?;
426
427 dir_page.entries.push(DirectoryEntry {
428 name: name.to_string(),
429 inode_ref: ObjectRef::new(inode_block),
430 inode_id,
431 kind: InodeKind::Directory,
432 });
433
434 let new_dp_block = self.allocator.allocate()?;
436 self.write_obj(new_dp_block, ObjectKind::DirectoryPage, &dir_page)?;
437
438 let mut new_root = root_inode.clone();
439 new_root.directory_page_ref = ObjectRef::new(new_dp_block);
440 new_root.modified_at = now_secs();
441 let new_root_block = self.allocator.allocate()?;
442 self.write_obj(new_root_block, ObjectKind::Inode, &new_root)?;
443
444 let new_sb = Superblock {
445 generation: sb.generation + 1,
446 root_inode_ref: ObjectRef::new(new_root_block),
447 };
448 self.commit_superblock(new_sb)?;
449
450 Ok(())
451 }
452
453 pub fn remove_file(&mut self, name: &str) -> FsResult<()> {
455 let sb = self
456 .superblock
457 .as_ref()
458 .ok_or(FsError::NotInitialized)?
459 .clone();
460 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
461 let mut dir_page: DirectoryPage = self.read_obj(root_inode.directory_page_ref.block_id)?;
462
463 let idx = dir_page
464 .entries
465 .iter()
466 .position(|e| e.name == name)
467 .ok_or_else(|| FsError::FileNotFound(name.to_string()))?;
468
469 let entry = &dir_page.entries[idx];
470 if entry.kind == InodeKind::Directory {
471 let dir_inode: Inode = self.read_obj(entry.inode_ref.block_id)?;
473 let sub_page: DirectoryPage = self.read_obj(dir_inode.directory_page_ref.block_id)?;
474 if !sub_page.entries.is_empty() {
475 return Err(FsError::DirectoryNotEmpty(name.to_string()));
476 }
477 }
478
479 dir_page.entries.remove(idx);
481
482 let new_dp_block = self.allocator.allocate()?;
484 self.write_obj(new_dp_block, ObjectKind::DirectoryPage, &dir_page)?;
485
486 let mut new_root = root_inode.clone();
487 new_root.directory_page_ref = ObjectRef::new(new_dp_block);
488 new_root.modified_at = now_secs();
489 let new_root_block = self.allocator.allocate()?;
490 self.write_obj(new_root_block, ObjectKind::Inode, &new_root)?;
491
492 let new_sb = Superblock {
493 generation: sb.generation + 1,
494 root_inode_ref: ObjectRef::new(new_root_block),
495 };
496 self.commit_superblock(new_sb)?;
497
498 Ok(())
499 }
500
501 pub fn rename(&mut self, old_name: &str, new_name: &str) -> FsResult<()> {
503 self.validate_name(new_name)?;
504 let sb = self
505 .superblock
506 .as_ref()
507 .ok_or(FsError::NotInitialized)?
508 .clone();
509 let root_inode: Inode = self.read_obj(sb.root_inode_ref.block_id)?;
510 let mut dir_page: DirectoryPage = self.read_obj(root_inode.directory_page_ref.block_id)?;
511
512 if dir_page.entries.iter().any(|e| e.name == new_name) {
514 return Err(FsError::FileAlreadyExists(new_name.to_string()));
515 }
516
517 let entry = dir_page
518 .entries
519 .iter_mut()
520 .find(|e| e.name == old_name)
521 .ok_or_else(|| FsError::FileNotFound(old_name.to_string()))?;
522
523 entry.name = new_name.to_string();
524
525 let new_dp_block = self.allocator.allocate()?;
527 self.write_obj(new_dp_block, ObjectKind::DirectoryPage, &dir_page)?;
528
529 let mut new_root = root_inode.clone();
530 new_root.directory_page_ref = ObjectRef::new(new_dp_block);
531 new_root.modified_at = now_secs();
532 let new_root_block = self.allocator.allocate()?;
533 self.write_obj(new_root_block, ObjectKind::Inode, &new_root)?;
534
535 let new_sb = Superblock {
536 generation: sb.generation + 1,
537 root_inode_ref: ObjectRef::new(new_root_block),
538 };
539 self.commit_superblock(new_sb)?;
540
541 Ok(())
542 }
543
544 pub fn sync(&self) -> FsResult<()> {
546 self.store.sync()
547 }
548
549 fn alloc_inode_id(&mut self) -> InodeId {
552 let id = self.next_inode_id;
553 self.next_inode_id += 1;
554 id
555 }
556
557 fn validate_name(&self, name: &str) -> FsResult<()> {
558 if name.is_empty() || name.contains('/') || name.contains('\0') {
559 return Err(FsError::Internal("invalid name".into()));
560 }
561 if name.len() > MAX_NAME_LEN {
562 return Err(FsError::NameTooLong(name.len(), MAX_NAME_LEN));
563 }
564 Ok(())
565 }
566
567 fn read_obj<T: serde::de::DeserializeOwned>(&self, block_id: u64) -> FsResult<T> {
568 read_encrypted_object(
569 self.store.as_ref(),
570 self.crypto.as_ref(),
571 &self.codec,
572 block_id,
573 )
574 }
575
576 fn write_obj<T: serde::Serialize>(
577 &self,
578 block_id: u64,
579 kind: ObjectKind,
580 obj: &T,
581 ) -> FsResult<()> {
582 write_encrypted_object(
583 self.store.as_ref(),
584 self.crypto.as_ref(),
585 &self.codec,
586 block_id,
587 kind,
588 obj,
589 )
590 }
591
592 fn read_all_chunks(&self, extent_map: &ExtentMap) -> FsResult<Vec<u8>> {
593 let mut entries = extent_map.entries.clone();
594 entries.sort_by_key(|e| e.chunk_index);
595
596 let mut buf = Vec::new();
597 for entry in &entries {
598 let chunk = read_encrypted_raw(
599 self.store.as_ref(),
600 self.crypto.as_ref(),
601 &self.codec,
602 entry.data_ref.block_id,
603 )?;
604 let len = entry.plaintext_len as usize;
606 if len <= chunk.len() {
607 buf.extend_from_slice(&chunk[..len]);
608 } else {
609 buf.extend_from_slice(&chunk);
610 }
611 }
612 Ok(buf)
613 }
614
615 fn read_storage_header(&self) -> FsResult<StorageHeader> {
616 let block = self.store.read_block(BLOCK_STORAGE_HEADER)?;
617 if block.len() < 4 {
618 return Err(FsError::InvalidSuperblock);
619 }
620 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
621 if len == 0 || 4 + len > block.len() {
622 return Err(FsError::InvalidSuperblock);
623 }
624 self.codec
625 .deserialize_object::<StorageHeader>(&block[4..4 + len])
626 }
627
628 fn commit_superblock(&mut self, sb: Superblock) -> FsResult<()> {
629 self.txn.commit(
630 self.store.as_ref(),
631 self.crypto.as_ref(),
632 &self.codec,
633 &self.allocator,
634 &sb,
635 )?;
636 self.superblock = Some(sb);
637 Ok(())
638 }
639
640 fn rebuild_allocator(&mut self, sb: &Superblock) -> FsResult<()> {
643 let (rp, _) = TransactionManager::recover_latest(self.store.as_ref(), &self.codec)?
650 .ok_or(FsError::InvalidRootPointer)?;
651 self.allocator.mark_allocated(rp.superblock_ref.block_id)?;
652
653 self.mark_inode_tree(sb.root_inode_ref.block_id)?;
655
656 Ok(())
660 }
661
662 fn mark_inode_tree(&mut self, inode_block: u64) -> FsResult<()> {
663 self.allocator.mark_allocated(inode_block)?;
664 let inode: Inode = self.read_obj(inode_block)?;
665
666 if inode.id >= self.next_inode_id {
667 self.next_inode_id = inode.id + 1;
668 }
669
670 match inode.kind {
671 InodeKind::Directory => {
672 if !inode.directory_page_ref.is_null() {
673 self.allocator
674 .mark_allocated(inode.directory_page_ref.block_id)?;
675 let dir_page: DirectoryPage =
676 self.read_obj(inode.directory_page_ref.block_id)?;
677 for entry in &dir_page.entries {
678 self.mark_inode_tree(entry.inode_ref.block_id)?;
679 }
680 }
681 }
682 InodeKind::File => {
683 if !inode.extent_map_ref.is_null() {
684 self.allocator
685 .mark_allocated(inode.extent_map_ref.block_id)?;
686 let extent_map: ExtentMap = self.read_obj(inode.extent_map_ref.block_id)?;
687 for entry in &extent_map.entries {
688 self.allocator.mark_allocated(entry.data_ref.block_id)?;
689 }
690 }
691 }
692 }
693 Ok(())
694 }
695}
696
697#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
699pub struct DirListEntry {
700 pub name: String,
701 pub kind: InodeKind,
702 pub size: u64,
703 pub inode_id: InodeId,
704}