Commit ddf2fac7 authored by Jan Lindström's avatar Jan Lindström

MDEV-11759: Encryption code in MariaDB 10.1/10.2 causes

compatibility problems

Pages that are encrypted contain post encryption checksum on
different location that normal checksum fields. Therefore,
we should before decryption check this checksum to avoid
unencrypting corrupted pages. After decryption we can use
traditional checksum check to detect if page is corrupted
or unencryption was done using incorrect key.

Pages that are page compressed do not contain any checksum,
here we need to fist unencrypt, decompress and finally
use tradional checksum check to detect page corruption
or that we used incorrect key in unencryption.

buf0buf.cc: buf_page_is_corrupted() mofified so that
compressed pages are skipped.

buf0buf.h, buf_block_init(), buf_page_init_low():
removed unnecessary page_encrypted, page_compressed,
stored_checksum, valculated_checksum fields from
buf_page_t

buf_page_get_gen(): use new buf_page_check_corrupt() function
to detect corrupted pages.

buf_page_check_corrupt(): If page was not yet decrypted
check if post encryption checksum still matches.
If page is not anymore encrypted, use buf_page_is_corrupted()
traditional checksum method.

If page is detected as corrupted and it is not encrypted
we print corruption message to error log.
If page is still encrypted or it was encrypted and now
corrupted, we will print message that page is
encrypted to error log.

buf_page_io_complete(): use new buf_page_check_corrupt()
function to detect corrupted pages.

buf_page_decrypt_after_read(): Verify post encryption
checksum before tring to decrypt.

fil0crypt.cc: fil_encrypt_buf() verify post encryption
checksum and ind fil_space_decrypt() return true
if we really decrypted the page.

fil_space_verify_crypt_checksum(): rewrite to use
the method used when calculating post encryption
checksum. We also check if post encryption checksum
matches that traditional checksum check does not
match.

fil0fil.ic: Add missed page type encrypted and page
compressed to fil_get_page_type_name()

Note that this change does not yet fix innochecksum tool,
that will be done in separate MDEV.

