1use crate::superblock::{BLOCK_SIZE, BLOCKS_PER_SEGMENT, 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;
13pub const ORPHANS_PER_BLOCK: usize = 1020;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq, FromBytes, Immutable, IntoBytes, KnownLayout)]
16#[repr(C, packed)]
17pub struct OrphanBlock {
18 pub ino: [u32; ORPHANS_PER_BLOCK],
19 pub reserved: u32,
20 pub blk_addr: u16,
21 pub blk_count: u16,
22 pub entry_count: u32,
23 pub check_sum: u32,
24}
25
26#[derive(Debug, Eq, PartialEq, FromBytes, Immutable, IntoBytes, KnownLayout)]
27#[repr(C, packed)]
28pub struct CheckpointHeader {
29 pub checkpoint_ver: u64,
30 pub user_block_count: u64,
31 pub valid_block_count: u64,
32 pub rsvd_segment_count: u32,
33 pub overprov_segment_count: u32,
34 pub free_segment_count: u32,
35 pub cur_node_segno: [u32; MAX_ACTIVE_NODE_LOGS],
36 pub cur_node_blkoff: [u16; MAX_ACTIVE_NODE_LOGS],
37 pub cur_data_segno: [u32; MAX_ACTIVE_DATA_LOGS],
38 pub cur_data_blkoff: [u16; MAX_ACTIVE_DATA_LOGS],
39 pub ckpt_flags: u32,
40 pub cp_pack_total_block_count: u32,
41 pub cp_pack_start_sum: u32,
42 pub valid_node_count: u32,
43 pub valid_inode_count: u32,
44 pub next_free_nid: u32,
45 pub sit_ver_bitmap_bytesize: u32,
46 pub nat_ver_bitmap_bytesize: u32,
47 pub checksum_offset: u32,
48 pub elapsed_time: u64,
49 pub alloc_type: [u8; MAX_ACTIVE_LOGS],
50 }
53
54#[derive(Debug)]
55pub struct CheckpointPack {
56 pub header: CheckpointHeader,
57 pub nat_bitmap: Vec<u8>,
58}
59
60pub const MAX_BITMAP_BYTES: usize =
61 BLOCK_SIZE - std::mem::size_of::<u32>() - std::mem::size_of::<CheckpointHeader>(); impl CheckpointPack {
64 pub async fn read_from_device(
65 device: &dyn storage_device::Device,
66 offset: u64,
67 cp_payload: u32,
68 ) -> Result<Self, Error> {
69 let mut segment = device.allocate_buffer(SEGMENT_SIZE).await;
70 device.read(offset, segment.as_mut()).await?;
71 let data = segment.to_vec();
72 Self::parse_checkpoint(&data, cp_payload)
73 }
74
75 pub fn parse_checkpoint(segment: &[u8], cp_payload: u32) -> Result<Self, Error> {
76 ensure!(
77 (cp_payload as usize) <= BLOCKS_PER_SEGMENT - 3,
78 "cp_payload exceeds segment capacity"
79 );
80 ensure!(
81 segment.len() >= std::mem::size_of::<CheckpointHeader>(),
82 "Segment too short for checkpoint"
83 );
84 let header =
85 CheckpointHeader::read_from_bytes(&segment[..std::mem::size_of::<CheckpointHeader>()])
86 .map_err(|_| anyhow!("Invalid checkpoint header"))?;
87 ensure!(
88 header.cp_pack_total_block_count > 0
89 && (header.cp_pack_total_block_count as usize) <= BLOCKS_PER_SEGMENT,
90 "Invalid cp_pack_total_block_count"
91 );
92 ensure!(
93 segment.len() >= header.cp_pack_total_block_count as usize * BLOCK_SIZE,
94 "Segment too short for checkpoint pack"
95 );
96 let len = header.checksum_offset as usize;
97 ensure!(len == BLOCK_SIZE - std::mem::size_of::<u32>(), "Bad checkpoint offset");
98 #[cfg(not(fuzz))]
99 {
100 let mut checksum: u32 = 0;
101 checksum
102 .as_mut_bytes()
103 .copy_from_slice(&segment[len..len + std::mem::size_of::<u32>()]);
104 let crc32 = f2fs_crc32(F2FS_MAGIC, &segment[..len]);
105 ensure!(crc32 == checksum, "Bad Checkpoint checksum ({crc32:08x} != {checksum:08x})");
106 }
107 let sit_ver_bitmap_bytesize = header.sit_ver_bitmap_bytesize as usize;
108 let nat_ver_bitmap_bytesize = header.nat_ver_bitmap_bytesize as usize;
109 ensure!(sit_ver_bitmap_bytesize > 0, "Invalid sit_bitmap size");
110 ensure!(nat_ver_bitmap_bytesize > 0, "Invalid nat_bitmap size");
111 ensure!(
112 nat_ver_bitmap_bytesize <= MAX_BITMAP_BYTES,
113 "NAT bitmap exceeds checkpoint capacity"
114 );
115
116 let nat_bitmap = if cp_payload == 0 {
117 ensure!(
118 sit_ver_bitmap_bytesize + nat_ver_bitmap_bytesize <= MAX_BITMAP_BYTES,
119 "SIT and NAT bitmaps exceed checkpoint capacity"
120 );
121 let nat_bitmap_start =
122 std::mem::size_of::<CheckpointHeader>() + sit_ver_bitmap_bytesize;
123 let nat_bitmap_end = nat_bitmap_start + nat_ver_bitmap_bytesize;
124 segment[nat_bitmap_start..nat_bitmap_end].to_vec()
125 } else {
126 ensure!(
127 sit_ver_bitmap_bytesize <= cp_payload as usize * BLOCK_SIZE,
128 "SIT bitmap exceeds cp_payload capacity"
129 );
130 let nat_bitmap_start = std::mem::size_of::<CheckpointHeader>();
131 let nat_bitmap_end = nat_bitmap_start + nat_ver_bitmap_bytesize;
132 segment[nat_bitmap_start..nat_bitmap_end].to_vec()
133 };
134
135 ensure!(header.cp_pack_start_sum > cp_payload, "cp_pack_start_sum too small");
136 ensure!(
137 header.cp_pack_start_sum < header.cp_pack_total_block_count,
138 "cp_pack_start_sum exceeds total blocks in checkpoint pack"
139 );
140 let backup_header_offset =
141 (header.cp_pack_total_block_count as usize - 1) * BLOCK_SIZE as usize;
142 let backup_header = CheckpointHeader::read_from_bytes(
143 &segment[backup_header_offset
144 ..backup_header_offset + std::mem::size_of::<CheckpointHeader>()],
145 )
146 .map_err(|_| anyhow!("Invalid backup header"))?;
147 ensure!(backup_header == header, "CheckpointHeader and backup differ");
149 Ok(Self { header, nat_bitmap })
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
159 fn test_checkpoint_parsing() {
160 assert!(CheckpointPack::parse_checkpoint(&[], 0).is_err());
161
162 let mut segment = Vec::new();
163 segment.resize(SEGMENT_SIZE, 0);
164 let mut header =
165 CheckpointHeader::read_from_bytes(&segment[..std::mem::size_of::<CheckpointHeader>()])
166 .unwrap();
167 header.checksum_offset = (BLOCK_SIZE - 4) as u32;
168 header.cp_pack_start_sum = 1;
169 header.cp_pack_total_block_count = 100;
170
171 let set_header = |segment: &mut [u8], header: &CheckpointHeader| {
173 segment[..std::mem::size_of::<CheckpointHeader>()].copy_from_slice(header.as_bytes());
174 let crc32 = f2fs_crc32(F2FS_MAGIC, &segment[..header.checksum_offset as usize]);
175 segment[header.checksum_offset as usize..header.checksum_offset as usize + 4]
176 .copy_from_slice(crc32.as_bytes());
177 };
178
179 {
181 header.checksum_offset = SEGMENT_SIZE as u32 - 3;
182 segment[..std::mem::size_of::<CheckpointHeader>()].copy_from_slice(header.as_bytes());
183 assert!(CheckpointPack::parse_checkpoint(&segment, 0).is_err());
184 }
185 {
187 header.checksum_offset = (BLOCK_SIZE - 4) as u32;
188 header.sit_ver_bitmap_bytesize = SEGMENT_SIZE as u32;
189 set_header(&mut segment, &header);
190 assert!(CheckpointPack::parse_checkpoint(&segment, 0).is_err());
191 }
192 {
194 header.sit_ver_bitmap_bytesize = 64;
195 header.nat_ver_bitmap_bytesize = SEGMENT_SIZE as u32;
196 set_header(&mut segment, &header);
197 assert!(CheckpointPack::parse_checkpoint(&segment, 0).is_err());
198 }
199 {
201 header.sit_ver_bitmap_bytesize = 2000;
202 header.nat_ver_bitmap_bytesize = 2000;
203 set_header(&mut segment, &header);
204 assert!(CheckpointPack::parse_checkpoint(&segment, 0).is_err());
205 }
206 {
208 header.sit_ver_bitmap_bytesize = 64;
209 header.nat_ver_bitmap_bytesize = 256;
210 header.cp_pack_total_block_count = 2048;
211 set_header(&mut segment, &header);
212 assert!(CheckpointPack::parse_checkpoint(&segment, 0).is_err());
213 }
214 {
216 header.checksum_offset = 8192 - 4;
217 header.cp_pack_total_block_count = 100;
218 set_header(&mut segment, &header);
219 assert!(CheckpointPack::parse_checkpoint(&segment, 0).is_err());
220 }
221 {
223 header.checksum_offset = (BLOCK_SIZE - 4) as u32;
224 header.cp_pack_start_sum = 0;
225 set_header(&mut segment, &header);
226 assert!(CheckpointPack::parse_checkpoint(&segment, 0).is_err());
227 }
228 {
230 header.cp_pack_start_sum = 100;
231 header.cp_pack_total_block_count = 100;
232 set_header(&mut segment, &header);
233 assert!(CheckpointPack::parse_checkpoint(&segment, 0).is_err());
234 }
235 {
237 header.checksum_offset = (BLOCK_SIZE - 4) as u32;
238 header.sit_ver_bitmap_bytesize = 64;
239 header.nat_ver_bitmap_bytesize = 256;
240 header.cp_pack_start_sum = 1;
241 header.cp_pack_total_block_count = 100;
242 set_header(&mut segment, &header);
243 segment.copy_within(..std::mem::size_of::<CheckpointHeader>(), BLOCK_SIZE * 99);
244 let result = CheckpointPack::parse_checkpoint(&segment, 0);
245 assert!(result.is_ok(), "{:?}", result);
246 }
247 {
249 assert!(CheckpointPack::parse_checkpoint(&segment, 1000).is_err());
250 }
251 {
253 header.sit_ver_bitmap_bytesize = 4096;
254 header.nat_ver_bitmap_bytesize = 256;
255 header.cp_pack_start_sum = 2;
256 header.cp_pack_total_block_count = 100;
257 let nat_start = std::mem::size_of::<CheckpointHeader>();
259 segment[nat_start..nat_start + 256].fill(0x5A);
260 set_header(&mut segment, &header);
261 segment.copy_within(..std::mem::size_of::<CheckpointHeader>(), BLOCK_SIZE * 99);
262 let result = CheckpointPack::parse_checkpoint(&segment, 1);
263 assert!(result.is_ok(), "{:?}", result);
264 assert_eq!(result.unwrap().nat_bitmap, vec![0x5A; 256]);
265 }
266 {
268 assert!(CheckpointPack::parse_checkpoint(&segment[..512], 0).is_err());
269 }
270 {
272 assert!(CheckpointPack::parse_checkpoint(&segment[..BLOCK_SIZE * 50], 0).is_err());
273 }
274 }
275}