Skip to content
Permalink
Browse files

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.
  • Loading branch information...
Thirunarayanan committed Jun 28, 2019
1 parent f7a4a87 commit e4a0dbfb4aa77a9d41039f5cd7144d98a6b9baed
@@ -100,6 +100,8 @@ ulong srv_page_size;
page_size_t univ_page_size(0, 0, false);
/* Current page number (0 based). */
unsigned long long cur_page_num;
/* Current space. */
unsigned long long cur_space;
/* Skip the checksum verification. */
static bool no_check;
/* Enabled for strict checksum verification. */
@@ -451,6 +453,27 @@ ulong read_file(
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.
@param[in] buf page frame
@param[in] page_size page size
@@ -462,10 +485,10 @@ ulong read_file(
static
bool
is_page_corrupted(
byte* buf,
byte* buf,
const page_size_t& page_size,
bool is_encrypted,
bool is_compressed)
bool is_encrypted,
bool is_compressed)
{

/* enable if page is corrupted. */
@@ -478,6 +501,24 @@ is_page_corrupted(
ulint space_id = mach_read_from_4(
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
also fsp_flags or crypt_data on page 0 */
if ((page_type == FIL_PAGE_PAGE_COMPRESSED && is_compressed) ||
@@ -1576,9 +1617,6 @@ int main(
FILE* fil_page_type = NULL;
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. */
bool is_system_tablespace = false;

@@ -1700,9 +1738,8 @@ int main(
/* enable variable is_system_tablespace when space_id of given
file is zero. Use to skip the checksum verification and rewrite
for doublewrite pages. */
is_system_tablespace = (!memcmp(&space_id, buf +
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 4))
? true : false;
cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID);
cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET);

/* Determine page size, zip_size and page compression
from fsp_flags and encryption metadata from page 0 */
@@ -1715,7 +1752,9 @@ int main(
srv_page_size = page_size.logical();
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 */
bytes = ulong(read_file(buf, partial_page_read, page_size.physical(), fil_in));

@@ -1731,6 +1770,7 @@ int main(
partial_page_read = false;
}


/* Now that we have full page 0 in buffer, check encryption */
bool is_encrypted = check_encryption(filename, page_size, buf);

@@ -1741,7 +1781,9 @@ int main(
unsigned long long tmp_allow_mismatches = allow_mismatches;
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) {
fprintf(stderr, "Error: Page 0 checksum mismatch, can't continue. \n");
@@ -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 */
if (start_page) {
if (!read_from_stdin) {
@@ -1902,6 +1974,7 @@ int main(
goto my_exit;
}

first_non_zero:
if (is_system_tablespace) {
/* enable when page is double write buffer.*/
skip_page = is_page_doublewritebuffer(buf);
@@ -1922,8 +1995,10 @@ int main(
checksum verification.*/
if (!no_check
&& !skip_page
&& (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))) {
goto my_exit;
}

@@ -33,7 +33,7 @@ CREATE TABLE t6 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB;
# Run innochecksum on t2
# Run innochecksum on t3
# Run innochecksum on t6
# no encryption corrupting the field should not have effect
# Space ID mismatch
# Restore the original tables
# Corrupt FIL_DATA+10 (data)
# Run innochecksum on t2
@@ -206,7 +206,8 @@ EOF
--exec $INNOCHECKSUM $t3_IBD

--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

--enable_result_log
@@ -0,0 +1,6 @@
# 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;
@@ -0,0 +1,52 @@
--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(

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 */
dberr_t err = os_aio(
req_type,

0 comments on commit e4a0dbf

Please sign in to comment.
You can’t perform that action at this time.