f2fs_reader/
checkpoint.rs1use crate::superblock::{f2fs_crc32, BLOCK_SIZE, F2FS_MAGIC, SEGMENT_SIZE};
5use anyhow::{anyhow, ensure, Error};
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 CKPT_FLAG_UNMOUNT: u32 = 0x1;
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 mut checksum: u32 = 0;
68 let len = header.checksum_offset as usize;
69 ensure!(len <= segment.len() - std::mem::size_of::<u32>(), "Bad checkpoint offset");
70 checksum.as_mut_bytes().copy_from_slice(&segment[len..len + std::mem::size_of::<u32>()]);
71 let crc32 = f2fs_crc32(F2FS_MAGIC, &segment[..len]);
72 ensure!(crc32 == checksum, "Bad Checkpoint checksum ({crc32:08x} != {checksum:08x})");
73 let sit_bitmap_start = std::mem::size_of::<CheckpointHeader>();
74 let nat_bitmap_start = sit_bitmap_start + header.sit_ver_bitmap_bytesize as usize;
75 let nat_bitmap_end = nat_bitmap_start + header.nat_ver_bitmap_bytesize as usize;
76 ensure!(sit_bitmap_start < nat_bitmap_start, "Invalid sit_bitmap size");
77 ensure!(nat_bitmap_start < nat_bitmap_end, "Invalid nat_bitmap size");
78 ensure!(nat_bitmap_end <= SEGMENT_SIZE, "Invalid nat_bitmap range");
79 let nat_bitmap = segment[nat_bitmap_start..nat_bitmap_end].to_vec();
80
81 let backup_header_offset =
82 (header.cp_pack_total_block_count as usize - 1) * BLOCK_SIZE as usize;
83 ensure!(
84 backup_header_offset + std::mem::size_of::<CheckpointHeader>() < SEGMENT_SIZE,
85 "Invalid cp_pack_total_block_count"
86 );
87 let backup_header = CheckpointHeader::read_from_bytes(
88 &segment[backup_header_offset
89 ..backup_header_offset + std::mem::size_of::<CheckpointHeader>()],
90 )
91 .map_err(|_| anyhow!("Invalid backup header"))?;
92 ensure!(backup_header == header, "CheckpointHeader and backup differ");
94 Ok(Self { header, nat_bitmap })
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
104 fn test_checkpoint_parsing() {
105 assert!(CheckpointPack::parse_checkpoint(&[]).is_err());
106
107 let mut segment = Vec::new();
108 segment.resize(SEGMENT_SIZE, 0);
109 let mut header =
110 CheckpointHeader::read_from_bytes(&segment[..std::mem::size_of::<CheckpointHeader>()])
111 .unwrap();
112
113 let set_header = |segment: &mut [u8], header: &CheckpointHeader| {
115 segment[..std::mem::size_of::<CheckpointHeader>()].copy_from_slice(header.as_bytes());
116 let crc32 = f2fs_crc32(F2FS_MAGIC, &segment[..header.checksum_offset as usize]);
117 segment[header.checksum_offset as usize..header.checksum_offset as usize + 4]
118 .copy_from_slice(crc32.as_bytes());
119 };
120
121 {
123 header.checksum_offset = SEGMENT_SIZE as u32 - 3;
124 segment[..std::mem::size_of::<CheckpointHeader>()].copy_from_slice(header.as_bytes());
125 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
126 }
127 {
129 header.checksum_offset = std::mem::size_of::<CheckpointHeader>() as u32;
130 header.sit_ver_bitmap_bytesize = SEGMENT_SIZE as u32;
131 set_header(&mut segment, &header);
132 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
133 }
134 {
136 header.sit_ver_bitmap_bytesize = 0;
137 header.nat_ver_bitmap_bytesize = SEGMENT_SIZE as u32;
138 set_header(&mut segment, &header);
139 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
140 }
141 {
143 header.sit_ver_bitmap_bytesize = SEGMENT_SIZE as u32 / 2;
144 header.nat_ver_bitmap_bytesize = SEGMENT_SIZE as u32 / 2;
145 set_header(&mut segment, &header);
146 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
147 }
148 {
150 header.sit_ver_bitmap_bytesize = SEGMENT_SIZE as u32 / 4;
151 header.nat_ver_bitmap_bytesize = SEGMENT_SIZE as u32 / 4;
152 header.cp_pack_total_block_count = 2048;
153 set_header(&mut segment, &header);
154 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
155 }
156 {
158 header.cp_pack_total_block_count = 100;
159 set_header(&mut segment, &header);
160 assert!(CheckpointPack::parse_checkpoint(&segment).is_err());
161 }
162 {
164 segment.copy_within(..std::mem::size_of::<CheckpointHeader>(), BLOCK_SIZE * 99);
165 let result = CheckpointPack::parse_checkpoint(&segment);
166 assert!(result.is_ok(), "{:?}", result);
167 }
168 }
169}