1use byteorder::{BigEndian, ByteOrder};
15use crate::innodb::constants::*;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ChecksumAlgorithm {
20 Crc32c,
22 InnoDB,
24 None,
26}
27
28pub fn validate_checksum(page_data: &[u8], page_size: u32) -> ChecksumResult {
32 let ps = page_size as usize;
33 if page_data.len() < ps {
34 return ChecksumResult {
35 algorithm: ChecksumAlgorithm::None,
36 valid: false,
37 stored_checksum: 0,
38 calculated_checksum: 0,
39 };
40 }
41
42 let stored_checksum = BigEndian::read_u32(&page_data[FIL_PAGE_SPACE_OR_CHKSUM..]);
43
44 if stored_checksum == 0xDEADBEEF {
46 return ChecksumResult {
47 algorithm: ChecksumAlgorithm::None,
48 valid: true,
49 stored_checksum,
50 calculated_checksum: 0xDEADBEEF,
51 };
52 }
53
54 if stored_checksum == 0 {
56 let all_zero = page_data[..ps].iter().all(|&b| b == 0);
57 if all_zero {
58 return ChecksumResult {
59 algorithm: ChecksumAlgorithm::None,
60 valid: true,
61 stored_checksum: 0,
62 calculated_checksum: 0,
63 };
64 }
65 }
66
67 let crc_checksum = calculate_crc32c(page_data, ps);
69 if stored_checksum == crc_checksum {
70 return ChecksumResult {
71 algorithm: ChecksumAlgorithm::Crc32c,
72 valid: true,
73 stored_checksum,
74 calculated_checksum: crc_checksum,
75 };
76 }
77
78 let innodb_checksum = calculate_innodb_checksum(page_data, ps);
80 if stored_checksum == innodb_checksum {
81 return ChecksumResult {
82 algorithm: ChecksumAlgorithm::InnoDB,
83 valid: true,
84 stored_checksum,
85 calculated_checksum: innodb_checksum,
86 };
87 }
88
89 ChecksumResult {
91 algorithm: ChecksumAlgorithm::Crc32c,
92 valid: false,
93 stored_checksum,
94 calculated_checksum: crc_checksum,
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct ChecksumResult {
101 pub algorithm: ChecksumAlgorithm,
103 pub valid: bool,
105 pub stored_checksum: u32,
107 pub calculated_checksum: u32,
109}
110
111fn calculate_crc32c(page_data: &[u8], page_size: usize) -> u32 {
122 let end = page_size - SIZE_FIL_TRAILER;
123
124 let crc1 = crc32c::crc32c(&page_data[FIL_PAGE_OFFSET..FIL_PAGE_FILE_FLUSH_LSN]);
126
127 let crc2 = crc32c::crc32c(&page_data[FIL_PAGE_DATA..end]);
129
130 crc1 ^ crc2
132}
133
134#[inline]
140fn ut_fold_ulint_pair(n1: u32, n2: u32) -> u32 {
141 let step = n1 ^ n2 ^ UT_HASH_RANDOM_MASK2;
142 let step = (step << 8).wrapping_add(n1);
143 let step = step ^ UT_HASH_RANDOM_MASK;
144 step.wrapping_add(n2)
145}
146
147fn ut_fold_binary(data: &[u8]) -> u32 {
153 let mut fold: u32 = 0;
154 for &byte in data {
155 fold = ut_fold_ulint_pair(fold, byte as u32);
156 }
157 fold
158}
159
160fn calculate_innodb_checksum(page_data: &[u8], page_size: usize) -> u32 {
167 let end = page_size - SIZE_FIL_TRAILER;
168
169 let fold1 = ut_fold_binary(&page_data[FIL_PAGE_OFFSET..FIL_PAGE_FILE_FLUSH_LSN]);
170 let fold2 = ut_fold_binary(&page_data[FIL_PAGE_DATA..end]);
171
172 fold1.wrapping_add(fold2)
173}
174
175pub fn validate_lsn(page_data: &[u8], page_size: u32) -> bool {
179 let ps = page_size as usize;
180 if page_data.len() < ps {
181 return false;
182 }
183 let header_lsn = BigEndian::read_u64(&page_data[FIL_PAGE_LSN..]);
184 let header_lsn_low32 = (header_lsn & 0xFFFFFFFF) as u32;
185
186 let trailer_offset = ps - SIZE_FIL_TRAILER;
187 let trailer_lsn_low32 = BigEndian::read_u32(&page_data[trailer_offset + 4..]);
188
189 header_lsn_low32 == trailer_lsn_low32
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn test_all_zero_page_is_valid() {
198 let page = vec![0u8; 16384];
199 let result = validate_checksum(&page, 16384);
200 assert!(result.valid);
201 }
202
203 #[test]
204 fn test_no_checksum_magic() {
205 let mut page = vec![0u8; 16384];
206 BigEndian::write_u32(&mut page[0..], 0xDEADBEEF);
207 let result = validate_checksum(&page, 16384);
208 assert!(result.valid);
209 assert_eq!(result.algorithm, ChecksumAlgorithm::None);
210 }
211
212 #[test]
213 fn test_lsn_validation_matching() {
214 let mut page = vec![0u8; 16384];
215 BigEndian::write_u64(&mut page[FIL_PAGE_LSN..], 0x12345678);
217 BigEndian::write_u32(&mut page[16380..], 0x12345678);
219 assert!(validate_lsn(&page, 16384));
220 }
221
222 #[test]
223 fn test_lsn_validation_mismatch() {
224 let mut page = vec![0u8; 16384];
225 BigEndian::write_u64(&mut page[FIL_PAGE_LSN..], 0x12345678);
226 BigEndian::write_u32(&mut page[16380..], 0xAAAAAAAA);
227 assert!(!validate_lsn(&page, 16384));
228 }
229}