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