1use crate::checkpoint::MAX_BITMAP_BYTES;
5use anyhow::{Error, anyhow, ensure};
6use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
7
8pub const F2FS_MAGIC: u32 = 0xf2f52010;
9pub const SUPERBLOCK_OFFSET: u64 = 1024;
11pub const BLOCK_SIZE: usize = 4096;
13pub const BLOCKS_PER_SEGMENT: usize = 512;
15pub const SEGMENT_SIZE: usize = BLOCK_SIZE * BLOCKS_PER_SEGMENT;
16
17pub fn f2fs_crc32(mut seed: u32, buf: &[u8]) -> u32 {
19 const CRC_POLY: u32 = 0xedb88320;
20 for ch in buf {
21 seed ^= *ch as u32;
22 for _ in 0..8 {
23 seed = (seed >> 1) ^ (if (seed & 1) == 1 { CRC_POLY } else { 0 });
24 }
25 }
26 seed
27}
28
29#[repr(C, packed)]
30#[derive(Copy, Clone, Debug, PartialEq, FromBytes, Immutable, IntoBytes, KnownLayout)]
31pub struct SuperBlock {
32 pub magic: u32, pub major_ver: u16, pub minor_ver: u16, pub log_sectorsize: u32, pub log_sectors_per_block: u32, pub log_blocksize: u32, pub log_blocks_per_seg: u32, pub segs_per_sec: u32, pub secs_per_zone: u32, pub checksum_offset: u32, pub block_count: u64, pub section_count: u32, pub segment_count: u32, pub segment_count_ckpt: u32, pub segment_count_sit: u32, pub segment_count_nat: u32, pub segment_count_ssa: u32, pub segment_count_main: u32, pub segment0_blkaddr: u32, pub cp_blkaddr: u32, pub sit_blkaddr: u32, pub nat_blkaddr: u32, pub ssa_blkaddr: u32, pub main_blkaddr: u32, pub root_ino: u32, pub node_ino: u32, pub meta_ino: u32, pub uuid: [u8; 16], pub volume_name: [u16; 512], pub extension_count: u32, pub extension_list: [[u8; 8]; 64],
63 pub cp_payload: u32, pub kernel_version: [u8; 256],
67 pub init_kernel_version: [u8; 256],
68 pub feature: u32,
69 pub encryption_level: u8,
70 pub encryption_salt: [u8; 16],
71 pub devices: [Device; 8],
72 pub quota_file_ino: [u32; 3],
73 pub hot_extension_count: u8,
74 pub charset_encoding: u16,
75 pub charset_encoding_flags: u16,
76 pub stop_checkpoint_reason: [u8; 32],
77 pub errors: [u8; 16],
78 _reserved: [u8; 258],
79 pub crc: u32,
80}
81
82pub const FEATURE_ENCRYPT: u32 = 0x00000001;
83pub const FEATURE_EXTRA_ATTR: u32 = 0x00000008;
84pub const FEATURE_PROJECT_QUOTA: u32 = 0x00000010;
85pub const FEATURE_QUOTA_INO: u32 = 0x00000080;
86pub const FEATURE_VERITY: u32 = 0x00000400;
87pub const FEATURE_SB_CHKSUM: u32 = 0x00000800;
88pub const FEATURE_CASEFOLD: u32 = 0x00001000;
89
90pub const SUPPORTED_FEATURES: u32 = FEATURE_ENCRYPT
91 | FEATURE_EXTRA_ATTR
92 | FEATURE_PROJECT_QUOTA
93 | FEATURE_QUOTA_INO
94 | FEATURE_VERITY
95 | FEATURE_SB_CHKSUM
96 | FEATURE_CASEFOLD;
97
98#[repr(C, packed)]
99#[derive(Copy, Clone, Debug, PartialEq, FromBytes, Immutable, IntoBytes, KnownLayout)]
100pub struct Device {
101 pub path: [u8; 64],
102 pub total_segments: u32,
103}
104
105impl SuperBlock {
106 pub async fn read_from_device(
108 device: &dyn storage_device::Device,
109 offset: u64,
110 ) -> Result<Self, Error> {
111 assert!(offset < BLOCK_SIZE as u64);
113 let mut block = device.allocate_buffer(BLOCK_SIZE).await;
114 device.read(0, block.as_mut()).await?;
115 let data = block.to_vec();
116 let buffer = &data[offset as usize..];
117 let superblock =
118 Self::read_from_bytes(buffer).map_err(|e| anyhow!("Failed to read superblock {e}"))?;
119 ensure!(superblock.magic == F2FS_MAGIC, "Invalid F2fs magic number");
120
121 ensure!(superblock.log_blocksize == 12, "Unsupported block size");
123 ensure!(superblock.log_blocks_per_seg == 9, "Unsupported segment size");
125
126 let feature = superblock.feature;
127 ensure!(feature & !SUPPORTED_FEATURES == 0, "Unsupported feature set {feature:08x}");
128 if superblock.feature & FEATURE_ENCRYPT != 0 {
129 ensure!(
131 superblock.encryption_level == 0 && superblock.encryption_salt == [0u8; 16],
132 "Unsupported encryption features"
133 );
134 }
135
136 #[cfg(not(fuzz))]
137 if superblock.feature & FEATURE_SB_CHKSUM != 0 {
138 let offset = superblock.checksum_offset as usize;
139 ensure!(offset <= std::mem::size_of::<SuperBlock>(), "Invalid checksum_offset");
140 let actual_checksum = f2fs_crc32(F2FS_MAGIC, &superblock.as_bytes()[..offset]);
141 ensure!(superblock.crc == actual_checksum, "Bad superblock checksum");
142 }
143 if superblock.feature & FEATURE_CASEFOLD != 0 {
144 ensure!(superblock.charset_encoding == 1, "Unsupported unicode charset");
146 const NO_COMPAT_FALLBACK: u16 = 2;
149 let charset_encoding_flags = superblock.charset_encoding_flags;
150 if charset_encoding_flags != NO_COMPAT_FALLBACK {
151 log::warn!("Unsupported charset_encoding_flags {charset_encoding_flags:04x}");
152 }
153 }
154
155 let cp_payload = superblock.cp_payload as usize;
156 let sit_ver_bitmap_bytesize = (((superblock.segment_count_sit as u64 / 2)
157 << superblock.log_blocks_per_seg)
158 / 8) as usize;
159 let nat_ver_bitmap_bytesize = (((superblock.segment_count_nat as u64 / 2)
160 << superblock.log_blocks_per_seg)
161 / 8) as usize;
162 if cp_payload == 0 {
163 ensure!(
164 sit_ver_bitmap_bytesize + nat_ver_bitmap_bytesize <= MAX_BITMAP_BYTES,
165 "SIT and NAT bitmaps exceed checkpoint capacity"
166 );
167 } else {
168 ensure!(
169 sit_ver_bitmap_bytesize <= cp_payload * BLOCK_SIZE
170 && nat_ver_bitmap_bytesize <= MAX_BITMAP_BYTES,
171 "SIT or NAT bitmap exceeds capacity"
172 );
173 }
174
175 Ok(superblock)
176 }
177
178 pub fn get_volume_name(&self) -> Result<String, Error> {
180 let volume_name = self.volume_name;
181 let end = volume_name.iter().position(|&x| x == 0).unwrap_or(volume_name.len());
182 String::from_utf16(&volume_name[..end]).map_err(|_| anyhow!("Bad UTF16 in volume name"))
183 }
184
185 pub fn get_total_size(&self) -> u64 {
187 (self.block_count as u64) * BLOCK_SIZE as u64
188 }
189}