Skip to main content

f2fs_reader/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4mod block_cache;
5mod checkpoint;
6mod crypto;
7mod dir;
8mod fsverity;
9mod inode;
10mod nat;
11mod reader;
12mod superblock;
13mod xattr;
14
15pub use checkpoint::{
16    CKPT_FLAG_COMPACT_SUMMARY, CP_ORPHAN_PRESENT_FLAG, CheckpointHeader, ORPHANS_PER_BLOCK,
17    OrphanBlock,
18};
19pub use dir::{DirEntry, FileType};
20pub use fsverity::FsVerityDescriptor;
21pub use inode::{AdviseFlags, Flags, InlineFlags, Inode, Mode};
22pub use reader::{F2fsReader, NEW_ADDR, NULL_ADDR};
23pub use superblock::{
24    BLOCK_SIZE, F2FS_MAGIC, FEATURE_CASEFOLD, FEATURE_ENCRYPT, FEATURE_EXTRA_ATTR,
25    FEATURE_PROJECT_QUOTA, FEATURE_QUOTA_INO, FEATURE_SB_CHKSUM, FEATURE_VERITY, SUPERBLOCK_OFFSET,
26    SUPPORTED_FEATURES, SuperBlock, f2fs_crc32,
27};
28pub use xattr::{Index as XattrIndex, XattrEntry};
29
30#[cfg(test)]
31fn open_f2fs_test_image() -> storage_device::fake_device::FakeDevice {
32    let compressed_image =
33        std::fs::read("/pkg/testdata/f2fs.img.zst").expect("failed to read f2fs image");
34    let decompressed_image = zstd::bulk::decompress(&compressed_image, 256 * 1024 * 1024)
35        .expect("failed to decompress f2fs image");
36    storage_device::fake_device::FakeDevice::from_vec(decompressed_image, BLOCK_SIZE as u32)
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use crate::reader::Reader;
43    use std::sync::Arc;
44    use std::sync::atomic::{AtomicUsize, Ordering};
45
46    #[fuchsia::test]
47    async fn test_readahead() {
48        let mut device = open_f2fs_test_image();
49        let read_count = Arc::new(AtomicUsize::new(0));
50        let read_count_clone = read_count.clone();
51
52        device.set_op_callback(move |op| {
53            if let storage_device::fake_device::Op::Read = op {
54                read_count_clone.fetch_add(1, Ordering::SeqCst);
55            }
56            Ok(())
57        });
58
59        let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
60
61        // Reset counter after initialization (initialization does some reads)
62        read_count.store(0, Ordering::SeqCst);
63
64        // Block 0x1000 = 4096.
65        let start_block = 0x1000;
66
67        // Read start_block. Should trigger readahead for start_block + 0..16.
68        // Total 16 blocks.
69        f2fs.read_raw_block(start_block).await.expect("read start_block");
70        assert_eq!(read_count.load(Ordering::SeqCst), 1, "First read should trigger 1 device read");
71
72        // Read next 3 blocks. Should be cached.
73        for i in 1..4 {
74            f2fs.read_raw_block(start_block + i).await.expect("read cached block");
75            assert_eq!(
76                read_count.load(Ordering::SeqCst),
77                1,
78                "Read {} should be cached",
79                start_block + i
80            );
81        }
82
83        // Read 16th block. Should trigger new readahead.
84        f2fs.read_raw_block(start_block + 16).await.expect("read start_block + 16");
85        assert_eq!(read_count.load(Ordering::SeqCst), 2, "Read should trigger 2nd device read");
86    }
87}