Fix test failures caused by buf page corruption injection.
parent bc4686f0
...@@ -2,7 +2,7 @@ call mtr.add_suppression("Plugin 'file_key_management' init function returned er ...@@ -2,7 +2,7 @@ call mtr.add_suppression("Plugin 'file_key_management' init function returned er
call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*"); call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*");
call mtr.add_suppression("Plugin 'file_key_management' registration.*failed"); call mtr.add_suppression("Plugin 'file_key_management' registration.*failed");
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id .* is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id .* is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
SET GLOBAL innodb_file_format = `Barracuda`; SET GLOBAL innodb_file_format = `Barracuda`;
SET GLOBAL innodb_file_per_table = ON; SET GLOBAL innodb_file_per_table = ON;
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id .* is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
# Start server with keys2.txt # Start server with keys2.txt
......
...@@ -16,7 +16,7 @@ call mtr.add_suppression("Plugin 'file_key_management' init function returned er ...@@ -16,7 +16,7 @@ call mtr.add_suppression("Plugin 'file_key_management' init function returned er
call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*"); call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*");
call mtr.add_suppression("Plugin 'file_key_management' registration.*failed"); call mtr.add_suppression("Plugin 'file_key_management' registration.*failed");
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id .* is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
# MDEV-8750: Server crashes in page_cur_is_after_last on altering table using a wrong encryption key # MDEV-8750: Server crashes in page_cur_is_after_last on altering table using a wrong encryption key
# #
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
# MDEV-8768: Server crash at file btr0btr.ic line 122 when checking encrypted table using incorrect keys # MDEV-8768: Server crash at file btr0btr.ic line 122 when checking encrypted table using incorrect keys
# #
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
# MDEV-8769: Server crash at file btr0btr.ic line 122 when defragmenting encrypted table using incorrect keys # MDEV-8769: Server crash at file btr0btr.ic line 122 when defragmenting encrypted table using incorrect keys
# #
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# #
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id .* is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
...@@ -18,7 +18,7 @@ SET GLOBAL innodb_file_per_table = ON; ...@@ -18,7 +18,7 @@ SET GLOBAL innodb_file_per_table = ON;
# MDEV-9559: Server without encryption configs crashes if selecting from an implicitly encrypted table # MDEV-9559: Server without encryption configs crashes if selecting from an implicitly encrypted table
# #
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# MDEV-11004: Unable to start (Segfault or os error 2) when encryption key missing # MDEV-11004: Unable to start (Segfault or os error 2) when encryption key missing
# #
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id .* is not found or used encryption algorithm or method does not match."); call mtr.add_suppression("InnoDB: However key management plugin or used key_version .* is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
--echo --echo
......
call mtr.add_suppression("InnoDB: Page for tablespace "); call mtr.add_suppression("InnoDB: Page for tablespace ");
call mtr.add_suppression("InnoDB: Invalid FSP_SPACE_FLAGS=0x"); call mtr.add_suppression("InnoDB: Invalid FSP_SPACE_FLAGS=0x");
call mtr.add_suppression("InnoDB: Corruption: Block in space_id .* in file .* corrupted");
call mtr.add_suppression("InnoDB: Based on page type .*");
FLUSH TABLES; FLUSH TABLES;
SET GLOBAL innodb_file_per_table = 1; SET GLOBAL innodb_file_per_table = 1;
SELECT @@innodb_file_per_table; SELECT @@innodb_file_per_table;
......
call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*"); call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*");
call mtr.add_suppression("InnoDB: Corruption: Block in space_id .* in file .* corrupted.");
call mtr.add_suppression("InnoDB: Based on page type .*");
CALL mtr.add_suppression("InnoDB: Error: Unable to read tablespace .* page no .* into the buffer pool after 100 attempts"); CALL mtr.add_suppression("InnoDB: Error: Unable to read tablespace .* page no .* into the buffer pool after 100 attempts");
CALL mtr.add_suppression("InnoDB: Database page corruption on disk or a failed"); CALL mtr.add_suppression("InnoDB: Database page corruption on disk or a failed");
CALL mtr.add_suppression("InnoDB: Space .* file test/t1 read of page .*"); CALL mtr.add_suppression("InnoDB: Space .* file test/t1 read of page .*");
...@@ -10,6 +12,7 @@ CALL mtr.add_suppression("InnoDB: fix the corruption by dumping, dropping, and r ...@@ -10,6 +12,7 @@ CALL mtr.add_suppression("InnoDB: fix the corruption by dumping, dropping, and r
CALL mtr.add_suppression("InnoDB: the corrupt table. You can use CHECK"); CALL mtr.add_suppression("InnoDB: the corrupt table. You can use CHECK");
CALL mtr.add_suppression("InnoDB: TABLE to scan your table for corruption."); CALL mtr.add_suppression("InnoDB: TABLE to scan your table for corruption.");
CALL mtr.add_suppression("InnoDB: See also .* about forcing recovery."); CALL mtr.add_suppression("InnoDB: See also .* about forcing recovery.");
flush tables;
# Create and populate the table to be corrupted # Create and populate the table to be corrupted
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB; CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB;
INSERT INTO t1 (b) VALUES ('corrupt me'); INSERT INTO t1 (b) VALUES ('corrupt me');
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
call mtr.add_suppression("InnoDB: Page for tablespace "); call mtr.add_suppression("InnoDB: Page for tablespace ");
call mtr.add_suppression("InnoDB: Invalid FSP_SPACE_FLAGS=0x"); call mtr.add_suppression("InnoDB: Invalid FSP_SPACE_FLAGS=0x");
call mtr.add_suppression("InnoDB: Corruption: Block in space_id .* in file .* corrupted");
call mtr.add_suppression("InnoDB: Based on page type .*");
FLUSH TABLES; FLUSH TABLES;
let MYSQLD_DATADIR =`SELECT @@datadir`; let MYSQLD_DATADIR =`SELECT @@datadir`;
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
-- source include/not_encrypted.inc -- source include/not_encrypted.inc
call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*"); call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*");
call mtr.add_suppression("InnoDB: Corruption: Block in space_id .* in file .* corrupted.");
call mtr.add_suppression("InnoDB: Based on page type .*");
# Don't test under valgrind, memory leaks will occur # Don't test under valgrind, memory leaks will occur
source include/not_valgrind.inc; source include/not_valgrind.inc;
...@@ -31,6 +33,7 @@ CALL mtr.add_suppression("InnoDB: fix the corruption by dumping, dropping, and r ...@@ -31,6 +33,7 @@ CALL mtr.add_suppression("InnoDB: fix the corruption by dumping, dropping, and r
CALL mtr.add_suppression("InnoDB: the corrupt table. You can use CHECK"); CALL mtr.add_suppression("InnoDB: the corrupt table. You can use CHECK");
CALL mtr.add_suppression("InnoDB: TABLE to scan your table for corruption."); CALL mtr.add_suppression("InnoDB: TABLE to scan your table for corruption.");
CALL mtr.add_suppression("InnoDB: See also .* about forcing recovery."); CALL mtr.add_suppression("InnoDB: See also .* about forcing recovery.");
flush tables;
--echo # Create and populate the table to be corrupted --echo # Create and populate the table to be corrupted
......
This diff is collapsed.
...@@ -383,10 +383,11 @@ buf_dblwr_init_or_load_pages( ...@@ -383,10 +383,11 @@ buf_dblwr_init_or_load_pages(
doublewrite = read_buf + TRX_SYS_DOUBLEWRITE; doublewrite = read_buf + TRX_SYS_DOUBLEWRITE;
if (mach_read_from_4(read_buf + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION) != 0) { if (mach_read_from_4(read_buf + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION) != 0) {
bool decrypted = false;
byte* tmp = fil_space_decrypt((ulint)TRX_SYS_SPACE, byte* tmp = fil_space_decrypt((ulint)TRX_SYS_SPACE,
read_buf + UNIV_PAGE_SIZE, read_buf + UNIV_PAGE_SIZE,
UNIV_PAGE_SIZE, /* page size */ UNIV_PAGE_SIZE, /* page size */
read_buf); read_buf, &decrypted);
doublewrite = tmp + TRX_SYS_DOUBLEWRITE; doublewrite = tmp + TRX_SYS_DOUBLEWRITE;
} }
...@@ -487,6 +488,7 @@ buf_dblwr_process() ...@@ -487,6 +488,7 @@ buf_dblwr_process()
byte* read_buf; byte* read_buf;
byte* unaligned_read_buf; byte* unaligned_read_buf;
recv_dblwr_t& recv_dblwr = recv_sys->dblwr; recv_dblwr_t& recv_dblwr = recv_sys->dblwr;
fil_space_t* space=NULL;
unaligned_read_buf = static_cast<byte*>(ut_malloc(2 * UNIV_PAGE_SIZE)); unaligned_read_buf = static_cast<byte*>(ut_malloc(2 * UNIV_PAGE_SIZE));
...@@ -514,6 +516,10 @@ buf_dblwr_process() ...@@ -514,6 +516,10 @@ buf_dblwr_process()
continue; continue;
} }
if (!space) {
space = fil_space_found_by_id(space_id);
}
ulint zip_size = fil_space_get_zip_size(space_id); ulint zip_size = fil_space_get_zip_size(space_id);
ut_ad(!buf_page_is_zeroes(page, zip_size)); ut_ad(!buf_page_is_zeroes(page, zip_size));
...@@ -548,9 +554,9 @@ buf_dblwr_process() ...@@ -548,9 +554,9 @@ buf_dblwr_process()
} }
if (fil_space_verify_crypt_checksum( if (fil_space_verify_crypt_checksum(
read_buf, zip_size) read_buf, zip_size, NULL, page_no)
|| !buf_page_is_corrupted( || !buf_page_is_corrupted(
true, read_buf, zip_size)) { true, read_buf, zip_size, space)) {
/* The page is good; there is no need /* The page is good; there is no need
to consult the doublewrite buffer. */ to consult the doublewrite buffer. */
continue; continue;
...@@ -573,8 +579,8 @@ buf_dblwr_process() ...@@ -573,8 +579,8 @@ buf_dblwr_process()
NULL, page, UNIV_PAGE_SIZE, NULL, true); NULL, page, UNIV_PAGE_SIZE, NULL, true);
} }
if (!fil_space_verify_crypt_checksum(page, zip_size) if (!fil_space_verify_crypt_checksum(page, zip_size, NULL, page_no)
&& buf_page_is_corrupted(true, page, zip_size)) { && buf_page_is_corrupted(true, page, zip_size, space)) {
if (!is_all_zero) { if (!is_all_zero) {
ib_logf(IB_LOG_LEVEL_WARN, ib_logf(IB_LOG_LEVEL_WARN,
"A doublewrite copy of page " "A doublewrite copy of page "
......
...@@ -625,6 +625,8 @@ fil_encrypt_buf( ...@@ -625,6 +625,8 @@ fil_encrypt_buf(
// store the post-encryption checksum after the key-version // store the post-encryption checksum after the key-version
mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum); mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum);
ut_ad(fil_space_verify_crypt_checksum(dst_frame, zip_size, NULL, offset));
srv_stats.pages_encrypted.inc(); srv_stats.pages_encrypted.inc();
return dst_frame; return dst_frame;
...@@ -676,6 +678,7 @@ fil_space_encrypt( ...@@ -676,6 +678,7 @@ fil_space_encrypt(
byte* comp_mem = NULL; byte* comp_mem = NULL;
byte* uncomp_mem = NULL; byte* uncomp_mem = NULL;
ulint size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; ulint size = (zip_size) ? zip_size : UNIV_PAGE_SIZE;
fil_space_t* tspace = fil_space_found_by_id(space);
if (page_compressed_encrypted) { if (page_compressed_encrypted) {
comp_mem = (byte *)malloc(UNIV_PAGE_SIZE); comp_mem = (byte *)malloc(UNIV_PAGE_SIZE);
...@@ -685,7 +688,7 @@ fil_space_encrypt( ...@@ -685,7 +688,7 @@ fil_space_encrypt(
src = uncomp_mem; src = uncomp_mem;
} }
bool corrupted1 = buf_page_is_corrupted(true, src, zip_size); bool corrupted1 = buf_page_is_corrupted(true, src, zip_size, tspace);
bool ok = fil_space_decrypt(crypt_data, tmp_mem, size, tmp, &err); bool ok = fil_space_decrypt(crypt_data, tmp_mem, size, tmp, &err);
/* Need to decompress the page if it was also compressed */ /* Need to decompress the page if it was also compressed */
...@@ -694,7 +697,7 @@ fil_space_encrypt( ...@@ -694,7 +697,7 @@ fil_space_encrypt(
fil_decompress_page(tmp_mem, comp_mem, UNIV_PAGE_SIZE, NULL); fil_decompress_page(tmp_mem, comp_mem, UNIV_PAGE_SIZE, NULL);
} }
bool corrupted = buf_page_is_corrupted(true, tmp_mem, zip_size); bool corrupted = buf_page_is_corrupted(true, tmp_mem, zip_size, tspace);
bool different = memcmp(src, tmp_mem, size); bool different = memcmp(src, tmp_mem, size);
if (!ok || corrupted || corrupted1 || err != DB_SUCCESS || different) { if (!ok || corrupted || corrupted1 || err != DB_SUCCESS || different) {
...@@ -858,19 +861,25 @@ fil_space_decrypt( ...@@ -858,19 +861,25 @@ fil_space_decrypt(
/****************************************************************** /******************************************************************
Decrypt a page Decrypt a page
@return encrypted page, or original not encrypted page if encryption is @param[in] space Tablespace id
not needed. */ @param[in] tmp_frame Temporary buffer used for decrypting
@param[in] page_size Page size
@param[in,out] src_frame Page to decrypt
@param[out] decrypted true if page was decrypted
@return decrypted page, or original not encrypted page if decryption is
not needed.*/
UNIV_INTERN UNIV_INTERN
byte* byte*
fil_space_decrypt( fil_space_decrypt(
/*==============*/ ulint space,
ulint space, /*!< in: Fil space id */ byte* tmp_frame,
byte* tmp_frame, /*!< in: temporary buffer */ ulint page_size,
ulint page_size, /*!< in: page size */ byte* src_frame,
byte* src_frame) /*!< in/out: page buffer */ bool* decrypted)
{ {
dberr_t err = DB_SUCCESS; dberr_t err = DB_SUCCESS;
byte* res = NULL; byte* res = NULL;
*decrypted = false;
bool encrypted = fil_space_decrypt( bool encrypted = fil_space_decrypt(
fil_space_get_crypt_data(space), fil_space_get_crypt_data(space),
...@@ -881,6 +890,7 @@ fil_space_decrypt( ...@@ -881,6 +890,7 @@ fil_space_decrypt(
if (err == DB_SUCCESS) { if (err == DB_SUCCESS) {
if (encrypted) { if (encrypted) {
*decrypted = true;
/* Copy the decrypted page back to page buffer, not /* Copy the decrypted page back to page buffer, not
really any other options. */ really any other options. */
memcpy(src_frame, tmp_frame, page_size); memcpy(src_frame, tmp_frame, page_size);
...@@ -934,83 +944,114 @@ fil_crypt_calculate_checksum( ...@@ -934,83 +944,114 @@ fil_crypt_calculate_checksum(
} }
/********************************************************************* /*********************************************************************
Verify checksum for a page (iff it's encrypted) Verify that post encryption checksum match calculated checksum.
NOTE: currently this function can only be run in single threaded mode This function should be called only if tablespace contains crypt_data
as it modifies srv_checksum_algorithm (temporarily) metadata (this is strong indication that tablespace is encrypted).
Function also verifies that traditional checksum does not match
calculated checksum as if it does page could be valid unencrypted,
encrypted, or corrupted.
@param[in] page Page to verify
@param[in] zip_size zip size
@param[in] space Tablespace
@param[in] pageno Page no
@return true if page is encrypted AND OK, false otherwise */ @return true if page is encrypted AND OK, false otherwise */
UNIV_INTERN UNIV_INTERN
bool bool
fil_space_verify_crypt_checksum( fil_space_verify_crypt_checksum(
/*============================*/ byte* page,
const byte* src_frame, /*!< in: page the verify */ ulint zip_size,
ulint zip_size) /*!< in: compressed size if const fil_space_t* space,
row_format compressed */ ulint pageno)
{ {
// key version uint key_version = mach_read_from_4(page+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
uint key_version = mach_read_from_4(
src_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
/* If page is not encrypted, return false */
if (key_version == 0) { if (key_version == 0) {
return false; // unencrypted page return false;
} }
/* "trick" the normal checksum routines by storing the post-encryption /* If no checksum is used, can't continue checking. */
* checksum into the normal checksum field allowing for reuse of if (srv_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_NONE) {
* the normal routines */ return(true);
}
// post encryption checksum /* Read stored post encryption checksum. */
ib_uint32_t stored_post_encryption = mach_read_from_4( ib_uint32_t checksum = mach_read_from_4(
src_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4); page + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4);
// save pre encryption checksum for restore in end of this function /* Declare empty pages non-corrupted */
ib_uint32_t stored_pre_encryption = mach_read_from_4( if (checksum == 0
src_frame + FIL_PAGE_SPACE_OR_CHKSUM); && *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_LSN) == 0) {
return (true);
}
ib_uint32_t checksum_field2 = mach_read_from_4( /* Compressed and encrypted pages do not have checksum. Assume not
src_frame + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM); corrupted. */
if (mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
return (true);
}
/** prepare frame for usage of normal checksum routines */ /* Compressed pages use different checksum method. We first store
mach_write_to_4(const_cast<byte*>(src_frame) + FIL_PAGE_SPACE_OR_CHKSUM, the post encryption checksum on checksum location and after function
stored_post_encryption); restore the original. */
if (zip_size) {
ib_uint32_t old = static_cast<ib_uint32_t>(mach_read_from_4(
page + FIL_PAGE_SPACE_OR_CHKSUM));
/* NOTE: this function is (currently) only run when restoring mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, checksum);
* dblwr-buffer, server is single threaded so it's safe to modify
* srv_checksum_algorithm */
srv_checksum_algorithm_t save_checksum_algorithm =
(srv_checksum_algorithm_t)srv_checksum_algorithm;
if (zip_size == 0 && bool valid = page_zip_verify_checksum(page, zip_size);
(save_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB ||
save_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_INNODB)) { mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, old);
/* handle ALGORITHM_INNODB specially,
* "downgrade" to ALGORITHM_INNODB and store BUF_NO_CHECKSUM_MAGIC return (valid);
* checksum_field2 is sort of pointless anyway...
*/
srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_INNODB;
mach_write_to_4(const_cast<byte*>(src_frame) +
UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM,
BUF_NO_CHECKSUM_MAGIC);
} }
/* verify checksums */ /* If stored checksum matches one of the calculated checksums
ibool corrupted = buf_page_is_corrupted(false, src_frame, zip_size); page is not corrupted. */
/** restore frame & algorithm */ ib_uint32_t cchecksum1 = buf_calc_page_crc32(page);
srv_checksum_algorithm = save_checksum_algorithm; ib_uint32_t cchecksum2 = (ib_uint32_t) buf_calc_page_new_checksum(
page);
bool encrypted = (checksum == cchecksum1 || checksum == cchecksum2
|| checksum == BUF_NO_CHECKSUM_MAGIC);
mach_write_to_4(const_cast<byte*>(src_frame) + /* Old InnoDB versions did not initialize
FIL_PAGE_SPACE_OR_CHKSUM, FIL_PAGE_FILE_FLUSH_LSN field so there could be garbage
stored_pre_encryption); and above checksum check could produce false positive.
Thus we also check does the traditional stored
checksum fields match the calculated one. Both of these
could naturally produce false positive but then
we just decrypt the page and after that corrupted
pages very probable stay corrupted and valid
pages very probable stay valid.
*/
ulint checksum1 = mach_read_from_4(
page + FIL_PAGE_SPACE_OR_CHKSUM);
mach_write_to_4(const_cast<byte*>(src_frame) + ulint checksum2 = mach_read_from_4(
UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM, page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM);
checksum_field2);
if (!corrupted) {
return true; // page was encrypted and checksum matched bool valid = (buf_page_is_checksum_valid_crc32(page,checksum1,checksum2)
} else { || buf_page_is_checksum_valid_none(page,checksum1,checksum2)
return false; // page was encrypted but checksum didn't match || buf_page_is_checksum_valid_innodb(page,checksum1, checksum2));
if (encrypted && valid) {
/* If page is encrypted and traditional checksums match,
page could be still encrypted, or not encrypted and valid or
corrupted. */
ib_logf(IB_LOG_LEVEL_ERROR,
" Page %lu in space %s (%lu) maybe corrupted."
" Post encryption checksum %u stored [%lu:%lu] key_version %u",
pageno,
space ? space->name : "N/A",
mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID),
checksum, checksum1, checksum2, key_version);
encrypted = false;
} }
return(encrypted);
} }
/***********************************************************************/ /***********************************************************************/
......
...@@ -2257,7 +2257,7 @@ fil_check_first_page(const page_t* page, ulint space_id, ulint flags) ...@@ -2257,7 +2257,7 @@ fil_check_first_page(const page_t* page, ulint space_id, ulint flags)
} }
if (buf_page_is_corrupted( if (buf_page_is_corrupted(
false, page, fsp_flags_get_zip_size(flags))) { false, page, fsp_flags_get_zip_size(flags), NULL)) {
return("checksum mismatch"); return("checksum mismatch");
} }
...@@ -4515,13 +4515,13 @@ fil_user_tablespace_find_space_id( ...@@ -4515,13 +4515,13 @@ fil_user_tablespace_find_space_id(
to UNIV_PAGE_SIZE. */ to UNIV_PAGE_SIZE. */
if (page_size == UNIV_PAGE_SIZE) { if (page_size == UNIV_PAGE_SIZE) {
uncompressed_ok = !buf_page_is_corrupted( uncompressed_ok = !buf_page_is_corrupted(
false, page, 0); false, page, 0, NULL);
} }
bool compressed_ok = false; bool compressed_ok = false;
if (page_size <= UNIV_PAGE_SIZE_DEF) { if (page_size <= UNIV_PAGE_SIZE_DEF) {
compressed_ok = !buf_page_is_corrupted( compressed_ok = !buf_page_is_corrupted(
false, page, page_size); false, page, page_size, NULL);
} }
if (uncompressed_ok || compressed_ok) { if (uncompressed_ok || compressed_ok) {
......
...@@ -366,7 +366,7 @@ fil_compress_page( ...@@ -366,7 +366,7 @@ fil_compress_page(
fil_decompress_page(uncomp_page, comp_page, len, NULL); fil_decompress_page(uncomp_page, comp_page, len, NULL);
if(buf_page_is_corrupted(false, uncomp_page, 0)) { if(buf_page_is_corrupted(false, uncomp_page, 0, space)) {
buf_page_print(uncomp_page, 0, BUF_PAGE_PRINT_NO_CRASH); buf_page_print(uncomp_page, 0, BUF_PAGE_PRINT_NO_CRASH);
ut_error; ut_error;
} }
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2016, MariaDB Corporation. Copyright (c) 2013, 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -642,19 +642,69 @@ buf_block_unfix( ...@@ -642,19 +642,69 @@ buf_block_unfix(
#else /* !UNIV_HOTBACKUP */ #else /* !UNIV_HOTBACKUP */
# define buf_block_modify_clock_inc(block) ((void) 0) # define buf_block_modify_clock_inc(block) ((void) 0)
#endif /* !UNIV_HOTBACKUP */ #endif /* !UNIV_HOTBACKUP */
/** Checks if the page is in crc32 checksum format.
@param[in] read_buf database page
@param[in] checksum_field1 new checksum field
@param[in] checksum_field2 old checksum field
@return true if the page is in crc32 checksum format */
bool
buf_page_is_checksum_valid_crc32(
const byte* read_buf,
ulint checksum_field1,
ulint checksum_field2)
MY_ATTRIBUTE((warn_unused_result));
/** Checks if the page is in innodb checksum format.
@param[in] read_buf database page
@param[in] checksum_field1 new checksum field
@param[in] checksum_field2 old checksum field
@return true if the page is in innodb checksum format */
bool
buf_page_is_checksum_valid_innodb(
const byte* read_buf,
ulint checksum_field1,
ulint checksum_field2)
MY_ATTRIBUTE((warn_unused_result));
/** Checks if the page is in none checksum format.
@param[in] read_buf database page
@param[in] checksum_field1 new checksum field
@param[in] checksum_field2 old checksum field
@return true if the page is in none checksum format */
bool
buf_page_is_checksum_valid_none(
const byte* read_buf,
ulint checksum_field1,
ulint checksum_field2)
MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//** /********************************************************************//**
Checks if a page is corrupt. Checks if a page is corrupt.
@return TRUE if corrupted */ @param[in] check_lsn true if LSN should be checked
UNIV_INTERN @param[in] read_buf Page to be checked
ibool @param[in] zip_size compressed size or 0
@param[in] space Pointer to tablespace
@return true if corrupted, false if not */
bool
buf_page_is_corrupted( buf_page_is_corrupted(
/*==================*/ bool check_lsn,
bool check_lsn, /*!< in: true if we need to check the const byte* read_buf,
and complain about the LSN */ ulint zip_size,
const byte* read_buf, /*!< in: a database page */ const fil_space_t* space)
ulint zip_size) /*!< in: size of compressed page; MY_ATTRIBUTE((warn_unused_result));
0 for uncompressed pages */
MY_ATTRIBUTE((nonnull, warn_unused_result)); /********************************************************************//**
Check if page is maybe compressed, encrypted or both when we encounter
corrupted page. Note that we can't be 100% sure if page is corrupted
or decrypt/decompress just failed.
@param[in] bpage Page
@return true if page corrupted, false if not */
bool
buf_page_check_corrupt(
buf_page_t* bpage) /*!< in/out: buffer page read from disk */
MY_ATTRIBUTE(( warn_unused_result));
/********************************************************************//** /********************************************************************//**
Checks if a page is all zeroes. Checks if a page is all zeroes.
@return TRUE if the page is all zeroes */ @return TRUE if the page is all zeroes */
...@@ -1490,7 +1540,7 @@ The hook that is called just after a page is read from disk. ...@@ -1490,7 +1540,7 @@ The hook that is called just after a page is read from disk.
The function decrypt disk content into buf_page_t and releases the The function decrypt disk content into buf_page_t and releases the
temporary buffer that was allocated in buf_page_decrypt_before_read */ temporary buffer that was allocated in buf_page_decrypt_before_read */
UNIV_INTERN UNIV_INTERN
ibool bool
buf_page_decrypt_after_read( buf_page_decrypt_after_read(
/*========================*/ /*========================*/
buf_page_t* page); /*!< in/out: buffer page read from disk */ buf_page_t* page); /*!< in/out: buffer page read from disk */
...@@ -1593,14 +1643,9 @@ struct buf_page_t{ ...@@ -1593,14 +1643,9 @@ struct buf_page_t{
operation needed. */ operation needed. */
unsigned key_version; /*!< key version for this block */ unsigned key_version; /*!< key version for this block */
bool page_encrypted; /*!< page is page encrypted */ bool encrypted; /*!< page is still encrypted */
bool page_compressed;/*!< page is page compressed */ bool corrupted; /*!< page is corrupted */
ulint stored_checksum;/*!< stored page checksum if page
encrypted */
bool encrypted; /*!< page is still encrypted */
ulint calculated_checksum;
/*!< calculated checksum if page
encrypted */
ulint real_size; /*!< Real size of the page ulint real_size; /*!< Real size of the page
Normal pages == UNIV_PAGE_SIZE Normal pages == UNIV_PAGE_SIZE
page compressed pages, payload page compressed pages, payload
...@@ -1714,6 +1759,7 @@ struct buf_page_t{ ...@@ -1714,6 +1759,7 @@ struct buf_page_t{
0 if the block was never accessed 0 if the block was never accessed
in the buffer pool. Protected by in the buffer pool. Protected by
block mutex */ block mutex */
ibool is_corrupt;
# if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG # if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
ibool file_page_was_freed; ibool file_page_was_freed;
/*!< this is set to TRUE when /*!< this is set to TRUE when
......
...@@ -328,7 +328,7 @@ fil_space_check_encryption_read( ...@@ -328,7 +328,7 @@ fil_space_check_encryption_read(
/****************************************************************** /******************************************************************
Decrypt a page Decrypt a page
@return true if page is decrypted, false if not. */ @return true if page decrypted, false if not.*/
UNIV_INTERN UNIV_INTERN
bool bool
fil_space_decrypt( fil_space_decrypt(
...@@ -336,9 +336,10 @@ fil_space_decrypt( ...@@ -336,9 +336,10 @@ fil_space_decrypt(
fil_space_crypt_t* crypt_data, /*!< in: crypt data */ fil_space_crypt_t* crypt_data, /*!< in: crypt data */
byte* tmp_frame, /*!< in: temporary buffer */ byte* tmp_frame, /*!< in: temporary buffer */
ulint page_size, /*!< in: page size */ ulint page_size, /*!< in: page size */
byte* src_frame, /*!< in:out: page buffer */ byte* src_frame, /*!< in: out: page buffer */
dberr_t* err); /*!< in: out: DB_SUCCESS or dberr_t* err) /*!< in: out: DB_SUCCESS or
error code */ error code */
MY_ATTRIBUTE((warn_unused_result));
/********************************************************************* /*********************************************************************
Encrypt buffer page Encrypt buffer page
...@@ -355,31 +356,46 @@ fil_space_encrypt( ...@@ -355,31 +356,46 @@ fil_space_encrypt(
ulint size, /*!< in: size of data to encrypt */ ulint size, /*!< in: size of data to encrypt */
byte* dst_frame); /*!< in: where to encrypt to */ byte* dst_frame); /*!< in: where to encrypt to */
/********************************************************************* /******************************************************************
Decrypt buffer page Decrypt a page
@return decrypted page, or original not encrypted page if decrypt is @param[in] space Tablespace id
@param[in] tmp_frame Temporary buffer used for decrypting
@param[in] page_size Page size
@param[in,out] src_frame Page to decrypt
@param[out] decrypted true if page was decrypted
@return decrypted page, or original not encrypted page if decryption is
not needed.*/ not needed.*/
UNIV_INTERN UNIV_INTERN
byte* byte*
fil_space_decrypt( fil_space_decrypt(
/*==============*/ /*==============*/
ulint space, /*!< in: tablespace id */ ulint space,
byte* src_frame, /*!< in: page frame */ byte* src_frame,
ulint page_size, /*!< in: size of data to encrypt */ ulint page_size,
byte* dst_frame) /*!< in: where to decrypt to */ byte* dst_frame,
bool* decrypted)
__attribute__((warn_unused_result)); __attribute__((warn_unused_result));
/********************************************************************* /*********************************************************************
fil_space_verify_crypt_checksum Verify that post encryption checksum match calculated checksum.
NOTE: currently this function can only be run in single threaded mode This function should be called only if tablespace contains crypt_data
as it modifies srv_checksum_algorithm (temporarily) metadata (this is strong indication that tablespace is encrypted).
Function also verifies that traditional checksum does not match
calculated checksum as if it does page could be valid unencrypted,
encrypted, or corrupted.
@param[in] page Page to verify
@param[in] zip_size zip size
@param[in] space Tablespace
@param[in] pageno Page no
@return true if page is encrypted AND OK, false otherwise */ @return true if page is encrypted AND OK, false otherwise */
UNIV_INTERN UNIV_INTERN
bool bool
fil_space_verify_crypt_checksum( fil_space_verify_crypt_checksum(
/*============================*/ byte* page,
const byte* src_frame,/*!< in: page frame */ ulint zip_size,
ulint zip_size); /*!< in: size of data to encrypt */ const fil_space_t* space,
ulint pageno)
__attribute__((warn_unused_result));
/********************************************************************* /*********************************************************************
Init threads for key rotation */ Init threads for key rotation */
......
...@@ -59,6 +59,8 @@ fil_get_page_type_name( ...@@ -59,6 +59,8 @@ fil_get_page_type_name(
switch(page_type) { switch(page_type) {
case FIL_PAGE_PAGE_COMPRESSED: case FIL_PAGE_PAGE_COMPRESSED:
return (const char*)"PAGE_COMPRESSED"; return (const char*)"PAGE_COMPRESSED";
case FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED:
return (const char*)"PAGE_COMPRESSED_ENCRYPTED";
case FIL_PAGE_INDEX: case FIL_PAGE_INDEX:
return (const char*)"INDEX"; return (const char*)"INDEX";
case FIL_PAGE_UNDO_LOG: case FIL_PAGE_UNDO_LOG:
...@@ -87,9 +89,13 @@ fil_get_page_type_name( ...@@ -87,9 +89,13 @@ fil_get_page_type_name(
return (const char*)"ZBLOB2"; return (const char*)"ZBLOB2";
case FIL_PAGE_TYPE_COMPRESSED: case FIL_PAGE_TYPE_COMPRESSED:
return (const char*)"ORACLE PAGE COMPRESSED"; return (const char*)"ORACLE PAGE COMPRESSED";
default:
return (const char*)"PAGE TYPE CORRUPTED"; /* No default to make compiler generate warning if
new page type is added but not handled here. */
} }
return (const char*)"PAGE TYPE CORRUPTED";
} }
/****************************************************************//** /****************************************************************//**
......
...@@ -2033,12 +2033,15 @@ PageConverter::validate( ...@@ -2033,12 +2033,15 @@ PageConverter::validate(
buf_block_t* block) UNIV_NOTHROW buf_block_t* block) UNIV_NOTHROW
{ {
buf_frame_t* page = get_frame(block); buf_frame_t* page = get_frame(block);
ulint space_id = mach_read_from_4(
page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
fil_space_t* space = fil_space_found_by_id(space_id);
/* Check that the page number corresponds to the offset in /* Check that the page number corresponds to the offset in
the file. Flag as corrupt if it doesn't. Disable the check the file. Flag as corrupt if it doesn't. Disable the check
for LSN in buf_page_is_corrupted() */ for LSN in buf_page_is_corrupted() */
if (buf_page_is_corrupted(false, page, get_zip_size()) if (buf_page_is_corrupted(false, page, get_zip_size(), space)
|| (page_get_page_no(page) != offset / m_page_size || (page_get_page_no(page) != offset / m_page_size
&& page_get_page_no(page) != 0)) { && page_get_page_no(page) != 0)) {
......
This diff is collapsed.
...@@ -383,10 +383,11 @@ buf_dblwr_init_or_load_pages( ...@@ -383,10 +383,11 @@ buf_dblwr_init_or_load_pages(
doublewrite = read_buf + TRX_SYS_DOUBLEWRITE; doublewrite = read_buf + TRX_SYS_DOUBLEWRITE;
if (mach_read_from_4(read_buf + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION) != 0) { if (mach_read_from_4(read_buf + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION) != 0) {
bool decrypted = false;
byte* tmp = fil_space_decrypt((ulint)TRX_SYS_SPACE, byte* tmp = fil_space_decrypt((ulint)TRX_SYS_SPACE,
read_buf + UNIV_PAGE_SIZE, read_buf + UNIV_PAGE_SIZE,
UNIV_PAGE_SIZE, /* page size */ UNIV_PAGE_SIZE, /* page size */
read_buf); read_buf, &decrypted);
doublewrite = tmp + TRX_SYS_DOUBLEWRITE; doublewrite = tmp + TRX_SYS_DOUBLEWRITE;
} }
...@@ -487,6 +488,7 @@ buf_dblwr_process() ...@@ -487,6 +488,7 @@ buf_dblwr_process()
byte* read_buf; byte* read_buf;
byte* unaligned_read_buf; byte* unaligned_read_buf;
recv_dblwr_t& recv_dblwr = recv_sys->dblwr; recv_dblwr_t& recv_dblwr = recv_sys->dblwr;
fil_space_t* space=NULL;
unaligned_read_buf = static_cast<byte*>(ut_malloc(2 * UNIV_PAGE_SIZE)); unaligned_read_buf = static_cast<byte*>(ut_malloc(2 * UNIV_PAGE_SIZE));
...@@ -514,6 +516,10 @@ buf_dblwr_process() ...@@ -514,6 +516,10 @@ buf_dblwr_process()
continue; continue;
} }
if (!space) {
space = fil_space_found_by_id(space_id);
}
ulint zip_size = fil_space_get_zip_size(space_id); ulint zip_size = fil_space_get_zip_size(space_id);
ut_ad(!buf_page_is_zeroes(page, zip_size)); ut_ad(!buf_page_is_zeroes(page, zip_size));
...@@ -548,9 +554,9 @@ buf_dblwr_process() ...@@ -548,9 +554,9 @@ buf_dblwr_process()
} }
if (fil_space_verify_crypt_checksum( if (fil_space_verify_crypt_checksum(
read_buf, zip_size) read_buf, zip_size, NULL, page_no)
|| !buf_page_is_corrupted( || !buf_page_is_corrupted(
true, read_buf, zip_size)) { true, read_buf, zip_size, space)) {
/* The page is good; there is no need /* The page is good; there is no need
to consult the doublewrite buffer. */ to consult the doublewrite buffer. */
continue; continue;
...@@ -573,8 +579,8 @@ buf_dblwr_process() ...@@ -573,8 +579,8 @@ buf_dblwr_process()
NULL, page, UNIV_PAGE_SIZE, NULL, true); NULL, page, UNIV_PAGE_SIZE, NULL, true);
} }
if (!fil_space_verify_crypt_checksum(page, zip_size) if (!fil_space_verify_crypt_checksum(page, zip_size, NULL, page_no)
&& buf_page_is_corrupted(true, page, zip_size)) { && buf_page_is_corrupted(true, page, zip_size, space)) {
if (!is_all_zero) { if (!is_all_zero) {
ib_logf(IB_LOG_LEVEL_WARN, ib_logf(IB_LOG_LEVEL_WARN,
"A doublewrite copy of page " "A doublewrite copy of page "
......
...@@ -625,6 +625,8 @@ fil_encrypt_buf( ...@@ -625,6 +625,8 @@ fil_encrypt_buf(
// store the post-encryption checksum after the key-version // store the post-encryption checksum after the key-version
mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum); mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum);
ut_ad(fil_space_verify_crypt_checksum(dst_frame, zip_size, NULL, offset));
srv_stats.pages_encrypted.inc(); srv_stats.pages_encrypted.inc();
return dst_frame; return dst_frame;
...@@ -676,6 +678,7 @@ fil_space_encrypt( ...@@ -676,6 +678,7 @@ fil_space_encrypt(
byte* comp_mem = NULL; byte* comp_mem = NULL;
byte* uncomp_mem = NULL; byte* uncomp_mem = NULL;
ulint size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; ulint size = (zip_size) ? zip_size : UNIV_PAGE_SIZE;
fil_space_t* tspace = fil_space_found_by_id(space);
if (page_compressed_encrypted) { if (page_compressed_encrypted) {
comp_mem = (byte *)malloc(UNIV_PAGE_SIZE); comp_mem = (byte *)malloc(UNIV_PAGE_SIZE);
...@@ -685,7 +688,7 @@ fil_space_encrypt( ...@@ -685,7 +688,7 @@ fil_space_encrypt(
src = uncomp_mem; src = uncomp_mem;
} }
bool corrupted1 = buf_page_is_corrupted(true, src, zip_size); bool corrupted1 = buf_page_is_corrupted(true, src, zip_size, tspace);
bool ok = fil_space_decrypt(crypt_data, tmp_mem, size, tmp, &err); bool ok = fil_space_decrypt(crypt_data, tmp_mem, size, tmp, &err);
/* Need to decompress the page if it was also compressed */ /* Need to decompress the page if it was also compressed */
...@@ -694,7 +697,7 @@ fil_space_encrypt( ...@@ -694,7 +697,7 @@ fil_space_encrypt(
fil_decompress_page(tmp_mem, comp_mem, UNIV_PAGE_SIZE, NULL); fil_decompress_page(tmp_mem, comp_mem, UNIV_PAGE_SIZE, NULL);
} }
bool corrupted = buf_page_is_corrupted(true, tmp_mem, zip_size); bool corrupted = buf_page_is_corrupted(true, tmp_mem, zip_size, tspace);
bool different = memcmp(src, tmp_mem, size); bool different = memcmp(src, tmp_mem, size);
if (!ok || corrupted || corrupted1 || err != DB_SUCCESS || different) { if (!ok || corrupted || corrupted1 || err != DB_SUCCESS || different) {
...@@ -858,19 +861,25 @@ fil_space_decrypt( ...@@ -858,19 +861,25 @@ fil_space_decrypt(
/****************************************************************** /******************************************************************
Decrypt a page Decrypt a page
@return encrypted page, or original not encrypted page if encryption is @param[in] space Tablespace id
not needed. */ @param[in] tmp_frame Temporary buffer used for decrypting
@param[in] page_size Page size
@param[in,out] src_frame Page to decrypt
@param[out] decrypted true if page was decrypted
@return decrypted page, or original not encrypted page if decryption is
not needed.*/
UNIV_INTERN UNIV_INTERN
byte* byte*
fil_space_decrypt( fil_space_decrypt(
/*==============*/ ulint space,
ulint space, /*!< in: Fil space id */ byte* tmp_frame,
byte* tmp_frame, /*!< in: temporary buffer */ ulint page_size,
ulint page_size, /*!< in: page size */ byte* src_frame,
byte* src_frame) /*!< in/out: page buffer */ bool* decrypted)
{ {
dberr_t err = DB_SUCCESS; dberr_t err = DB_SUCCESS;
byte* res = NULL; byte* res = NULL;
*decrypted = false;
bool encrypted = fil_space_decrypt( bool encrypted = fil_space_decrypt(
fil_space_get_crypt_data(space), fil_space_get_crypt_data(space),
...@@ -881,6 +890,7 @@ fil_space_decrypt( ...@@ -881,6 +890,7 @@ fil_space_decrypt(
if (err == DB_SUCCESS) { if (err == DB_SUCCESS) {
if (encrypted) { if (encrypted) {
*decrypted = true;
/* Copy the decrypted page back to page buffer, not /* Copy the decrypted page back to page buffer, not
really any other options. */ really any other options. */
memcpy(src_frame, tmp_frame, page_size); memcpy(src_frame, tmp_frame, page_size);
...@@ -934,83 +944,114 @@ fil_crypt_calculate_checksum( ...@@ -934,83 +944,114 @@ fil_crypt_calculate_checksum(
} }
/********************************************************************* /*********************************************************************
Verify checksum for a page (iff it's encrypted) Verify that post encryption checksum match calculated checksum.
NOTE: currently this function can only be run in single threaded mode This function should be called only if tablespace contains crypt_data
as it modifies srv_checksum_algorithm (temporarily) metadata (this is strong indication that tablespace is encrypted).
Function also verifies that traditional checksum does not match
calculated checksum as if it does page could be valid unencrypted,
encrypted, or corrupted.
@param[in] page Page to verify
@param[in] zip_size zip size
@param[in] space Tablespace
@param[in] pageno Page no
@return true if page is encrypted AND OK, false otherwise */ @return true if page is encrypted AND OK, false otherwise */
UNIV_INTERN UNIV_INTERN
bool bool
fil_space_verify_crypt_checksum( fil_space_verify_crypt_checksum(
/*============================*/ byte* page,
const byte* src_frame, /*!< in: page the verify */ ulint zip_size,
ulint zip_size) /*!< in: compressed size if const fil_space_t* space,
row_format compressed */ ulint pageno)
{ {
// key version uint key_version = mach_read_from_4(page+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
uint key_version = mach_read_from_4(
src_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
/* If page is not encrypted, return false */
if (key_version == 0) { if (key_version == 0) {
return false; // unencrypted page return false;
} }
/* "trick" the normal checksum routines by storing the post-encryption /* If no checksum is used, can't continue checking. */
* checksum into the normal checksum field allowing for reuse of if (srv_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_NONE) {
* the normal routines */ return(true);
}
// post encryption checksum /* Read stored post encryption checksum. */
ib_uint32_t stored_post_encryption = mach_read_from_4( ib_uint32_t checksum = mach_read_from_4(
src_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4); page + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4);
// save pre encryption checksum for restore in end of this function /* Declare empty pages non-corrupted */
ib_uint32_t stored_pre_encryption = mach_read_from_4( if (checksum == 0
src_frame + FIL_PAGE_SPACE_OR_CHKSUM); && *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_LSN) == 0) {
return (true);
}
ib_uint32_t checksum_field2 = mach_read_from_4( /* Compressed and encrypted pages do not have checksum. Assume not
src_frame + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM); corrupted. */
if (mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
return (true);
}
/** prepare frame for usage of normal checksum routines */ /* Compressed pages use different checksum method. We first store
mach_write_to_4(const_cast<byte*>(src_frame) + FIL_PAGE_SPACE_OR_CHKSUM, the post encryption checksum on checksum location and after function
stored_post_encryption); restore the original. */
if (zip_size) {
ib_uint32_t old = static_cast<ib_uint32_t>(mach_read_from_4(
page + FIL_PAGE_SPACE_OR_CHKSUM));
/* NOTE: this function is (currently) only run when restoring mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, checksum);
* dblwr-buffer, server is single threaded so it's safe to modify
* srv_checksum_algorithm */
srv_checksum_algorithm_t save_checksum_algorithm =
(srv_checksum_algorithm_t)srv_checksum_algorithm;
if (zip_size == 0 && bool valid = page_zip_verify_checksum(page, zip_size);
(save_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB ||
save_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_INNODB)) { mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, old);
/* handle ALGORITHM_INNODB specially,
* "downgrade" to ALGORITHM_INNODB and store BUF_NO_CHECKSUM_MAGIC return (valid);
* checksum_field2 is sort of pointless anyway...
*/
srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_INNODB;
mach_write_to_4(const_cast<byte*>(src_frame) +
UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM,
BUF_NO_CHECKSUM_MAGIC);
} }
/* verify checksums */ /* If stored checksum matches one of the calculated checksums
ibool corrupted = buf_page_is_corrupted(false, src_frame, zip_size); page is not corrupted. */
/** restore frame & algorithm */ ib_uint32_t cchecksum1 = buf_calc_page_crc32(page);
srv_checksum_algorithm = save_checksum_algorithm; ib_uint32_t cchecksum2 = (ib_uint32_t) buf_calc_page_new_checksum(
page);
bool encrypted = (checksum == cchecksum1 || checksum == cchecksum2
|| checksum == BUF_NO_CHECKSUM_MAGIC);
mach_write_to_4(const_cast<byte*>(src_frame) + /* Old InnoDB versions did not initialize
FIL_PAGE_SPACE_OR_CHKSUM, FIL_PAGE_FILE_FLUSH_LSN field so there could be garbage
stored_pre_encryption); and above checksum check could produce false positive.
Thus we also check does the traditional stored
checksum fields match the calculated one. Both of these
could naturally produce false positive but then
we just decrypt the page and after that corrupted
pages very probable stay corrupted and valid
pages very probable stay valid.
*/
ulint checksum1 = mach_read_from_4(
page + FIL_PAGE_SPACE_OR_CHKSUM);
mach_write_to_4(const_cast<byte*>(src_frame) + ulint checksum2 = mach_read_from_4(
UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM, page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM);
checksum_field2);
if (!corrupted) {
return true; // page was encrypted and checksum matched bool valid = (buf_page_is_checksum_valid_crc32(page,checksum1,checksum2)
} else { || buf_page_is_checksum_valid_none(page,checksum1,checksum2)
return false; // page was encrypted but checksum didn't match || buf_page_is_checksum_valid_innodb(page,checksum1, checksum2));
if (encrypted && valid) {
/* If page is encrypted and traditional checksums match,
page could be still encrypted, or not encrypted and valid or
corrupted. */
ib_logf(IB_LOG_LEVEL_ERROR,
" Page %lu in space %s (%lu) maybe corrupted."
" Post encryption checksum %u stored [%lu:%lu] key_version %u",
pageno,
space ? space->name : "N/A",
mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID),
checksum, checksum1, checksum2, key_version);
encrypted = false;
} }
return(encrypted);
} }
/***********************************************************************/ /***********************************************************************/
......
...@@ -2309,7 +2309,7 @@ fil_check_first_page(const page_t* page, ulint space_id, ulint flags) ...@@ -2309,7 +2309,7 @@ fil_check_first_page(const page_t* page, ulint space_id, ulint flags)
} }
if (buf_page_is_corrupted( if (buf_page_is_corrupted(
false, page, fsp_flags_get_zip_size(flags))) { false, page, fsp_flags_get_zip_size(flags), NULL)) {
return("checksum mismatch"); return("checksum mismatch");
} }
...@@ -4547,13 +4547,13 @@ fil_user_tablespace_find_space_id( ...@@ -4547,13 +4547,13 @@ fil_user_tablespace_find_space_id(
to UNIV_PAGE_SIZE. */ to UNIV_PAGE_SIZE. */
if (page_size == UNIV_PAGE_SIZE) { if (page_size == UNIV_PAGE_SIZE) {
uncompressed_ok = !buf_page_is_corrupted( uncompressed_ok = !buf_page_is_corrupted(
false, page, 0); false, page, 0, NULL);
} }
bool compressed_ok = false; bool compressed_ok = false;
if (page_size <= UNIV_PAGE_SIZE_DEF) { if (page_size <= UNIV_PAGE_SIZE_DEF) {
compressed_ok = !buf_page_is_corrupted( compressed_ok = !buf_page_is_corrupted(
false, page, page_size); false, page, page_size, NULL);
} }
if (uncompressed_ok || compressed_ok) { if (uncompressed_ok || compressed_ok) {
......
...@@ -366,7 +366,7 @@ fil_compress_page( ...@@ -366,7 +366,7 @@ fil_compress_page(
fil_decompress_page(uncomp_page, comp_page, len, NULL); fil_decompress_page(uncomp_page, comp_page, len, NULL);
if(buf_page_is_corrupted(false, uncomp_page, 0)) { if(buf_page_is_corrupted(false, uncomp_page, 0, space)) {
buf_page_print(uncomp_page, 0, BUF_PAGE_PRINT_NO_CRASH); buf_page_print(uncomp_page, 0, BUF_PAGE_PRINT_NO_CRASH);
ut_error; ut_error;
} }
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2016, MariaDB Corporation. Copyright (c) 2013, 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -639,19 +639,68 @@ buf_block_unfix( ...@@ -639,19 +639,68 @@ buf_block_unfix(
#else /* !UNIV_HOTBACKUP */ #else /* !UNIV_HOTBACKUP */
# define buf_block_modify_clock_inc(block) ((void) 0) # define buf_block_modify_clock_inc(block) ((void) 0)
#endif /* !UNIV_HOTBACKUP */ #endif /* !UNIV_HOTBACKUP */
/** Checks if the page is in crc32 checksum format.
@param[in] read_buf database page
@param[in] checksum_field1 new checksum field
@param[in] checksum_field2 old checksum field
@return true if the page is in crc32 checksum format */
bool
buf_page_is_checksum_valid_crc32(
const byte* read_buf,
ulint checksum_field1,
ulint checksum_field2)
MY_ATTRIBUTE((warn_unused_result));
/** Checks if the page is in innodb checksum format.
@param[in] read_buf database page
@param[in] checksum_field1 new checksum field
@param[in] checksum_field2 old checksum field
@return true if the page is in innodb checksum format */
bool
buf_page_is_checksum_valid_innodb(
const byte* read_buf,
ulint checksum_field1,
ulint checksum_field2)
MY_ATTRIBUTE((warn_unused_result));
/** Checks if the page is in none checksum format.
@param[in] read_buf database page
@param[in] checksum_field1 new checksum field
@param[in] checksum_field2 old checksum field
@return true if the page is in none checksum format */
bool
buf_page_is_checksum_valid_none(
const byte* read_buf,
ulint checksum_field1,
ulint checksum_field2)
MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//** /********************************************************************//**
Checks if a page is corrupt. Checks if a page is corrupt.
@return TRUE if corrupted */ @param[in] check_lsn true if LSN should be checked
UNIV_INTERN @param[in] read_buf Page to be checked
ibool @param[in] zip_size compressed size or 0
@param[in] space Pointer to tablespace
@return true if corrupted, false if not */
bool
buf_page_is_corrupted( buf_page_is_corrupted(
/*==================*/ bool check_lsn,
bool check_lsn, /*!< in: true if we need to check the const byte* read_buf,
and complain about the LSN */ ulint zip_size,
const byte* read_buf, /*!< in: a database page */ const fil_space_t* space)
ulint zip_size) /*!< in: size of compressed page; MY_ATTRIBUTE((warn_unused_result));
0 for uncompressed pages */ /********************************************************************//**
MY_ATTRIBUTE((nonnull, warn_unused_result)); Check if page is maybe compressed, encrypted or both when we encounter
corrupted page. Note that we can't be 100% sure if page is corrupted
or decrypt/decompress just failed.
@param[in] bpage Page
@return true if page corrupted, false if not */
bool
buf_page_check_corrupt(
buf_page_t* bpage) /*!< in/out: buffer page read from disk */
MY_ATTRIBUTE(( warn_unused_result));
/********************************************************************//** /********************************************************************//**
Checks if a page is all zeroes. Checks if a page is all zeroes.
@return TRUE if the page is all zeroes */ @return TRUE if the page is all zeroes */
...@@ -1524,7 +1573,7 @@ The hook that is called just after a page is read from disk. ...@@ -1524,7 +1573,7 @@ The hook that is called just after a page is read from disk.
The function decrypt disk content into buf_page_t and releases the The function decrypt disk content into buf_page_t and releases the
temporary buffer that was allocated in buf_page_decrypt_before_read */ temporary buffer that was allocated in buf_page_decrypt_before_read */
UNIV_INTERN UNIV_INTERN
ibool bool
buf_page_decrypt_after_read( buf_page_decrypt_after_read(
/*========================*/ /*========================*/
buf_page_t* page); /*!< in/out: buffer page read from disk */ buf_page_t* page); /*!< in/out: buffer page read from disk */
...@@ -1630,15 +1679,9 @@ struct buf_page_t{ ...@@ -1630,15 +1679,9 @@ struct buf_page_t{
if written again we check is TRIM if written again we check is TRIM
operation needed. */ operation needed. */
unsigned key_version; /*!< key version for this block */ unsigned key_version; /*!< key version for this block */
bool page_encrypted; /*!< page is page encrypted */ bool encrypted; /*!< page is still encrypted */
bool page_compressed;/*!< page is page compressed */ bool corrupted; /*!< page is corrupted */
ulint stored_checksum;/*!< stored page checksum if page
encrypted */
bool encrypted; /*!< page is still encrypted */
ulint calculated_checksum;
/*!< calculated checksum if page
encrypted */
ulint real_size; /*!< Real size of the page ulint real_size; /*!< Real size of the page
Normal pages == UNIV_PAGE_SIZE Normal pages == UNIV_PAGE_SIZE
...@@ -2318,7 +2361,6 @@ buf_pool_mutex_exit( ...@@ -2318,7 +2361,6 @@ buf_pool_mutex_exit(
/*================*/ /*================*/
buf_pool_t* buf_pool); /*!< in: buffer pool */ buf_pool_t* buf_pool); /*!< in: buffer pool */
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
#include "buf0buf.ic" #include "buf0buf.ic"
#endif #endif
......
...@@ -328,7 +328,7 @@ fil_space_check_encryption_read( ...@@ -328,7 +328,7 @@ fil_space_check_encryption_read(
/****************************************************************** /******************************************************************
Decrypt a page Decrypt a page
@return true if page is decrypted, false if not. */ @return true if page decrypted, false if not.*/
UNIV_INTERN UNIV_INTERN
bool bool
fil_space_decrypt( fil_space_decrypt(
...@@ -336,9 +336,10 @@ fil_space_decrypt( ...@@ -336,9 +336,10 @@ fil_space_decrypt(
fil_space_crypt_t* crypt_data, /*!< in: crypt data */ fil_space_crypt_t* crypt_data, /*!< in: crypt data */
byte* tmp_frame, /*!< in: temporary buffer */ byte* tmp_frame, /*!< in: temporary buffer */
ulint page_size, /*!< in: page size */ ulint page_size, /*!< in: page size */
byte* src_frame, /*!< in:out: page buffer */ byte* src_frame, /*!< in: out: page buffer */
dberr_t* err); /*!< in: out: DB_SUCCESS or dberr_t* err) /*!< in: out: DB_SUCCESS or
error code */ error code */
MY_ATTRIBUTE((warn_unused_result));
/********************************************************************* /*********************************************************************
Encrypt buffer page Encrypt buffer page
...@@ -355,31 +356,46 @@ fil_space_encrypt( ...@@ -355,31 +356,46 @@ fil_space_encrypt(
ulint size, /*!< in: size of data to encrypt */ ulint size, /*!< in: size of data to encrypt */
byte* dst_frame); /*!< in: where to encrypt to */ byte* dst_frame); /*!< in: where to encrypt to */
/********************************************************************* /******************************************************************
Decrypt buffer page Decrypt a page
@return decrypted page, or original not encrypted page if decrypt is @param[in] space Tablespace id
@param[in] tmp_frame Temporary buffer used for decrypting
@param[in] page_size Page size
@param[in,out] src_frame Page to decrypt
@param[out] decrypted true if page was decrypted
@return decrypted page, or original not encrypted page if decryption is
not needed.*/ not needed.*/
UNIV_INTERN UNIV_INTERN
byte* byte*
fil_space_decrypt( fil_space_decrypt(
/*==============*/ /*==============*/
ulint space, /*!< in: tablespace id */ ulint space,
byte* src_frame, /*!< in: page frame */ byte* src_frame,
ulint page_size, /*!< in: size of data to encrypt */ ulint page_size,
byte* dst_frame) /*!< in: where to decrypt to */ byte* dst_frame,
bool* decrypted)
__attribute__((warn_unused_result)); __attribute__((warn_unused_result));
/********************************************************************* /*********************************************************************
fil_space_verify_crypt_checksum Verify that post encryption checksum match calculated checksum.
NOTE: currently this function can only be run in single threaded mode This function should be called only if tablespace contains crypt_data
as it modifies srv_checksum_algorithm (temporarily) metadata (this is strong indication that tablespace is encrypted).
Function also verifies that traditional checksum does not match
calculated checksum as if it does page could be valid unencrypted,
encrypted, or corrupted.
@param[in] page Page to verify
@param[in] zip_size zip size
@param[in] space Tablespace
@param[in] pageno Page no
@return true if page is encrypted AND OK, false otherwise */ @return true if page is encrypted AND OK, false otherwise */
UNIV_INTERN UNIV_INTERN
bool bool
fil_space_verify_crypt_checksum( fil_space_verify_crypt_checksum(
/*============================*/ byte* page,
const byte* src_frame,/*!< in: page frame */ ulint zip_size,
ulint zip_size); /*!< in: size of data to encrypt */ const fil_space_t* space,
ulint pageno)
__attribute__((warn_unused_result));
/********************************************************************* /*********************************************************************
Init threads for key rotation */ Init threads for key rotation */
......
...@@ -59,6 +59,8 @@ fil_get_page_type_name( ...@@ -59,6 +59,8 @@ fil_get_page_type_name(
switch(page_type) { switch(page_type) {
case FIL_PAGE_PAGE_COMPRESSED: case FIL_PAGE_PAGE_COMPRESSED:
return (const char*)"PAGE_COMPRESSED"; return (const char*)"PAGE_COMPRESSED";
case FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED:
return (const char*)"PAGE_COMPRESSED_ENCRYPTED";
case FIL_PAGE_INDEX: case FIL_PAGE_INDEX:
return (const char*)"INDEX"; return (const char*)"INDEX";
case FIL_PAGE_UNDO_LOG: case FIL_PAGE_UNDO_LOG:
...@@ -87,9 +89,13 @@ fil_get_page_type_name( ...@@ -87,9 +89,13 @@ fil_get_page_type_name(
return (const char*)"ZBLOB2"; return (const char*)"ZBLOB2";
case FIL_PAGE_TYPE_COMPRESSED: case FIL_PAGE_TYPE_COMPRESSED:
return (const char*)"ORACLE PAGE COMPRESSED"; return (const char*)"ORACLE PAGE COMPRESSED";
default:
return (const char*)"PAGE TYPE CORRUPTED"; /* No default to make compiler generate warning if
new page type is added but not handled here. */
} }
return (const char*)"PAGE TYPE CORRUPTED";
} }
/****************************************************************//** /****************************************************************//**
......
...@@ -40,6 +40,7 @@ Created 2012-02-08 by Sunny Bains. ...@@ -40,6 +40,7 @@ Created 2012-02-08 by Sunny Bains.
#include "row0mysql.h" #include "row0mysql.h"
#include "srv0start.h" #include "srv0start.h"
#include "row0quiesce.h" #include "row0quiesce.h"
#include "buf0buf.h"
#include <vector> #include <vector>
...@@ -2036,12 +2037,15 @@ PageConverter::validate( ...@@ -2036,12 +2037,15 @@ PageConverter::validate(
buf_block_t* block) UNIV_NOTHROW buf_block_t* block) UNIV_NOTHROW
{ {
buf_frame_t* page = get_frame(block); buf_frame_t* page = get_frame(block);
ulint space_id = mach_read_from_4(
page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
fil_space_t* space = fil_space_found_by_id(space_id);
/* Check that the page number corresponds to the offset in /* Check that the page number corresponds to the offset in
the file. Flag as corrupt if it doesn't. Disable the check the file. Flag as corrupt if it doesn't. Disable the check
for LSN in buf_page_is_corrupted() */ for LSN in buf_page_is_corrupted() */
if (buf_page_is_corrupted(false, page, get_zip_size()) if (buf_page_is_corrupted(false, page, get_zip_size(), space)
|| (page_get_page_no(page) != offset / m_page_size || (page_get_page_no(page) != offset / m_page_size
&& page_get_page_no(page) != 0)) { && page_get_page_no(page) != 0)) {
......
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