744 lines
26 KiB
C++
744 lines
26 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 1995, 2019, Oracle and/or its affiliates. All Rights Reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License, version 2.0, as published by the
|
|
Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including but not
|
|
limited to OpenSSL) that is licensed under separate terms, as designated in a
|
|
particular file or component or in included license documentation. The authors
|
|
of MySQL hereby grant you an additional permission to link the program and
|
|
your derivative works with the separately licensed software that they have
|
|
included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
/** @file buf/checksum.cc
|
|
Buffer pool checksum functions, also linked from /extra/innochecksum.cc
|
|
|
|
Created Aug 11, 2011 Vasil Dimov
|
|
*******************************************************/
|
|
|
|
#include <sys/types.h>
|
|
#include <zlib.h>
|
|
|
|
#include "buf0buf.h"
|
|
#include "buf0types.h"
|
|
#include "fil0fil.h"
|
|
#include "mach0data.h"
|
|
#include "my_dbug.h"
|
|
#include "page0size.h"
|
|
#include "srv0srv.h"
|
|
#include "univ.i"
|
|
#include "ut0crc32.h"
|
|
#include "ut0rnd.h"
|
|
#ifdef UNIV_HOTBACKUP
|
|
#include "buf0checksum.h"
|
|
#endif /* UNIV_HOTBACKUP */
|
|
|
|
/** the macro MYSQL_SYSVAR_ENUM() requires "long unsigned int" and if we
|
|
use srv_checksum_algorithm_t here then we get a compiler error:
|
|
ha_innodb.cc:12251: error: cannot convert 'srv_checksum_algorithm_t*' to
|
|
'long unsigned int*' in initialization */
|
|
ulong srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_INNODB;
|
|
|
|
/** set if we have found pages matching legacy big endian checksum */
|
|
static bool legacy_big_endian_checksum = false;
|
|
|
|
/** Calculates the CRC32 checksum of a page. The value is stored to the page
|
|
when it is written to a file and also checked for a match when reading from
|
|
the file. When reading we allow both normal CRC32 and CRC-legacy-big-endian
|
|
variants. Note that we must be careful to calculate the same value on 32-bit
|
|
and 64-bit architectures.
|
|
@param[in] page buffer page (UNIV_PAGE_SIZE bytes)
|
|
@param[in] use_legacy_big_endian if true then use big endian
|
|
byteorder when converting byte strings to integers
|
|
@return checksum */
|
|
uint32_t buf_calc_page_crc32(const byte *page,
|
|
bool use_legacy_big_endian /* = false */) {
|
|
/* Since the field FIL_PAGE_FILE_FLUSH_LSN, and in versions <= 4.1.x
|
|
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, are written outside the buffer pool
|
|
to the first pages of data files, we have to skip them in the page
|
|
checksum calculation.
|
|
We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
|
|
checksum is stored, and also the last 8 bytes of page because
|
|
there we store the old formula checksum. */
|
|
|
|
ut_crc32_func_t crc32_func =
|
|
use_legacy_big_endian ? ut_crc32_legacy_big_endian : ut_crc32;
|
|
|
|
const uint32_t c1 = crc32_func(page + FIL_PAGE_OFFSET,
|
|
FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET);
|
|
|
|
const uint32_t c2 =
|
|
crc32_func(page + FIL_PAGE_DATA,
|
|
UNIV_PAGE_SIZE - FIL_PAGE_DATA - FIL_PAGE_END_LSN_OLD_CHKSUM);
|
|
|
|
return (c1 ^ c2);
|
|
}
|
|
|
|
/** Calculates a page checksum which is stored to the page when it is written
|
|
to a file. Note that we must be careful to calculate the same value on
|
|
32-bit and 64-bit architectures.
|
|
@return checksum */
|
|
ulint buf_calc_page_new_checksum(const byte *page) /*!< in: buffer page */
|
|
{
|
|
ulint checksum;
|
|
|
|
/* Since the field FIL_PAGE_FILE_FLUSH_LSN, and in versions <= 4.1.x
|
|
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, are written outside the buffer pool
|
|
to the first pages of data files, we have to skip them in the page
|
|
checksum calculation.
|
|
We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
|
|
checksum is stored, and also the last 8 bytes of page because
|
|
there we store the old formula checksum. */
|
|
|
|
checksum =
|
|
ut_fold_binary(page + FIL_PAGE_OFFSET,
|
|
FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET) +
|
|
ut_fold_binary(page + FIL_PAGE_DATA, UNIV_PAGE_SIZE - FIL_PAGE_DATA -
|
|
FIL_PAGE_END_LSN_OLD_CHKSUM);
|
|
checksum = checksum & 0xFFFFFFFFUL;
|
|
|
|
return (checksum);
|
|
}
|
|
|
|
/** In versions < 4.0.14 and < 4.1.1 there was a bug that the checksum only
|
|
looked at the first few bytes of the page. This calculates that old
|
|
checksum.
|
|
NOTE: we must first store the new formula checksum to
|
|
FIL_PAGE_SPACE_OR_CHKSUM before calculating and storing this old checksum
|
|
because this takes that field as an input!
|
|
@return checksum */
|
|
ulint buf_calc_page_old_checksum(const byte *page) /*!< in: buffer page */
|
|
{
|
|
ulint checksum;
|
|
|
|
checksum = ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN);
|
|
|
|
checksum = checksum & 0xFFFFFFFFUL;
|
|
|
|
return (checksum);
|
|
}
|
|
|
|
/** Return a printable string describing the checksum algorithm.
|
|
@return algorithm name */
|
|
const char *buf_checksum_algorithm_name(
|
|
srv_checksum_algorithm_t algo) /*!< in: algorithm */
|
|
{
|
|
switch (algo) {
|
|
case SRV_CHECKSUM_ALGORITHM_CRC32:
|
|
return ("crc32");
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
|
|
return ("strict_crc32");
|
|
case SRV_CHECKSUM_ALGORITHM_INNODB:
|
|
return ("innodb");
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
|
|
return ("strict_innodb");
|
|
case SRV_CHECKSUM_ALGORITHM_NONE:
|
|
return ("none");
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
|
|
return ("strict_none");
|
|
}
|
|
|
|
ut_error;
|
|
}
|
|
|
|
/** Do lsn checks on a page during innodb recovery.
|
|
@param[in] check_lsn if recv_lsn_checks_on & check_lsn
|
|
perform lsn check
|
|
@param[in] read_buf buffer containing the page. */
|
|
inline void buf_page_lsn_check(bool check_lsn, const byte *read_buf) {
|
|
#if !defined(UNIV_HOTBACKUP) && !defined(UNIV_LIBRARY)
|
|
if (check_lsn && recv_lsn_checks_on) {
|
|
lsn_t current_lsn;
|
|
const lsn_t page_lsn = mach_read_from_8(read_buf + FIL_PAGE_LSN);
|
|
|
|
/* Since we are going to reset the page LSN during the import
|
|
phase it makes no sense to spam the log with error messages. */
|
|
current_lsn = log_get_lsn(*log_sys);
|
|
|
|
if (current_lsn < page_lsn) {
|
|
const space_id_t space_id =
|
|
mach_read_from_4(read_buf + FIL_PAGE_SPACE_ID);
|
|
const page_no_t page_no = mach_read_from_4(read_buf + FIL_PAGE_OFFSET);
|
|
|
|
auto space = fil_space_get(space_id);
|
|
|
|
#ifdef UNIV_NO_ERR_MSGS
|
|
ib::error()
|
|
#else
|
|
ib::error(ER_IB_MSG_146)
|
|
#endif /* UNIV_NO_ERR_MSGS */
|
|
<< "Tablespace '" << space->name << "'"
|
|
<< " Page " << page_id_t(space_id, page_no) << " log sequence number "
|
|
<< page_lsn << " is in the future! Current system"
|
|
<< " log sequence number " << current_lsn << ".";
|
|
|
|
#ifdef UNIV_NO_ERR_MSGS
|
|
ib::error()
|
|
#else
|
|
ib::error(ER_IB_MSG_147)
|
|
#endif /* UNIV_NO_ERR_MSGS */
|
|
<< "Your database may be corrupt or you may have copied the InnoDB"
|
|
<< " tablespace but not the InnoDB log files. " << FORCE_RECOVERY_MSG;
|
|
}
|
|
}
|
|
#endif /* !UNIV_HOTBACKUP && !UNIV_LIBRARY */
|
|
}
|
|
|
|
/** Checks if the page is in innodb checksum format.
|
|
@param[in] checksum_field1 new checksum field
|
|
@param[in] checksum_field2 old checksum field
|
|
@param[in] algo current checksum algorithm
|
|
@return true if the page is in innodb checksum format. */
|
|
bool BlockReporter::is_checksum_valid_innodb(
|
|
ulint checksum_field1, ulint checksum_field2,
|
|
const srv_checksum_algorithm_t algo) const {
|
|
/* There are 2 valid formulas for
|
|
checksum_field2 (old checksum field) which algo=innodb could have
|
|
written to the page:
|
|
|
|
1. Very old versions of InnoDB only stored 8 byte lsn to the
|
|
start and the end of the page.
|
|
|
|
2. Newer InnoDB versions store the old formula checksum
|
|
(buf_calc_page_old_checksum()). */
|
|
|
|
ulint old_checksum = buf_calc_page_old_checksum(m_read_buf);
|
|
ulint new_checksum = buf_calc_page_new_checksum(m_read_buf);
|
|
|
|
print_innodb_checksum(old_checksum, new_checksum, checksum_field1,
|
|
checksum_field2, algo);
|
|
|
|
if (checksum_field2 != mach_read_from_4(m_read_buf + FIL_PAGE_LSN) &&
|
|
checksum_field2 != old_checksum) {
|
|
return (false);
|
|
}
|
|
|
|
/* old field is fine, check the new field */
|
|
|
|
/* InnoDB versions < 4.0.14 and < 4.1.1 stored the space id
|
|
(always equal to 0), to FIL_PAGE_SPACE_OR_CHKSUM */
|
|
|
|
return (checksum_field1 == 0 || checksum_field1 == new_checksum);
|
|
}
|
|
|
|
/** Checks if the page is in none checksum format.
|
|
@param[in] checksum_field1 new checksum field
|
|
@param[in] checksum_field2 old checksum field
|
|
@param[in] algo current checksum algorithm
|
|
@return true if the page is in none checksum format. */
|
|
bool BlockReporter::is_checksum_valid_none(
|
|
ulint checksum_field1, ulint checksum_field2,
|
|
const srv_checksum_algorithm_t algo) const {
|
|
print_strict_none(checksum_field1, checksum_field2, algo);
|
|
|
|
return (checksum_field1 == checksum_field2 &&
|
|
checksum_field1 == BUF_NO_CHECKSUM_MAGIC);
|
|
}
|
|
|
|
/** Checks if the page is in crc32 checksum format.
|
|
@param[in] checksum_field1 new checksum field
|
|
@param[in] checksum_field2 old checksum field
|
|
@param[in] algo current checksum algorithm
|
|
@param[in] use_legacy_big_endian big endian algorithm
|
|
@return true if the page is in crc32 checksum format. */
|
|
bool BlockReporter::is_checksum_valid_crc32(ulint checksum_field1,
|
|
ulint checksum_field2,
|
|
const srv_checksum_algorithm_t algo,
|
|
bool use_legacy_big_endian) const {
|
|
if (checksum_field1 != checksum_field2) {
|
|
return (false);
|
|
}
|
|
|
|
uint32_t crc32 = buf_calc_page_crc32(m_read_buf, use_legacy_big_endian);
|
|
|
|
print_strict_crc32(checksum_field1, checksum_field2, crc32, algo);
|
|
|
|
return (checksum_field1 == crc32);
|
|
}
|
|
|
|
/** Checks if a page is corrupt.
|
|
@retval true if page is corrupt
|
|
@retval false if page is not corrupt */
|
|
bool BlockReporter::is_corrupted() const {
|
|
ulint checksum_field1;
|
|
ulint checksum_field2;
|
|
|
|
if (!m_page_size.is_compressed() &&
|
|
memcmp(
|
|
m_read_buf + FIL_PAGE_LSN + 4,
|
|
m_read_buf + m_page_size.logical() - FIL_PAGE_END_LSN_OLD_CHKSUM + 4,
|
|
4)) {
|
|
/* Stored log sequence numbers at the start and the end
|
|
of page do not match */
|
|
|
|
return (true);
|
|
}
|
|
|
|
buf_page_lsn_check(m_check_lsn, m_read_buf);
|
|
|
|
/* Check whether the checksum fields have correct values */
|
|
|
|
if (srv_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_NONE ||
|
|
m_skip_checksum) {
|
|
return (false);
|
|
}
|
|
|
|
if (m_page_size.is_compressed()) {
|
|
return (!verify_zip_checksum());
|
|
}
|
|
|
|
checksum_field1 = mach_read_from_4(m_read_buf + FIL_PAGE_SPACE_OR_CHKSUM);
|
|
|
|
checksum_field2 = mach_read_from_4(m_read_buf + m_page_size.logical() -
|
|
FIL_PAGE_END_LSN_OLD_CHKSUM);
|
|
|
|
#if FIL_PAGE_LSN % 8
|
|
#error "FIL_PAGE_LSN must be 64 bit aligned"
|
|
#endif
|
|
|
|
/* declare empty pages non-corrupted */
|
|
if (checksum_field1 == 0 && checksum_field2 == 0 &&
|
|
mach_read_from_8(m_read_buf + FIL_PAGE_LSN) == 0) {
|
|
/* make sure that the page is really empty */
|
|
|
|
bool empty = true;
|
|
#ifndef UNIV_HOTBACKUP
|
|
for (ulint i = 0; i < m_page_size.logical(); i++) {
|
|
/* The FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID has been
|
|
repurposed for page compression. It can be
|
|
set for uncompressed empty pages. */
|
|
|
|
if ((i < FIL_PAGE_FILE_FLUSH_LSN ||
|
|
i >= FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID) &&
|
|
m_read_buf[i] != 0) {
|
|
empty = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
report_empty_page(empty);
|
|
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
return (!empty);
|
|
}
|
|
|
|
const page_id_t page_id(mach_read_from_4(m_read_buf + FIL_PAGE_SPACE_ID),
|
|
mach_read_from_4(m_read_buf + FIL_PAGE_OFFSET));
|
|
|
|
DBUG_EXECUTE_IF("buf_page_import_corrupt_failure", return (true););
|
|
const srv_checksum_algorithm_t curr_algo =
|
|
static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm);
|
|
|
|
bool legacy_checksum_checked = false;
|
|
|
|
switch (curr_algo) {
|
|
case SRV_CHECKSUM_ALGORITHM_CRC32:
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
|
|
|
|
if (is_checksum_valid_crc32(checksum_field1, checksum_field2, curr_algo,
|
|
false)) {
|
|
return (false);
|
|
}
|
|
|
|
if (is_checksum_valid_none(checksum_field1, checksum_field2, curr_algo)) {
|
|
#ifndef UNIV_HOTBACKUP
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_NONE,
|
|
page_id);
|
|
}
|
|
|
|
print_crc32_checksum(checksum_field1, checksum_field2);
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
return (false);
|
|
}
|
|
|
|
/* We need to check whether the stored checksum matches legacy
|
|
big endian checksum or Innodb checksum. We optimize the order
|
|
based on earlier results. if earlier we have found pages
|
|
matching legacy big endian checksum, we try to match it first.
|
|
Otherwise we check innodb checksum first. */
|
|
if (legacy_big_endian_checksum) {
|
|
if (is_checksum_valid_crc32(checksum_field1, checksum_field2, curr_algo,
|
|
true)) {
|
|
return (false);
|
|
}
|
|
legacy_checksum_checked = true;
|
|
}
|
|
|
|
if (is_checksum_valid_innodb(checksum_field1, checksum_field2,
|
|
curr_algo)) {
|
|
#ifndef UNIV_HOTBACKUP
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_INNODB,
|
|
page_id);
|
|
}
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
return (false);
|
|
}
|
|
|
|
/* If legacy checksum is not checked, do it now. */
|
|
if (!legacy_checksum_checked &&
|
|
is_checksum_valid_crc32(checksum_field1, checksum_field2, curr_algo,
|
|
true)) {
|
|
legacy_big_endian_checksum = true;
|
|
return (false);
|
|
}
|
|
|
|
print_crc32_fail();
|
|
return (true);
|
|
|
|
case SRV_CHECKSUM_ALGORITHM_INNODB:
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
|
|
|
|
if (is_checksum_valid_innodb(checksum_field1, checksum_field2,
|
|
curr_algo)) {
|
|
return (false);
|
|
}
|
|
|
|
if (is_checksum_valid_none(checksum_field1, checksum_field2, curr_algo)) {
|
|
#ifndef UNIV_HOTBACKUP
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_NONE,
|
|
page_id);
|
|
}
|
|
|
|
print_strict_innodb(checksum_field1, checksum_field2);
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
return (false);
|
|
}
|
|
|
|
if (is_checksum_valid_crc32(checksum_field1, checksum_field2, curr_algo,
|
|
false) ||
|
|
is_checksum_valid_crc32(checksum_field1, checksum_field2, curr_algo,
|
|
true)) {
|
|
#ifndef UNIV_HOTBACKUP
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_CRC32,
|
|
page_id);
|
|
}
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
|
|
return (false);
|
|
}
|
|
|
|
print_innodb_fail();
|
|
return (true);
|
|
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
|
|
|
|
if (is_checksum_valid_none(checksum_field1, checksum_field2, curr_algo)) {
|
|
return (false);
|
|
}
|
|
|
|
if (is_checksum_valid_crc32(checksum_field1, checksum_field2, curr_algo,
|
|
false) ||
|
|
is_checksum_valid_crc32(checksum_field1, checksum_field2, curr_algo,
|
|
true)) {
|
|
#ifndef UNIV_HOTBACKUP
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_CRC32,
|
|
page_id);
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
return (false);
|
|
}
|
|
|
|
if (is_checksum_valid_innodb(checksum_field1, checksum_field2,
|
|
curr_algo)) {
|
|
#ifndef UNIV_HOTBACKUP
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_INNODB,
|
|
page_id);
|
|
#endif /* !UNIV_HOTBACKUP */
|
|
return (false);
|
|
}
|
|
|
|
print_none_fail();
|
|
return (true);
|
|
|
|
case SRV_CHECKSUM_ALGORITHM_NONE:
|
|
/* should have returned FALSE earlier */
|
|
break;
|
|
/* no default so the compiler will emit a warning if new enum
|
|
is added and not handled here */
|
|
}
|
|
|
|
ut_error;
|
|
return (false);
|
|
}
|
|
|
|
/** Calculate the compressed page checksum.
|
|
@param[in] algo checksum algorithm to use
|
|
@param[in] use_legacy_big_endian only used if algo is
|
|
SRV_CHECKSUM_ALGORITHM_CRC32 or SRV_CHECKSUM_ALGORITHM_STRICT_CRC32 -
|
|
if true then use big endian byteorder when converting byte strings to
|
|
integers.
|
|
@return page checksum */
|
|
uint32_t BlockReporter::calc_zip_checksum(
|
|
srv_checksum_algorithm_t algo,
|
|
bool use_legacy_big_endian /* = false */) const {
|
|
return (calc_zip_checksum(m_read_buf, m_page_size.physical(), algo,
|
|
use_legacy_big_endian));
|
|
}
|
|
|
|
/** Calculate the compressed page checksum. This variant
|
|
should be used when only the page_size_t is unknown and
|
|
only physical page_size of compressed page is available
|
|
@param[in] read_buf buffer holding the page
|
|
@param[in] phys_page_size physical page size
|
|
@param[in] algo checksum algorithm to use
|
|
@param[in] use_legacy_big_endian only used if algo is
|
|
SRV_CHECKSUM_ALGORITHM_CRC32 or SRV_CHECKSUM_ALGORITHM_STRICT_CRC32 -
|
|
if true then use big endian byteorder when converting byte strings to
|
|
integers.
|
|
@return page checksum */
|
|
uint32_t BlockReporter::calc_zip_checksum(
|
|
const byte *read_buf, ulint phys_page_size, srv_checksum_algorithm_t algo,
|
|
bool use_legacy_big_endian /* = false */) const {
|
|
uLong adler;
|
|
ib_uint32_t crc32;
|
|
const Bytef *s = read_buf;
|
|
const ulint size = phys_page_size;
|
|
|
|
/* Exclude FIL_PAGE_SPACE_OR_CHKSUM, FIL_PAGE_LSN,
|
|
and FIL_PAGE_FILE_FLUSH_LSN from the checksum. */
|
|
|
|
switch (algo) {
|
|
case SRV_CHECKSUM_ALGORITHM_CRC32:
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
|
|
|
|
ut_ad(size > FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
|
|
|
|
crc32 = ut_crc32(s + FIL_PAGE_OFFSET, FIL_PAGE_LSN - FIL_PAGE_OFFSET) ^
|
|
ut_crc32(s + FIL_PAGE_TYPE, 2) ^
|
|
ut_crc32(s + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
|
|
size - FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
|
|
|
|
return (crc32);
|
|
case SRV_CHECKSUM_ALGORITHM_INNODB:
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
|
|
ut_ad(size > FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
|
|
|
|
adler = adler32(0L, s + FIL_PAGE_OFFSET, FIL_PAGE_LSN - FIL_PAGE_OFFSET);
|
|
adler = adler32(adler, s + FIL_PAGE_TYPE, 2);
|
|
adler =
|
|
adler32(adler, s + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID,
|
|
static_cast<uInt>(size) - FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
|
|
|
|
return ((ib_uint32_t)adler);
|
|
case SRV_CHECKSUM_ALGORITHM_NONE:
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
|
|
return (BUF_NO_CHECKSUM_MAGIC);
|
|
/* no default so the compiler will emit a warning if new enum
|
|
is added and not handled here */
|
|
}
|
|
|
|
ut_error;
|
|
}
|
|
|
|
/** Verify a compressed page's checksum.
|
|
@retval true if stored checksum is valid according
|
|
to the value of srv_checksum_algorithm
|
|
@retval false if stored schecksum is not valid according
|
|
to the value of srv_checksum_algorithm */
|
|
bool BlockReporter::verify_zip_checksum() const {
|
|
const uint32_t stored = static_cast<uint32_t>(
|
|
mach_read_from_4(m_read_buf + FIL_PAGE_SPACE_OR_CHKSUM));
|
|
|
|
#if FIL_PAGE_LSN % 8
|
|
#error "FIL_PAGE_LSN must be 64 bit aligned"
|
|
#endif
|
|
|
|
/* Check if page is empty */
|
|
if (stored == 0 && mach_read_from_8(m_read_buf + FIL_PAGE_LSN) == 0) {
|
|
/* make sure that the page is really empty */
|
|
|
|
ulint i;
|
|
bool empty = true;
|
|
for (i = 0; i < m_page_size.physical(); i++) {
|
|
if (*((const char *)m_read_buf + i) != 0) {
|
|
empty = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
report_empty_page(empty);
|
|
|
|
/* Empty page */
|
|
return (empty);
|
|
}
|
|
|
|
const srv_checksum_algorithm_t curr_algo =
|
|
static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm);
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_NONE) {
|
|
return (true);
|
|
}
|
|
|
|
page_no_t page_no = mach_read_from_4(m_read_buf + FIL_PAGE_OFFSET);
|
|
space_id_t space_id = mach_read_from_4(m_read_buf + FIL_PAGE_SPACE_ID);
|
|
const page_id_t page_id(space_id, page_no);
|
|
|
|
const uint32_t calc = calc_zip_checksum(curr_algo);
|
|
|
|
print_compressed_checksum(calc, stored);
|
|
|
|
if (stored == calc) {
|
|
return (true);
|
|
}
|
|
|
|
bool legacy_checksum_checked = false;
|
|
|
|
switch (curr_algo) {
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
|
|
case SRV_CHECKSUM_ALGORITHM_CRC32:
|
|
|
|
if (stored == BUF_NO_CHECKSUM_MAGIC) {
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_NONE,
|
|
page_id);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
/* We need to check whether the stored checksum matches legacy
|
|
big endian checksum or Innodb checksum. We optimize the order
|
|
based on earlier results. if earlier we have found pages
|
|
matching legacy big endian checksum, we try to match it first.
|
|
Otherwise we check innodb checksum first. */
|
|
if (legacy_big_endian_checksum) {
|
|
if (stored == calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_CRC32, true)) {
|
|
return (true);
|
|
}
|
|
legacy_checksum_checked = true;
|
|
}
|
|
|
|
if (stored == calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_INNODB)) {
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_INNODB,
|
|
page_id);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
/* If legacy checksum is not checked, do it now. */
|
|
if (!legacy_checksum_checked &&
|
|
stored == calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_CRC32, true)) {
|
|
/* This page's checksum has been created by the
|
|
legacy software CRC32 implementation on big endian
|
|
CPUs which generates a different result than the
|
|
normal CRC32. */
|
|
legacy_big_endian_checksum = true;
|
|
return (true);
|
|
}
|
|
|
|
break;
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
|
|
case SRV_CHECKSUM_ALGORITHM_INNODB:
|
|
|
|
if (stored == BUF_NO_CHECKSUM_MAGIC) {
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_NONE,
|
|
page_id);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
if (stored == calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_CRC32) ||
|
|
stored == calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_CRC32, true)) {
|
|
if (curr_algo == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_CRC32,
|
|
page_id);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
break;
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
|
|
|
|
if (stored == calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_CRC32) ||
|
|
stored == calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_CRC32, true)) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_CRC32,
|
|
page_id);
|
|
return (true);
|
|
}
|
|
|
|
if (stored == calc_zip_checksum(SRV_CHECKSUM_ALGORITHM_INNODB)) {
|
|
page_warn_strict_checksum(curr_algo, SRV_CHECKSUM_ALGORITHM_INNODB,
|
|
page_id);
|
|
return (true);
|
|
}
|
|
|
|
break;
|
|
case SRV_CHECKSUM_ALGORITHM_NONE:
|
|
ut_error;
|
|
/* no default so the compiler will emit a warning if new enum
|
|
is added and not handled here */
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
/** Issue a warning when the checksum that is stored in the page is valid,
|
|
but different than the global setting innodb_checksum_algorithm.
|
|
@param[in] curr_algo current checksum algorithm
|
|
@param[in] page_checksum page valid checksum
|
|
@param[in] page_id page identifier */
|
|
void BlockReporter::page_warn_strict_checksum(
|
|
srv_checksum_algorithm_t curr_algo, srv_checksum_algorithm_t page_checksum,
|
|
const page_id_t &page_id) const {
|
|
srv_checksum_algorithm_t curr_algo_nonstrict;
|
|
switch (curr_algo) {
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
|
|
curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_CRC32;
|
|
break;
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
|
|
curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_INNODB;
|
|
break;
|
|
case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
|
|
curr_algo_nonstrict = SRV_CHECKSUM_ALGORITHM_NONE;
|
|
break;
|
|
default:
|
|
ut_error;
|
|
}
|
|
|
|
#ifdef UNIV_NO_ERR_MSGS
|
|
ib::warn()
|
|
#else
|
|
ib::warn(ER_IB_MSG_148)
|
|
#endif /* UNIV_NO_ERR_MSGS */
|
|
|
|
<< "innodb_checksum_algorithm is set to \""
|
|
<< buf_checksum_algorithm_name(curr_algo) << "\""
|
|
<< " but the page " << page_id << " contains a valid checksum \""
|
|
<< buf_checksum_algorithm_name(page_checksum) << "\". "
|
|
<< " Accepting the page as valid. Change"
|
|
<< " innodb_checksum_algorithm to \""
|
|
<< buf_checksum_algorithm_name(curr_algo_nonstrict)
|
|
<< "\" to silently accept such pages or rewrite all pages"
|
|
<< " so that they contain \""
|
|
<< buf_checksum_algorithm_name(curr_algo_nonstrict) << "\" checksum.";
|
|
}
|
|
|
|
/** Print the given page_id_t object.
|
|
@param[in,out] out the output stream
|
|
@param[in] page_id the page_id_t object to be printed
|
|
@return the output stream */
|
|
std::ostream &operator<<(std::ostream &out, const page_id_t &page_id) {
|
|
out << "[page id: space=" << page_id.m_space
|
|
<< ", page number=" << page_id.m_page_no << "]";
|
|
return (out);
|
|
}
|