1use byteorder::{BigEndian, ByteOrder};
2
3use crate::innodb::constants::*;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum ChecksumAlgorithm {
8 Crc32c,
10 InnoDB,
12 None,
14}
15
16pub fn validate_checksum(page_data: &[u8], page_size: u32) -> ChecksumResult {
20 let ps = page_size as usize;
21 if page_data.len() < ps {
22 return ChecksumResult {
23 algorithm: ChecksumAlgorithm::None,
24 valid: false,
25 stored_checksum: 0,
26 calculated_checksum: 0,
27 };
28 }
29
30 let stored_checksum = BigEndian::read_u32(&page_data[FIL_PAGE_SPACE_OR_CHKSUM..]);
31
32 if stored_checksum == 0xDEADBEEF {
34 return ChecksumResult {
35 algorithm: ChecksumAlgorithm::None,
36 valid: true,
37 stored_checksum,
38 calculated_checksum: 0xDEADBEEF,
39 };
40 }
41
42 if stored_checksum == 0 {
44 let all_zero = page_data[..ps].iter().all(|&b| b == 0);
45 if all_zero {
46 return ChecksumResult {
47 algorithm: ChecksumAlgorithm::None,
48 valid: true,
49 stored_checksum: 0,
50 calculated_checksum: 0,
51 };
52 }
53 }
54
55 let crc_checksum = calculate_crc32c(page_data, ps);
57 if stored_checksum == crc_checksum {
58 return ChecksumResult {
59 algorithm: ChecksumAlgorithm::Crc32c,
60 valid: true,
61 stored_checksum,
62 calculated_checksum: crc_checksum,
63 };
64 }
65
66 let innodb_checksum = calculate_innodb_checksum(page_data, ps);
68 if stored_checksum == innodb_checksum {
69 return ChecksumResult {
70 algorithm: ChecksumAlgorithm::InnoDB,
71 valid: true,
72 stored_checksum,
73 calculated_checksum: innodb_checksum,
74 };
75 }
76
77 ChecksumResult {
79 algorithm: ChecksumAlgorithm::Crc32c,
80 valid: false,
81 stored_checksum,
82 calculated_checksum: crc_checksum,
83 }
84}
85
86#[derive(Debug, Clone)]
88pub struct ChecksumResult {
89 pub algorithm: ChecksumAlgorithm,
90 pub valid: bool,
91 pub stored_checksum: u32,
92 pub calculated_checksum: u32,
93}
94
95fn calculate_crc32c(page_data: &[u8], page_size: usize) -> u32 {
105 let end = page_size - SIZE_FIL_TRAILER;
106
107 let crc = crc32c::crc32c(&page_data[FIL_PAGE_OFFSET..FIL_PAGE_FILE_FLUSH_LSN]);
109
110 crc32c::crc32c_append(crc, &page_data[FIL_PAGE_DATA..end])
112}
113
114#[inline]
119fn ut_fold_ulint_pair(n1: u64, n2: u64) -> u64 {
120 let mask2 = UT_HASH_RANDOM_MASK2 as u64;
121 let mask = UT_HASH_RANDOM_MASK as u64;
122 ((((n1 ^ n2 ^ mask2) << 8).wrapping_add(n1)) ^ mask).wrapping_add(n2)
123}
124
125fn ut_fold_binary(data: &[u8]) -> u64 {
130 let mut fold: u64 = 0;
131 let len = data.len();
132 let aligned_len = len & !7; let mut i = 0;
136 while i < aligned_len {
137 fold = ut_fold_ulint_pair(fold, BigEndian::read_u32(&data[i..]) as u64);
138 i += 4;
139 fold = ut_fold_ulint_pair(fold, BigEndian::read_u32(&data[i..]) as u64);
140 i += 4;
141 }
142
143 let remainder = len & 7;
145 match remainder {
146 7 => {
147 fold = ut_fold_ulint_pair(fold, data[i] as u64);
148 i += 1;
149 fold = ut_fold_ulint_pair(fold, data[i] as u64);
150 i += 1;
151 fold = ut_fold_ulint_pair(fold, data[i] as u64);
152 i += 1;
153 fold = ut_fold_ulint_pair(fold, BigEndian::read_u32(&data[i..]) as u64);
154 }
155 6 => {
156 fold = ut_fold_ulint_pair(fold, data[i] as u64);
157 i += 1;
158 fold = ut_fold_ulint_pair(fold, data[i] as u64);
159 i += 1;
160 fold = ut_fold_ulint_pair(fold, BigEndian::read_u32(&data[i..]) as u64);
161 }
162 5 => {
163 fold = ut_fold_ulint_pair(fold, data[i] as u64);
164 i += 1;
165 fold = ut_fold_ulint_pair(fold, BigEndian::read_u32(&data[i..]) as u64);
166 }
167 4 => {
168 fold = ut_fold_ulint_pair(fold, BigEndian::read_u32(&data[i..]) as u64);
169 }
170 3 => {
171 fold = ut_fold_ulint_pair(fold, data[i] as u64);
172 i += 1;
173 fold = ut_fold_ulint_pair(fold, data[i] as u64);
174 i += 1;
175 fold = ut_fold_ulint_pair(fold, data[i] as u64);
176 }
177 2 => {
178 fold = ut_fold_ulint_pair(fold, data[i] as u64);
179 i += 1;
180 fold = ut_fold_ulint_pair(fold, data[i] as u64);
181 }
182 1 => {
183 fold = ut_fold_ulint_pair(fold, data[i] as u64);
184 }
185 _ => {}
186 }
187
188 fold
189}
190
191fn calculate_innodb_checksum(page_data: &[u8], page_size: usize) -> u32 {
198 let end = page_size - SIZE_FIL_TRAILER;
199
200 let fold1 = ut_fold_binary(&page_data[FIL_PAGE_OFFSET..FIL_PAGE_FILE_FLUSH_LSN]);
202
203 let fold2 = ut_fold_binary(&page_data[FIL_PAGE_DATA..end]);
205
206 fold1.wrapping_add(fold2) as u32
208}
209
210pub fn validate_lsn(page_data: &[u8], page_size: u32) -> bool {
214 let ps = page_size as usize;
215 if page_data.len() < ps {
216 return false;
217 }
218 let header_lsn = BigEndian::read_u64(&page_data[FIL_PAGE_LSN..]);
219 let header_lsn_low32 = (header_lsn & 0xFFFFFFFF) as u32;
220
221 let trailer_offset = ps - SIZE_FIL_TRAILER;
222 let trailer_lsn_low32 = BigEndian::read_u32(&page_data[trailer_offset + 4..]);
223
224 header_lsn_low32 == trailer_lsn_low32
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn test_all_zero_page_is_valid() {
233 let page = vec![0u8; 16384];
234 let result = validate_checksum(&page, 16384);
235 assert!(result.valid);
236 }
237
238 #[test]
239 fn test_no_checksum_magic() {
240 let mut page = vec![0u8; 16384];
241 BigEndian::write_u32(&mut page[0..], 0xDEADBEEF);
242 let result = validate_checksum(&page, 16384);
243 assert!(result.valid);
244 assert_eq!(result.algorithm, ChecksumAlgorithm::None);
245 }
246
247 #[test]
248 fn test_lsn_validation_matching() {
249 let mut page = vec![0u8; 16384];
250 BigEndian::write_u64(&mut page[FIL_PAGE_LSN..], 0x12345678);
252 BigEndian::write_u32(&mut page[16380..], 0x12345678);
254 assert!(validate_lsn(&page, 16384));
255 }
256
257 #[test]
258 fn test_lsn_validation_mismatch() {
259 let mut page = vec![0u8; 16384];
260 BigEndian::write_u64(&mut page[FIL_PAGE_LSN..], 0x12345678);
261 BigEndian::write_u32(&mut page[16380..], 0xAAAAAAAA);
262 assert!(!validate_lsn(&page, 16384));
263 }
264}