summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmysql-test/mysql-test-run.pl6
-rw-r--r--mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result4
-rw-r--r--mysql-test/suite/encryption/t/innodb_encrypt_log_corruption.test5
-rw-r--r--mysql-test/suite/innodb/r/innodb-32k.result4
-rw-r--r--mysql-test/suite/innodb/r/innodb-64k.result4
-rw-r--r--mysql-test/suite/innodb/r/innodb-bigblob.result4
-rw-r--r--mysql-test/suite/innodb/r/log_corruption.result5
-rw-r--r--mysql-test/suite/innodb/r/log_file.result2
-rw-r--r--mysql-test/suite/innodb/t/innodb-32k.test4
-rw-r--r--mysql-test/suite/innodb/t/innodb-64k.test5
-rw-r--r--mysql-test/suite/innodb/t/innodb-bigblob.test5
-rw-r--r--mysql-test/suite/innodb/t/log_corruption.test12
-rw-r--r--mysql-test/suite/innodb/t/log_file.test2
-rw-r--r--mysql-test/suite/innodb/t/log_file_size.test3
-rw-r--r--mysql-test/suite/innodb/t/log_file_size_checkpoint.test5
-rw-r--r--storage/innobase/handler/ha_innodb.cc4
-rw-r--r--storage/innobase/include/fil0crypt.h1
-rw-r--r--storage/innobase/include/log0crypt.h95
-rw-r--r--storage/innobase/include/log0log.h49
-rw-r--r--storage/innobase/include/log0recv.h9
-rw-r--r--storage/innobase/log/log0crypt.cc674
-rw-r--r--storage/innobase/log/log0log.cc120
-rw-r--r--storage/innobase/log/log0recv.cc220
-rw-r--r--storage/innobase/mysql-test/storage_engine/define_engine.inc4
-rw-r--r--storage/innobase/srv/srv0start.cc61
25 files changed, 399 insertions, 908 deletions
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index b2c98692a22..718a8350d88 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -4342,10 +4342,7 @@ sub extract_warning_lines ($$) {
qr/error .*connecting to master/,
qr/InnoDB: Error: in ALTER TABLE `test`.`t[12]`/,
qr/InnoDB: Error: table `test`.`t[12]` .*does not exist in the InnoDB internal/,
- qr/InnoDB: Warning: Setting innodb_use_sys_malloc/,
qr/InnoDB: Warning: a long semaphore wait:/,
- qr/InnoDB: Disabling redo log encryption/,
- qr/InnoDB: Redo log crypto: Can't initialize to key version -1u/,
qr/InnoDB: Dumping buffer pool.*/,
qr/InnoDB: Buffer pool.*/,
qr/InnoDB: Warning: Writer thread is waiting this semaphore/,
@@ -4419,9 +4416,6 @@ sub extract_warning_lines ($$) {
qr|InnoDB: TABLE to scan your table for corruption|,
qr/InnoDB: See also */,
qr/InnoDB: Cannot open .*ib_buffer_pool.* for reading: No such file or directory*/,
- qr/InnoDB: Upgrading redo log:*/,
- qr|InnoDB: Starting to delete and rewrite log files.|,
- qr/InnoDB: New log files created, LSN=*/,
qr|InnoDB: Creating foreign key constraint system tables.|,
qr/InnoDB: Table .*mysql.*innodb_table_stats.* not found./,
qr/InnoDB: User stopword table .* does not exist./
diff --git a/mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result b/mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result
index 16a5c91a953..65e03a028a0 100644
--- a/mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result
+++ b/mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result
@@ -41,7 +41,7 @@ WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
FOUND /InnoDB: Invalid log block checksum. block: 2372 checkpoint no: 1 expected: 3362026715 found: 144444122/ in mysqld.1.err
-FOUND /InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\./ in mysqld.1.err
+FOUND /InnoDB: Missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\./ in mysqld.1.err
# --innodb-force-recovery=6 (skip the entire redo log)
SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
@@ -54,7 +54,6 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
-FOUND /InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\./ in mysqld.1.err
# --innodb-force-recovery=6 (skip the entire redo log)
SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
@@ -90,6 +89,7 @@ SELECT COUNT(*) `1` FROM INFORMATION_SCHEMA.ENGINES WHERE engine='innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
1
1
+FOUND /InnoDB: Encrypting redo log/ in mysqld.1.err
ib_buffer_pool
ib_logfile0
ib_logfile1
diff --git a/mysql-test/suite/encryption/t/innodb_encrypt_log_corruption.test b/mysql-test/suite/encryption/t/innodb_encrypt_log_corruption.test
index 85ce09e0901..f1642e83e32 100644
--- a/mysql-test/suite/encryption/t/innodb_encrypt_log_corruption.test
+++ b/mysql-test/suite/encryption/t/innodb_encrypt_log_corruption.test
@@ -3,9 +3,12 @@
SELECT COUNT(*) `1` FROM INFORMATION_SCHEMA.ENGINES WHERE engine='innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
+--source include/shutdown_mysqld.inc
+--let SEARCH_PATTERN= InnoDB: Encrypting redo log
+--source include/search_pattern_in_file.inc
--let $restart_parameters=
---source include/restart_mysqld.inc
+--source include/start_mysqld.inc
--list_files $bugdir
--remove_files_wildcard $bugdir
diff --git a/mysql-test/suite/innodb/r/innodb-32k.result b/mysql-test/suite/innodb/r/innodb-32k.result
index 29374689a3b..140b9b87c2f 100644
--- a/mysql-test/suite/innodb/r/innodb-32k.result
+++ b/mysql-test/suite/innodb/r/innodb-32k.result
@@ -1,7 +1,3 @@
-call mtr.add_suppression("InnoDB: Warning: innodb_page_size has been changed from default value ");
-call mtr.add_suppression("InnoDB: Resizing redo log from ");
-call mtr.add_suppression("InnoDB: Starting to delete and rewrite log files");
-call mtr.add_suppression("InnoDB: New log files created, LSN=");
call mtr.add_suppression("Innodb: Cannot add field.*row size is");
# Test 1) Show the page size from Information Schema
SELECT variable_value FROM information_schema.global_status
diff --git a/mysql-test/suite/innodb/r/innodb-64k.result b/mysql-test/suite/innodb/r/innodb-64k.result
index 9271ad70fb4..f72ba8ef8b5 100644
--- a/mysql-test/suite/innodb/r/innodb-64k.result
+++ b/mysql-test/suite/innodb/r/innodb-64k.result
@@ -1,7 +1,3 @@
-call mtr.add_suppression("InnoDB: Warning: innodb_page_size has been changed from default value *");
-call mtr.add_suppression("InnoDB: Resizing redo log from *");
-call mtr.add_suppression("InnoDB: Starting to delete and rewrite log files.");
-call mtr.add_suppression("InnoDB: New log files created, LSN=*");
# Test 1) Show the page size from Information Schema
SELECT variable_value FROM information_schema.global_status
WHERE LOWER(variable_name) = 'innodb_page_size';
diff --git a/mysql-test/suite/innodb/r/innodb-bigblob.result b/mysql-test/suite/innodb/r/innodb-bigblob.result
index 20fe0ce5c43..dcde9804cdc 100644
--- a/mysql-test/suite/innodb/r/innodb-bigblob.result
+++ b/mysql-test/suite/innodb/r/innodb-bigblob.result
@@ -1,7 +1,3 @@
-call mtr.add_suppression("Resizing redo log from *");
-call mtr.add_suppression("Starting to delete and rewrite log files.");
-call mtr.add_suppression("New log files created, LSN=*");
-call mtr.add_suppression("Writer thread is waiting this semaphore");
create table foo (id varchar(37) not null, content longblob) engine=INNODB;
insert into foo (id, content) values('xyz', '');
update foo set content=repeat('a', 43941888) where id='xyz';
diff --git a/mysql-test/suite/innodb/r/log_corruption.result b/mysql-test/suite/innodb/r/log_corruption.result
index ad06398e4e2..ccf5f73a3a3 100644
--- a/mysql-test/suite/innodb/r/log_corruption.result
+++ b/mysql-test/suite/innodb/r/log_corruption.result
@@ -41,7 +41,7 @@ WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
FOUND /InnoDB: Invalid log block checksum. block: 2372 checkpoint no: 1 expected: 3362026715 found: 144444122/ in mysqld.1.err
-FOUND /InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\./ in mysqld.1.err
+FOUND /InnoDB: Missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\./ in mysqld.1.err
# --innodb-force-recovery=6 (skip the entire redo log)
SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
@@ -54,7 +54,6 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
-FOUND /InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\./ in mysqld.1.err
# --innodb-force-recovery=6 (skip the entire redo log)
SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
@@ -90,7 +89,7 @@ SELECT * FROM INFORMATION_SCHEMA.ENGINES
WHERE engine = 'innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
ENGINE SUPPORT COMMENT TRANSACTIONS XA SAVEPOINTS
-FOUND /InnoDB: Redo log crypto: getting mysqld crypto key from key version failed err = 4294967295/ in mysqld.1.err
+FOUND /InnoDB: Obtaining redo log encryption key version 1 failed/ in mysqld.1.err
FOUND /InnoDB: Decrypting checkpoint failed/ in mysqld.1.err
ib_buffer_pool
ib_logfile0
diff --git a/mysql-test/suite/innodb/r/log_file.result b/mysql-test/suite/innodb/r/log_file.result
index b0351232ed9..eb96dc48199 100644
--- a/mysql-test/suite/innodb/r/log_file.result
+++ b/mysql-test/suite/innodb/r/log_file.result
@@ -378,7 +378,7 @@ WHERE engine='innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
1
1
-FOUND /Resizing redo log from 2\*\d+ to 3\*\d+ pages, LSN=\d+/ in mysqld.1.err
+FOUND /Resizing redo log from 2\*\d+ to 3\*\d+ pages; LSN=\d+/ in mysqld.1.err
# Cleanup
bak_ib_logfile0
bak_ib_logfile1
diff --git a/mysql-test/suite/innodb/t/innodb-32k.test b/mysql-test/suite/innodb/t/innodb-32k.test
index 53a2d3a7442..80a05c350d0 100644
--- a/mysql-test/suite/innodb/t/innodb-32k.test
+++ b/mysql-test/suite/innodb/t/innodb-32k.test
@@ -3,10 +3,6 @@
--source include/have_innodb.inc
--source include/have_innodb_32k.inc
-call mtr.add_suppression("InnoDB: Warning: innodb_page_size has been changed from default value ");
-call mtr.add_suppression("InnoDB: Resizing redo log from ");
-call mtr.add_suppression("InnoDB: Starting to delete and rewrite log files");
-call mtr.add_suppression("InnoDB: New log files created, LSN=");
call mtr.add_suppression("Innodb: Cannot add field.*row size is");
let $MYSQLD_DATADIR= `select @@datadir`;
diff --git a/mysql-test/suite/innodb/t/innodb-64k.test b/mysql-test/suite/innodb/t/innodb-64k.test
index c611b6cb2e2..13351450cfc 100644
--- a/mysql-test/suite/innodb/t/innodb-64k.test
+++ b/mysql-test/suite/innodb/t/innodb-64k.test
@@ -3,11 +3,6 @@
--source include/have_innodb.inc
--source include/have_innodb_64k.inc
-call mtr.add_suppression("InnoDB: Warning: innodb_page_size has been changed from default value *");
-call mtr.add_suppression("InnoDB: Resizing redo log from *");
-call mtr.add_suppression("InnoDB: Starting to delete and rewrite log files.");
-call mtr.add_suppression("InnoDB: New log files created, LSN=*");
-
let $MYSQLD_DATADIR= `select @@datadir`;
--echo # Test 1) Show the page size from Information Schema
diff --git a/mysql-test/suite/innodb/t/innodb-bigblob.test b/mysql-test/suite/innodb/t/innodb-bigblob.test
index d72e20487e4..799dfe42d48 100644
--- a/mysql-test/suite/innodb/t/innodb-bigblob.test
+++ b/mysql-test/suite/innodb/t/innodb-bigblob.test
@@ -6,11 +6,6 @@
let $status_orig=`SELECT @@innodb_status_output`;
--enable_query_log
-call mtr.add_suppression("Resizing redo log from *");
-call mtr.add_suppression("Starting to delete and rewrite log files.");
-call mtr.add_suppression("New log files created, LSN=*");
-call mtr.add_suppression("Writer thread is waiting this semaphore");
-
create table foo (id varchar(37) not null, content longblob) engine=INNODB;
insert into foo (id, content) values('xyz', '');
update foo set content=repeat('a', 43941888) where id='xyz';
diff --git a/mysql-test/suite/innodb/t/log_corruption.test b/mysql-test/suite/innodb/t/log_corruption.test
index 0f59f64ebdf..abeadbd8f02 100644
--- a/mysql-test/suite/innodb/t/log_corruption.test
+++ b/mysql-test/suite/innodb/t/log_corruption.test
@@ -9,11 +9,11 @@ call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE faile
call mtr.add_suppression("InnoDB: Unsupported redo log format");
call mtr.add_suppression("InnoDB: No valid checkpoint found");
call mtr.add_suppression("InnoDB: Invalid (log block|redo log header) checksum");
-call mtr.add_suppression("InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT");
+call mtr.add_suppression("InnoDB: Missing MLOG_CHECKPOINT");
call mtr.add_suppression("InnoDB: MLOG_FILE_NAME incorrect");
call mtr.add_suppression("InnoDB: ############### CORRUPT LOG RECORD FOUND");
call mtr.add_suppression("InnoDB: Found corrupted log");
-call mtr.add_suppression("InnoDB: Redo log crypto: getting mysqld crypto key from key version failed");
+call mtr.add_suppression("InnoDB: Obtaining redo log encryption key version 1 failed");
call mtr.add_suppression("InnoDB: Decrypting checkpoint failed");
--enable_query_log
@@ -206,7 +206,7 @@ eval $check_no_innodb;
--source include/shutdown_mysqld.inc
let SEARCH_PATTERN=InnoDB: Invalid log block checksum. block: 2372 checkpoint no: 1 expected: 3362026715 found: 144444122;
--source include/search_pattern_in_file.inc
-let SEARCH_PATTERN=InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\.;
+let SEARCH_PATTERN=InnoDB: Missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\.;
--source include/search_pattern_in_file.inc
--echo # --innodb-force-recovery=6 (skip the entire redo log)
--let $restart_parameters= $dirs --innodb-force-recovery=6
@@ -232,14 +232,12 @@ print OUT pack("H*x[5]", "C0DEBA5E0022000c0000000138");
print OUT pack("H*x[475]H*", "12860cb7809781e80006626f677573", "089C0ADA");
EOF
--copy_file $bugdir/ib_logfile0 $bugdir/ib_logfile
-# Anything below innodb_force_recovery=6 must find a valid redo log.
+# Anything below innodb_force_recovery=6 must find an invalid redo log.
# Missing tablespace files are tolerated already with innodb_force_recovery=1.
--let $restart_parameters= $dirs --innodb-force-recovery=5
--source include/start_mysqld.inc
eval $check_no_innodb;
--source include/shutdown_mysqld.inc
-let SEARCH_PATTERN=InnoDB: Ignoring the redo log due to missing MLOG_CHECKPOINT between the checkpoint 1213964 and the end 1213952\.;
---source include/search_pattern_in_file.inc
--echo # --innodb-force-recovery=6 (skip the entire redo log)
--let $restart_parameters= $dirs --innodb-force-recovery=6
--source include/start_mysqld.inc
@@ -340,7 +338,7 @@ EOF
if (!$no_cleanup) {
eval $check_no_innodb;
--source include/shutdown_mysqld.inc
---let SEARCH_PATTERN= InnoDB: Redo log crypto: getting mysqld crypto key from key version failed err = 4294967295
+--let SEARCH_PATTERN= InnoDB: Obtaining redo log encryption key version 1 failed
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= InnoDB: Decrypting checkpoint failed
--source include/search_pattern_in_file.inc
diff --git a/mysql-test/suite/innodb/t/log_file.test b/mysql-test/suite/innodb/t/log_file.test
index f7a8ef36cc0..bb131b506fc 100644
--- a/mysql-test/suite/innodb/t/log_file.test
+++ b/mysql-test/suite/innodb/t/log_file.test
@@ -231,7 +231,7 @@ let SEARCH_PATTERN=Only one log file found;
--source include/start_mysqld.inc
eval $check_yes_innodb;
--source include/shutdown_mysqld.inc
-let SEARCH_PATTERN=Resizing redo log from 2\*\d+ to 3\*\d+ pages, LSN=\d+;
+--let SEARCH_PATTERN=Resizing redo log from 2\*\d+ to 3\*\d+ pages; LSN=\d+
--source include/search_pattern_in_file.inc
--let $restart_parameters=
diff --git a/mysql-test/suite/innodb/t/log_file_size.test b/mysql-test/suite/innodb/t/log_file_size.test
index 25988fc6fd8..1f109881eca 100644
--- a/mysql-test/suite/innodb/t/log_file_size.test
+++ b/mysql-test/suite/innodb/t/log_file_size.test
@@ -10,9 +10,6 @@ if (`SELECT @@innodb_log_file_size = 1048576`) {
}
--disable_query_log
-call mtr.add_suppression("InnoDB: Resizing redo log");
-call mtr.add_suppression("InnoDB: Starting to delete and rewrite log files");
-call mtr.add_suppression("InnoDB: New log files created");
call mtr.add_suppression("InnoDB: The log sequence numbers [0-9]+ and [0-9]+ in ibdata files do not match the log sequence number [0-9]+ in the ib_logfiles");
call mtr.add_suppression("syntax error in innodb_log_group_home_dir");
call mtr.add_suppression("Plugin 'InnoDB' init function returned error");
diff --git a/mysql-test/suite/innodb/t/log_file_size_checkpoint.test b/mysql-test/suite/innodb/t/log_file_size_checkpoint.test
index 26e0bdf5e2c..16b71bfd1f4 100644
--- a/mysql-test/suite/innodb/t/log_file_size_checkpoint.test
+++ b/mysql-test/suite/innodb/t/log_file_size_checkpoint.test
@@ -8,11 +8,6 @@ let $n=250;
let $t=veryLongTableNameToCreateMLOG_FILE_NAMErecords;
--disable_query_log
-call mtr.add_suppression("InnoDB: Resizing redo log");
-call mtr.add_suppression("InnoDB: Starting to delete and rewrite log files");
-call mtr.add_suppression("InnoDB: New log files created");
-FLUSH TABLES;
-
let $i=$n;
while ($i)
{
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 170a9d5b5a3..8ad0c308760 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -89,7 +89,7 @@ this program; if not, write to the Free Software Foundation, Inc.,
#include "fts0types.h"
#include "ibuf0ibuf.h"
#include "lock0lock.h"
-#include "log0log.h"
+#include "log0crypt.h"
#include "mem0mem.h"
#include "mtr0mtr.h"
#include "os0file.h"
@@ -3760,7 +3760,7 @@ static
void
innodb_log_checksums_func_update(bool check)
{
- log_checksum_algorithm_ptr = check
+ log_checksum_algorithm_ptr = check || srv_encrypt_log
? log_block_calc_checksum_crc32
: log_block_calc_checksum_none;
}
diff --git a/storage/innobase/include/fil0crypt.h b/storage/innobase/include/fil0crypt.h
index 72dd6c0d9c9..7185857c039 100644
--- a/storage/innobase/include/fil0crypt.h
+++ b/storage/innobase/include/fil0crypt.h
@@ -27,6 +27,7 @@ Created 04/01/2015 Jan Lindström
#define fil0crypt_h
#include "os0event.h"
+#include "my_crypt.h"
/**
* Magic pattern in start of crypt data on page 0
diff --git a/storage/innobase/include/log0crypt.h b/storage/innobase/include/log0crypt.h
index 6762b621155..d1282043665 100644
--- a/storage/innobase/include/log0crypt.h
+++ b/storage/innobase/include/log0crypt.h
@@ -22,27 +22,21 @@ Innodb log encrypt/decrypt
Created 11/25/2013 Minli Zhu
Modified Jan Lindström jan.lindstrom@mariadb.com
+MDEV-11782: Rewritten for MariaDB 10.2 by Marko Mäkelä, MariaDB Corporation.
*******************************************************/
#ifndef log0crypt_h
#define log0crypt_h
-#include "univ.i"
-#include "ut0byte.h"
-#include "my_crypt.h"
+#include "log0log.h"
-typedef int Crypt_result;
-
-/* If true, enable redo log encryption. */
+/** innodb_encrypt_log: whether to encrypt the redo log */
extern my_bool srv_encrypt_log;
-/***********************************************************************
-Set next checkpoint's key version to latest one, and generate new key */
+/** Initialize the redo log encryption key.
+@return whether the operation succeeded */
UNIV_INTERN
-void
-log_crypt_set_ver_and_key(
-/*======================*/
- ib_uint64_t next_checkpoint_no);/*!< in: next checkpoint no */
-
+bool
+log_crypt_init();
/*********************************************************************//**
Writes the crypto (version, msg and iv) info, which has been used for
@@ -64,78 +58,23 @@ log_crypt_101_read_checkpoint(const byte* buf);
/** Decrypt a MariaDB 10.1 redo log block.
@param[in,out] buf log block
@return whether the decryption was successful */
+UNIV_INTERN
bool
log_crypt_101_read_block(byte* buf);
-/*********************************************************************//**
-Read the crypto (version, msg and iv) info, which has been used for
-log blocks with lsn <= this checkpoint's lsn, from a log header's
-checkpoint buf. */
+/** Read the checkpoint crypto (version, msg and iv) info.
+@param[in] buf checkpoint buffer
+@return whether the operation was successful */
UNIV_INTERN
bool
-log_crypt_read_checkpoint_buf(
-/*===========================*/
- const byte* buf); /*!< in: checkpoint buffer */
-
-/********************************************************
-Encrypt one or more log block before it is flushed to disk */
-UNIV_INTERN
-void
-log_encrypt_before_write(
-/*=====================*/
- ib_uint64_t next_checkpoint_no, /*!< in: log group to be flushed */
- byte* block, /*!< in/out: pointer to a log block */
- const ulint size); /*!< in: size of log blocks */
-
-/********************************************************
-Decrypt a specified log segment after they are read from a log file to a buffer.
-*/
-UNIV_INTERN
-void
-log_decrypt_after_read(
-/*===================*/
- byte* frame, /*!< in/out: log segment */
- const ulint size); /*!< in: log segment size */
-
-/* Error codes for crypt info */
-typedef enum {
- LOG_UNENCRYPTED = 0,
- LOG_CRYPT_KEY_NOT_FOUND = 1,
- LOG_DECRYPT_MAYBE_FAILED = 2
-} log_crypt_err_t;
+log_crypt_read_checkpoint_buf(const byte* buf);
-/********************************************************
-Check is the checkpoint information encrypted. This check
-is based on fact has log group crypt info and based
-on this crypt info was the key version different from
-unencrypted key version. There is no realible way to
-distinguish encrypted log block from corrupted log block,
-but if log block corruption is found this function is
-used to find out if log block is maybe encrypted but
-encryption key, key management plugin or encryption
-algorithm does not match.
-@return TRUE, if log block may be encrypted */
+/** Encrypt or decrypt log blocks.
+@param[in,out] buf log blocks to encrypt or decrypt
+@param[in] size size of the buffer, in bytes
+@param[in] decrypt whether to decrypt instead of encrypting */
UNIV_INTERN
-ibool
-log_crypt_block_maybe_encrypted(
-/*============================*/
- const byte* log_block, /*!< in: log block */
- log_crypt_err_t* err_info); /*!< out: error info */
-
-/********************************************************
-Print crypt error message to error log */
-UNIV_INTERN
-void
-log_crypt_print_error(
-/*==================*/
- log_crypt_err_t err_info); /*!< out: error info */
-
-/*********************************************************************//**
-Print checkpoint no from log block and all encryption keys from
-checkpoints if they are present. Used for problem analysis. */
void
-log_crypt_print_checkpoint_keys(
-/*============================*/
- const byte* log_block);
+log_crypt(byte* buf, ulint size, bool decrypt = false);
#endif // log0crypt.h
diff --git a/storage/innobase/include/log0log.h b/storage/innobase/include/log0log.h
index 9ba7ca48edc..ae484b36260 100644
--- a/storage/innobase/include/log0log.h
+++ b/storage/innobase/include/log0log.h
@@ -37,13 +37,9 @@ Created 12/9/1995 Heikki Tuuri
#include "univ.i"
#include "dyn0buf.h"
#include "sync0rw.h"
-#include "log0crypt.h"
#include "log0types.h"
#include "os0event.h"
-/** Redo log buffer */
-struct log_t;
-
/** Redo log group */
struct log_group_t;
@@ -275,15 +271,6 @@ objects! */
void
log_check_margins(void);
-/******************************************************//**
-Reads a specified log segment to a buffer. */
-void
-log_group_read_log_seg(
-/*===================*/
- byte* buf, /*!< in: buffer where to read */
- log_group_t* group, /*!< in: log group */
- lsn_t start_lsn, /*!< in: read area start */
- lsn_t end_lsn); /*!< in: read area end */
/********************************************************//**
Sets the field values in group to correspond to a given lsn. For this function
to work, the values must already be correctly initialized to correspond to
@@ -449,9 +436,6 @@ void
log_mem_free(void);
/*==============*/
-/** Redo log system */
-extern log_t* log_sys;
-
/** Whether to generate and require checksums on the redo log pages */
extern my_bool innodb_log_checksums;
@@ -508,6 +492,12 @@ extern my_bool innodb_log_checksums;
#define LOG_CHECKPOINT_LSN 8
#define LOG_CHECKPOINT_OFFSET 16
#define LOG_CHECKPOINT_LOG_BUF_SIZE 24
+/** MariaDB 10.2.5 encrypted redo log encryption key version (32 bits)*/
+#define LOG_CHECKPOINT_CRYPT_KEY 32
+/** MariaDB 10.2.5 encrypted redo log random nonce (32 bits) */
+#define LOG_CHECKPOINT_CRYPT_NONCE 36
+/** MariaDB 10.2.5 encrypted redo log random message (MY_AES_BLOCK_SIZE) */
+#define LOG_CHECKPOINT_CRYPT_MESSAGE 40
/** Offsets of a log file header */
/* @{ */
@@ -538,19 +528,11 @@ or the MySQL version that created the redo log file. */
/** The redo log format identifier corresponding to the current format version.
Stored in LOG_HEADER_FORMAT. */
#define LOG_HEADER_FORMAT_CURRENT 1
+/** Encrypted MariaDB redo log */
+#define LOG_HEADER_FORMAT_ENCRYPTED (1U<<31)
/* @} */
-/** MariaDB Server 10.1 encrypted redo log offsets */
-/* @{ */
-#define LOG_CRYPT_VER (20 + 32 * 9)
-#define LOG_CRYPT_MAX_ENTRIES (5)
-#define LOG_CRYPT_ENTRY_SIZE (4 + 4 + 2 * MY_AES_BLOCK_SIZE)
-#define LOG_CRYPT_SIZE (1 + 1 + \
- (LOG_CRYPT_MAX_ENTRIES * \
- LOG_CRYPT_ENTRY_SIZE))
-/* @} */
-
#define LOG_CHECKPOINT_1 OS_FILE_LOG_BLOCK_SIZE
/* first checkpoint field in the log
header; we write alternately to the
@@ -609,6 +591,12 @@ struct log_group_t{
byte* checkpoint_buf;
/** list of log groups */
UT_LIST_NODE_T(log_group_t) log_groups;
+
+ /** @return whether the redo log is encrypted */
+ bool is_encrypted() const
+ {
+ return((format & LOG_HEADER_FORMAT_ENCRYPTED) != 0);
+ }
};
/** Redo log buffer */
@@ -750,8 +738,17 @@ struct log_t{
byte* checkpoint_buf; /*!< checkpoint header is read to this
buffer */
/* @} */
+
+ /** @return whether the redo log is encrypted */
+ bool is_encrypted() const
+ {
+ return(UT_LIST_GET_FIRST(log_groups)->is_encrypted());
+ }
};
+/** Redo log system */
+extern log_t* log_sys;
+
/** Test if flush order mutex is owned. */
#define log_flush_order_mutex_own() \
mutex_own(&log_sys->log_flush_order_mutex)
diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h
index 6b4f817a9d0..24a83ec2ab1 100644
--- a/storage/innobase/include/log0recv.h
+++ b/storage/innobase/include/log0recv.h
@@ -297,13 +297,4 @@ use these free frames to read in pages when we start applying the
log records to the database. */
extern ulint recv_n_pool_free_frames;
-/******************************************************//**
-Checks the 4-byte checksum to the trailer checksum field of a log
-block. */
-bool
-log_block_checksum_is_ok(
-/*===================================*/
- const byte* block, /*!< in: pointer to a log block */
- bool print_err); /*!< in print error ? */
-
#endif
diff --git a/storage/innobase/log/log0crypt.cc b/storage/innobase/log/log0crypt.cc
index cde9768e78e..09c37459e1f 100644
--- a/storage/innobase/log/log0crypt.cc
+++ b/storage/innobase/log/log0crypt.cc
@@ -22,55 +22,44 @@ Innodb log encrypt/decrypt
Created 11/25/2013 Minli Zhu Google
Modified Jan Lindström jan.lindstrom@mariadb.com
+MDEV-11782: Rewritten for MariaDB 10.2 by Marko Mäkelä, MariaDB Corporation.
*******************************************************/
#include "m_string.h"
#include "log0crypt.h"
-#include <my_crypt.h>
-#include <my_crypt.h>
+#include "my_crypt.h"
-#include "log0log.h"
+#include "log0crypt.h"
#include "srv0start.h" // for srv_start_lsn
#include "log0recv.h" // for recv_sys
-#include "ha_prototypes.h" // IB_LOG_
-
-#include "my_crypt.h"
-
-/* Used for debugging */
-// #define DEBUG_CRYPT 1
-#define UNENCRYPTED_KEY_VER 0
-
-/* If true, enable redo log encryption. */
-extern my_bool srv_encrypt_log;
-
-
-#include <algorithm> // std::sort
-#include <deque>
-
-/* If true, enable redo log encryption. */
-UNIV_INTERN my_bool srv_encrypt_log = FALSE;
-/*
- Sub system type for InnoDB redo log crypto.
- Set and used to validate crypto msg.
-*/
-static const byte redo_log_purpose_byte = 0x02;
+/** innodb_encrypt_log: whether to encrypt the redo log */
+my_bool srv_encrypt_log;
+/** Redo log encryption key ID */
#define LOG_DEFAULT_ENCRYPTION_KEY 1
-/*
- Store this many keys into each checkpoint info
-*/
-static const size_t kMaxSavedKeys = LOG_CRYPT_MAX_ENTRIES;
-
struct crypt_info_t {
- ib_uint64_t checkpoint_no; /*!< checkpoint no */
+ ulint checkpoint_no; /*!< checkpoint no; 32 bits */
uint key_version; /*!< mysqld key version */
- byte crypt_msg[MY_AES_BLOCK_SIZE];
- byte crypt_key[MY_AES_BLOCK_SIZE];
- byte crypt_nonce[MY_AES_BLOCK_SIZE];
+ union {
+ uint32_t u[MY_AES_BLOCK_SIZE / sizeof(uint32_t)];
+ byte b[MY_AES_BLOCK_SIZE];
+ } crypt_msg;
+ union {
+ uint32_t u[MY_AES_BLOCK_SIZE / sizeof(uint32_t)];
+ byte b[MY_AES_BLOCK_SIZE];
+ } crypt_key;
+ union {
+ uint32_t u;
+ byte b[4];
+ } crypt_nonce;
};
-static std::deque<crypt_info_t> crypt_info;
+/** The crypt info */
+static crypt_info_t info;
+
+/** Crypt info when upgrading from 10.1 */
+static crypt_info_t infos[5];
/*********************************************************************//**
Get a log block's start lsn.
@@ -93,145 +82,79 @@ Get crypt info from checkpoint.
@return a crypt info or NULL if not present. */
static
const crypt_info_t*
-get_crypt_info(
-/*===========*/
- ib_uint64_t checkpoint_no)
+get_crypt_info(ulint checkpoint_no)
{
- size_t items = crypt_info.size();
-
/* a log block only stores 4-bytes of checkpoint no */
checkpoint_no &= 0xFFFFFFFF;
- for (size_t i = 0; i < items; i++) {
- struct crypt_info_t* it = &crypt_info[i];
+ for (unsigned i = 0; i < 5; i++) {
+ const crypt_info_t* it = &infos[i];
- if (it->checkpoint_no == checkpoint_no) {
+ if (it->key_version && it->checkpoint_no == checkpoint_no) {
return it;
}
}
/* If checkpoint contains more than one key and we did not
find the correct one use the first one. */
- if (items) {
- return (&crypt_info[0]);
- }
-
- return NULL;
+ return infos;
}
-/*********************************************************************//**
-Get crypt info from log block
-@return a crypt info or NULL if not present. */
-static
-const crypt_info_t*
-get_crypt_info(
-/*===========*/
- const byte* log_block)
-{
- ib_uint64_t checkpoint_no = log_block_get_checkpoint_no(log_block);
- return get_crypt_info(checkpoint_no);
-}
-
-/*********************************************************************//**
-Print checkpoint no from log block and all encryption keys from
-checkpoints if they are present. Used for problem analysis. */
+/** Encrypt or decrypt log blocks.
+@param[in,out] buf log blocks to encrypt or decrypt
+@param[in] size size of the buffer, in bytes
+@param[in] decrypt whether to decrypt instead of encrypting */
+UNIV_INTERN
void
-log_crypt_print_checkpoint_keys(
-/*============================*/
- const byte* log_block)
+log_crypt(byte* buf, ulint size, bool decrypt)
{
- ib_uint64_t checkpoint_no = log_block_get_checkpoint_no(log_block);
-
- if (crypt_info.size()) {
- ib::info() << "Redo log checkpoint encryption: " << checkpoint_no << " [ chk key ]: ";
- for (size_t i = 0; i < crypt_info.size(); i++) {
- struct crypt_info_t* it = &crypt_info[i];
- ib::info() << "[" << it->checkpoint_no
- << "," << it->key_version
- << "]";
- }
- }
-}
+ ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
+ ut_a(info.key_version);
-/*********************************************************************//**
-Call AES CTR to encrypt/decrypt log blocks. */
-static
-Crypt_result
-log_blocks_crypt(
-/*=============*/
- const byte* block, /*!< in: blocks before encrypt/decrypt*/
- ulint size, /*!< in: size of block */
- byte* dst_block, /*!< out: blocks after encrypt/decrypt */
- int what, /*!< in: encrypt or decrypt*/
- const crypt_info_t* crypt_info) /*!< in: crypt info or NULL */
-{
- byte *log_block = (byte*)block;
- Crypt_result rc = MY_AES_OK;
uint dst_len;
- byte aes_ctr_counter[MY_AES_BLOCK_SIZE];
- byte is_encrypt= what == ENCRYPTION_FLAG_ENCRYPT;
- lsn_t lsn = is_encrypt ? log_sys->lsn : srv_start_lsn;
+ uint32_t aes_ctr_iv[MY_AES_BLOCK_SIZE / sizeof(uint32_t)];
+ compile_time_assert(sizeof(uint32_t) == 4);
- const uint src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE;
- for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE) {
- ulint log_block_no = log_block_get_hdr_no(log_block);
- lsn_t log_block_start_lsn = log_block_get_start_lsn(
- lsn, log_block_no);
-
- const crypt_info_t* info = crypt_info == NULL ? get_crypt_info(log_block) :
- crypt_info;
-#ifdef DEBUG_CRYPT
- fprintf(stderr,
- "%s %lu chkpt: %lu key: %u lsn: %lu\n",
- is_encrypt ? "crypt" : "decrypt",
- log_block_no,
- log_block_get_checkpoint_no(log_block),
- info ? info->key_version : 0,
- log_block_start_lsn);
-#endif
- /* If no key is found from checkpoint assume the log_block
- to be unencrypted. If checkpoint contains the encryption key
- compare log_block current checksum, if checksum matches,
- block can't be encrypted. */
- if (info == NULL ||
- info->key_version == UNENCRYPTED_KEY_VER ||
- (log_block_checksum_is_ok(log_block, false) &&
- what == ENCRYPTION_FLAG_DECRYPT)) {
- memcpy(dst_block, log_block, OS_FILE_LOG_BLOCK_SIZE);
- goto next;
- }
+#define LOG_CRYPT_HDR_SIZE 4
- ut_ad(what == ENCRYPTION_FLAG_DECRYPT ? !log_block_checksum_is_ok(log_block, false) :
- log_block_checksum_is_ok(log_block, false));
-
- // Assume log block header is not encrypted
- memcpy(dst_block, log_block, LOG_BLOCK_HDR_SIZE);
-
- // aes_ctr_counter = nonce(3-byte) + start lsn to a log block
- // (8-byte) + lbn (4-byte) + abn
- // (1-byte, only 5 bits are used). "+" means concatenate.
- bzero(aes_ctr_counter, MY_AES_BLOCK_SIZE);
- memcpy(aes_ctr_counter, info->crypt_nonce, 3);
- mach_write_to_8(aes_ctr_counter + 3, log_block_start_lsn);
- mach_write_to_4(aes_ctr_counter + 11, log_block_no);
- bzero(aes_ctr_counter + 15, 1);
-
- int rc;
- rc = encryption_crypt(log_block + LOG_BLOCK_HDR_SIZE, src_len,
- dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
- (unsigned char*)(info->crypt_key), 16,
- aes_ctr_counter, MY_AES_BLOCK_SIZE,
- what | ENCRYPTION_FLAG_NOPAD,
- LOG_DEFAULT_ENCRYPTION_KEY,
- info->key_version);
+ for (const byte* const end = buf + size; buf != end;
+ buf += OS_FILE_LOG_BLOCK_SIZE) {
+ byte dst[OS_FILE_LOG_BLOCK_SIZE - LOG_CRYPT_HDR_SIZE];
+ const ulint log_block_no = log_block_get_hdr_no(buf);
+
+ /* The log block number is not encrypted. */
+ *aes_ctr_iv =
+#ifdef WORDS_BIGENDIAN
+ ~LOG_BLOCK_FLUSH_BIT_MASK
+#else
+ ~(LOG_BLOCK_FLUSH_BIT_MASK >> 24)
+#endif
+ & (*reinterpret_cast<uint32_t*>(dst)
+ = *reinterpret_cast<const uint32_t*>(
+ buf + LOG_BLOCK_HDR_NO));
+#if LOG_BLOCK_HDR_NO + 4 != LOG_CRYPT_HDR_SIZE
+# error "LOG_BLOCK_HDR_NO has been moved; redo log format affected!"
+#endif
+ aes_ctr_iv[1] = info.crypt_nonce.u;
+ mach_write_to_8(reinterpret_cast<byte*>(aes_ctr_iv + 2),
+ log_block_get_start_lsn(
+ decrypt ? srv_start_lsn : log_sys->lsn,
+ log_block_no));
+
+ int rc = encryption_crypt(
+ buf + LOG_CRYPT_HDR_SIZE, sizeof dst, dst, &dst_len,
+ const_cast<byte*>(info.crypt_key.b),
+ sizeof info.crypt_key,
+ reinterpret_cast<byte*>(aes_ctr_iv), sizeof aes_ctr_iv,
+ decrypt
+ ? ENCRYPTION_FLAG_DECRYPT | ENCRYPTION_FLAG_NOPAD
+ : ENCRYPTION_FLAG_ENCRYPT | ENCRYPTION_FLAG_NOPAD,
+ LOG_DEFAULT_ENCRYPTION_KEY,
+ info.key_version);
ut_a(rc == MY_AES_OK);
- ut_a(dst_len == src_len);
-next:
- log_block += OS_FILE_LOG_BLOCK_SIZE;
- dst_block += OS_FILE_LOG_BLOCK_SIZE;
+ ut_a(dst_len == sizeof dst);
+ memcpy(buf + LOG_CRYPT_HDR_SIZE, dst, sizeof dst);
}
-
- return rc;
}
/*********************************************************************//**
@@ -243,216 +166,75 @@ init_crypt_key(
/*===========*/
crypt_info_t* info) /*< in/out: crypt info */
{
- if (info->key_version == UNENCRYPTED_KEY_VER) {
- memset(info->crypt_key, 0, sizeof(info->crypt_key));
- memset(info->crypt_msg, 0, sizeof(info->crypt_msg));
- memset(info->crypt_nonce, 0, sizeof(info->crypt_nonce));
- return true;
- }
-
- byte mysqld_key[MY_AES_MAX_KEY_LENGTH] = {0};
+ byte mysqld_key[MY_AES_MAX_KEY_LENGTH];
uint keylen= sizeof(mysqld_key);
uint rc;
+ compile_time_assert(16 == sizeof info->crypt_key);
+
rc = encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY, info->key_version, mysqld_key, &keylen);
if (rc) {
ib::error()
- << "Redo log crypto: getting mysqld crypto key "
- << "from key version failed err = " << rc
- << " Reason could be that requested key_version "
- << info->key_version
- << "is not found or required encryption "
- << " key management is not found.";
+ << "Obtaining redo log encryption key version "
+ << info->key_version << " failed (" << rc
+ << "). Maybe the key or the required encryption "
+ << " key management was not found.";
return false;
}
uint dst_len;
int err= my_aes_crypt(MY_AES_ECB, ENCRYPTION_FLAG_NOPAD|ENCRYPTION_FLAG_ENCRYPT,
- info->crypt_msg, sizeof(info->crypt_msg), //src, srclen
- info->crypt_key, &dst_len, //dst, &dstlen
- (unsigned char*)&mysqld_key, sizeof(mysqld_key),
- NULL, 0);
+ info->crypt_msg.b, sizeof info->crypt_msg,
+ info->crypt_key.b,
+ &dst_len,
+ mysqld_key, sizeof mysqld_key, NULL, 0);
if (err != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) {
- fprintf(stderr,
- "\nInnodb redo log crypto: getting redo log crypto key "
- "failed err = %d len = %u.\n", err, dst_len);
+ ib::error() << "Getting redo log crypto key failed: err = "
+ << err << ", len = " << dst_len;
return false;
}
return true;
}
-/*********************************************************************//**
-Compare function for checkpoint numbers
-@return true if first checkpoint is larger than second one */
-static
+/** Initialize the redo log encryption key.
+@return whether the operation succeeded */
+UNIV_INTERN
bool
-mysort(const crypt_info_t& i,
- const crypt_info_t& j)
+log_crypt_init()
{
- return i.checkpoint_no > j.checkpoint_no;
-}
+ ut_ad(log_mutex_own());
+ ut_ad(log_sys->is_encrypted());
-/*********************************************************************//**
-Add crypt info to set if it is not already present
-@return true if successfull, false if not- */
-static
-bool
-add_crypt_info(
-/*===========*/
- crypt_info_t* info, /*!< in: crypt info */
- bool checkpoint_read)/*!< in: do we read checkpoint */
-{
- const crypt_info_t* found=NULL;
- /* so that no one is searching array while we modify it */
- ut_ad(mutex_own(&(log_sys->mutex)));
-
- found = get_crypt_info(info->checkpoint_no);
-
- /* If one crypt info is found then we add a new one only if we
- are reading checkpoint from the log. New checkpoints will always
- use the first created crypt info. */
- if (found != NULL &&
- ( found->checkpoint_no == info->checkpoint_no || !checkpoint_read)) {
- // already present...
- return true;
- }
+ info.key_version = encryption_key_get_latest_version(
+ LOG_DEFAULT_ENCRYPTION_KEY);
- if (!init_crypt_key(info)) {
+ if (my_random_bytes(info.crypt_msg.b, sizeof info.crypt_msg)
+ != MY_AES_OK
+ || my_random_bytes(info.crypt_nonce.b, sizeof info.crypt_nonce)
+ != MY_AES_OK) {
+ ib::error() << "innodb_encrypt_log: my_random_bytes() failed";
return false;
}
- crypt_info.push_back(*info);
-
- /* a log block only stores 4-bytes of checkpoint no */
- crypt_info.back().checkpoint_no &= 0xFFFFFFFF;
-
- // keep keys sorted, assuming that last added key will be used most
- std::sort(crypt_info.begin(), crypt_info.end(), mysort);
-
- return true;
-}
-
-/*********************************************************************//**
-Encrypt log blocks. */
-UNIV_INTERN
-Crypt_result
-log_blocks_encrypt(
-/*===============*/
- const byte* block, /*!< in: blocks before encryption */
- const ulint size, /*!< in: size of blocks, must be multiple of a log block */
- byte* dst_block) /*!< out: blocks after encryption */
-{
- return log_blocks_crypt(block, size, dst_block, ENCRYPTION_FLAG_ENCRYPT, NULL);
-}
-
-/*********************************************************************//**
-Set next checkpoint's key version to latest one, and generate current
-key. Key version 0 means no encryption. */
-UNIV_INTERN
-void
-log_crypt_set_ver_and_key(
-/*======================*/
- ib_uint64_t next_checkpoint_no)
-{
- crypt_info_t info;
- info.checkpoint_no = next_checkpoint_no;
-
- if (!srv_encrypt_log) {
- info.key_version = UNENCRYPTED_KEY_VER;
- } else {
- info.key_version = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
- }
-
- if (info.key_version == UNENCRYPTED_KEY_VER) {
- memset(info.crypt_msg, 0, sizeof(info.crypt_msg));
- memset(info.crypt_nonce, 0, sizeof(info.crypt_nonce));
- } else {
- if (my_random_bytes(info.crypt_msg, MY_AES_BLOCK_SIZE) != MY_AES_OK) {
- ib::error()
- << "Redo log crypto: generate "
- << MY_AES_BLOCK_SIZE
- << "-byte random number as crypto msg failed.";
- ut_error;
- }
-
- if (my_random_bytes(info.crypt_nonce, MY_AES_BLOCK_SIZE) != MY_AES_OK) {
- ib::error()
- << "Redo log crypto: generate "
- << MY_AES_BLOCK_SIZE
- << "-byte random number as AES_CTR nonce failed.";
- ut_error;
- }
-
- }
-
- add_crypt_info(&info, false);
+ return init_crypt_key(&info);
}
-/********************************************************
-Encrypt one or more log block before it is flushed to disk */
+/** Encrypt a log segment.
+@param[in] next_checkpoint_no checkpoint number
+@param[in] buf log segment
+@param[in] size size of the buffer, in bytes
+ (integer multiple of log block size) */
UNIV_INTERN
void
-log_encrypt_before_write(
-/*=====================*/
- ib_uint64_t next_checkpoint_no, /*!< in: log group to be flushed */
- byte* block, /*!< in/out: pointer to a log block */
- const ulint size) /*!< in: size of log blocks */
+log_encrypt(
+ ib_uint64_t next_checkpoint_no,
+ byte* buf,
+ const ulint size)
{
- ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
-
- const crypt_info_t* info = get_crypt_info(next_checkpoint_no);
- if (info == NULL) {
- return;
- }
-
- /* If the key is not encrypted or user has requested not to
- encrypt, do not change log block. */
- if (info->key_version == UNENCRYPTED_KEY_VER || !srv_encrypt_log) {
- return;
- }
-
- byte* dst_frame = (byte*)malloc(size);
-
- //encrypt log blocks content
- Crypt_result result = log_blocks_crypt(block, size, dst_frame, ENCRYPTION_FLAG_ENCRYPT, NULL);
-
- if (result == MY_AES_OK) {
- ut_ad(block[0] == dst_frame[0]);
- memcpy(block, dst_frame, size);
- }
- free(dst_frame);
-
- if (unlikely(result != MY_AES_OK)) {
- ut_error;
- }
-}
-
-/********************************************************
-Decrypt a specified log segment after they are read from a log file to a buffer.
-*/
-void
-log_decrypt_after_read(
-/*===================*/
- byte* frame, /*!< in/out: log segment */
- const ulint size) /*!< in: log segment size */
-{
- ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
- byte* dst_frame = (byte*)malloc(size);
-
- // decrypt log blocks content
- Crypt_result result = log_blocks_crypt(frame, size, dst_frame, ENCRYPTION_FLAG_DECRYPT, NULL);
-
- if (result == MY_AES_OK) {
- memcpy(frame, dst_frame, size);
- }
- free(dst_frame);
-
- if (unlikely(result != MY_AES_OK)) {
- ut_error;
- }
+ log_crypt(buf, size);
}
/** Read the MariaDB 10.1 checkpoint crypto (version, msg and iv) info.
@@ -467,13 +249,13 @@ log_crypt_101_read_checkpoint(const byte* buf)
const size_t n = *buf++ == 2 ? std::min(unsigned(*buf++), 5U) : 0;
for (size_t i = 0; i < n; i++) {
- struct crypt_info_t info;
+ struct crypt_info_t& info = infos[i];
info.checkpoint_no = mach_read_from_4(buf);
info.key_version = mach_read_from_4(buf + 4);
- memcpy(info.crypt_msg, buf + 8, MY_AES_BLOCK_SIZE);
- memcpy(info.crypt_nonce, buf + 24, MY_AES_BLOCK_SIZE);
+ memcpy(info.crypt_msg.b, buf + 8, sizeof info.crypt_msg);
+ memcpy(info.crypt_nonce.b, buf + 24, sizeof info.crypt_nonce);
- if (!add_crypt_info(&info, true)) {
+ if (!init_crypt_key(&info)) {
return false;
}
buf += 4 + 4 + 2 * MY_AES_BLOCK_SIZE;
@@ -491,7 +273,8 @@ log_crypt_101_read_block(byte* buf)
{
ut_ad(log_block_calc_checksum_format_0(buf)
!= log_block_get_checksum(buf));
- const crypt_info_t* info = get_crypt_info(buf);
+ const crypt_info_t* info = get_crypt_info(
+ log_block_get_checkpoint_no(buf));
if (!info || info->key_version == 0) {
return false;
@@ -499,27 +282,27 @@ log_crypt_101_read_block(byte* buf)
byte dst[OS_FILE_LOG_BLOCK_SIZE];
uint dst_len;
- byte aes_ctr_counter[MY_AES_BLOCK_SIZE];
+ byte aes_ctr_iv[MY_AES_BLOCK_SIZE];
const uint src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE;
ulint log_block_no = log_block_get_hdr_no(buf);
- lsn_t log_block_start_lsn = log_block_get_start_lsn(
- srv_start_lsn, log_block_no);
/* The log block header is not encrypted. */
memcpy(dst, buf, LOG_BLOCK_HDR_SIZE);
- memcpy(aes_ctr_counter, info->crypt_nonce, 3);
- mach_write_to_8(aes_ctr_counter + 3, log_block_start_lsn);
- mach_write_to_4(aes_ctr_counter + 11, log_block_no);
- aes_ctr_counter[15] = 0;
+ memcpy(aes_ctr_iv, info->crypt_nonce.b, 3);
+ mach_write_to_8(aes_ctr_iv + 3,
+ log_block_get_start_lsn(srv_start_lsn, log_block_no));
+ memcpy(aes_ctr_iv + 11, buf, 4);
+ aes_ctr_iv[11] &= ~(LOG_BLOCK_FLUSH_BIT_MASK >> 24);
+ aes_ctr_iv[15] = 0;
int rc = encryption_crypt(buf + LOG_BLOCK_HDR_SIZE, src_len,
dst + LOG_BLOCK_HDR_SIZE, &dst_len,
- const_cast<byte*>(info->crypt_key),
+ const_cast<byte*>(info->crypt_key.b),
MY_AES_BLOCK_SIZE,
- aes_ctr_counter, MY_AES_BLOCK_SIZE,
+ aes_ctr_iv, MY_AES_BLOCK_SIZE,
ENCRYPTION_FLAG_DECRYPT
| ENCRYPTION_FLAG_NOPAD,
LOG_DEFAULT_ENCRYPTION_KEY,
@@ -535,184 +318,47 @@ log_crypt_101_read_block(byte* buf)
return true;
}
-/*********************************************************************//**
-Writes the crypto (version, msg and iv) info, which has been used for
-log blocks with lsn <= this checkpoint's lsn, to a log header's
-checkpoint buf. */
+/** Add the encryption information to a redo log checkpoint buffer.
+@param[in,out] buf checkpoint buffer */
UNIV_INTERN
void
-log_crypt_write_checkpoint_buf(
-/*===========================*/
- byte* buf) /*!< in/out: checkpoint buffer */
+log_crypt_write_checkpoint_buf(byte* buf)
{
- byte *save = buf;
-
- // Only write kMaxSavedKeys (sort keys to remove oldest)
- std::sort(crypt_info.begin(), crypt_info.end(), mysort);
- while (crypt_info.size() > kMaxSavedKeys) {
- crypt_info.pop_back();
- }
-
- bool encrypted = false;
- for (size_t i = 0; i < crypt_info.size(); i++) {
- const crypt_info_t & it = crypt_info[i];
- if (it.key_version != UNENCRYPTED_KEY_VER) {
- encrypted = true;
- break;
- }
- }
-
- if (encrypted == false) {
- // if no encryption is inuse then zero out
- // crypt data for upward/downward compability
- memset(buf + LOG_CRYPT_VER, 0, LOG_CRYPT_SIZE);
- return;
- }
-
- ib_uint64_t checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
- buf += LOG_CRYPT_VER;
-
- mach_write_to_1(buf + 0, redo_log_purpose_byte);
- mach_write_to_1(buf + 1, crypt_info.size());
- buf += 2;
- for (size_t i = 0; i < crypt_info.size(); i++) {
- struct crypt_info_t* it = &crypt_info[i];
- mach_write_to_4(buf + 0, static_cast<ulint>(it->checkpoint_no));
- mach_write_to_4(buf + 4, it->key_version);
- memcpy(buf + 8, it->crypt_msg, MY_AES_BLOCK_SIZE);
- memcpy(buf + 24, it->crypt_nonce, MY_AES_BLOCK_SIZE);
- buf += LOG_CRYPT_ENTRY_SIZE;
- }
-
-#ifdef DEBUG_CRYPT
- fprintf(stderr, "write chk: %lu [ chk key ]: ", checkpoint_no);
- for (size_t i = 0; i < crypt_info.size(); i++) {
- struct crypt_info_t* it = &crypt_info[i];
- fprintf(stderr, "[ %lu %u ] ",
- it->checkpoint_no,
- it->key_version);
- }
- fprintf(stderr, "\n");
-#else
- (void)checkpoint_no; // unused variable
-#endif
- ut_a((ulint)(buf - save) <= OS_FILE_LOG_BLOCK_SIZE);
+ ut_ad(info.key_version);
+ compile_time_assert(16 == sizeof info.crypt_msg);
+ compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE
+ - LOG_CHECKPOINT_CRYPT_NONCE
+ == sizeof info.crypt_nonce);
+
+ memcpy(buf + LOG_CHECKPOINT_CRYPT_MESSAGE, info.crypt_msg.b,
+ sizeof info.crypt_msg);
+ memcpy(buf + LOG_CHECKPOINT_CRYPT_NONCE, info.crypt_nonce.b,
+ sizeof info.crypt_nonce);
+ mach_write_to_4(buf + LOG_CHECKPOINT_CRYPT_KEY, info.key_version);
}
-/*********************************************************************//**
-Read the crypto (version, msg and iv) info, which has been used for
-log blocks with lsn <= this checkpoint's lsn, from a log header's
-checkpoint buf. */
+/** Read the checkpoint crypto (version, msg and iv) info.
+@param[in] buf checkpoint buffer
+@return whether the operation was successful */
UNIV_INTERN
bool
-log_crypt_read_checkpoint_buf(
-/*===========================*/
- const byte* buf) { /*!< in: checkpoint buffer */
-
- buf += LOG_CRYPT_VER;
-
- byte scheme = buf[0];
- if (scheme != redo_log_purpose_byte) {
- return true;
- }
- buf++;
- size_t n = buf[0];
- buf++;
-
- for (size_t i = 0; i < n; i++) {
- struct crypt_info_t info;
- info.checkpoint_no = mach_read_from_4(buf + 0);
- info.key_version = mach_read_from_4(buf + 4);
- memcpy(info.crypt_msg, buf + 8, MY_AES_BLOCK_SIZE);
- memcpy(info.crypt_nonce, buf + 24, MY_AES_BLOCK_SIZE);
-
- if (!add_crypt_info(&info, true)) {
- return false;
- }
- buf += LOG_CRYPT_ENTRY_SIZE;
- }
-
-#ifdef DEBUG_CRYPT
- fprintf(stderr, "read [ chk key ]: ");
- for (size_t i = 0; i < crypt_info.size(); i++) {
- struct crypt_info_t* it = &crypt_info[i];
- fprintf(stderr, "[ %lu %u ] ",
- it->checkpoint_no,
- it->key_version);
- }
- fprintf(stderr, "\n");
-#endif
- return true;
-}
-
-/********************************************************
-Check is the checkpoint information encrypted. This check
-is based on fact has log group crypt info and based
-on this crypt info was the key version different from
-unencrypted key version. There is no realible way to
-distinguish encrypted log block from corrupted log block,
-but if log block corruption is found this function is
-used to find out if log block is maybe encrypted but
-encryption key, key management plugin or encryption
-algorithm does not match.
-@return TRUE, if log block may be encrypted */
-UNIV_INTERN
-ibool
-log_crypt_block_maybe_encrypted(
-/*============================*/
- const byte* log_block, /*!< in: log block */
- log_crypt_err_t* err_info) /*!< out: error info */
+log_crypt_read_checkpoint_buf(const byte* buf)
{
- ibool maybe_encrypted = FALSE;
- const crypt_info_t* crypt_info;
+ info.checkpoint_no = mach_read_from_4(buf + (LOG_CHECKPOINT_NO + 4));
+ info.key_version = mach_read_from_4(buf + LOG_CHECKPOINT_CRYPT_KEY);
- *err_info = LOG_UNENCRYPTED;
- crypt_info = get_crypt_info(log_block);
-
- if (crypt_info &&
- crypt_info->key_version != UNENCRYPTED_KEY_VER) {
- byte mysqld_key[MY_AES_BLOCK_SIZE] = {0};
- uint keylen= sizeof(mysqld_key);
-
- /* Log block contains crypt info and based on key
- version block could be encrypted. */
- *err_info = LOG_DECRYPT_MAYBE_FAILED;
- maybe_encrypted = TRUE;
-
- if (encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY,
- crypt_info->key_version, mysqld_key, &keylen)) {
- *err_info = LOG_CRYPT_KEY_NOT_FOUND;
- }
- }
+#if MY_AES_BLOCK_SIZE != 16
+# error "MY_AES_BLOCK_SIZE != 16; redo log checkpoint format affected"
+#endif
+ compile_time_assert(16 == sizeof info.crypt_msg);
+ compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE
+ - LOG_CHECKPOINT_CRYPT_NONCE
+ == sizeof info.crypt_nonce);
- return (maybe_encrypted);
-}
+ memcpy(info.crypt_msg.b, buf + LOG_CHECKPOINT_CRYPT_MESSAGE,
+ sizeof info.crypt_msg);
+ memcpy(info.crypt_nonce.b, buf + LOG_CHECKPOINT_CRYPT_NONCE,
+ sizeof info.crypt_nonce);
-/********************************************************
-Print crypt error message to error log */
-UNIV_INTERN
-void
-log_crypt_print_error(
-/*==================*/
- log_crypt_err_t err_info) /*!< out: error info */
-{
- switch(err_info) {
- case LOG_CRYPT_KEY_NOT_FOUND:
- ib::error()
- << "Redo log crypto: getting mysqld crypto key "
- << "from key version failed. Reason could be that "
- << "requested key version is not found or required "
- << "encryption key management plugin is not found.";
- break;
- case LOG_DECRYPT_MAYBE_FAILED:
- ib::error()
- << "Redo log crypto: failed to decrypt log block. "
- << "Reason could be that requested key version is "
- << "not found, required encryption key management "
- << "plugin is not found or configured encryption "
- << "algorithm and/or method does not match.";
- break;
- default:
- ut_error; /* Real bug */
- }
+ return init_crypt_key(&info);
}
diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc
index 1fe87f1250e..ca052138dd9 100644
--- a/storage/innobase/log/log0log.cc
+++ b/storage/innobase/log/log0log.cc
@@ -40,6 +40,7 @@ Created 12/9/1995 Heikki Tuuri
#include "log0log.ic"
#endif
+#include "log0crypt.h"
#include "mem0mem.h"
#include "buf0buf.h"
#include "buf0flu.h"
@@ -56,9 +57,6 @@ Created 12/9/1995 Heikki Tuuri
#include "srv0mon.h"
#include "sync0sync.h"
-/* Used for debugging */
-// #define DEBUG_CRYPT 1
-
/*
General philosophy of InnoDB redo-logs:
@@ -898,7 +896,9 @@ log_group_init(
group->id = id;
group->n_files = n_files;
- group->format = LOG_HEADER_FORMAT_CURRENT;
+ group->format = srv_encrypt_log
+ ? LOG_HEADER_FORMAT_CURRENT | LOG_HEADER_FORMAT_ENCRYPTED
+ : LOG_HEADER_FORMAT_CURRENT;
group->file_size = file_size;
group->space_id = space_id;
group->state = LOG_GROUP_OK;
@@ -987,11 +987,13 @@ log_group_file_header_flush(
ut_ad(!recv_no_log_write);
ut_ad(group->id == 0);
ut_a(nth_file < group->n_files);
+ ut_ad((group->format & ~LOG_HEADER_FORMAT_ENCRYPTED)
+ == LOG_HEADER_FORMAT_CURRENT);
buf = *(group->file_header_bufs + nth_file);
memset(buf, 0, OS_FILE_LOG_BLOCK_SIZE);
- mach_write_to_4(buf + LOG_HEADER_FORMAT, LOG_HEADER_FORMAT_CURRENT);
+ mach_write_to_4(buf + LOG_HEADER_FORMAT, group->format);
mach_write_to_8(buf + LOG_HEADER_START_LSN, start_lsn);
strcpy(reinterpret_cast<char*>(buf) + LOG_HEADER_CREATOR,
LOG_HEADER_CREATOR_CURRENT);
@@ -1115,6 +1117,10 @@ loop:
|| log_block_get_hdr_no(buf)
== log_block_convert_lsn_to_no(start_lsn));
+ if (log_sys->is_encrypted()) {
+ log_crypt(buf, write_len);
+ }
+
/* Calculate the checksums for each log block and write them to
the trailer fields of the log blocks */
@@ -1135,8 +1141,6 @@ loop:
ut_a(next_offset / UNIV_PAGE_SIZE <= ULINT_MAX);
- log_encrypt_before_write(log_sys->next_checkpoint_no,
- buf, write_len);
const ulint page_no
= (ulint) (next_offset / univ_page_size.physical());
@@ -1625,7 +1629,9 @@ log_group_checkpoint(
mach_write_to_8(buf + LOG_CHECKPOINT_NO, log_sys->next_checkpoint_no);
mach_write_to_8(buf + LOG_CHECKPOINT_LSN, log_sys->next_checkpoint_lsn);
- log_crypt_write_checkpoint_buf(buf);
+ if (log_sys->is_encrypted()) {
+ log_crypt_write_checkpoint_buf(buf);
+ }
lsn_offset = log_group_calc_lsn_offset(log_sys->next_checkpoint_lsn,
group);
@@ -1689,30 +1695,16 @@ log_group_header_read(
/** Write checkpoint info to the log header and invoke log_mutex_exit().
@param[in] sync whether to wait for the write to complete */
void
-log_write_checkpoint_info(
- bool sync)
+log_write_checkpoint_info(bool sync)
{
- log_group_t* group;
-
ut_ad(log_mutex_own());
+ ut_ad(!srv_read_only_mode);
- if (!srv_read_only_mode) {
- for (group = UT_LIST_GET_FIRST(log_sys->log_groups);
- group;
- group = UT_LIST_GET_NEXT(log_groups, group)) {
-
- log_group_checkpoint(group);
- }
- }
+ for (log_group_t* group = UT_LIST_GET_FIRST(log_sys->log_groups);
+ group;
+ group = UT_LIST_GET_NEXT(log_groups, group)) {
- /* generate key version and key used to encrypt future blocks,
- *
- * NOTE: the +1 is as the next_checkpoint_no will be updated once
- * the checkpoint info has been written and THEN blocks will be encrypted
- * with new key
- */
- if (srv_encrypt_log) {
- log_crypt_set_ver_and_key(log_sys->next_checkpoint_no + 1);
+ log_group_checkpoint(group);
}
log_mutex_exit();
@@ -1991,78 +1983,6 @@ loop:
}
}
-/******************************************************//**
-Reads a specified log segment to a buffer. */
-void
-log_group_read_log_seg(
-/*===================*/
- byte* buf, /*!< in: buffer where to read */
- log_group_t* group, /*!< in: log group */
- lsn_t start_lsn, /*!< in: read area start */
- lsn_t end_lsn) /*!< in: read area end */
-{
- ulint len;
- lsn_t source_offset;
-
- ut_ad(log_mutex_own());
-
-loop:
- source_offset = log_group_calc_lsn_offset(start_lsn, group);
-
- ut_a(end_lsn - start_lsn <= ULINT_MAX);
- len = (ulint) (end_lsn - start_lsn);
-
- ut_ad(len != 0);
-
- if ((source_offset % group->file_size) + len > group->file_size) {
-
- /* If the above condition is true then len (which is ulint)
- is > the expression below, so the typecast is ok */
- len = (ulint) (group->file_size -
- (source_offset % group->file_size));
- }
-
- log_sys->n_log_ios++;
-
- MONITOR_INC(MONITOR_LOG_IO);
-
- ut_a(source_offset / UNIV_PAGE_SIZE <= ULINT_MAX);
-
- const ulint page_no
- = (ulint) (source_offset / univ_page_size.physical());
-
- fil_io(IORequestLogRead, true,
- page_id_t(group->space_id, page_no),
- univ_page_size,
- (ulint) (source_offset % univ_page_size.physical()),
- len, buf, NULL);
-
-#ifdef DEBUG_CRYPT
- fprintf(stderr, "BEFORE DECRYPT: block: %lu checkpoint: %lu %.8lx %.8lx offset %lu\n",
- log_block_get_hdr_no(buf),
- log_block_get_checkpoint_no(buf),
- log_block_calc_checksum(buf),
- log_block_get_checksum(buf), source_offset);
-#endif
-
- log_decrypt_after_read(buf, len);
-
-#ifdef DEBUG_CRYPT
- fprintf(stderr, "AFTER DECRYPT: block: %lu checkpoint: %lu %.8lx %.8lx\n",
- log_block_get_hdr_no(buf),
- log_block_get_checkpoint_no(buf),
- log_block_calc_checksum(buf),
- log_block_get_checksum(buf));
-#endif
- start_lsn += len;
- buf += len;
-
- if (start_lsn != end_lsn) {
-
- goto loop;
- }
-}
-
/**
Checks that there is enough free space in the log to start a new query step.
Flushes the log buffer or makes a new checkpoint if necessary. NOTE: this
diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc
index 8c6080d4d7e..7c4a7f70945 100644
--- a/storage/innobase/log/log0recv.cc
+++ b/storage/innobase/log/log0recv.cc
@@ -688,6 +688,99 @@ recv_sys_debug_free(void)
mutex_exit(&(recv_sys->mutex));
}
+/** Read a log segment to a buffer.
+@param[out] buf buffer
+@param[in] group redo log files
+@param[in] start_lsn read area start
+@param[in] end_lsn read area end
+@return valid end_lsn */
+static
+lsn_t
+log_group_read_log_seg(
+ byte* buf,
+ const log_group_t* group,
+ lsn_t start_lsn,
+ lsn_t end_lsn)
+{
+ ulint len;
+ lsn_t source_offset;
+
+ ut_ad(log_mutex_own());
+
+loop:
+ source_offset = log_group_calc_lsn_offset(start_lsn, group);
+
+ ut_a(end_lsn - start_lsn <= ULINT_MAX);
+ len = (ulint) (end_lsn - start_lsn);
+
+ ut_ad(len != 0);
+
+ const bool at_eof = (source_offset % group->file_size) + len
+ > group->file_size;
+ if (at_eof) {
+ /* If the above condition is true then len (which is ulint)
+ is > the expression below, so the typecast is ok */
+ len = (ulint) (group->file_size -
+ (source_offset % group->file_size));
+ }
+
+ log_sys->n_log_ios++;
+
+ MONITOR_INC(MONITOR_LOG_IO);
+
+ ut_a(source_offset / UNIV_PAGE_SIZE <= ULINT_MAX);
+
+ const ulint page_no
+ = (ulint) (source_offset / univ_page_size.physical());
+
+ fil_io(IORequestLogRead, true,
+ page_id_t(group->space_id, page_no),
+ univ_page_size,
+ (ulint) (source_offset % univ_page_size.physical()),
+ len, buf, NULL);
+
+ for (ulint l = 0; l < len; l += OS_FILE_LOG_BLOCK_SIZE,
+ buf += OS_FILE_LOG_BLOCK_SIZE,
+ start_lsn += OS_FILE_LOG_BLOCK_SIZE) {
+ const ulint block_number = log_block_get_hdr_no(buf);
+
+ if (block_number != log_block_convert_lsn_to_no(start_lsn)) {
+ /* Garbage or an incompletely written log block.
+ We will not report any error, because this can
+ happen when InnoDB was killed while it was
+ writing redo log. We simply treat this as an
+ abrupt end of the redo log. */
+ return(start_lsn);
+ }
+
+ if (innodb_log_checksums || group->is_encrypted()) {
+ ulint crc = log_block_calc_checksum_crc32(buf);
+ ulint cksum = log_block_get_checksum(buf);
+
+ if (crc != cksum) {
+ ib::error() << "Invalid log block checksum."
+ << " block: " << block_number
+ << " checkpoint no: "
+ << log_block_get_checkpoint_no(buf)
+ << " expected: " << crc
+ << " found: " << cksum;
+ return(start_lsn);
+ }
+
+ if (group->is_encrypted()) {
+ log_crypt(buf, OS_FILE_LOG_BLOCK_SIZE, true);
+ }
+ }
+ }
+
+ if (start_lsn != end_lsn) {
+
+ goto loop;
+ }
+
+ return(start_lsn);
+}
+
/********************************************************//**
Copies a log segment from the most up-to-date log group to the other log
groups, so that they all contain the latest log data. Also writes the info
@@ -695,8 +788,7 @@ about the latest checkpoint to the groups, and inits the fields in the group
memory structs to up-to-date values. */
static
void
-recv_synchronize_groups(void)
-/*=========================*/
+recv_synchronize_groups()
{
const lsn_t recovered_lsn = recv_sys->recovered_lsn;
@@ -730,8 +822,10 @@ recv_synchronize_groups(void)
over the max checkpoint info, thus making the preservation of max
checkpoint info on disk certain */
- log_write_checkpoint_info(true);
- log_mutex_enter();
+ if (!srv_read_only_mode) {
+ log_write_checkpoint_info(true);
+ log_mutex_enter();
+ }
}
/** Check the consistency of a log header block.
@@ -799,7 +893,7 @@ recv_find_max_checkpoint_0(
buf + LOG_CHECKPOINT_NO);
if (!log_crypt_101_read_checkpoint(buf)) {
- ib::warn() << "Decrypting checkpoint failed";
+ ib::error() << "Decrypting checkpoint failed";
continue;
}
@@ -930,6 +1024,7 @@ recv_find_max_checkpoint(
return(recv_find_max_checkpoint_0(
max_group, max_field));
case LOG_HEADER_FORMAT_CURRENT:
+ case LOG_HEADER_FORMAT_CURRENT | LOG_HEADER_FORMAT_ENCRYPTED:
break;
default:
/* Ensure that the string is NUL-terminated. */
@@ -963,6 +1058,13 @@ recv_find_max_checkpoint(
continue;
}
+ if (group->is_encrypted()
+ && !log_crypt_read_checkpoint_buf(buf)) {
+ ib::error() << "Reading checkpoint"
+ " encryption info failed.";
+ continue;
+ }
+
group->state = LOG_GROUP_OK;
group->lsn = mach_read_from_8(
@@ -972,11 +1074,6 @@ recv_find_max_checkpoint(
checkpoint_no = mach_read_from_8(
buf + LOG_CHECKPOINT_NO);
- if (!log_crypt_read_checkpoint_buf(buf)) {
- ib::error() << "Reading checkpoint encryption info failed.";
- return DB_ERROR;
- }
-
DBUG_PRINT("ib_log",
("checkpoint " UINT64PF " at " LSN_PF
" found in group " ULINTPF,
@@ -1008,28 +1105,6 @@ recv_find_max_checkpoint(
return(DB_SUCCESS);
}
-/** Check the 4-byte checksum to the trailer checksum field of a log
-block.
-@param[in] block log block
-@param[in] print_err whether to report checksum mismatch
-@return whether the checksum matches */
-bool
-log_block_checksum_is_ok(const byte* block, bool print_err)
-{
- bool valid
- = log_block_get_checksum(block) == log_block_calc_checksum(block);
-
- if (!valid && print_err) {
- ib::error() << "Invalid log block checksum."
- << " block: " << log_block_get_hdr_no(block)
- << " checkpoint no: " << log_block_get_checkpoint_no(block)
- << " expected: " << log_block_calc_checksum(block)
- << " found: " << log_block_get_checksum(block);
- }
-
- return(valid || !innodb_log_checksums);
-}
-
/** Try to parse a single log record body and also applies it if
specified.
@param[in] type redo log entry type
@@ -2721,13 +2796,11 @@ recv_scan_log_recs(
dberr_t* err) /*!< out: error code or DB_SUCCESS */
{
const byte* log_block = buf;
- ulint no;
lsn_t scanned_lsn = start_lsn;
bool finished = false;
ulint data_len;
bool more_data = false;
bool apply = recv_sys->mlog_checkpoint_lsn != 0;
- bool maybe_encrypted = false;
ut_ad(start_lsn % OS_FILE_LOG_BLOCK_SIZE == 0);
ut_ad(len % OS_FILE_LOG_BLOCK_SIZE == 0);
@@ -2736,42 +2809,6 @@ recv_scan_log_recs(
do {
ut_ad(!finished);
- no = log_block_get_hdr_no(log_block);
- ulint expected_no = log_block_convert_lsn_to_no(scanned_lsn);
- if (no != expected_no) {
- /* Garbage or an incompletely written log block.
-
- We will not report any error, because this can
- happen when InnoDB was killed while it was
- writing redo log. We simply treat this as an
- abrupt end of the redo log. */
- finished = true;
- break;
- }
-
- if (!log_block_checksum_is_ok(log_block, true)) {
- log_crypt_err_t log_crypt_err;
-
- maybe_encrypted = log_crypt_block_maybe_encrypted(log_block,
- &log_crypt_err);
-
- /* Print checkpoint encryption keys if present */
- log_crypt_print_checkpoint_keys(log_block);
- if (maybe_encrypted) {
- /* Log block maybe encrypted finish processing*/
- log_crypt_print_error(log_crypt_err);
- *err = DB_ERROR;
- return (TRUE);
- }
-
- /* Garbage or an incompletely written log block.
-
- This could be the result of killing the server
- while it was writing this log block. We treat
- this as an abrupt end of the redo log. */
- finished = true;
- break;
- }
if (log_block_get_flush_bit(log_block)) {
/* This block was a start of a log flush operation:
@@ -2972,7 +3009,7 @@ recv_group_scan_log_recs(
- (recv_n_pool_free_frames * srv_buf_pool_instances));
*err = DB_SUCCESS;
- end_lsn = *contiguous_lsn = ut_uint64_align_down(
+ group->scanned_lsn = end_lsn = *contiguous_lsn = ut_uint64_align_down(
*contiguous_lsn, OS_FILE_LOG_BLOCK_SIZE);
do {
@@ -2990,18 +3027,18 @@ recv_group_scan_log_recs(
}
start_lsn = end_lsn;
- end_lsn += RECV_SCAN_SIZE;
-
- log_group_read_log_seg(
- log_sys->buf, group, start_lsn, end_lsn);
- } while (!recv_scan_log_recs(
+ end_lsn = log_group_read_log_seg(
+ log_sys->buf, group, start_lsn,
+ start_lsn + RECV_SCAN_SIZE);
+ } while (end_lsn != start_lsn
+ && !recv_scan_log_recs(
available_mem, &store_to_hash, log_sys->buf,
- RECV_SCAN_SIZE,
+ end_lsn - start_lsn,
checkpoint_lsn,
start_lsn, contiguous_lsn, &group->scanned_lsn, err));
if (recv_sys->found_corrupt_log || recv_sys->found_corrupt_fs) {
- ib::error() << "Found corrupted log when looking checkpoint lsn: "
+ ib::error() << "Found corrupted log when looking for checkpoint lsn: "
<< contiguous_lsn << " error = " << *err;
DBUG_RETURN(false);
}
@@ -3185,8 +3222,7 @@ recv_recovery_from_checkpoint_start(
if (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO) {
- ib::info() << "The user has set SRV_FORCE_NO_LOG_REDO on,"
- " skipping log redo";
+ ib::info() << "innodb_force_recovery=6 skips redo log apply";
return(DB_SUCCESS);
}
@@ -3231,6 +3267,7 @@ recv_recovery_from_checkpoint_start(
log_mutex_exit();
return(recv_log_format_0_recover(checkpoint_lsn));
case LOG_HEADER_FORMAT_CURRENT:
+ case LOG_HEADER_FORMAT_CURRENT | LOG_HEADER_FORMAT_ENCRYPTED:
break;
default:
ut_ad(0);
@@ -3260,14 +3297,12 @@ recv_recovery_from_checkpoint_start(
if (recv_sys->mlog_checkpoint_lsn == 0) {
if (!srv_read_only_mode
&& group->scanned_lsn != checkpoint_lsn) {
- ib::error() << "Ignoring the redo log due to missing"
+ ib::error() << "Missing"
" MLOG_CHECKPOINT between the checkpoint "
<< checkpoint_lsn << " and the end "
<< group->scanned_lsn << ".";
- if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {
- log_mutex_exit();
- return(DB_ERROR);
- }
+ log_mutex_exit();
+ return(DB_ERROR);
}
group->scanned_lsn = checkpoint_lsn;
@@ -3380,10 +3415,6 @@ recv_recovery_from_checkpoint_start(
log_sys->next_checkpoint_lsn = checkpoint_lsn;
log_sys->next_checkpoint_no = checkpoint_no + 1;
- /* here the checkpoint info is written without any redo logging ongoing
- * and next_checkpoint_no is updated directly hence no +1 */
- log_crypt_set_ver_and_key(log_sys->next_checkpoint_no);
-
recv_synchronize_groups();
if (!recv_needed_recovery) {
@@ -3407,8 +3438,7 @@ recv_recovery_from_checkpoint_start(
MONITOR_SET(MONITOR_LSN_CHECKPOINT_AGE,
log_sys->lsn - log_sys->last_checkpoint_lsn);
- log_sys->next_checkpoint_no = checkpoint_no + 1;
- log_crypt_set_ver_and_key(log_sys->next_checkpoint_no);
+ log_sys->next_checkpoint_no = ++checkpoint_no;
mutex_enter(&recv_sys->mutex);
@@ -3546,18 +3576,14 @@ recv_reset_logs(
which we add
LOG_BLOCK_HDR_SIZE */
{
- log_group_t* group;
-
ut_ad(log_mutex_own());
log_sys->lsn = ut_uint64_align_up(lsn, OS_FILE_LOG_BLOCK_SIZE);
- group = UT_LIST_GET_FIRST(log_sys->log_groups);
-
- while (group) {
+ for (log_group_t* group = UT_LIST_GET_FIRST(log_sys->log_groups);
+ group; group = UT_LIST_GET_NEXT(log_groups, group)) {
group->lsn = log_sys->lsn;
group->lsn_offset = LOG_FILE_HDR_SIZE;
- group = UT_LIST_GET_NEXT(log_groups, group);
}
log_sys->buf_next_to_write = 0;
diff --git a/storage/innobase/mysql-test/storage_engine/define_engine.inc b/storage/innobase/mysql-test/storage_engine/define_engine.inc
index 77e384d2351..7d7b0c7407a 100644
--- a/storage/innobase/mysql-test/storage_engine/define_engine.inc
+++ b/storage/innobase/mysql-test/storage_engine/define_engine.inc
@@ -41,9 +41,5 @@ let $default_char_type = CHAR(8);
# e.g. creation of an additional schema or table, etc.
# The cleanup part should be defined in cleanup_engine.inc
-CALL mtr.add_suppression("InnoDB: Resizing redo log from .* to .* pages, LSN=.*");
-CALL mtr.add_suppression("InnoDB: Starting to delete and rewrite log files.");
-CALL mtr.add_suppression("InnoDB: New log files created, LSN=.*");
-
--enable_query_log
--enable_result_log
diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc
index 69d094cdc86..849f45f59dc 100644
--- a/storage/innobase/srv/srv0start.cc
+++ b/storage/innobase/srv/srv0start.cc
@@ -63,7 +63,7 @@ Created 2/16/1996 Heikki Tuuri
#include "fsp0fsp.h"
#include "rem0rec.h"
#include "mtr0mtr.h"
-#include "log0log.h"
+#include "log0crypt.h"
#include "log0recv.h"
#include "page0page.h"
#include "page0cur.h"
@@ -481,6 +481,9 @@ create_log_files(
/* Create a log checkpoint. */
log_mutex_enter();
+ if (log_sys->is_encrypted() && !log_crypt_init()) {
+ return(DB_ERROR);
+ }
ut_d(recv_no_log_write = false);
recv_reset_logs(lsn);
log_mutex_exit();
@@ -536,7 +539,7 @@ create_log_files_rename(
fil_open_log_and_system_tablespace_files();
- ib::warn() << "New log files created, LSN=" << lsn;
+ ib::info() << "New log files created, LSN=" << lsn;
return(err);
}
@@ -1362,17 +1365,24 @@ srv_prepare_to_delete_redo_log_files(
flushed_lsn = log_sys->lsn;
{
- ib::warn warning;
+ ib::info info;
if (srv_log_file_size == 0) {
- warning << "Upgrading redo log: ";
+ info << "Upgrading redo log: ";
+ } else if (n_files != srv_n_log_files
+ || srv_log_file_size
+ != srv_log_file_size_requested) {
+ info << "Resizing redo log from "
+ << n_files << "*" << srv_log_file_size
+ << " to ";
+ } else if (srv_encrypt_log) {
+ info << "Encrypting redo log: ";
} else {
- warning << "Resizing redo log from "
- << n_files << "*"
- << srv_log_file_size << " to ";
+ info << "Removing redo log encryption: ";
}
- warning << srv_n_log_files << "*"
- << srv_log_file_size_requested
- << " pages, LSN=" << flushed_lsn;
+
+ info << srv_n_log_files << "*"
+ << srv_log_file_size_requested
+ << " pages; LSN=" << flushed_lsn;
}
/* Flush the old log files. */
@@ -2185,6 +2195,14 @@ files_checked:
recv_sys->dblwr.pages.clear();
+ if (err == DB_SUCCESS && !srv_read_only_mode) {
+ log_mutex_enter();
+ if (log_sys->is_encrypted() && !log_crypt_init()) {
+ err = DB_ERROR;
+ }
+ log_mutex_exit();
+ }
+
if (err == DB_SUCCESS) {
/* Initialize the change buffer. */
err = dict_boot();
@@ -2349,18 +2367,15 @@ files_checked:
return(srv_init_abort(err));
}
- if (!srv_force_recovery
- && !recv_sys->found_corrupt_log
- && (srv_log_file_size_requested != srv_log_file_size
- || srv_n_log_files_found != srv_n_log_files)) {
- /* Prepare to replace the redo log files. */
-
- if (srv_read_only_mode) {
- ib::error() << "Cannot resize log files"
- " in read-only mode.";
- return(srv_init_abort(DB_READ_ONLY));
- }
-
+ if (srv_force_recovery == SRV_FORCE_NO_LOG_REDO) {
+ /* Completely ignore the redo log. */
+ } else if (srv_read_only_mode) {
+ /* Leave the redo log alone. */
+ } else if (srv_log_file_size_requested == srv_log_file_size
+ && srv_n_log_files_found == srv_n_log_files
+ && log_sys->is_encrypted() == srv_encrypt_log) {
+ /* No need to upgrade or resize the redo log. */
+ } else {
/* Prepare to delete the old redo log files */
flushed_lsn = srv_prepare_to_delete_redo_log_files(i);
@@ -2394,7 +2409,7 @@ files_checked:
/* Free the old log file space. */
log_group_close_all();
- ib::warn() << "Starting to delete and rewrite log"
+ ib::info() << "Starting to delete and rewrite log"
" files.";
srv_log_file_size = srv_log_file_size_requested;