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;
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
68 keys: HashMap<[u8; 16], [u8; 64]>,
70 cache: BlockCache,
71}
72
73impl Drop for F2fsReader {
74 fn drop(&mut self) {
75 self.keys.values_mut().for_each(|v| {
77 *v = [0u8; 64];
78 });
79 }
80}
81
82impl F2fsReader {
83 pub fn superblock(&self) -> &SuperBlock {
84 &self.superblock
85 }
86
87 pub fn checkpoint(&self) -> &CheckpointPack {
88 &self.checkpoint
89 }
90
91 pub async fn open_device(device: Arc<dyn Device>) -> Result<Self, Error> {
92 let (superblock, checkpoints) =
93 match Self::try_from_superblock(device.as_ref(), SUPERBLOCK_OFFSET).await {
94 Ok(x) => x,
95 Err(e) => Self::try_from_superblock(device.as_ref(), SUPERBLOCK_OFFSET * 2)
96 .await
97 .map_err(|_| e)?,
98 };
99
100 let mut last_error = anyhow!("No checkpoints found");
101
102 for (checkpoint, cp_start_block) in checkpoints {
103 let mut this = Self {
104 device: device.clone(),
105 superblock,
106 checkpoint,
107 cp_start_block,
108 nat: Nat::new(0, vec![], HashMap::new()),
109 keys: HashMap::with_capacity(16),
110 cache: BlockCache::new(1024, BLOCK_SIZE),
111 };
112
113 match this.read_nat_journal().await {
114 Ok(nat_journal) => {
115 this.nat = Nat::new(
116 this.superblock.nat_blkaddr,
117 this.checkpoint.nat_bitmap.clone(),
118 nat_journal,
119 );
120 return Ok(this);
121 }
122 Err(e) => {
123 let ver = this.checkpoint.header.checkpoint_ver;
124 log::warn!(
125 "Failed to initialize from checkpoint (Ver {} at {}): {}. Trying next.",
126 ver,
127 cp_start_block,
128 e
129 );
130 last_error = e;
131 }
133 }
134 }
135
136 Err(last_error)
137 }
138
139 async fn try_from_superblock(
140 device: &dyn Device,
141 superblock_offset: u64,
142 ) -> Result<(SuperBlock, Vec<(CheckpointPack, u32)>), Error> {
143 let superblock = SuperBlock::read_from_device(device, superblock_offset).await?;
144 let checkpoint_addr = superblock.cp_blkaddr;
145 let checkpoint_a_offset = BLOCK_SIZE as u64 * checkpoint_addr as u64;
146 let checkpoint_b_offset = checkpoint_a_offset + SEGMENT_SIZE as u64;
147
148 let mut checkpoints = Vec::new();
149
150 if let Ok(cp) = CheckpointPack::read_from_device(device, checkpoint_a_offset).await {
152 checkpoints.push((cp, checkpoint_addr));
153 }
154 if let Ok(cp) = CheckpointPack::read_from_device(device, checkpoint_b_offset).await {
155 checkpoints.push((cp, checkpoint_addr + BLOCKS_PER_SEGMENT as u32));
156 }
157
158 if checkpoints.is_empty() {
159 bail!("Failed to read any valid checkpoint");
160 }
161
162 checkpoints.sort_by(|(a, _), (b, _)| {
164 let va = a.header.checkpoint_ver;
165 let vb = b.header.checkpoint_ver;
166 vb.cmp(&va)
167 });
168
169 const MIN_METADATA_SEGMENT_COUNT: u32 = 8;
171
172 let first_cp = &checkpoints[0].0;
174
175 let metadata_segment_count = superblock
177 .segment_count_sit
178 .checked_add(superblock.segment_count_nat)
179 .and_then(|v| v.checked_add(first_cp.header.rsvd_segment_count))
180 .and_then(|v| v.checked_add(superblock.segment_count_ssa))
181 .and_then(|v| v.checked_add(superblock.segment_count_ckpt))
182 .ok_or_else(|| anyhow::anyhow!("Segment counts overflow"))?;
183 ensure!(
184 metadata_segment_count <= superblock.segment_count
185 && metadata_segment_count >= MIN_METADATA_SEGMENT_COUNT,
186 "Bad segment counts in checkpoint"
187 );
188 Ok((superblock, checkpoints))
189 }
190
191 pub fn checkpoint_start_addr(&self) -> u32 {
193 self.cp_start_block
194 }
195
196 fn nat(&self) -> &Nat {
197 &self.nat
198 }
199 pub fn summary_block_addr(&self) -> u32 {
202 let mut offset = self.checkpoint.header.cp_pack_start_sum;
203 if self.checkpoint.header.ckpt_flags & CP_ORPHAN_PRESENT_FLAG != 0 {
204 offset += 1;
207 }
208 self.checkpoint_start_addr() + offset
209 }
210
211 async fn read_nat_journal(&mut self) -> Result<HashMap<u32, RawNatEntry>, Error> {
212 if self.checkpoint.header.ckpt_flags & CKPT_FLAG_COMPACT_SUMMARY != 0 {
213 let summary_addr = self.summary_block_addr();
216 let block = self.read_raw_block(summary_addr).await?;
217 let n_nats = block.as_ptr_slice().read::<u16>().unwrap();
218 let nat_journal = block
219 .as_ptr_slice()
220 .subslice(2..2 + std::mem::size_of::<NatJournal>())
221 .read::<NatJournal>()
222 .unwrap();
223 ensure!(
224 (n_nats as usize) <= nat_journal.entries.len(),
225 "n_nats {} larger than block size {}",
226 n_nats,
227 nat_journal.entries.len()
228 );
229 Ok(HashMap::from_iter(
230 nat_journal.entries[..n_nats as usize].iter().map(|e| (e.ino, e.entry)),
231 ))
232 } else {
233 let summary_addr = self.summary_block_addr();
236 let block = self.read_raw_block(summary_addr).await?;
237
238 let summary = block
239 .as_ptr_slice()
240 .read::<SummaryBlock>()
241 .ok_or_else(|| anyhow!("Block size too small"))?;
242 ensure!(summary.footer.entry_type == 0u8, "sum_type != 0 in summary footer");
243 #[cfg(not(fuzz))]
244 {
245 let data = block.to_vec();
246 let actual_checksum = f2fs_crc32(F2FS_MAGIC, &data[..BLOCK_SIZE - 4]);
247 let expected_checksum = summary.footer.check_sum;
248 if actual_checksum != expected_checksum {
249 log::warn!(
251 "Summary block checksum mismatch (actual: 0x{:x}, expected: 0x{:x}). \
252 This is normal for checkpoints with CP_CRC_RECOVERY_FLAG.",
253 actual_checksum,
254 expected_checksum
255 );
256 }
257 }
258 let n_nats = summary.n_nats;
259 ensure!(
260 (n_nats as usize) <= summary.nat_journal.entries.len(),
261 "n_nats {} larger than block size {}",
262 n_nats,
263 summary.nat_journal.entries.len()
264 );
265 let mut out = HashMap::new();
266 for i in 0..n_nats as usize {
267 out.insert(
268 summary.nat_journal.entries[i].ino,
269 summary.nat_journal.entries[i].entry,
270 );
271 }
272 Ok(out)
273 }
274 }
275
276 pub fn root_ino(&self) -> u32 {
277 self.superblock.root_ino
278 }
279
280 pub fn max_ino(&self) -> u32 {
283 (self.checkpoint.nat_bitmap.len() * 8) as u32
284 }
285
286 pub fn add_key(&mut self, main_key: &[u8; 64]) -> [u8; 16] {
289 let identifier = fscrypt::main_key_to_identifier(main_key);
290 println!("Adding key with identifier {}", hex::encode(identifier));
291 self.keys.insert(identifier.clone(), main_key.clone());
292 identifier
293 }
294
295 pub async fn readdir(&self, ino: u32) -> Result<Vec<DirEntry>, Error> {
297 let inode = Inode::try_load(self, ino).await?;
298 let decryptor = self.get_decryptor_for_inode(&inode);
299 let mode = inode.header.mode;
300 let advise_flags = inode.header.advise_flags;
301 let flags = inode.header.flags;
302 ensure!(mode.contains(inode::Mode::Directory), "not a directory");
303 if let Some(entries) = inode.get_inline_dir_entries(
304 advise_flags.contains(inode::AdviseFlags::Encrypted),
305 flags.contains(inode::Flags::Casefold),
306 &decryptor,
307 )? {
308 Ok(entries)
309 } else {
310 let mut entries = Vec::new();
311
312 for mut extent in inode.data_blocks() {
317 for _ in 0..extent.length {
318 let dentry_block = self
319 .read_raw_block(extent.physical_block_num)
320 .await?
321 .as_ptr_slice()
322 .read::<DentryBlock>()
323 .ok_or_else(|| anyhow!("Block size too small"))?;
324 entries.append(&mut dentry_block.get_entries(
325 ino,
326 advise_flags.contains(inode::AdviseFlags::Encrypted),
327 flags.contains(inode::Flags::Casefold),
328 &decryptor,
329 )?);
330 extent.physical_block_num += 1;
331 }
332 }
333 Ok(entries)
334 }
335 }
336
337 pub async fn read_inode(&self, ino: u32) -> Result<Box<Inode>, Error> {
339 Inode::try_load(self, ino).await
340 }
341
342 pub fn read_symlink(&self, inode: &Inode) -> Result<Box<[u8]>, Error> {
344 if let Some(inline_data) = inode.inline_data.as_deref() {
345 let mut filename = inline_data.to_vec();
346 if inode.header.advise_flags.contains(inode::AdviseFlags::Encrypted) {
347 ensure!(filename.len() >= 2, "invalid encrypted symlink");
349 let symlink_len = u16::read_from_bytes(&filename[..2]).unwrap();
350 filename.drain(..2);
351 filename.truncate(symlink_len as usize);
352 ensure!(symlink_len == filename.len() as u16, "invalid encrypted symlink");
353 if let Some(decryptor) = self.get_decryptor_for_inode(inode) {
354 decryptor.decrypt_filename_data(inode.footer.ino, &mut filename);
355 } else {
356 let proxy_filename: String =
358 fscrypt::proxy_filename::ProxyFilename::new_with_hash_code(0, &filename)
359 .into();
360 filename = proxy_filename.as_bytes().to_vec();
361 }
362 while let Some(0) = filename.last() {
365 filename.pop();
366 }
367 }
368 Ok(filename.into_boxed_slice())
369 } else {
370 bail!("Not a valid symlink");
371 }
372 }
373
374 pub async fn read_data(&self, inode: &Inode, block_num: u32) -> Result<Option<Vec<u8>>, Error> {
377 let inline_flags = inode.header.inline_flags;
378 ensure!(
379 !inline_flags.contains(crate::InlineFlags::Data),
380 "Can't use read_data() on inline file."
381 );
382 let block_addr = inode.data_block_addr(block_num);
383 if block_addr == NULL_ADDR || block_addr == NEW_ADDR {
384 return Ok(None);
386 }
387 let buffer = self.read_raw_block(block_addr).await?;
388 let mut data = buffer.to_vec();
389 if let Some(decryptor) = self.get_decryptor_for_inode(inode) {
390 decryptor.decrypt_data(inode.footer.ino, block_num, &mut data);
391 }
392 Ok(Some(data))
393 }
394}
395
396#[async_trait]
397impl Reader for F2fsReader {
398 async fn read_raw_block(&self, block_addr: u32) -> Result<Buffer<'_>, Error> {
400 if let Some(block) = self.cache.get_buffer(block_addr, self.device.as_ref()).await {
401 return Ok(block);
402 }
403
404 const READAHEAD: u64 = 16;
405 let end = std::cmp::min(block_addr as u64 + READAHEAD, self.device.block_count());
406 let count = end.saturating_sub(block_addr as u64).max(1) as usize;
407
408 let mut buffer = self.device.allocate_buffer(count * BLOCK_SIZE).await;
409 self.device
410 .read(block_addr as u64 * BLOCK_SIZE as u64, buffer.as_mut())
411 .await
412 .map_err(|_| anyhow!("device read failed"))?;
413
414 for i in 0..count {
415 let subslice = buffer.as_ptr_slice().subslice(i * BLOCK_SIZE..(i + 1) * BLOCK_SIZE);
416 self.cache.insert(block_addr + i as u32, subslice.to_vec());
417 }
418 Ok(self.cache.get_buffer(block_addr, self.device.as_ref()).await.unwrap())
419 }
420
421 async fn read_node(&self, nid: u32) -> Result<Buffer<'_>, Error> {
422 let nat_entry = self.get_nat_entry(nid).await?;
423 self.read_raw_block(nat_entry.block_addr).await
424 }
425
426 fn get_key(&self, identifier: &[u8; 16]) -> Option<&[u8; 64]> {
427 self.keys.get(identifier)
428 }
429
430 fn fs_uuid(&self) -> &[u8; 16] {
431 &self.superblock.uuid
432 }
433
434 async fn get_nat_entry(&self, nid: u32) -> Result<RawNatEntry, Error> {
435 if let Some(entry) = self.nat().nat_journal.get(&nid) {
436 return Ok(*entry);
437 }
438 let nat_block_addr = self.nat().get_nat_block_for_entry(nid)?;
439 let offset = self.nat().get_nat_block_offset_for_entry(nid);
440 let block = self.read_raw_block(nat_block_addr).await?;
441 let entry = block
442 .as_ptr_slice()
443 .subslice(offset..offset + std::mem::size_of::<RawNatEntry>())
444 .read::<RawNatEntry>()
445 .ok_or_else(|| anyhow!("Block size too small"))?;
446 Ok(entry)
447 }
448}
449
450#[cfg(test)]
451mod test {
452 use super::*;
453 use crate::dir::FileType;
454 use crate::xattr;
455 use std::collections::HashSet;
456 use std::path::PathBuf;
457 use std::sync::Arc;
458
459 use storage_device::fake_device::FakeDevice;
460
461 fn open_test_image(path: &str) -> FakeDevice {
462 let path = std::path::PathBuf::from(path);
463 println!("path is {path:?}");
464 FakeDevice::from_image(
465 zstd::Decoder::new(std::fs::File::open(&path).expect("open image"))
466 .expect("decompress image"),
467 BLOCK_SIZE as u32,
468 )
469 .expect("open image")
470 }
471
472 #[fuchsia::test]
473 async fn test_open_fs() {
474 let device = open_test_image("/pkg/testdata/f2fs.img.zst");
475
476 let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
477 assert_eq!(f2fs.root_ino(), 3);
479 let superblock = &f2fs.superblock;
480 let major_ver = superblock.major_ver;
481 let minor_ver = superblock.minor_ver;
482 assert_eq!(major_ver, 1);
483 assert_eq!(minor_ver, 16);
484 assert_eq!(superblock.get_total_size(), 256 << 20);
485 assert_eq!(superblock.get_volume_name().expect("get volume name"), "testimage");
486 }
487
488 async fn resolve_inode_path(f2fs: &F2fsReader, path: &str) -> Result<u32, Error> {
490 let path = PathBuf::from(path.strip_prefix("/").unwrap());
491 let mut ino = f2fs.root_ino();
492 for filename in &path {
493 let entries = f2fs.readdir(ino).await?;
494 if let Some(entry) = entries.iter().filter(|e| *e.filename == *filename).next() {
495 ino = entry.ino;
496 } else {
497 bail!("Not found.");
498 }
499 }
500 Ok(ino)
501 }
502
503 #[fuchsia::test]
504 async fn test_basic_dirs() {
505 let device = open_test_image("/pkg/testdata/f2fs.img.zst");
506
507 let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
508 let root_ino = f2fs.root_ino();
509 let root_entries = f2fs.readdir(root_ino).await.expect("readdir");
510 assert_eq!(root_entries.len(), 7);
511 assert_eq!(root_entries[0].filename, "a");
512 assert_eq!(root_entries[0].file_type, FileType::Directory);
513 assert_eq!(root_entries[1].filename, "large_dir");
514 assert_eq!(root_entries[2].filename, "large_dir2");
515 assert_eq!(root_entries[3].filename, "sparse.dat");
516 assert_eq!(root_entries[4].filename, "verity");
517 assert_eq!(root_entries[5].filename, "fscrypt");
518 assert_eq!(root_entries[6].filename, "large_zero");
519
520 let inlined_file_ino =
521 resolve_inode_path(&f2fs, "/a/b/c/inlined").await.expect("resolve inlined");
522 let inode = Inode::try_load(&f2fs, inlined_file_ino).await.expect("load inode");
523 let block_size = inode.header.block_size;
524 let size = inode.header.size;
525 assert_eq!(block_size, 1);
526 assert_eq!(size, 12);
527 assert_eq!(inode.inline_data.unwrap().as_ref(), "inline_data\n".as_bytes());
528
529 const REG_FILE_SIZE: u64 = 8 * BLOCK_SIZE as u64 + 8;
530 const REG_FILE_BLOCKS: u64 = 9 + 1;
531 let regular_file_ino =
532 resolve_inode_path(&f2fs, "/a/b/c/regular").await.expect("resolve regular");
533 let inode = Inode::try_load(&f2fs, regular_file_ino).await.expect("load inode");
534 let block_size = inode.header.block_size;
535 let size = inode.header.size;
536 assert_eq!(block_size, REG_FILE_BLOCKS);
537 assert_eq!(size, REG_FILE_SIZE);
538 assert!(inode.inline_data.is_none());
539 for i in 0..8 {
540 assert_eq!(
541 f2fs.read_data(&inode, i).await.expect("read data").unwrap(),
542 vec![0u8; BLOCK_SIZE]
543 );
544 }
545 assert_eq!(
546 &f2fs.read_data(&inode, 8).await.expect("read data").unwrap()[..9],
547 b"01234567\0"
548 );
549
550 let symlink_ino =
551 resolve_inode_path(&f2fs, "/a/b/c/symlink").await.expect("resolve symlink");
552 let inode = Inode::try_load(&f2fs, symlink_ino).await.expect("load inode");
553 assert_eq!(f2fs.read_symlink(&inode).expect("read_symlink").as_ref(), b"regular");
554
555 let hardlink_ino =
556 resolve_inode_path(&f2fs, "/a/b/c/hardlink").await.expect("resolve hardlink");
557 let inode = Inode::try_load(&f2fs, hardlink_ino).await.expect("load inode");
558 let block_size = inode.header.block_size;
559 let size = inode.header.size;
560 assert_eq!(block_size, REG_FILE_BLOCKS);
561 assert_eq!(size, REG_FILE_SIZE);
562
563 let chowned_ino =
564 resolve_inode_path(&f2fs, "/a/b/c/chowned").await.expect("resolve chowned");
565 let inode = Inode::try_load(&f2fs, chowned_ino).await.expect("load inode");
566 let uid = inode.header.uid;
567 let gid = inode.header.gid;
568 assert_eq!(uid, 999);
569 assert_eq!(gid, 999);
570
571 let large_dir = resolve_inode_path(&f2fs, "/large_dir").await.expect("resolve large_dir");
572 assert_eq!(f2fs.readdir(large_dir).await.expect("readdir").len(), 2001);
573
574 let large_dir2 = resolve_inode_path(&f2fs, "/large_dir2").await.expect("resolve large_dir");
575 assert_eq!(f2fs.readdir(large_dir2).await.expect("readdir").len(), 1);
576
577 let sparse_dat =
578 resolve_inode_path(&f2fs, "/sparse.dat").await.expect("resolve sparse.dat");
579 let inode = Inode::try_load(&f2fs, sparse_dat).await.expect("load inode");
580 let data_blocks: Vec<_> = inode.data_blocks().into_iter().collect();
581 assert_eq!(data_blocks.len(), 6);
582 assert_eq!(data_blocks[0].logical_block_num, 0);
583 assert_eq!(data_blocks[0].length, 1);
584 let block =
586 f2fs.read_raw_block(data_blocks[0].physical_block_num).await.expect("read sparse");
587 assert_eq!(&block.to_vec()[..3], b"foo");
588 assert_eq!(data_blocks[1].logical_block_num, 923);
590 assert_eq!(data_blocks[1].length, 1);
591 assert_eq!(data_blocks[2].logical_block_num, 1941);
592 assert_eq!(data_blocks[2].length, 1);
593 assert_eq!(data_blocks[3].logical_block_num, 2959);
594 assert_eq!(data_blocks[3].length, 1);
595 assert_eq!(data_blocks[4].logical_block_num, 1039283);
596 assert_eq!(data_blocks[4].length, 1);
597 assert_eq!(data_blocks[5].logical_block_num, 104671683);
598 assert_eq!(data_blocks[5].length, 2);
599 let block =
600 f2fs.read_raw_block(data_blocks[5].physical_block_num).await.expect("read sparse");
601 assert_eq!(block.to_vec(), vec![0; BLOCK_SIZE]);
602 assert_eq!(
604 &f2fs.read_data(&inode, 104671684).await.expect("read data block").unwrap()[..3],
605 b"bar"
606 );
607 assert!(f2fs.read_data(&inode, 104671684 - 10).await.expect("read data block").is_none());
609 }
610
611 #[fuchsia::test]
612 async fn test_xattr() {
613 let device = open_test_image("/pkg/testdata/f2fs.img.zst");
614
615 let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
616 let sparse_dat =
617 resolve_inode_path(&f2fs, "/sparse.dat").await.expect("resolve sparse.dat");
618 let inode = Inode::try_load(&f2fs, sparse_dat).await.expect("load inode");
619 assert_eq!(
620 inode.xattr,
621 vec![
622 xattr::XattrEntry {
623 index: xattr::Index::User,
624 name: Box::new(b"a".to_owned()),
625 value: Box::new(b"value".to_owned())
626 },
627 xattr::XattrEntry {
628 index: xattr::Index::User,
629 name: Box::new(b"c".to_owned()),
630 value: Box::new(b"value".to_owned())
631 },
632 xattr::XattrEntry {
633 index: xattr::Index::User,
634 name: Box::new(b"padding_test_1".to_owned()),
635 value: Box::new(b"v".to_owned())
636 },
637 xattr::XattrEntry {
638 index: xattr::Index::User,
639 name: Box::new(b"padding_test_2".to_owned()),
640 value: Box::new(b"va".to_owned())
641 },
642 xattr::XattrEntry {
643 index: xattr::Index::User,
644 name: Box::new(b"padding_test_3".to_owned()),
645 value: Box::new(b"val".to_owned())
646 },
647 xattr::XattrEntry {
648 index: xattr::Index::User,
649 name: Box::new(b"padding_test_4".to_owned()),
650 value: Box::new(b"valu".to_owned())
651 },
652 xattr::XattrEntry {
653 index: xattr::Index::User,
654 name: Box::new(b"padding_test_5".to_owned()),
655 value: Box::new(b"value".to_owned())
656 },
657 ]
658 );
659 }
660
661 #[fuchsia::test]
662 async fn test_fsverity() {
663 let device = open_test_image("/pkg/testdata/f2fs.img.zst");
664 let mut f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
665 f2fs.add_key(&[0u8; 64]);
666 let verity_files = vec![
667 "/verity/inlined",
668 "/verity/regular",
669 "/verity/merkle_layers.dat",
670 "/fscrypt/a/b/regular",
671 ];
672 for file_path in verity_files {
673 let file = resolve_inode_path(&f2fs, file_path).await.expect("resolve file");
674 let inode = Inode::try_load(&f2fs, file).await.expect("load inode");
675 assert!(inode.header.advise_flags.contains(inode::AdviseFlags::Verity));
676 }
677 let file = resolve_inode_path(&f2fs, "/a/b/c/regular").await.expect("resolve file");
679 let inode = Inode::try_load(&f2fs, file).await.expect("load inode");
680 assert!(!inode.header.advise_flags.contains(inode::AdviseFlags::Verity));
681 }
683
684 #[fuchsia::test]
685 async fn test_fbe() {
686 let str_a = "2ll82QAAAADywluz1Ule7OVNBxUfa5Mw";
695 let str_b = "sttckQAAAADLBOCVVgjrZ-CXNkj5E6Cr";
696 let str_symlink = "zHAtQgAAAACRNPQYvCKuQo5F8rQUORg3";
697 let bytes_symlink_content = b"AAAAAAAAAADUsYZ_qNiiouF7e40xm65S";
698
699 let mut expected : HashSet<_> = [ "2ll82QAAAADywluz1Ule7OVNBxUfa5Mw",
701 "65OSUQAAAADqOiZJcQ1El2dpVdYMy84l",
702 "7vcnbgAAAAAOWdQfi4wK46uRGQBD0YSy",
703 "9Gsv9QAAAADjTeJ_9WdCxZMVTiSWhsWR",
704 "FAqGXAAAAAD1jOLXaZN-o8X9PoS67GI7",
705 "Rq5qZAAAAAA3y2lvAqesYDnVJWMklWnj",
706 "S93sdgAAAABo-YmXNPKtv4wxQCcUslTu",
707 "VP8QBwAAAAATw6Ozex0N2gMYrnDsB2aH",
708 "xUNjwgAAAADB0pEx5ovwx-AS02L0d1j7VMBRXzM4YnBri2pbasOqbFLhtegXr9kDGNcYd_hyk2mOkQIqu8hk7eARlFl-bq1yLhikhIT9HVC3FMrI7vQ-ewncEjXLDP3KK6RtH3r34S89AlzJZ4DVfXrr_Q5N5mANBbGTzeO70aJHL0Ms-MgkKwjHcbIxXLwcjE2B-mssLAvXam58pSD-aazxS_J2hrxOHGoUYiVJ-rXHozmKxBdWAO6OUW65",
709 ].into_iter().collect();
710
711 let device = open_test_image("/pkg/testdata/f2fs.img.zst");
712
713 let mut f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
714
715 resolve_inode_path(&f2fs, "/fscrypt/a/b/regular")
719 .await
720 .expect_err("resolve fscrypt regular");
721 let fscrypt_dir_ino =
722 resolve_inode_path(&f2fs, "/fscrypt").await.expect("resolve encrypted dir");
723 let entries = f2fs.readdir(fscrypt_dir_ino).await.expect("readdir");
724 println!("entries {entries:?}");
725
726 for entry in entries {
727 assert!(expected.remove(entry.filename.as_str()), "unexpected entry {entry:?}");
728 }
729 assert!(expected.is_empty());
730
731 resolve_inode_path(&f2fs, &format!("/fscrypt/{str_a}"))
732 .await
733 .expect("resolve encrypted dir");
734 let enc_symlink_ino =
735 resolve_inode_path(&f2fs, &format!("/fscrypt/{str_a}/{str_b}/{str_symlink}"))
736 .await
737 .expect("resolve encrypted symlink");
738 let symlink_inode =
739 Inode::try_load(&f2fs, enc_symlink_ino).await.expect("load symlink inode");
740 assert_eq!(
741 &*f2fs.read_symlink(&symlink_inode).expect("read_symlink"),
742 bytes_symlink_content
743 );
744
745 f2fs.add_key(&[0u8; 64]);
747 resolve_inode_path(&f2fs, "/fscrypt/a/b/regular").await.expect("resolve fscrypt regular");
748 let inlined_ino = resolve_inode_path(&f2fs, "/fscrypt/a/b/inlined")
749 .await
750 .expect("resolve fscrypt inlined");
751 let short_file = Inode::try_load(&f2fs, inlined_ino).await.expect("load symlink inode");
752 assert!(
753 !short_file.header.inline_flags.contains(inode::InlineFlags::Data),
754 "encrypted files shouldn't be inlined"
755 );
756 let short_data =
757 f2fs.read_data(&short_file, 0).await.expect("read_data").expect("non-empty page");
758 assert_eq!(&short_data[..short_file.header.size as usize], b"test45678abcdef_12345678");
759
760 let symlink_ino = resolve_inode_path(&f2fs, "/fscrypt/a/b/symlink")
761 .await
762 .expect("resolve fscrypt symlink");
763 assert_eq!(symlink_ino, enc_symlink_ino);
764
765 let symlink_inode = Inode::try_load(&f2fs, symlink_ino).await.expect("load symlink inode");
766 let symlink = f2fs.read_symlink(&symlink_inode).expect("read_symlink");
767 assert_eq!(*symlink, *b"inlined");
768 }
769
770 #[fuchsia::test]
771 async fn test_summary_block_addr() {
772 let device = open_test_image("/pkg/testdata/f2fs.img.zst");
773 let mut f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
774
775 f2fs.checkpoint.header.ckpt_flags = 0; f2fs.checkpoint.header.cp_pack_start_sum = 100;
778 let base = f2fs.checkpoint_start_addr();
779 assert_eq!(f2fs.summary_block_addr(), base + 100);
780
781 f2fs.checkpoint.header.ckpt_flags = CP_ORPHAN_PRESENT_FLAG;
783 assert_eq!(f2fs.summary_block_addr(), base + 100 + 1);
784
785 f2fs.checkpoint.header.ckpt_flags = CP_ORPHAN_PRESENT_FLAG | CKPT_FLAG_COMPACT_SUMMARY;
787 assert_eq!(f2fs.summary_block_addr(), base + 100 + 1);
788 }
789}