MDEV-19781 Add page id matching check in innochecksum tool

Added the condition in innochecksum tool to check page id mismatch.
This could catch the write corruption caused by InnoDB.

Added the debug insert inside fil_io() to check whether it writes
the page to wrong offset.
parent f7a4a871
...@@ -100,6 +100,8 @@ ulong srv_page_size; ...@@ -100,6 +100,8 @@ ulong srv_page_size;
page_size_t univ_page_size(0, 0, false); page_size_t univ_page_size(0, 0, false);
/* Current page number (0 based). */ /* Current page number (0 based). */
unsigned long long cur_page_num; unsigned long long cur_page_num;
/* Current space. */
unsigned long long cur_space;
/* Skip the checksum verification. */ /* Skip the checksum verification. */
static bool no_check; static bool no_check;
/* Enabled for strict checksum verification. */ /* Enabled for strict checksum verification. */
...@@ -451,6 +453,27 @@ ulong read_file( ...@@ -451,6 +453,27 @@ ulong read_file(
return bytes; return bytes;
} }
/** Check whether the page contains all zeroes.
@param[in] buf page
@param[in] size physical size of the page
@return true if the page is all zeroes; else false */
static bool is_page_all_zeroes(
byte* buf,
ulint size)
{
/* On pages that are not all zero, the page number
must match. */
const ulint* p = reinterpret_cast<const ulint*>(buf);
const ulint* const end = reinterpret_cast<const ulint*>(buf + size);
do {
if (*p++) {
return false;
}
} while (p != end);
return true;
}
/** Check if page is corrupted or not. /** Check if page is corrupted or not.
@param[in] buf page frame @param[in] buf page frame
@param[in] page_size page size @param[in] page_size page size
...@@ -462,10 +485,10 @@ ulong read_file( ...@@ -462,10 +485,10 @@ ulong read_file(
static static
bool bool
is_page_corrupted( is_page_corrupted(
byte* buf, byte* buf,
const page_size_t& page_size, const page_size_t& page_size,
bool is_encrypted, bool is_encrypted,
bool is_compressed) bool is_compressed)
{ {
/* enable if page is corrupted. */ /* enable if page is corrupted. */
...@@ -478,6 +501,24 @@ is_page_corrupted( ...@@ -478,6 +501,24 @@ is_page_corrupted(
ulint space_id = mach_read_from_4( ulint space_id = mach_read_from_4(
buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
if (mach_read_from_4(buf + FIL_PAGE_OFFSET) != cur_page_num
|| space_id != cur_space) {
/* On pages that are not all zero, the page number
must match. */
if (is_page_all_zeroes(buf, page_size.physical())) {
return false;
}
if (is_log_enabled) {
fprintf(log_file,
"page id mismatch space::" ULINTPF
" page::%llu \n",
space_id, cur_page_num);
}
return true;
}
/* We can't trust only a page type, thus we take account /* We can't trust only a page type, thus we take account
also fsp_flags or crypt_data on page 0 */ also fsp_flags or crypt_data on page 0 */
if ((page_type == FIL_PAGE_PAGE_COMPRESSED && is_compressed) || if ((page_type == FIL_PAGE_PAGE_COMPRESSED && is_compressed) ||
...@@ -1576,9 +1617,6 @@ int main( ...@@ -1576,9 +1617,6 @@ int main(
FILE* fil_page_type = NULL; FILE* fil_page_type = NULL;
fpos_t pos; fpos_t pos;
/* Use to check the space id of given file. If space_id is zero,
then check whether page is doublewrite buffer.*/
ulint space_id = 0UL;
/* enable when space_id of given file is zero. */ /* enable when space_id of given file is zero. */
bool is_system_tablespace = false; bool is_system_tablespace = false;
...@@ -1700,9 +1738,8 @@ int main( ...@@ -1700,9 +1738,8 @@ int main(
/* enable variable is_system_tablespace when space_id of given /* enable variable is_system_tablespace when space_id of given
file is zero. Use to skip the checksum verification and rewrite file is zero. Use to skip the checksum verification and rewrite
for doublewrite pages. */ for doublewrite pages. */
is_system_tablespace = (!memcmp(&space_id, buf + cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID);
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 4)) cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET);
? true : false;
/* Determine page size, zip_size and page compression /* Determine page size, zip_size and page compression
from fsp_flags and encryption metadata from page 0 */ from fsp_flags and encryption metadata from page 0 */
...@@ -1715,7 +1752,9 @@ int main( ...@@ -1715,7 +1752,9 @@ int main(
srv_page_size = page_size.logical(); srv_page_size = page_size.logical();
bool is_compressed = FSP_FLAGS_HAS_PAGE_COMPRESSION(flags); bool is_compressed = FSP_FLAGS_HAS_PAGE_COMPRESSION(flags);
if (page_size.physical() > UNIV_ZIP_SIZE_MIN) { if (physical_page_size == UNIV_ZIP_SIZE_MIN) {
partial_page_read = false;
} else {
/* Read rest of the page 0 to determine crypt_data */ /* Read rest of the page 0 to determine crypt_data */
bytes = ulong(read_file(buf, partial_page_read, page_size.physical(), fil_in)); bytes = ulong(read_file(buf, partial_page_read, page_size.physical(), fil_in));
...@@ -1731,6 +1770,7 @@ int main( ...@@ -1731,6 +1770,7 @@ int main(
partial_page_read = false; partial_page_read = false;
} }
/* Now that we have full page 0 in buffer, check encryption */ /* Now that we have full page 0 in buffer, check encryption */
bool is_encrypted = check_encryption(filename, page_size, buf); bool is_encrypted = check_encryption(filename, page_size, buf);
...@@ -1741,7 +1781,9 @@ int main( ...@@ -1741,7 +1781,9 @@ int main(
unsigned long long tmp_allow_mismatches = allow_mismatches; unsigned long long tmp_allow_mismatches = allow_mismatches;
allow_mismatches = 0; allow_mismatches = 0;
exit_status = verify_checksum(buf, page_size, is_encrypted, is_compressed, &mismatch_count); exit_status = verify_checksum(
buf, page_size, is_encrypted,
is_compressed, &mismatch_count);
if (exit_status) { if (exit_status) {
fprintf(stderr, "Error: Page 0 checksum mismatch, can't continue. \n"); fprintf(stderr, "Error: Page 0 checksum mismatch, can't continue. \n");
...@@ -1805,6 +1847,36 @@ int main( ...@@ -1805,6 +1847,36 @@ int main(
} }
} }
off_t cur_offset = 0;
/* Find the first non all-zero page and fetch the
space id from there. */
while (is_page_all_zeroes(buf, physical_page_size)) {
bytes = ulong(read_file(
buf, false, physical_page_size,
fil_in));
if (feof(fil_in)) {
fprintf(stderr, "All are "
"zero-filled pages.");
goto my_exit;
}
cur_offset++;
}
cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID);
is_system_tablespace = (cur_space == 0);
if (cur_offset > 0) {
/* Re-read the non-zero page to check the
checksum. So move the file pointer to
previous position and reset the page number too. */
cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET);
if (!start_page) {
goto first_non_zero;
}
}
/* seek to the necessary position */ /* seek to the necessary position */
if (start_page) { if (start_page) {
if (!read_from_stdin) { if (!read_from_stdin) {
...@@ -1902,6 +1974,7 @@ int main( ...@@ -1902,6 +1974,7 @@ int main(
goto my_exit; goto my_exit;
} }
first_non_zero:
if (is_system_tablespace) { if (is_system_tablespace) {
/* enable when page is double write buffer.*/ /* enable when page is double write buffer.*/
skip_page = is_page_doublewritebuffer(buf); skip_page = is_page_doublewritebuffer(buf);
...@@ -1922,8 +1995,10 @@ int main( ...@@ -1922,8 +1995,10 @@ int main(
checksum verification.*/ checksum verification.*/
if (!no_check if (!no_check
&& !skip_page && !skip_page
&& (exit_status = verify_checksum(buf, page_size, && (exit_status = verify_checksum(
is_encrypted, is_compressed, &mismatch_count))) { buf, page_size,
is_encrypted, is_compressed,
&mismatch_count))) {
goto my_exit; goto my_exit;
} }
......
...@@ -33,7 +33,7 @@ CREATE TABLE t6 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB; ...@@ -33,7 +33,7 @@ CREATE TABLE t6 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB;
# Run innochecksum on t2 # Run innochecksum on t2
# Run innochecksum on t3 # Run innochecksum on t3
# Run innochecksum on t6 # Run innochecksum on t6
# no encryption corrupting the field should not have effect # Space ID mismatch
# Restore the original tables # Restore the original tables
# Corrupt FIL_DATA+10 (data) # Corrupt FIL_DATA+10 (data)
# Run innochecksum on t2 # Run innochecksum on t2
......
...@@ -206,7 +206,8 @@ EOF ...@@ -206,7 +206,8 @@ EOF
--exec $INNOCHECKSUM $t3_IBD --exec $INNOCHECKSUM $t3_IBD
--echo # Run innochecksum on t6 --echo # Run innochecksum on t6
--echo # no encryption corrupting the field should not have effect --echo # Space ID mismatch
--error 1
--exec $INNOCHECKSUM $t6_IBD --exec $INNOCHECKSUM $t6_IBD
--enable_result_log --enable_result_log
......
# Set the environmental variables
create table t1(f1 int not null)engine=innodb;
insert into t1 values(1), (2), (3);
# Change the page offset
FOUND 1 /page id mismatch/ in result.log
drop table t1;
--source include/have_innodb.inc
--echo # Set the environmental variables
let MYSQLD_BASEDIR= `SELECT @@basedir`;
let MYSQLD_DATADIR= `SELECT @@datadir`;
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
create table t1(f1 int not null)engine=innodb;
insert into t1 values(1), (2), (3);
let $resultlog=$MYSQLTEST_VARDIR/tmp/result.log;
--source include/shutdown_mysqld.inc
--echo # Change the page offset
perl;
use strict;
use warnings;
use Fcntl qw(:DEFAULT :seek);
do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl";
my $page_size = $ENV{INNODB_PAGE_SIZE};
sysopen IBD_FILE, "$ENV{MYSQLD_DATADIR}/test/t1.ibd", O_RDWR
|| die "Cannot open t1.ibd\n";
sysread(IBD_FILE, $_, 38) || die "Cannot read t1.ibd\n";
my $space = unpack("x[34]N", $_);
sysseek(IBD_FILE, $page_size * 3, SEEK_SET) || die "Cannot seek t1.ibd\n";
my $head = pack("Nx[18]", 4); # better to have a valid page number
my $body = chr(0) x ($page_size - 38 - 8);
# Calculate innodb_checksum_algorithm=crc32 for the unencrypted page.
# The following bytes are excluded:
# bytes 0..3 (the checksum is stored there)
# bytes 26..37 (encryption key version, post-encryption checksum, tablespace id)
# bytes $page_size-8..$page_size-1 (checksum, LSB of FIL_PAGE_LSN)
my $polynomial = 0x82f63b78; # CRC-32C
my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
my $page= pack("N",$ck).$head.pack("NNN",1,$ck,$space).$body.pack("Nx[4]",$ck);
die unless syswrite(IBD_FILE, $page, $page_size) == $page_size;
close IBD_FILE;
EOF
--error 1
exec $INNOCHECKSUM -C crc32 -l $resultlog $MYSQLD_DATADIR/test/t1.ibd;
let SEARCH_FILE = $MYSQLTEST_VARDIR/tmp/result.log;
let SEARCH_PATTERN=page id mismatch;
--source include/search_pattern_in_file.inc
--remove_file $resultlog
--source include/start_mysqld.inc
drop table t1;
...@@ -5056,6 +5056,20 @@ fil_io( ...@@ -5056,6 +5056,20 @@ fil_io(
req_type.set_fil_node(node); req_type.set_fil_node(node);
#ifdef UNIV_DEBUG
if (req_type.is_write()
&& page_id.space() != SRV_LOG_SPACE_FIRST_ID
&& (page_id.space() != TRX_SYS_SPACE
|| buf_dblwr == NULL
|| !(page_id.page_no() >=
(buf_dblwr->block1 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE)
|| page_id.page_no() >=
(buf_dblwr->block2 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE)))) {
ut_ad(offset == page_id.page_no() * page_size.physical());
}
#endif /* UNIV_DEBUG */
/* Queue the aio request */ /* Queue the aio request */
dberr_t err = os_aio( dberr_t err = os_aio(
req_type, req_type,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment