1 /*****************************************************************************
2 
3 Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2017, 2019, MariaDB Corporation.
5 
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 
18 *****************************************************************************/
19 
20 /**************************************************//**
21 @file buf/buf0checksum.cc
22 Buffer pool checksum functions, also linked from /extra/innochecksum.cc
23 
24 Created Aug 11, 2011 Vasil Dimov
25 *******************************************************/
26 
27 #include "buf0checksum.h"
28 #include "fil0fil.h"
29 #include "ut0crc32.h"
30 #include "ut0rnd.h"
31 
32 #ifndef UNIV_INNOCHECKSUM
33 #include "srv0srv.h"
34 #endif /* !UNIV_INNOCHECKSUM */
35 
36 /** the macro MYSQL_SYSVAR_ENUM() requires "long unsigned int" and if we
37 use srv_checksum_algorithm_t here then we get a compiler error:
38 ha_innodb.cc:12251: error: cannot convert 'srv_checksum_algorithm_t*' to
39   'long unsigned int*' in initialization */
40 ulong	srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_INNODB;
41 
42 /** Calculate the CRC32 checksum of a page. The value is stored to the page
43 when it is written to a file and also checked for a match when reading from
44 the file. Note that we must be careful to calculate the same value on all
45 architectures.
46 @param[in]	page			buffer page (srv_page_size bytes)
47 @return	CRC-32C */
buf_calc_page_crc32(const byte * page)48 uint32_t buf_calc_page_crc32(const byte* page)
49 {
50 	/* Note: innodb_checksum_algorithm=crc32 could and should have
51 	included the entire page in the checksum, and CRC-32 values
52 	should be combined with the CRC-32 function, not with
53 	exclusive OR. We stick to the current algorithm in order to
54 	remain compatible with old data files. */
55 	return ut_crc32(page + FIL_PAGE_OFFSET,
56 			FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
57 			- FIL_PAGE_OFFSET)
58 		^ ut_crc32(page + FIL_PAGE_DATA,
59 			   srv_page_size
60 			   - (FIL_PAGE_DATA + FIL_PAGE_END_LSN_OLD_CHKSUM));
61 }
62 
63 /** Calculate a checksum which is stored to the page when it is written
64 to a file. Note that we must be careful to calculate the same value on
65 32-bit and 64-bit architectures.
66 @param[in]	page	file page (srv_page_size bytes)
67 @return checksum */
68 uint32_t
buf_calc_page_new_checksum(const byte * page)69 buf_calc_page_new_checksum(const byte* page)
70 {
71 	ulint checksum;
72 
73 	/* Since the field FIL_PAGE_FILE_FLUSH_LSN, and in versions <= 4.1.x
74 	FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, are written outside the buffer pool
75 	to the first pages of data files, we have to skip them in the page
76 	checksum calculation.
77 	We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
78 	checksum is stored, and also the last 8 bytes of page because
79 	there we store the old formula checksum. */
80 
81 	checksum = ut_fold_binary(page + FIL_PAGE_OFFSET,
82 				  FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
83 				  - FIL_PAGE_OFFSET)
84 		+ ut_fold_binary(page + FIL_PAGE_DATA,
85 				 srv_page_size - FIL_PAGE_DATA
86 				 - FIL_PAGE_END_LSN_OLD_CHKSUM);
87 	return(static_cast<uint32_t>(checksum));
88 }
89 
90 /** In MySQL before 4.0.14 or 4.1.1 there was an InnoDB bug that
91 the checksum only looked at the first few bytes of the page.
92 This calculates that old checksum.
93 NOTE: we must first store the new formula checksum to
94 FIL_PAGE_SPACE_OR_CHKSUM before calculating and storing this old checksum
95 because this takes that field as an input!
96 @param[in]	page	file page (srv_page_size bytes)
97 @return checksum */
98 uint32_t
buf_calc_page_old_checksum(const byte * page)99 buf_calc_page_old_checksum(const byte* page)
100 {
101 	return(static_cast<uint32_t>
102 	       (ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION)));
103 }
104 
105 /** Return a printable string describing the checksum algorithm.
106 @param[in]	algo	algorithm
107 @return algorithm name */
108 const char*
buf_checksum_algorithm_name(srv_checksum_algorithm_t algo)109 buf_checksum_algorithm_name(srv_checksum_algorithm_t algo)
110 {
111 	switch (algo) {
112 	case SRV_CHECKSUM_ALGORITHM_CRC32:
113 		return("crc32");
114 	case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
115 		return("strict_crc32");
116 	case SRV_CHECKSUM_ALGORITHM_INNODB:
117 		return("innodb");
118 	case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
119 		return("strict_innodb");
120 	case SRV_CHECKSUM_ALGORITHM_NONE:
121 		return("none");
122 	case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
123 		return("strict_none");
124 	case SRV_CHECKSUM_ALGORITHM_FULL_CRC32:
125 		return("full_crc32");
126 	case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32:
127 		return("strict_full_crc32");
128 	}
129 
130 	ut_error;
131 	return(NULL);
132 }
133