summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThirunarayanan Balathandayuthapani <thiru@mariadb.com>2019-08-16 18:11:32 +0530
committerThirunarayanan Balathandayuthapani <thiru@mariadb.com>2019-08-16 18:11:32 +0530
commitdc91372de3ab4b9b849a0710b4b086619d87b6cd (patch)
tree87dd7507489d8741c4622f3f4295c022aa1d577e
parentfe6eac0cf7ecf1b38e8cf243104e185b8c67eae8 (diff)
downloadmariadb-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.result26
-rw-r--r--mysql-test/suite/encryption/t/file_creation.opt1
-rw-r--r--mysql-test/suite/encryption/t/file_creation.test41
-rw-r--r--storage/innobase/fil/fil0crypt.cc27
-rw-r--r--storage/innobase/fil/fil0fil.cc20
-rw-r--r--storage/innobase/include/fil0crypt.h6
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