diff options
-rw-r--r-- | extra/mariabackup/fil_cur.cc | 50 | ||||
-rw-r--r-- | mysql-test/suite/innodb/include/crc32.pl | 33 | ||||
-rw-r--r-- | mysql-test/suite/mariabackup/encrypted_page_corruption.result | 2 | ||||
-rw-r--r-- | mysql-test/suite/mariabackup/encrypted_page_corruption.test | 33 |
4 files changed, 93 insertions, 25 deletions
diff --git a/extra/mariabackup/fil_cur.cc b/extra/mariabackup/fil_cur.cc index e8ef2df3ba1..a080f7e5420 100644 --- a/extra/mariabackup/fil_cur.cc +++ b/extra/mariabackup/fil_cur.cc @@ -329,7 +329,7 @@ xb_fil_cur_read( xb_a((to_read & (page_size - 1)) == 0); - npages = (ulint) (to_read / cursor->page_size.physical()); + npages = (ulint) (to_read / page_size); retry_count = 10; ret = XB_FIL_CUR_SUCCESS; @@ -346,7 +346,7 @@ read_retry: cursor->buf_read = 0; cursor->buf_npages = 0; cursor->buf_offset = offset; - cursor->buf_page_no = (ulint)(offset / cursor->page_size.physical()); + cursor->buf_page_no = (ulint)(offset / page_size); if (!os_file_read(IORequestRead, cursor->file, cursor->buf, offset, (ulint) to_read)) { @@ -360,11 +360,32 @@ read_retry: ulint page_no = cursor->buf_page_no + i; ulint page_type = mach_read_from_2(page + FIL_PAGE_TYPE); - if (cursor->space_id == TRX_SYS_SPACE && - page_no >= FSP_EXTENT_SIZE && - page_no < FSP_EXTENT_SIZE * 3) { - /* We ignore the doublewrite buffer pages */ - } else if (mach_read_from_4( + if (cursor->space_id == TRX_SYS_SPACE + && page_no >= FSP_EXTENT_SIZE + && page_no < FSP_EXTENT_SIZE * 3) { + /* We ignore the doublewrite buffer pages */ + } else if (mach_read_from_4(page + FIL_PAGE_OFFSET) != page_no + && space->id != TRX_SYS_SPACE) { + /* On pages that are not all zero, the + page number must match. + + There may be a mismatch on tablespace ID, + because files may be renamed during backup. + We disable the page number check + on the system tablespace, because it may consist + of multiple files, and here we count the pages + from the start of each file.) + + The first 38 and last 8 bytes are never encrypted. */ + const ulint* p = reinterpret_cast<ulint*>(page); + const ulint* const end = reinterpret_cast<ulint*>( + page + page_size); + do { + if (*p++) { + goto corrupted; + } + } while (p != end); + } else if (mach_read_from_4( page + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION) && space->crypt_data @@ -372,9 +393,6 @@ read_retry: != CRYPT_SCHEME_UNENCRYPTED && fil_space_verify_crypt_checksum( page, cursor->page_size)) { - ut_ad(mach_read_from_4(page + FIL_PAGE_SPACE_ID) - == space->id); - bool decrypted = false; memcpy(tmp_page, page, page_size); @@ -392,23 +410,27 @@ read_retry: cursor->page_size, space)) { goto corrupted; } - } else if (page_type == FIL_PAGE_PAGE_COMPRESSED) { - memcpy(tmp_page, page, cursor->page_size.physical()); + memcpy(tmp_page, page, page_size); page_decomp: ulint decomp = fil_page_decompress(tmp_frame, tmp_page); + page_type = mach_read_from_2(tmp_page + FIL_PAGE_TYPE); if (!decomp || (decomp != srv_page_size && cursor->page_size.is_compressed()) + || page_type == FIL_PAGE_PAGE_COMPRESSED + || page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED || buf_page_is_corrupted(true, tmp_page, cursor->page_size, space)) { goto corrupted; } - } else if (buf_page_is_corrupted(true, page, cursor->page_size, - space)) { + } else if (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED + || buf_page_is_corrupted(true, page, + cursor->page_size, + space)) { corrupted: retry_count--; if (retry_count == 0) { diff --git a/mysql-test/suite/innodb/include/crc32.pl b/mysql-test/suite/innodb/include/crc32.pl new file mode 100644 index 00000000000..c2bce09dd36 --- /dev/null +++ b/mysql-test/suite/innodb/include/crc32.pl @@ -0,0 +1,33 @@ +# The following is Public Domain / Creative Commons CC0 from +# http://billauer.co.il/blog/2011/05/perl-crc32-crc-xs-module/ + +sub mycrc32 { + my ($input, $init_value, $polynomial) = @_; + + $init_value = 0 unless (defined $init_value); + $polynomial = 0xedb88320 unless (defined $polynomial); + + my @lookup_table; + + for (my $i=0; $i<256; $i++) { + my $x = $i; + for (my $j=0; $j<8; $j++) { + if ($x & 1) { + $x = ($x >> 1) ^ $polynomial; + } else { + $x = $x >> 1; + } + } + push @lookup_table, $x; + } + + my $crc = $init_value ^ 0xffffffff; + + foreach my $x (unpack ('C*', $input)) { + $crc = (($crc >> 8) & 0xffffff) ^ $lookup_table[ ($crc ^ $x) & 0xff ]; + } + + $crc = $crc ^ 0xffffffff; + + return $crc; +} diff --git a/mysql-test/suite/mariabackup/encrypted_page_corruption.result b/mysql-test/suite/mariabackup/encrypted_page_corruption.result index bbcd3bba816..6e879d58214 100644 --- a/mysql-test/suite/mariabackup/encrypted_page_corruption.result +++ b/mysql-test/suite/mariabackup/encrypted_page_corruption.result @@ -1,4 +1,4 @@ -call mtr.add_suppression("\\[ERROR\\] InnoDB: The page .* in file .* cannot be decrypted."); +call mtr.add_suppression("\\[ERROR\\] InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=3\\] in file '.*test.t1\\.ibd' cannot be decrypted."); CREATE TABLE t1(c VARCHAR(128)) ENGINE INNODB, encrypted=yes; insert into t1 select repeat('a',100); # Corrupt the table diff --git a/mysql-test/suite/mariabackup/encrypted_page_corruption.test b/mysql-test/suite/mariabackup/encrypted_page_corruption.test index f8f7bdb6567..12aabc5cfd9 100644 --- a/mysql-test/suite/mariabackup/encrypted_page_corruption.test +++ b/mysql-test/suite/mariabackup/encrypted_page_corruption.test @@ -1,11 +1,12 @@ --source include/have_file_key_management.inc +--source include/innodb_page_size.inc -call mtr.add_suppression("\\[ERROR\\] InnoDB: The page .* in file .* cannot be decrypted."); +call mtr.add_suppression("\\[ERROR\\] InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=3\\] in file '.*test.t1\\.ibd' cannot be decrypted."); CREATE TABLE t1(c VARCHAR(128)) ENGINE INNODB, encrypted=yes; insert into t1 select repeat('a',100); -let $MYSQLD_DATADIR=`select @@datadir`; -let t1_IBD = $MYSQLD_DATADIR/test/t1.ibd; +let MYSQLD_DATADIR=`select @@datadir`; +let INNODB_PAGE_SIZE=`select @@innodb_page_size`; --source include/shutdown_mysqld.inc @@ -15,17 +16,29 @@ perl; use strict; use warnings; use Fcntl qw(:DEFAULT :seek); +do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl"; -my $ibd_file = $ENV{'t1_IBD'}; +my $page_size = $ENV{INNODB_PAGE_SIZE}; -my $chunk; -my $len; +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"; -sysopen IBD_FILE, $ibd_file, O_RDWR || die "Unable to open $ibd_file"; -sysseek IBD_FILE, 16384 * 3, SEEK_CUR; -$chunk = '\xAA\xAA\xAA\xAA'; -syswrite IBD_FILE, $chunk, 4; +my $head = pack("Nx[18]", 3); # 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 |