summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThirunarayanan Balathandayuthapani <thiru@mariadb.com>2019-06-28 17:19:05 +0530
committerThirunarayanan Balathandayuthapani <thiru@mariadb.com>2019-06-28 17:19:05 +0530
commitae17d0681b1df96059b87bdb2ae0c2aeb645123f (patch)
treeba9b9083356332b6323a4bd5263872c9f5f22dae
parent6d2b2365682d9b85d4df30f02e07301c3b11caba (diff)
downloadmariadb-git-bb-10.2-MDEV-19871.tar.gz
MDEV-19781 Add page id matching check in innochecksum toolbb-10.2-MDEV-19871
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.
-rw-r--r--extra/innochecksum.cc106
-rw-r--r--mysql-test/suite/encryption/r/innochecksum.result2
-rw-r--r--mysql-test/suite/encryption/t/innochecksum.test3
-rw-r--r--mysql-test/suite/innodb/r/page_id_innochecksum.result6
-rw-r--r--mysql-test/suite/innodb/t/page_id_innochecksum.test52
-rw-r--r--storage/innobase/fil/fil0fil.cc14
6 files changed, 169 insertions, 14 deletions
diff --git a/extra/innochecksum.cc b/extra/innochecksum.cc
index 3abc541b028..49045cd570a 100644
--- a/extra/innochecksum.cc
+++ b/extra/innochecksum.cc
@@ -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 */
@@ -1741,7 +1778,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 +1844,47 @@ 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. */
+ if (!start_page) {
+ offset = cur_offset * physical_page_size;
+#ifdef _WIN32
+ if (_fseeki64(fil_in, offset, SEEK_SET)) {
+#else
+ if (fseeko(fil_in, offset, SEEK_SET)) {
+#endif /* _WIN32 */
+ perror("Error: Unable to seek to "
+ "necessary offset");
+ exit_status = 1;
+ goto my_exit;
+ }
+ }
+
+ cur_page_num = (mach_read_from_4(buf + FIL_PAGE_OFFSET) - 1);
+ }
+
/* seek to the necessary position */
if (start_page) {
if (!read_from_stdin) {
@@ -1922,8 +2002,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;
}
diff --git a/mysql-test/suite/encryption/r/innochecksum.result b/mysql-test/suite/encryption/r/innochecksum.result
index 6ea54f3d053..2c1279cd232 100644
--- a/mysql-test/suite/encryption/r/innochecksum.result
+++ b/mysql-test/suite/encryption/r/innochecksum.result
@@ -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
diff --git a/mysql-test/suite/encryption/t/innochecksum.test b/mysql-test/suite/encryption/t/innochecksum.test
index f1c1b65d418..47da8525130 100644
--- a/mysql-test/suite/encryption/t/innochecksum.test
+++ b/mysql-test/suite/encryption/t/innochecksum.test
@@ -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
diff --git a/mysql-test/suite/innodb/r/page_id_innochecksum.result b/mysql-test/suite/innodb/r/page_id_innochecksum.result
new file mode 100644
index 00000000000..e2c13442fe6
--- /dev/null
+++ b/mysql-test/suite/innodb/r/page_id_innochecksum.result
@@ -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;
diff --git a/mysql-test/suite/innodb/t/page_id_innochecksum.test b/mysql-test/suite/innodb/t/page_id_innochecksum.test
new file mode 100644
index 00000000000..5bd8a58fcd5
--- /dev/null
+++ b/mysql-test/suite/innodb/t/page_id_innochecksum.test
@@ -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;
diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc
index 08c3a699a04..f718b8bdc52 100644
--- a/storage/innobase/fil/fil0fil.cc
+++ b/storage/innobase/fil/fil0fil.cc
@@ -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,