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::CheckpointHeader;
16pub use dir::{DirEntry, FileType};
17pub use fsverity::FsVerityDescriptor;
18pub use inode::{AdviseFlags, Flags, InlineFlags, Inode, Mode};
19pub use reader::{F2fsReader, NEW_ADDR, NULL_ADDR};
20pub use superblock::{
21    BLOCK_SIZE, F2FS_MAGIC, FEATURE_CASEFOLD, FEATURE_ENCRYPT, FEATURE_EXTRA_ATTR,
22    FEATURE_PROJECT_QUOTA, FEATURE_QUOTA_INO, FEATURE_SB_CHKSUM, FEATURE_VERITY, SUPERBLOCK_OFFSET,
23    SUPPORTED_FEATURES, SuperBlock, f2fs_crc32,
24};
25pub use xattr::{Index as XattrIndex, XattrEntry};
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30    use crate::reader::Reader;
31    use std::sync::Arc;
32    use std::sync::atomic::{AtomicUsize, Ordering};
33
34    // Copied from reader.rs tests, might want to deduplicate later if it grows.
35    fn open_test_image(path: &str) -> storage_device::fake_device::FakeDevice {
36        use storage_device::fake_device::FakeDevice;
37        let path = std::path::PathBuf::from(path);
38        println!("path is {path:?}");
39        FakeDevice::from_image(
40            zstd::Decoder::new(std::fs::File::open(&path).expect("open image"))
41                .expect("decompress image"),
42            BLOCK_SIZE as u32,
43        )
44        .expect("open image")
45    }
46
47    #[fuchsia::test]
48    async fn test_readahead() {
49        let mut device = open_test_image("/pkg/testdata/f2fs.img.zst");
50        let read_count = Arc::new(AtomicUsize::new(0));
51        let read_count_clone = read_count.clone();
52
53        device.set_op_callback(move |op| {
54            if let storage_device::fake_device::Op::Read = op {
55                read_count_clone.fetch_add(1, Ordering::SeqCst);
56            }
57            Ok(())
58        });
59
60        let f2fs = F2fsReader::open_device(Arc::new(device)).await.expect("open ok");
61
62        // Reset counter after initialization (initialization does some reads)
63        read_count.store(0, Ordering::SeqCst);
64
65        // Block 0x1000 = 4096.
66        let start_block = 0x1000;
67
68        // Read start_block. Should trigger readahead for start_block + 0..16.
69        // Total 16 blocks.
70        f2fs.read_raw_block(start_block).await.expect("read start_block");
71        assert_eq!(read_count.load(Ordering::SeqCst), 1, "First read should trigger 1 device read");
72
73        // Read next 3 blocks. Should be cached.
74        for i in 1..4 {
75            f2fs.read_raw_block(start_block + i).await.expect("read cached block");
76            assert_eq!(
77                read_count.load(Ordering::SeqCst),
78                1,
79                "Read {} should be cached",
80                start_block + i
81            );
82        }
83
84        // Read 16th block. Should trigger new readahead.
85        f2fs.read_raw_block(start_block + 16).await.expect("read start_block + 16");
86        assert_eq!(read_count.load(Ordering::SeqCst), 2, "Read should trigger 2nd device read");
87    }
88}