1use crate::block_cache::BlockCache;
5use crate::checkpoint::*;
6use crate::crypto;
7use crate::dir::{DentryBlock, DirEntry};
8use crate::inode::{self, Inode};
9use crate::nat::{Nat, NatJournal, RawNatEntry, SummaryBlock};
10use crate::superblock::{
11 BLOCK_SIZE, BLOCKS_PER_SEGMENT, F2FS_MAGIC, SEGMENT_SIZE, SUPERBLOCK_OFFSET, SuperBlock,
12 f2fs_crc32,
13};
14use anyhow::{Error, anyhow, bail, ensure};
15use async_trait::async_trait;
16use std::collections::{HashMap, HashSet};
17use std::sync::Arc;
18use storage_device::Device;
19use storage_device::buffer::Buffer;
20use zerocopy::FromBytes;
21
22pub const NULL_ADDR: u32 = 0;
24pub const NEW_ADDR: u32 = 0xffffffff;
26
27#[async_trait]
30pub(super) trait Reader {
31 async fn read_raw_block(&self, block_addr: u32) -> Result<Buffer<'_>, Error>;
34
35 async fn read_node(&self, nid: u32) -> Result<Buffer<'_>, Error>;
37
38 fn get_key(&self, _identifier: &[u8; 16]) -> Option<&[u8; 64]> {
40 None
41 }
42
43 fn fs_uuid(&self) -> &[u8; 16];
45
46 fn get_decryptor_for_inode(&self, inode: &Inode) -> Option<crypto::PerFileDecryptor> {
49 if let Some(context) = inode.context {
50 if let Some(main_key) = self.get_key(&context.main_key_identifier) {
51 return Some(crypto::PerFileDecryptor::new(main_key, context, self.fs_uuid()));
52 }
53 }
54 None
55 }
56
57 async fn get_nat_entry(&self, nid: u32) -> Result<RawNatEntry, Error>;
59}
60
61pub struct F2fsReader {
62 device: Arc<dyn Device>,
63 superblock: SuperBlock, checkpoint: CheckpointPack, cp_start_block: u32, nat: Nat,
67 orphan_inodes: HashSet<u32>,
68
69 keys: HashMap<[u8; 16], [u8; 64]>,
71 cache: BlockCache,
72}
73
74impl Drop for F2fsReader {
75 fn drop(&mut self) {
76 self.keys.values_mut().for_each(|v| {
78 *v = [0u8; 64];
79 });
80 }
81}
82
83impl F2fsReader {
84 pub fn superblock(&self) -> &SuperBlock {
85 &self.superblock
86 }
87
88 pub fn checkpoint(&self) -> &CheckpointPack {
89 &self.checkpoint
90 }
91
92 pub async fn open_device(device: Arc<dyn Device>) -> Result<Self, Error> {
93 let (superblock, checkpoints) =
94 match Self::try_from_superblock(device.as_ref(), SUPERBLOCK_OFFSET).await {
95 Ok(x) => x,
96 Err(e) => Self::try_from_superblock(device.as_ref(), SUPERBLOCK_OFFSET * 2)
97 .await
98 .map_err(|_| e)?,
99 };
100
101 let mut last_error = anyhow!("No checkpoints found");
102
103 for (checkpoint, cp_start_block) in checkpoints {
104 let mut this = Self {
105 device: device.clone(),
106 superblock,
107 checkpoint,
108 cp_start_block,
109 nat: Nat::new(0, vec![], HashMap::new()),
110 orphan_inodes: HashSet::new(),
111 keys: HashMap::with_capacity(16),
112 cache: BlockCache::new(1024, BLOCK_SIZE),
113 };
114
115 let nat_journal = match this.read_nat_journal().await {
116 Ok(j) => j,
117 Err(e) => {
118 let ver = this.checkpoint.header.checkpoint_ver;
119 log::warn!(
120 "Failed to initialize NAT journal from checkpoint (Ver {} at {}): {}. Trying next.",
121 ver,
122 cp_start_block,
123 e
124 );
125 last_error = e;
126 continue;
127 }
128 };
129
130 match this.read_orphan_inodes().await {
131 Ok(orphans) => {
132 this.orphan_inodes = orphans;
133 this.nat = Nat::new(
134 this.superblock.nat_blkaddr,
135 this.checkpoint.nat_bitmap.clone(),
136 nat_journal,
137 );
138 return Ok(this);
139 }
140 Err(e) => {
141 let ver = this.checkpoint.header.checkpoint_ver;
142 log::warn!(
143 "Failed to read orphan inodes from checkpoint (Ver {} at {}): {}. Trying next.",
144 ver,
145 cp_start_block,
146 e
147 );
148 last_error = e;
149 continue;
150 }
151 }
152 }
153
154 Err(last_error)
155 }
156
157 async fn try_from_superblock(
158 device: &dyn Device,
159 superblock_offset: u64,
160 ) -> Result<(SuperBlock, Vec<(CheckpointPack, u32)>), Error> {
161 let superblock = SuperBlock::read_from_device(device, superblock_offset).await?;
162 let checkpoint_addr = superblock.cp_blkaddr;
163 let checkpoint_a_offset = BLOCK_SIZE as u64 * checkpoint_addr as u64;
164 let checkpoint_b_offset = checkpoint_a_offset + SEGMENT_SIZE as u64;
165
166 let mut checkpoints = Vec::new();
167
168 if let Ok(cp) =
170 CheckpointPack::read_from_device(device, checkpoint_a_offset, superblock.cp_payload)
171 .await
172 {
173 checkpoints.push((cp, checkpoint_addr));
174 }
175 if let Ok(cp) =
176 CheckpointPack::read_from_device(device, checkpoint_b_offset, superblock.cp_payload)
177 .await
178 {
179 checkpoints.push((cp, checkpoint_addr + BLOCKS_PER_SEGMENT as u32));
180 }
181
182 if checkpoints.is_empty() {
183 bail!("Failed to read any valid checkpoint");
184 }
185
186 checkpoints.sort_by(|(a, _), (b, _)| {
188 let va = a.header.checkpoint_ver;
189 let vb = b.header.checkpoint_ver;
190 vb.cmp(&va)
191 });
192
193 const MIN_METADATA_SEGMENT_COUNT: u32 = 8;
195
196 let first_cp = &checkpoints[0].0;
198
199 let metadata_segment_count = superblock
201 .segment_count_sit
202 .checked_add(superblock.segment_count_nat)
203 .and_then(|v| v.checked_add(first_cp.header.rsvd_segment_count))
204 .and_then(|v| v.checked_add(superblock.segment_count_ssa))
205 .and_then(|v| v.checked_add(superblock.segment_count_ckpt))
206 .ok_or_else(|| anyhow::anyhow!("Segment counts overflow"))?;
207 ensure!(
208 metadata_segment_count <= superblock.segment_count
209 && metadata_segment_count >= MIN_METADATA_SEGMENT_COUNT,
210 "Bad segment counts in checkpoint"
211 );
212 Ok((superblock, checkpoints))
213 }
214
215 pub fn checkpoint_start_addr(&self) -> u32 {
217 self.cp_start_block
218 }
219
220 fn nat(&self) -> &Nat {
221 &self.nat
222 }
223 pub fn summary_block_addr(&self) -> u32 {
225 self.checkpoint_start_addr() + self.checkpoint.header.cp_pack_start_sum
226 }
227
228 async fn read_nat_journal(&mut self) -> Result<HashMap<u32, RawNatEntry>, Error> {
229 if self.checkpoint.header.ckpt_flags & CKPT_FLAG_COMPACT_SUMMARY != 0 {
230 let summary_addr = self.summary_block_addr();
233 let block = self.read_raw_block(summary_addr).await?;
234 let n_nats = block.as_ptr_slice().read::<u16>().unwrap();
235 let nat_journal = block
236 .as_ptr_slice()
237 .subslice(2..2 + std::mem::size_of::<NatJournal>())
238 .read::<NatJournal>()
239 .unwrap();
240 ensure!(
241 (n_nats as usize) <= nat_journal.entries.len(),
242 "n_nats {} larger than block size {}",
243 n_nats,
244 nat_journal.entries.len()
245 );
246 Ok(HashMap::from_iter(
247 nat_journal.entries[..n_nats as usize].iter().map(|e| (e.ino, e.entry)),
248 ))
249 } else {
250 let summary_addr = self.summary_block_addr();
253 let block = self.read_raw_block(summary_addr).await?;
254
255 let summary = block
256 .as_ptr_slice()
257 .read::<SummaryBlock>()
258 .ok_or_else(|| anyhow!("Block size too small"))?;
259 ensure!(summary.footer.entry_type == 0u8, "sum_type != 0 in summary footer");
260 #[cfg(not(fuzz))]
261 {
262 let data = block.to_vec();
263 let actual_checksum = f2fs_crc32(F2FS_MAGIC, &data[..BLOCK_SIZE - 4]);
264 let expected_checksum = summary.footer.check_sum;
265 if actual_checksum != expected_checksum {
266 log::warn!(
268 "Summary block checksum mismatch (actual: 0x{:x}, expected: 0x{:x}). \
269 This is normal for checkpoints with CP_CRC_RECOVERY_FLAG.",
270 actual_checksum,
271 expected_checksum
272 );
273 }
274 }
275 let n_nats = summary.n_nats;
276 ensure!(
277 (n_nats as usize) <= summary.nat_journal.entries.len(),
278 "n_nats {} larger than block size {}",
279 n_nats,
280 summary.nat_journal.entries.len()
281 );
282 let mut out = HashMap::new();
283 for i in 0..n_nats as usize {
284 out.insert(
285 summary.nat_journal.entries[i].ino,
286 summary.nat_journal.entries[i].entry,
287 );
288 }
289 Ok(out)
290 }
291 }
292
293 async fn read_orphan_inodes(&mut self) -> Result<HashSet<u32>, Error> {
294 let mut orphans = HashSet::new();
295 if self.checkpoint.header.ckpt_flags & CP_ORPHAN_PRESENT_FLAG != 0 {
296 let start_blk = self.checkpoint_start_addr() + 1 + self.superblock.cp_payload;
297 let end_blk = self.summary_block_addr();
298 ensure!(start_blk < end_blk, "CP_ORPHAN_PRESENT_FLAG set with zero orphan blocks");
299 let total_orphan_blocks = (end_blk - start_blk) as u16;
300 let root_ino = self.superblock.root_ino;
301 for blk_addr in start_blk..end_blk {
302 let block = self.read_raw_block(blk_addr).await?;
303 let orphan_block = block
304 .as_ptr_slice()
305 .read::<OrphanBlock>()
306 .ok_or_else(|| anyhow!("Block size too small for OrphanBlock"))?;
307 let blk_index = (blk_addr - start_blk + 1) as u16;
308 let blk_addr_val = orphan_block.blk_addr;
309 let blk_count_val = orphan_block.blk_count;
310 ensure!(
311 blk_addr_val == blk_index,
312 "Invalid orphan block blk_addr: {blk_addr_val} != {blk_index}"
313 );
314 ensure!(
315 blk_count_val == total_orphan_blocks,
316 "Invalid orphan block blk_count: {blk_count_val} != {total_orphan_blocks}"
317 );
318 #[cfg(not(fuzz))]
319 if orphan_block.check_sum != 0 {
320 let data = block.to_vec();
321 let expected_crc = orphan_block.check_sum;
322 let actual_crc = f2fs_crc32(F2FS_MAGIC, &data[..BLOCK_SIZE - 4]);
323 ensure!(
324 actual_crc == expected_crc,
325 "Bad OrphanBlock checksum ({actual_crc:08x} != {expected_crc:08x})"
326 );
327 }
328 let entry_count = orphan_block.entry_count as usize;
329 ensure!(
330 entry_count <= ORPHANS_PER_BLOCK,
331 "Invalid orphan entry count: {entry_count} > {ORPHANS_PER_BLOCK}"
332 );
333 for i in 0..entry_count {
334 let ino = orphan_block.ino[i];
335 ensure!(ino >= root_ino && ino != NEW_ADDR, "Invalid orphan ino {ino}");
336 orphans.insert(ino);
337 }
338 }
339 }
340 Ok(orphans)
341 }
342
343 pub fn orphan_inodes(&self) -> &HashSet<u32> {
345 &self.orphan_inodes
346 }
347
348 pub fn is_orphan(&self, ino: u32) -> bool {
350 self.orphan_inodes.contains(&ino)
351 }
352
353 pub fn root_ino(&self) -> u32 {
354 self.superblock.root_ino
355 }
356
357 pub fn max_ino(&self) -> u32 {
360 (self.checkpoint.nat_bitmap.len() * 8) as u32
361 }
362
363 pub fn add_key(&mut self, main_key: &[u8; 64]) -> [u8; 16] {
366 let identifier = fscrypt::main_key_to_identifier(main_key);
367 println!("Adding key with identifier {}", hex::encode(identifier));
368 self.keys.insert(identifier.clone(), main_key.clone());
369 identifier
370 }
371
372 pub async fn readdir(&self, ino: u32) -> Result<Vec<DirEntry>, Error> {
374 let inode = Inode::try_load(self, ino).await?;
375 let decryptor = self.get_decryptor_for_inode(&inode);
376 let mode = inode.header.mode;
377 let advise_flags = inode.header.advise_flags;
378 let flags = inode.header.flags;
379 ensure!(mode.contains(inode::Mode::Directory), "not a directory");
380 if let Some(entries) = inode.get_inline_dir_entries(
381 advise_flags.contains(inode::AdviseFlags::Encrypted),
382 flags.contains(inode::Flags::Casefold),
383 &decryptor,
384 )? {
385 Ok(entries)
386 } else {
387 let mut entries = Vec::new();
388
389 for mut extent in inode.data_blocks() {
394 for _ in 0..extent.length {
395 let dentry_block = self
396 .read_raw_block(extent.physical_block_num)
397 .await?
398 .as_ptr_slice()
399 .read::<DentryBlock>()
400 .ok_or_else(|| anyhow!("Block size too small"))?;
401 entries.append(&mut dentry_block.get_entries(
402 ino,
403 advise_flags.contains(inode::AdviseFlags::Encrypted),
404 flags.contains(inode::Flags::Casefold),
405 &decryptor,
406 )?);
407 extent.physical_block_num += 1;
408 }
409 }
410 Ok(entries)
411 }
412 }
413
414 pub async fn read_inode(&self, ino: u32) -> Result<Box<Inode>, Error> {
416 Inode::try_load(self, ino).await
417 }
418
419 pub fn read_symlink(&self, inode: &Inode) -> Result<Box<[u8]>, Error> {
421 if let Some(inline_data) = inode.inline_data.as_deref() {
422 let mut filename = inline_data.to_vec();
423 if inode.header.advise_flags.contains(inode::AdviseFlags::Encrypted) {
424 ensure!(filename.len() >= 2, "invalid encrypted symlink");
426 let symlink_len = u16::read_from_bytes(&filename[..2]).unwrap();
427 filename.drain(..2);
428 filename.truncate(symlink_len as usize);
429 ensure!(symlink_len == filename.len() as u16, "invalid encrypted symlink");
430 if let Some(decryptor) = self.get_decryptor_for_inode(inode) {
431 decryptor.decrypt_filename_data(inode.footer.ino, &mut filename);
432 } else {
433 let proxy_filename: String =
435 fscrypt::proxy_filename::ProxyFilename::new_with_hash_code(0, &filename)
436 .into();
437 filename = proxy_filename.as_bytes().to_vec();
438 }
439 while let Some(0) = filename.last() {
442 filename.pop();
443 }
444 }
445 Ok(filename.into_boxed_slice())
446 } else {
447 bail!("Not a valid symlink");
448 }
449 }
450
451 pub async fn read_data(&self, inode: &Inode, block_num: u32) -> Result<Option<Vec<u8>>, Error> {
454 let inline_flags = inode.header.inline_flags;
455 ensure!(
456 !inline_flags.contains(crate::InlineFlags::Data),
457 "Can't use read_data() on inline file."
458 );
459 let block_addr = inode.data_block_addr(block_num);
460 if block_addr == NULL_ADDR || block_addr == NEW_ADDR {
461 return Ok(None);
463 }
464 let buffer = self.read_raw_block(block_addr).await?;
465 let mut data = buffer.to_vec();
466 if let Some(decryptor) = self.get_decryptor_for_inode(inode) {
467 decryptor.decrypt_data(inode.footer.ino, block_num, &mut data);
468 }
469 Ok(Some(data))
470 }
471}
472
473#[async_trait]
474impl Reader for F2fsReader {
475 async fn read_raw_block(&self, block_addr: u32) -> Result<Buffer<'_>, Error> {
477 if let Some(block) = self.cache.get_buffer(block_addr, self.device.as_ref()).await {
478 return Ok(block);
479 }
480
481 const READAHEAD: u64 = 16;
482 let end = std::cmp::min(block_addr as u64 + READAHEAD, self.device.block_count());
483 let count = end.saturating_sub(block_addr as u64).max(1) as usize;
484
485 let mut buffer = self.device.allocate_buffer(count * BLOCK_SIZE).await;
486 self.device
487 .read(block_addr as u64 * BLOCK_SIZE as u64, buffer.as_mut())
488 .await
489 .map_err(|_| anyhow!("device read failed"))?;
490
491 for i in 0..count {
492 let subslice = buffer.as_ptr_slice().subslice(i * BLOCK_SIZE..(i + 1) * BLOCK_SIZE);
493 self.cache.insert(block_addr + i as u32, subslice.to_vec());
494 }
495 Ok(self.cache.get_buffer(block_addr, self.device.as_ref()).await.unwrap())
496 }
497
498 async fn read_node(&self, nid: u32) -> Result<Buffer<'_>, Error> {
499 let nat_entry = self.get_nat_entry(nid).await?;
500 self.read_raw_block(nat_entry.block_addr).await
501 }
502
503 fn get_key(&self, identifier: &[u8; 16]) -> Option<&[u8; 64]> {
504 self.keys.get(identifier)
505 }
506
507 fn fs_uuid(&self) -> &[u8; 16] {
508 &self.superblock.uuid
509 }
510
511 async fn get_nat_entry(&self, nid: u32) -> Result<RawNatEntry, Error> {
512 if let Some(entry) = self.nat().nat_journal.get(&nid) {
513 return Ok(*entry);
514 }
515 let nat_block_addr = self.nat().get_nat_block_for_entry(nid)?;
516 let offset = self.nat().get_nat_block_offset_for_entry(nid);
517 let block = self.read_raw_block(nat_block_addr).await?;
518 let entry = block
519 .as_ptr_slice()
520 .subslice(offset..offset + std::mem::size_of::<RawNatEntry>())
521 .read::<RawNatEntry>()
522 .ok_or_else(|| anyhow!("Block size too small"))?;
523 Ok(entry)
524 }
525}
526
527#[cfg(test)]
528mod test {
529 use super::*;
530 use crate::dir::FileType;
531 use crate::{open_f2fs_test_image, xattr};
532 use std::collections::HashSet;
533 use std::path::PathBuf;
534 use std::sync::Arc;
535
536 #[fuchsia::test]
537 async fn test_open_fs() {
538 let device = open_f2fs_test_image();
539
540 let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
541 assert_eq!(f2fs.root_ino(), 3);
543 let superblock = &f2fs.superblock;
544 let major_ver = superblock.major_ver;
545 let minor_ver = superblock.minor_ver;
546 assert_eq!(major_ver, 1);
547 assert_eq!(minor_ver, 16);
548 assert_eq!(superblock.get_total_size(), 256 << 20);
549 assert_eq!(superblock.get_volume_name().expect("get volume name"), "testimage");
550 }
551
552 async fn resolve_inode_path(f2fs: &F2fsReader, path: &str) -> Result<u32, Error> {
554 let path = PathBuf::from(path.strip_prefix("/").unwrap());
555 let mut ino = f2fs.root_ino();
556 for filename in &path {
557 let entries = f2fs.readdir(ino).await?;
558 if let Some(entry) = entries.iter().filter(|e| *e.filename == *filename).next() {
559 ino = entry.ino;
560 } else {
561 bail!("Not found.");
562 }
563 }
564 Ok(ino)
565 }
566
567 #[fuchsia::test]
568 async fn test_basic_dirs() {
569 let device = open_f2fs_test_image();
570
571 let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
572 let root_ino = f2fs.root_ino();
573 let root_entries = f2fs.readdir(root_ino).await.expect("readdir");
574 assert_eq!(root_entries.len(), 7);
575 assert_eq!(root_entries[0].filename, "a");
576 assert_eq!(root_entries[0].file_type, FileType::Directory);
577 assert_eq!(root_entries[1].filename, "large_dir");
578 assert_eq!(root_entries[2].filename, "large_dir2");
579 assert_eq!(root_entries[3].filename, "sparse.dat");
580 assert_eq!(root_entries[4].filename, "verity");
581 assert_eq!(root_entries[5].filename, "fscrypt");
582 assert_eq!(root_entries[6].filename, "large_zero");
583
584 let inlined_file_ino =
585 resolve_inode_path(&f2fs, "/a/b/c/inlined").await.expect("resolve inlined");
586 let inode = Inode::try_load(&f2fs, inlined_file_ino).await.expect("load inode");
587 let block_size = inode.header.block_size;
588 let size = inode.header.size;
589 assert_eq!(block_size, 1);
590 assert_eq!(size, 12);
591 assert_eq!(inode.inline_data.unwrap().as_ref(), "inline_data\n".as_bytes());
592
593 const REG_FILE_SIZE: u64 = 8 * BLOCK_SIZE as u64 + 8;
594 const REG_FILE_BLOCKS: u64 = 9 + 1;
595 let regular_file_ino =
596 resolve_inode_path(&f2fs, "/a/b/c/regular").await.expect("resolve regular");
597 let inode = Inode::try_load(&f2fs, regular_file_ino).await.expect("load inode");
598 let block_size = inode.header.block_size;
599 let size = inode.header.size;
600 assert_eq!(block_size, REG_FILE_BLOCKS);
601 assert_eq!(size, REG_FILE_SIZE);
602 assert!(inode.inline_data.is_none());
603 for i in 0..8 {
604 assert_eq!(
605 f2fs.read_data(&inode, i).await.expect("read data").unwrap(),
606 vec![0u8; BLOCK_SIZE]
607 );
608 }
609 assert_eq!(
610 &f2fs.read_data(&inode, 8).await.expect("read data").unwrap()[..9],
611 b"01234567\0"
612 );
613
614 let symlink_ino =
615 resolve_inode_path(&f2fs, "/a/b/c/symlink").await.expect("resolve symlink");
616 let inode = Inode::try_load(&f2fs, symlink_ino).await.expect("load inode");
617 assert_eq!(f2fs.read_symlink(&inode).expect("read_symlink").as_ref(), b"regular");
618
619 let hardlink_ino =
620 resolve_inode_path(&f2fs, "/a/b/c/hardlink").await.expect("resolve hardlink");
621 let inode = Inode::try_load(&f2fs, hardlink_ino).await.expect("load inode");
622 let block_size = inode.header.block_size;
623 let size = inode.header.size;
624 assert_eq!(block_size, REG_FILE_BLOCKS);
625 assert_eq!(size, REG_FILE_SIZE);
626
627 let chowned_ino =
628 resolve_inode_path(&f2fs, "/a/b/c/chowned").await.expect("resolve chowned");
629 let inode = Inode::try_load(&f2fs, chowned_ino).await.expect("load inode");
630 let uid = inode.header.uid;
631 let gid = inode.header.gid;
632 assert_eq!(uid, 999);
633 assert_eq!(gid, 999);
634
635 let large_dir = resolve_inode_path(&f2fs, "/large_dir").await.expect("resolve large_dir");
636 assert_eq!(f2fs.readdir(large_dir).await.expect("readdir").len(), 2001);
637
638 let large_dir2 = resolve_inode_path(&f2fs, "/large_dir2").await.expect("resolve large_dir");
639 assert_eq!(f2fs.readdir(large_dir2).await.expect("readdir").len(), 1);
640
641 let sparse_dat =
642 resolve_inode_path(&f2fs, "/sparse.dat").await.expect("resolve sparse.dat");
643 let inode = Inode::try_load(&f2fs, sparse_dat).await.expect("load inode");
644 let data_blocks: Vec<_> = inode.data_blocks().into_iter().collect();
645 assert_eq!(data_blocks.len(), 6);
646 assert_eq!(data_blocks[0].logical_block_num, 0);
647 assert_eq!(data_blocks[0].length, 1);
648 let block =
650 f2fs.read_raw_block(data_blocks[0].physical_block_num).await.expect("read sparse");
651 assert_eq!(&block.to_vec()[..3], b"foo");
652 assert_eq!(data_blocks[1].logical_block_num, 923);
654 assert_eq!(data_blocks[1].length, 1);
655 assert_eq!(data_blocks[2].logical_block_num, 1941);
656 assert_eq!(data_blocks[2].length, 1);
657 assert_eq!(data_blocks[3].logical_block_num, 2959);
658 assert_eq!(data_blocks[3].length, 1);
659 assert_eq!(data_blocks[4].logical_block_num, 1039283);
660 assert_eq!(data_blocks[4].length, 1);
661 assert_eq!(data_blocks[5].logical_block_num, 104671683);
662 assert_eq!(data_blocks[5].length, 2);
663 let block =
664 f2fs.read_raw_block(data_blocks[5].physical_block_num).await.expect("read sparse");
665 assert_eq!(block.to_vec(), vec![0; BLOCK_SIZE]);
666 assert_eq!(
668 &f2fs.read_data(&inode, 104671684).await.expect("read data block").unwrap()[..3],
669 b"bar"
670 );
671 assert!(f2fs.read_data(&inode, 104671684 - 10).await.expect("read data block").is_none());
673 }
674
675 #[fuchsia::test]
676 async fn test_xattr() {
677 let device = open_f2fs_test_image();
678
679 let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
680 let sparse_dat =
681 resolve_inode_path(&f2fs, "/sparse.dat").await.expect("resolve sparse.dat");
682 let inode = Inode::try_load(&f2fs, sparse_dat).await.expect("load inode");
683 assert_eq!(
684 inode.xattr,
685 vec![
686 xattr::XattrEntry {
687 index: xattr::Index::User,
688 name: Box::new(b"a".to_owned()),
689 value: Box::new(b"value".to_owned())
690 },
691 xattr::XattrEntry {
692 index: xattr::Index::User,
693 name: Box::new(b"c".to_owned()),
694 value: Box::new(b"value".to_owned())
695 },
696 xattr::XattrEntry {
697 index: xattr::Index::User,
698 name: Box::new(b"padding_test_1".to_owned()),
699 value: Box::new(b"v".to_owned())
700 },
701 xattr::XattrEntry {
702 index: xattr::Index::User,
703 name: Box::new(b"padding_test_2".to_owned()),
704 value: Box::new(b"va".to_owned())
705 },
706 xattr::XattrEntry {
707 index: xattr::Index::User,
708 name: Box::new(b"padding_test_3".to_owned()),
709 value: Box::new(b"val".to_owned())
710 },
711 xattr::XattrEntry {
712 index: xattr::Index::User,
713 name: Box::new(b"padding_test_4".to_owned()),
714 value: Box::new(b"valu".to_owned())
715 },
716 xattr::XattrEntry {
717 index: xattr::Index::User,
718 name: Box::new(b"padding_test_5".to_owned()),
719 value: Box::new(b"value".to_owned())
720 },
721 ]
722 );
723 }
724
725 #[fuchsia::test]
726 async fn test_fsverity() {
727 let device = open_f2fs_test_image();
728 let mut f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
729 f2fs.add_key(&[0u8; 64]);
730 let verity_files = vec![
731 "/verity/inlined",
732 "/verity/regular",
733 "/verity/merkle_layers.dat",
734 "/fscrypt/a/b/regular",
735 ];
736 for file_path in verity_files {
737 let file = resolve_inode_path(&f2fs, file_path).await.expect("resolve file");
738 let inode = Inode::try_load(&f2fs, file).await.expect("load inode");
739 assert!(inode.header.advise_flags.contains(inode::AdviseFlags::Verity));
740 }
741 let file = resolve_inode_path(&f2fs, "/a/b/c/regular").await.expect("resolve file");
743 let inode = Inode::try_load(&f2fs, file).await.expect("load inode");
744 assert!(!inode.header.advise_flags.contains(inode::AdviseFlags::Verity));
745 }
747
748 #[fuchsia::test]
749 async fn test_fbe() {
750 let str_a = "AlHTPgAAAAATu-OD4ljvFNw4Xpas_OeI";
759 let str_b = "GeiwsgAAAADc8JQtaJ7UbZ0GcT5yeHTZ";
760 let str_symlink = "QL5PAgAAAAAZjFRhVvAC80KXi6rlzmfr";
761 let bytes_symlink_content = b"AAAAAAAAAACavudfzv7yT0fMluSoe0NC";
762
763 let mut expected: HashSet<_> = [
764 "0KaBCgAAAADG-AqRst0R8y9D-kCCD14F",
766 "9g1xNQAAAAC2nhbquKF00IMYQ7Rbv25_",
767 "a5gOXwAAAADZWT1MUGPQdgNHBaXlkhT7",
768 "AlHTPgAAAAATu-OD4ljvFNw4Xpas_OeI",
769 "Jn1pZAAAAAB3bJB-bzY1dlQxj0NSkyU_u0-fevU6zycmvnjbTtHRwm3od3n2wl621OGeZhjQSYn2HlSwshPGwIzUQVeCv0Zb247T_qPD0EiM4PcaKLrr6gSt7-PrSVC2R9EsCKj8yAnwvi2bJKJvnghFE8wLV6pLN11nmbOI9q6yDB1ELRj2l2yke4iH_9zOSD-8PvBySzdx3L-h-V79-T0EFAOvlVLlqVfMIR9xgsXwe_xpjgHsFculb9Le",
770 "qHmmggAAAAAwObeaSgYbdMa0L9iXDYIN",
771 "UJqOTwAAAACAFLttEsV6RiVStfTM6q94",
772 "VERkwQAAAADytl0b_Ou0EoBXyYA9e_qI",
773 "WPM5KgAAAACfvfszUbrR943loZZ-__gO",
774 ]
775 .into_iter()
776 .collect();
777
778 let device = open_f2fs_test_image();
779
780 let mut f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
781
782 resolve_inode_path(&f2fs, "/fscrypt/a/b/regular")
786 .await
787 .expect_err("resolve fscrypt regular");
788 let fscrypt_dir_ino =
789 resolve_inode_path(&f2fs, "/fscrypt").await.expect("resolve encrypted dir");
790 let entries = f2fs.readdir(fscrypt_dir_ino).await.expect("readdir");
791 println!("entries {entries:?}");
792
793 for entry in entries {
794 assert!(expected.remove(entry.filename.as_str()), "unexpected entry {entry:?}");
795 }
796 assert!(expected.is_empty());
797
798 resolve_inode_path(&f2fs, &format!("/fscrypt/{str_a}"))
799 .await
800 .expect("resolve encrypted dir");
801 let enc_symlink_ino =
802 resolve_inode_path(&f2fs, &format!("/fscrypt/{str_a}/{str_b}/{str_symlink}"))
803 .await
804 .expect("resolve encrypted symlink");
805 let symlink_inode =
806 Inode::try_load(&f2fs, enc_symlink_ino).await.expect("load symlink inode");
807 assert_eq!(
808 &*f2fs.read_symlink(&symlink_inode).expect("read_symlink"),
809 bytes_symlink_content
810 );
811
812 f2fs.add_key(&[0u8; 64]);
814 resolve_inode_path(&f2fs, "/fscrypt/a/b/regular").await.expect("resolve fscrypt regular");
815 let inlined_ino = resolve_inode_path(&f2fs, "/fscrypt/a/b/inlined")
816 .await
817 .expect("resolve fscrypt inlined");
818 let short_file = Inode::try_load(&f2fs, inlined_ino).await.expect("load symlink inode");
819 assert!(
820 !short_file.header.inline_flags.contains(inode::InlineFlags::Data),
821 "encrypted files shouldn't be inlined"
822 );
823 let short_data =
824 f2fs.read_data(&short_file, 0).await.expect("read_data").expect("non-empty page");
825 assert_eq!(&short_data[..short_file.header.size as usize], b"test45678abcdef_12345678");
826
827 let symlink_ino = resolve_inode_path(&f2fs, "/fscrypt/a/b/symlink")
828 .await
829 .expect("resolve fscrypt symlink");
830 assert_eq!(symlink_ino, enc_symlink_ino);
831
832 let symlink_inode = Inode::try_load(&f2fs, symlink_ino).await.expect("load symlink inode");
833 let symlink = f2fs.read_symlink(&symlink_inode).expect("read_symlink");
834 assert_eq!(*symlink, *b"inlined");
835 }
836
837 #[fuchsia::test]
838 async fn test_summary_block_addr() {
839 let device = open_f2fs_test_image();
840 let mut f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
841
842 f2fs.checkpoint.header.ckpt_flags = 0; f2fs.checkpoint.header.cp_pack_start_sum = 100;
845 let base = f2fs.checkpoint_start_addr();
846 assert_eq!(f2fs.summary_block_addr(), base + 100);
847
848 f2fs.checkpoint.header.ckpt_flags = CP_ORPHAN_PRESENT_FLAG;
850 assert_eq!(f2fs.summary_block_addr(), base + 100);
851
852 f2fs.checkpoint.header.ckpt_flags = CP_ORPHAN_PRESENT_FLAG | CKPT_FLAG_COMPACT_SUMMARY;
854 assert_eq!(f2fs.summary_block_addr(), base + 100);
855 }
856
857 #[fuchsia::test]
858 async fn test_orphan_inodes() {
859 let device = open_f2fs_test_image();
860 let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
861 assert_eq!(f2fs.orphan_inodes().len(), 3);
862 for &ino in f2fs.orphan_inodes() {
863 assert!(f2fs.is_orphan(ino));
864 }
865 assert!(!f2fs.is_orphan(f2fs.root_ino()));
866 assert!(!f2fs.is_orphan(99999));
867
868 let entries = f2fs.readdir(f2fs.root_ino()).await.expect("readdir ok");
870 for entry in entries {
871 assert!(
872 !f2fs.is_orphan(entry.ino),
873 "Orphan inode {} unexpectedly found in root dentry",
874 entry.ino
875 );
876 }
877 }
878}