1use core::{mem::MaybeUninit, ptr::copy_nonoverlapping};
31
32#[cfg(feature = "std")]
33extern crate std;
34
35use super::*;
36
37use alloc::boxed::Box;
38use alloc::string::String;
39use alloc::borrow::ToOwned;
40
41use crc::CRC_32_ISO_HDLC;
42
43const CRC32: crc::Crc<u32> = crc::Crc::<u32>::new(&CRC_32_ISO_HDLC);
44const CRC32_VERIFY: u32 = 0x2144DF1C;
45
46pub(crate) fn crc32_hash(bytes: &[u8]) -> u32 {
47 let mut digest = CRC32.digest();
48 digest.update(bytes);
49 digest.finalize()
50}
51
52pub(crate) fn crc32_verify(bytes: &[u8]) -> Result<(), FsError> {
53 if CRC32_VERIFY == crc32_hash(bytes) {
54 Ok(())
55 } else {
56 Err(FsError::ValidationFailed)
57 }
58}
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61pub enum EntryKind {
62 Directory,
63 File,
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67#[repr(C)]
68pub struct EntryId {
69 pub(crate) block_no: u64,
72 pub(crate) generation: u64,
75}
76
77pub const ROOT_DIR_ID: EntryId = EntryId {
78 block_no: 1,
79 generation: 1,
80};
81
82impl EntryId {
83 pub(crate) fn new(block_no: u64, generation: u64) -> Self {
84 Self {
85 block_no,
86 generation,
87 }
88 }
89
90 pub fn kind(&self) -> EntryKind {
91 if (self.generation & 1) == 1 {
92 EntryKind::Directory
93 } else {
94 EntryKind::File
95 }
96 }
97}
98
99#[derive(Clone, Copy, Debug)]
100#[repr(C)]
101pub struct Timestamp {
102 pub secs: u64,
103 pub ns: u32,
104 _pad: u32,
105}
106
107impl Timestamp {
108 pub fn now() -> Self {
109 {
110 #![cfg(feature = "std")]
111 let ts = std::time::UNIX_EPOCH.elapsed().unwrap();
112
113 Self {
114 secs: ts.as_secs(),
115 ns: ts.subsec_nanos(),
116 _pad: 0,
117 }
118 }
119 {
120 #![cfg(not(feature = "std"))]
121 Self {
122 secs: 0,
123 ns: 0,
124 _pad: 0,
125 }
126 }
127 }
128
129 pub const fn zero() -> Self {
130 Self {
131 secs: 0,
132 ns: 0,
133 _pad: 0,
134 }
135 }
136}
137
138#[cfg(feature = "std")]
139impl From<Timestamp> for std::time::SystemTime {
140 fn from(ts: Timestamp) -> Self {
141 let dur = std::time::Duration::new(ts.secs, ts.ns);
142 std::time::SystemTime::checked_add(&std::time::UNIX_EPOCH, dur).unwrap()
143 }
144}
145
146#[derive(Clone, Copy)]
148#[repr(C)]
149pub(crate) struct DirEntryInternal {
150 pub id: EntryId,
151 pub name: [u8; 255], pub name_len: u8,
153}
154
155const _: () = assert!(core::mem::size_of::<DirEntryInternal>() == 272);
156
157impl DirEntryInternal {
158 pub fn set_name(&mut self, name: &str) {
159 let bytes = name.as_bytes();
160 assert!(bytes.len() <= 255);
161
162 unsafe { copy_nonoverlapping(bytes.as_ptr(), self.name.as_mut_ptr(), bytes.len()) };
163 self.name_len = bytes.len() as u8;
164 }
165
166 pub fn to_owned(&self) -> Result<DirEntry, FsError> {
167 Ok(DirEntry {
168 id: self.id,
169 name: core::str::from_utf8(&self.name[0..(self.name_len as usize)])
170 .map_err(|_| FsError::Utf8Error)?
171 .to_owned(),
172 })
173 }
174}
175
176#[derive(Debug)]
177pub struct DirEntry {
178 pub id: EntryId,
179 pub name: String,
180}
181
182#[derive(Clone, Copy, Debug)]
183#[repr(C)]
184pub(crate) struct EntryMetadata {
185 pub id: EntryId,
186 pub parent_id: EntryId,
187 pub size: u64, pub created: Timestamp,
189 pub modified: Timestamp,
190 pub accessed: Timestamp, pub user_data: [u64; 4], pub _reserved_2: u32,
193 pub crc32: u32, }
195
196const _: () = assert!(core::mem::size_of::<EntryMetadata>() == 128);
197const _: () = assert!(core::mem::size_of::<EntryMetadata>() < (BLOCK_SIZE as usize));
198
199const _: () =
202 assert!(core::mem::size_of::<EntryMetadata>() <= core::mem::size_of::<DirEntryInternal>());
203
204pub(crate) const MAX_ENTRIES_IN_DATA_BLOCK: u64 =
206 BLOCK_SIZE / (core::mem::size_of::<DirEntryInternal>() as u64);
207const _: () = assert!(MAX_ENTRIES_IN_DATA_BLOCK == 15);
208
209pub(crate) const MAX_ENTRIES_IN_META_BLOCK: u64 = MAX_ENTRIES_IN_DATA_BLOCK - 1;
211const _: () = assert!(MAX_ENTRIES_IN_META_BLOCK == 14);
212
213pub(crate) const MAX_ENTRIES_ONLY_DATA_BLOCKS: u64 =
215 MAX_LINKS_IN_META_BLOCK * MAX_ENTRIES_IN_DATA_BLOCK;
216const _: () = assert!(MAX_ENTRIES_ONLY_DATA_BLOCKS == 7440);
217
218pub(crate) const MAX_ENTRIES_COVERED_BY_FIRST_LEVEL_BLOCKLIST: u64 =
219 MAX_ENTRIES_IN_DATA_BLOCK * 512;
220const _: () = assert!(MAX_ENTRIES_COVERED_BY_FIRST_LEVEL_BLOCKLIST == 7680);
221
222pub(crate) const MAX_BYTES_IN_META_BLOCK: u64 =
224 BLOCK_SIZE - (core::mem::size_of::<EntryMetadata>() as u64);
225const _: () = assert!(MAX_BYTES_IN_META_BLOCK == 3968);
226
227pub(crate) const MAX_LINKS_IN_META_BLOCK: u64 = MAX_BYTES_IN_META_BLOCK / 8;
229const _: () = assert!(MAX_LINKS_IN_META_BLOCK == 496);
230
231pub(crate) const MAX_BYTES_ONLY_DATA_BLOCKS: u64 = MAX_LINKS_IN_META_BLOCK * BLOCK_SIZE;
233const _: () = assert!(MAX_BYTES_ONLY_DATA_BLOCKS == 2_031_616);
234
235pub(crate) const BYTES_COVERED_BY_FIRST_LEVEL_BLOCKLIST: u64 = BLOCK_SIZE * BLOCK_SIZE / 8;
236const _: () = assert!(BYTES_COVERED_BY_FIRST_LEVEL_BLOCKLIST == 4096 * 512); pub(crate) const MAX_BYTES_SINGLE_LEVEL_LIST_BLOCKS: u64 =
240 MAX_LINKS_IN_META_BLOCK * BYTES_COVERED_BY_FIRST_LEVEL_BLOCKLIST;
241const _: () = assert!(MAX_BYTES_SINGLE_LEVEL_LIST_BLOCKS == 1_040_187_392); pub(crate) const BYTES_COVERED_BY_SECOND_LEVEL_BLOCKLIST: u64 =
244 BYTES_COVERED_BY_FIRST_LEVEL_BLOCKLIST * 512;
245const _: () = assert!(BYTES_COVERED_BY_SECOND_LEVEL_BLOCKLIST == 1024 * 1024 * 1024); pub(crate) const MAX_BYTES_LIST_OF_LISTS_BLOCKS: u64 =
249 MAX_LINKS_IN_META_BLOCK * BYTES_COVERED_BY_SECOND_LEVEL_BLOCKLIST;
250const _: () = assert!(MAX_BYTES_LIST_OF_LISTS_BLOCKS == 532_575_944_704); impl EntryMetadata {
253 pub fn new(id: EntryId, parent_id: EntryId) -> Self {
254 let now = Timestamp::now();
255 Self {
256 id,
257 parent_id,
258 size: 0,
259 created: now,
260 modified: now,
261 accessed: Timestamp::zero(),
262 user_data: [0; 4],
263 _reserved_2: 0,
264 crc32: 0,
265 }
266 }
267
268 pub fn as_bytes(&self) -> &[u8] {
269 unsafe {
270 core::slice::from_raw_parts(
271 self as *const _ as usize as *const u8,
272 core::mem::size_of::<Self>(),
273 )
274 }
275 }
276
277 pub fn set_crc32(&mut self) {
278 let bytes = unsafe {
279 core::slice::from_raw_parts(
280 self as *const _ as usize as *const u8,
281 core::mem::size_of::<Self>() - 4,
282 )
283 };
284 let crc32 = crc32_hash(bytes);
285 self.crc32 = crc32;
286 }
287
288 fn validate_basic(&self, id: EntryId) -> Result<(), FsError> {
289 crc32_verify(self.as_bytes())?;
290 if self.id != id {
291 return Err(FsError::ValidationFailed);
292 }
293 Ok(())
294 }
295
296 pub fn validate(&self, id: EntryId) -> Result<(), FsError> {
297 match id.kind() {
298 EntryKind::Directory => self.validate_dir(id),
299 EntryKind::File => self.validate_file(id),
300 }
301 }
302
303 pub fn validate_dir(&self, id: EntryId) -> Result<(), FsError> {
304 self.validate_basic(id)?;
305 if self.id.kind() != EntryKind::Directory {
306 return Err(FsError::ValidationFailed);
307 }
308 if self.size > MAX_DIR_ENTRIES {
309 return Err(FsError::ValidationFailed);
310 }
311
312 Ok(())
313 }
314
315 pub fn validate_file(&self, id: EntryId) -> Result<(), FsError> {
316 self.validate_basic(id)?;
317 if self.id.kind() != EntryKind::File {
318 return Err(FsError::ValidationFailed);
319 }
320
321 Ok(())
322 }
323}
324
325#[derive(Clone, Copy, Debug)]
326#[repr(C)]
327pub struct Attr {
328 pub id: EntryId,
329 pub size: u64, pub created: Timestamp,
331 pub modified: Timestamp,
332 pub accessed: Timestamp, pub user_data: [u64; 4], }
335
336impl From<&EntryMetadata> for Attr {
337 fn from(meta: &EntryMetadata) -> Self {
338 Self {
339 id: meta.id,
340 size: meta.size,
341 created: meta.created,
342 modified: meta.modified,
343 accessed: meta.accessed,
344 user_data: meta.user_data,
345 }
346 }
347}
348
349pub(crate) const MAGIC: u64 = 0x0c51_a0bb_b108_3d14; pub(crate) const TXN_TYPE_NONE: u32 = 0;
353pub(crate) const TXN_TYPE_ADD_NODE: u32 = 1;
354pub(crate) const TXN_TYPE_ADD_BYTES: u32 = 2;
355pub(crate) const TXN_TYPE_REMOVE_NODE: u32 = 3;
356pub(crate) const TXN_TYPE_REMOVE_BYTES: u32 = 4;
357pub(crate) const TXN_TYPE_MOVE: u32 = 5;
358
359#[derive(Clone, Copy, PartialEq, Eq)]
367#[repr(C)]
368pub(crate) struct SuperblockHeader {
369 pub magic: u64, pub version: u64, pub num_blocks: u64, pub free_blocks: u64, pub generation: u64, pub freelist_head: u64, pub empty_area_start: u64, pub txn_meta_block: u64, pub txn_data_block: u64, pub txn_link_block: u64, pub txn_list_of_links_block: u64, pub txn_blocks_owner: u64, pub txn_type: u32, pub crc32: u32, }
384
385impl SuperblockHeader {
386 pub fn as_bytes(&self) -> &[u8] {
387 unsafe {
388 core::slice::from_raw_parts(
389 self as *const _ as usize as *const u8,
390 core::mem::size_of::<Self>(),
391 )
392 }
393 }
394
395 pub fn set_crc32(&mut self) {
396 let bytes = unsafe {
397 core::slice::from_raw_parts(
398 self as *const _ as usize as *const u8,
399 core::mem::size_of::<Self>() - 4,
400 )
401 };
402 let crc32 = crc32_hash(bytes);
403 self.crc32 = crc32;
404 }
405
406 pub fn validate(&self) -> Result<(), FsError> {
407 crc32_verify(self.as_bytes())?;
408 if self.version != 1 {
409 return Err(FsError::UnsupportedVersion);
410 }
411 if self.magic == MAGIC {
412 Ok(())
413 } else {
414 return Err(FsError::ValidationFailed);
415 }
416 }
417}
418
419#[derive(Clone, Copy)]
420#[repr(C, align(4096))]
421pub(crate) struct Block {
422 bytes: [u8; 4096],
423}
424
425const _: () = assert!(core::mem::size_of::<Block>() as u64 == BLOCK_SIZE);
426
427impl Block {
428 pub const fn new_uninit() -> Self {
429 #[allow(invalid_value)]
430 unsafe {
431 MaybeUninit::<Self>::uninit().assume_init()
432 }
433 }
434
435 pub const fn new_zeroed() -> Self {
436 Self { bytes: [0; 4096] }
437 }
438
439 pub unsafe fn get<T>(&self) -> &T {
440 debug_assert!(core::mem::size_of::<T>() as u64 <= BLOCK_SIZE);
441
442 (self.bytes.as_ptr() as usize as *const T)
443 .as_ref()
444 .unwrap_unchecked()
445 }
446
447 pub unsafe fn get_mut<T>(&mut self) -> &mut T {
448 debug_assert!(core::mem::size_of::<T>() as u64 <= BLOCK_SIZE);
449
450 (self.bytes.as_ptr() as usize as *mut T)
451 .as_mut()
452 .unwrap_unchecked()
453 }
454
455 pub fn as_bytes(&self) -> &[u8] {
456 unsafe {
457 core::slice::from_raw_parts(
458 (self as *const _ as usize as *const u8)
459 .as_ref()
460 .unwrap_unchecked(),
461 BLOCK_SIZE as usize,
462 )
463 }
464 }
465
466 pub fn as_data_bytes_in_meta(&self) -> &[u8] {
467 unsafe {
468 core::slice::from_raw_parts(
469 (((self as *const _ as usize) + core::mem::size_of::<EntryMetadata>())
470 as *const u8)
471 .as_ref()
472 .unwrap_unchecked(),
473 BLOCK_SIZE as usize - core::mem::size_of::<EntryMetadata>(),
474 )
475 }
476 }
477
478 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
479 unsafe {
480 core::slice::from_raw_parts_mut(
481 (self as *mut _ as usize as *mut u8)
482 .as_mut()
483 .unwrap_unchecked(),
484 BLOCK_SIZE as usize,
485 )
486 }
487 }
488
489 pub fn as_data_bytes_in_meta_mut(&mut self) -> &mut [u8] {
490 unsafe {
491 core::slice::from_raw_parts_mut(
492 (((self as *mut _ as usize) + core::mem::size_of::<EntryMetadata>()) as *mut u8)
493 .as_mut()
494 .unwrap_unchecked(),
495 BLOCK_SIZE as usize - core::mem::size_of::<EntryMetadata>(),
496 )
497 }
498 }
499
500 pub fn set_dir_entry(&mut self, pos: usize, id: EntryId, name: &str) {
501 let entry = self.get_dir_entry_mut(pos);
502 entry.id = id;
503 entry.set_name(name);
504 }
505
506 pub fn get_dir_entry(&self, pos: usize) -> &DirEntryInternal {
507 assert!(pos as u64 <= MAX_ENTRIES_IN_DATA_BLOCK);
508 let offset = pos * core::mem::size_of::<DirEntryInternal>();
509 unsafe {
510 ((self.bytes.as_ptr() as usize + offset) as *const DirEntryInternal)
511 .as_ref()
512 .unwrap_unchecked()
513 }
514 }
515
516 pub fn get_dir_entry_mut(&mut self, pos: usize) -> &mut DirEntryInternal {
517 assert!(pos <= MAX_ENTRIES_IN_DATA_BLOCK as usize);
518 let offset = pos * core::mem::size_of::<DirEntryInternal>();
519 unsafe {
520 ((self.bytes.as_ptr() as usize + offset) as *mut DirEntryInternal)
521 .as_mut()
522 .unwrap_unchecked()
523 }
524 }
525
526 pub fn get_datablock_no_in_meta(&self, data_block_idx: u64) -> u64 {
527 assert!(data_block_idx <= MAX_LINKS_IN_META_BLOCK);
528 let offset = core::mem::size_of::<EntryMetadata>() + ((data_block_idx as usize) << 3);
529 debug_assert!(offset < BLOCK_SIZE as usize);
530 unsafe { *((self.bytes.as_ptr() as usize + offset) as *const u64) }
531 }
532
533 pub fn set_datablock_no_in_meta(&mut self, data_block_idx: u64, block_no: u64) {
534 assert!(data_block_idx <= MAX_LINKS_IN_META_BLOCK);
535 let offset = core::mem::size_of::<EntryMetadata>() + ((data_block_idx as usize) << 3);
536 assert!(offset < BLOCK_SIZE as usize);
537 unsafe {
538 *((self.bytes.as_ptr() as usize + offset) as *mut u64) = block_no;
539 }
540 }
541
542 pub fn get_datablock_no_in_link(&self, data_block_idx: u64) -> u64 {
543 assert!(data_block_idx < 512);
544 let offset = (data_block_idx as usize) << 3;
545 unsafe { *((self.bytes.as_ptr() as usize + offset) as *const u64) }
546 }
547
548 pub fn set_datablock_no_in_link(&mut self, data_block_idx: u64, block_no: u64) {
549 assert!(data_block_idx < 512);
550 let offset = (data_block_idx as usize) << 3;
551 unsafe {
552 *((self.bytes.as_ptr() as usize + offset) as *mut u64) = block_no;
553 }
554 }
555}
556
557pub(crate) struct Superblock {
558 block: Box<Block>,
559}
560
561impl Superblock {
562 pub fn from(block: Box<Block>) -> Result<Self, FsError> {
563 unsafe {
564 let fbh = block.get::<SuperblockHeader>();
565 fbh.validate()?;
566 }
567
568 Ok(Self { block })
569 }
570
571 pub fn header(&self) -> &SuperblockHeader {
572 unsafe { self.block.get::<SuperblockHeader>() }
573 }
574
575 pub fn header_mut(&mut self) -> &mut SuperblockHeader {
576 unsafe { self.block.get_mut::<SuperblockHeader>() }
577 }
578
579 pub fn ___as_bytes(&self) -> &[u8] {
580 self.block.as_bytes()
581 }
582
583 pub fn block(&self) -> &Block {
584 &self.block
585 }
586}
587
588pub(crate) fn validate_filename(name: &str) -> Result<(), FsError> {
589 if name.as_bytes().len() > 255 || name.contains('/') || name == "." || name == ".." {
590 return Err(FsError::InvalidArgument);
591 }
592
593 Ok(())
594}
595
596pub(crate) fn align_up(what: u64, how: u64) -> u64 {
597 debug_assert!(how.is_power_of_two());
598 (what + how - 1) & !(how - 1)
599}