f2fs_reader/
checkpoint.rs1use crate::superblock::{BLOCK_SIZE, F2FS_MAGIC, SEGMENT_SIZE, f2fs_crc32};
5use anyhow::{Error, anyhow, ensure};
6use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
7
8const MAX_ACTIVE_NODE_LOGS: usize = 8;
9const MAX_ACTIVE_DATA_LOGS: usize = 8;
10const MAX_ACTIVE_LOGS: usize = 16;
11pub const CP_ORPHAN_PRESENT_FLAG: u32 = 0x2;
12pub const CKPT_FLAG_COMPACT_SUMMARY: u32 = 0x4;
13
14#[derive(Debug, Eq, PartialEq, FromBytes, Immutable, IntoBytes, KnownLayout)]
15#[repr(C, packed)]
16pub struct CheckpointHeader {
17 pub checkpoint_ver: u64,
18 pub user_block_count: u64,
19 pub valid_block_count: u64,
20 pub rsvd_segment_count: u32,
21 pub overprov_segment_count: u32,
22 pub free_segment_count: u32,
23 pub cur_node_segno: [u32; MAX_ACTIVE_NODE_LOGS],
24 pub cur_node_blkoff: [u16; MAX_ACTIVE_NODE_LOGS],
25 pub cur_data_segno: [u32; MAX_ACTIVE_DATA_LOGS],
26 pub cur_data_blkoff: [u16; MAX_ACTIVE_DATA_LOGS],
27 pub ckpt_flags: u32,
28 pub cp_pack_total_block_count: u32,
29 pub cp_pack_start_sum: u32,
30 pub valid_node_count: u32,
31 pub valid_inode_count: u32,
32 pub next_free_nid: u32,
33 pub sit_ver_bitmap_bytesize: u32,
34 pub nat_ver_bitmap_bytesize: u32,
35 pub checksum_offset: u32,
36 pub elapsed_time: u64,
37 pub alloc_type: [u8; MAX_ACTIVE_LOGS],
38 }
41
42#[derive(Debug)]
43pub struct CheckpointPack {
44 pub header: CheckpointHeader,
45 pub nat_bitmap: Vec<u8>,
46}
47
48impl CheckpointPack {
49 pub async fn read_from_device(
50 device: &dyn storage_device::Device,
51 offset: u64,
52 ) -> Result<Self, Error> {
53 let mut segment = device.allocate_buffer(SEGMENT_SIZE).await;
54 device.read(offset, segment.as_mut()).await?;
55 let segment = segment.as_slice();
56 Self::parse_checkpoint(segment)
57 }
58
59 fn parse_checkpoint(segment: &[u8]) -> Result<Self, Error> {
60 ensure!(
61 segment.len() >= std::mem::size_of::<CheckpointHeader>(),
62 "Segment too short for checkpoint"
63 );
64 let header =
65 CheckpointHeader::read_from_bytes(&segment[..std::mem::size_of::<CheckpointHeader>()])
66 .map_err(|_| anyhow!("Invalid checkpoint header"))?;
67 let len = header.checksum_offset as usize;
68 ensure!(len <= segment.len() - std::mem::size_of::<u32>(), "Bad checkpoint offset");
69 #[cfg(not(fuzz))]
70 {
71 let mut checksum: u32 = 0;
72 checksum
73 .as_mut_bytes()
74 .copy_from_slice(&segment[len..len + std::mem::size_of::<u32>()]);
75 let crc32 = f2fs_crc32(F2FS_MAGIC, &segment[..len]);
76 ensure!(crc32 == checksum, "Bad Checkpoint checksum ({crc32:08x} != {checksum:08x})");
77 }
78 let sit_bitmap_start = std::mem::size_of::<CheckpointHeader>();
79 let nat_bitmap_start = sit_bitmap_start + header.sit_ver_bitmap_bytesize as usize;
80 let nat_bitmap_end = nat_bitmap_start + header.nat_ver_bitmap_bytesize as usize;
81 ensure!(sit_bitmap_start < nat_bitmap_start, "Invalid sit_bitmap size");
82 ensure!(nat_bitmap_start < nat_bitmap_end, "Invalid nat_bitmap size");
83 ensure!(nat_bitmap_end <= SEGMENT_SIZE, "Invalid nat_bitmap range");
84 let nat_bitmap = segment[nat_bitmap_start..nat_bitmap_end].to_vec();
85
86 ensure!(
87 header.cp_pack_total_block_count > 0,
88 "Invalid cp_pack_total_block_count (must be > 0)"
89 );
90 let backup_header_offset =
91 (header.cp_pack_total_block_count as usize - 1) * BLOCK_SIZE as usize;
92 ensure!(
93 backup_header_offset + std::mem::size_of::<CheckpointHeader>() <= SEGMENT_SIZE,
94 "Invalid cp_pack_total_block_count"
95 );
96 let backup_header = CheckpointHeader::read_from_bytes(
97 &segment[backup_header_offset
98 ..backup_header_offset + std::mem::size_of::<CheckpointHeader>()],
99 )
100 .map_err(|_| anyhow!("Invalid backup header"))?;
101 ensure!(backup_header == header, "CheckpointHeader and backup differ");
103 Ok(Self { header, nat_bitmap })
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
113 fn test_checkpoint_parsing() {
114 assert!(CheckpointPack::parse_checkpoint(&[]).is_err());
115
116 let mut segment = Vec::new();
117 segment.resize(SEGMENT_SIZE, 0);
118 let mut header =
119 CheckpointHeader::read_from_bytes(&segment[..std::mem::size_of::<CheckpointHeader>()])
120 .unwrap();
121
122 let set_header = |segment: &mut [u8], header: &CheckpointHeader| {
124 segment[..std::mem::size_of::<CheckpointHeader>()].copy_from_slice(header.as_bytes());
125 let crc32 = f2fs_crc32(F2FS_MAGIC, &segment[..header.checksum_offset as usize]);
126 segment[header.checksum_offset as usize..header.checksum_offset as usize + 4]
127 .copy_from_slice(crc32.as_bytes());
128 };
129
130 {
132 header.checksum_offset = SEGMENT_SIZE as u32 - 3;
133 segment[..std::mem::size_of::<CheckpointHeader>()].copy_from_slice(header.as_bytes());
134 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
135 }
136 {
138 header.checksum_offset = std::mem::size_of::<CheckpointHeader>() as u32;
139 header.sit_ver_bitmap_bytesize = SEGMENT_SIZE as u32;
140 set_header(&mut segment, &header);
141 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
142 }
143 {
145 header.sit_ver_bitmap_bytesize = 0;
146 header.nat_ver_bitmap_bytesize = SEGMENT_SIZE as u32;
147 set_header(&mut segment, &header);
148 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
149 }
150 {
152 header.sit_ver_bitmap_bytesize = SEGMENT_SIZE as u32 / 2;
153 header.nat_ver_bitmap_bytesize = SEGMENT_SIZE as u32 / 2;
154 set_header(&mut segment, &header);
155 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
156 }
157 {
159 header.sit_ver_bitmap_bytesize = SEGMENT_SIZE as u32 / 4;
160 header.nat_ver_bitmap_bytesize = SEGMENT_SIZE as u32 / 4;
161 header.cp_pack_total_block_count = 2048;
162 set_header(&mut segment, &header);
163 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
164 }
165 {
167 header.cp_pack_total_block_count = 100;
168 set_header(&mut segment, &header);
169 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
170 }
171 {
173 segment.copy_within(..std::mem::size_of::<CheckpointHeader>(), BLOCK_SIZE * 99);
174 let result = CheckpointPack::parse_checkpoint(&segment);
175 assert!(result.is_ok(), "{:?}", result);
176 }
177 }
178}