diff options
author | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2019-08-16 18:11:32 +0530 |
---|---|---|
committer | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2019-08-16 18:11:32 +0530 |
commit | dc91372de3ab4b9b849a0710b4b086619d87b6cd (patch) | |
tree | 87dd7507489d8741c4622f3f4295c022aa1d577e | |
parent | fe6eac0cf7ecf1b38e8cf243104e185b8c67eae8 (diff) | |
download | mariadb-git-dc91372de3ab4b9b849a0710b4b086619d87b6cd.tar.gz |
Problem:
========
During ibd file creation, InnoDB flushes the page0 without crypt
information. During recovery, InnoDB encounters encrypted page read
before initialising the crypt data of the tablespace. So it leads t
corruption of page and doesn't allow innodb to start.
Solution:
=========
Write crypt_data information in page0 while creating .ibd file creation.
During recovery, crypt_data will be initialised while processing
MLOG_FILE_NAME redo log record.
-rw-r--r-- | mysql-test/suite/encryption/r/file_creation.result | 26 | ||||
-rw-r--r-- | mysql-test/suite/encryption/t/file_creation.opt | 1 | ||||
-rw-r--r-- | mysql-test/suite/encryption/t/file_creation.test | 41 | ||||
-rw-r--r-- | storage/innobase/fil/fil0crypt.cc | 27 | ||||
-rw-r--r-- | storage/innobase/fil/fil0fil.cc | 20 | ||||
-rw-r--r-- | storage/innobase/include/fil0crypt.h | 6 |
6 files changed, 114 insertions, 7 deletions
diff --git a/mysql-test/suite/encryption/r/file_creation.result b/mysql-test/suite/encryption/r/file_creation.result new file mode 100644 index 00000000000..22fe271145c --- /dev/null +++ b/mysql-test/suite/encryption/r/file_creation.result @@ -0,0 +1,26 @@ +SET GLOBAL innodb_encrypt_tables = ON; +SET GLOBAL innodb_encryption_threads = 1; +SET GLOBAL innodb_max_dirty_pages_pct = 99; +SHOW VARIABLES LIKE 'innodb_encrypt%'; +Variable_name Value +innodb_encrypt_log OFF +innodb_encrypt_tables ON +innodb_encrypt_temporary_tables OFF +innodb_encryption_rotate_key_age 1 +innodb_encryption_rotation_iops 100 +innodb_encryption_threads 1 +CREATE TABLE t1(f1 INT NOT NULL, f2 CHAR(255), f3 CHAR(255), +f4 CHAR(255), f5 CHAR(255))ENGINE=INNODB; +INSERT INTO t1 VALUES(1, "mysql", "mariadb", "batman", "superman"); +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +# Wait max 10 min for key encryption threads to encrypt all spaces +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize note Table does not support optimize, doing recreate + analyze instead +test.t1 optimize status OK +ALTER TABLE t1 FORCE; +# Kill the server +DROP TABLE t1; diff --git a/mysql-test/suite/encryption/t/file_creation.opt b/mysql-test/suite/encryption/t/file_creation.opt new file mode 100644 index 00000000000..7d3f2da7971 --- /dev/null +++ b/mysql-test/suite/encryption/t/file_creation.opt @@ -0,0 +1 @@ +--innodb-tablespaces-encryption diff --git a/mysql-test/suite/encryption/t/file_creation.test b/mysql-test/suite/encryption/t/file_creation.test new file mode 100644 index 00000000000..a0c90975a37 --- /dev/null +++ b/mysql-test/suite/encryption/t/file_creation.test @@ -0,0 +1,41 @@ +--source include/have_innodb.inc +--source include/have_example_key_management_plugin.inc + +# embedded does not support restart +-- source include/not_embedded.inc + +# +# MDEV-19348 MariaBackup prepare fails with InnoDB: Database page corruption +# on disk or a failed file read +# + +SET GLOBAL innodb_encrypt_tables = ON; +SET GLOBAL innodb_encryption_threads = 1; +SET GLOBAL innodb_max_dirty_pages_pct = 99; +SHOW VARIABLES LIKE 'innodb_encrypt%'; + +CREATE TABLE t1(f1 INT NOT NULL, f2 CHAR(255), f3 CHAR(255), + f4 CHAR(255), f5 CHAR(255))ENGINE=INNODB; + +INSERT INTO t1 VALUES(1, "mysql", "mariadb", "batman", "superman"); +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; + +--let $tables_count= `select count(*) + 1 from information_schema.tables where engine = 'InnoDB'` + +--echo # Wait max 10 min for key encryption threads to encrypt all spaces +--let $wait_timeout= 600 +--let $wait_condition=SELECT COUNT(*) >= $tables_count FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0; +--source include/wait_condition.inc + +OPTIMIZE TABLE t1; + +--source ../../suite/innodb/include/no_checkpoint_start.inc +ALTER TABLE t1 FORCE; +--let CLEANUP_IF_CHECKPOINT=DROP TABLE t1; +--source ../../suite/innodb/include/no_checkpoint_end.inc + +--source include/start_mysqld.inc +DROP TABLE t1; diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 9b8373a4d55..90b9aba70aa 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -356,6 +356,33 @@ fil_space_destroy_crypt_data( } } +/** Fill crypt data information to the give page. +It should be called during ibd file creation. +@param[in] flags tablespace flags +@param[in,out] page first page of the tablespace */ +void +fil_space_crypt_t::fill_page0( + ulint flags, + byte* page) +{ + const uint len = sizeof(iv); + const ulint offset = FSP_HEADER_OFFSET + + fsp_header_get_encryption_offset(page_size_t(flags)); + page0_offset = offset; + + memcpy(page + offset, CRYPT_MAGIC, MAGIC_SZ); + mach_write_to_1(page + offset + MAGIC_SZ, type); + mach_write_to_1(page + offset + MAGIC_SZ + 1, len); + memcpy(page + offset + MAGIC_SZ + 2, &iv, len); + + mach_write_to_4(page + offset + MAGIC_SZ + 2 + len, + min_key_version); + mach_write_to_4(page + offset + MAGIC_SZ + 2 + len + 4, + key_id); + mach_write_to_1(page + offset + MAGIC_SZ + 2 + len + 8, + encryption); +} + /****************************************************************** Write crypt data to a page (0) @param[in] space tablespace diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index f06179bf094..e37cb963c28 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -3671,6 +3671,19 @@ fil_ibd_create( fsp_header_init_fields(page, space_id, flags); mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, space_id); + /* Create crypt data if the tablespace is either encrypted or user has + requested it to remain unencrypted. */ + if (mode == FIL_ENCRYPTION_ON || mode == FIL_ENCRYPTION_OFF || + srv_encrypt_tables) { + crypt_data = fil_space_create_crypt_data(mode, key_id); + } + + if (crypt_data) { + /* Write crypt data information in page0 while creating + ibd file. */ + crypt_data->fill_page0(flags, page); + } + const page_size_t page_size(flags); IORequest request(IORequest::WRITE); @@ -3732,13 +3745,6 @@ fil_ibd_create( } } - /* Create crypt data if the tablespace is either encrypted or user has - requested it to remain unencrypted. */ - if (mode == FIL_ENCRYPTION_ON || mode == FIL_ENCRYPTION_OFF || - srv_encrypt_tables) { - crypt_data = fil_space_create_crypt_data(mode, key_id); - } - space = fil_space_create(name, space_id, flags, FIL_TYPE_TABLESPACE, crypt_data, mode); if (!space) { diff --git a/storage/innobase/include/fil0crypt.h b/storage/innobase/include/fil0crypt.h index 9b9f0da6fdd..24108f95167 100644 --- a/storage/innobase/include/fil0crypt.h +++ b/storage/innobase/include/fil0crypt.h @@ -180,6 +180,12 @@ struct fil_space_crypt_t : st_encryption_scheme return (encryption == FIL_ENCRYPTION_OFF); } + /** Fill crypt data information to the give page. + It should be called during ibd file creation. + @param[in] flags tablespace flags + @param[in,out] page first page of the tablespace */ + void fill_page0(ulint flags, byte* page); + /** Write crypt data to a page (0) @param[in] space tablespace @param[in,out] page0 first page of the tablespace |