MDEV-11782: Redefine the innodb_encrypt_log format

Write only one encryption key to the checkpoint page.
Use 4 bytes of nonce. Encrypt more of each redo log block,
only skipping the 4-byte field LOG_BLOCK_HDR_NO which the
initialization vector is derived from.

Issue notes, not warning messages for rewriting the redo log files.

recv_recovery_from_checkpoint_finish(): Do not generate any redo log,
because we must avoid that before rewriting the redo log files, or
otherwise a crash during a redo log rewrite (removing or adding
encryption) may end up making the database unrecoverable.
Instead, do these tasks in innobase_start_or_create_for_mysql().

Issue a firm "Missing MLOG_CHECKPOINT" error message. Remove some
unreachable code and duplicated error messages for log corruption.

LOG_HEADER_FORMAT_ENCRYPTED: A flag for identifying an encrypted redo
log format.

log_group_t::is_encrypted(), log_t::is_encrypted(): Determine
if the redo log is in encrypted format.

recv_find_max_checkpoint(): Interpret LOG_HEADER_FORMAT_ENCRYPTED.

srv_prepare_to_delete_redo_log_files(): Display NOTE messages about
adding or removing encryption. Do not issue warnings for redo log
resizing any more.

innobase_start_or_create_for_mysql(): Rebuild the redo logs also when
the encryption changes.

innodb_log_checksums_func_update(): Always use the CRC-32C checksum
if innodb_encrypt_log. If needed, issue a warning
that innodb_encrypt_log implies innodb_log_checksums.

log_group_write_buf(): Compute the checksum on the encrypted
block contents, so that transmission errors or incomplete blocks can be
detected without decrypting.

Rewrite most of the redo log encryption code. Only remember one
encryption key at a time (but remember up to 5 when upgrading from the
MariaDB 10.1 format.)
This commit is contained in:
Marko Mäkelä 2017-02-10 12:11:42 +02:00
parent 743ac7c2d0
commit 2af28a363c
34 changed files with 749 additions and 1289 deletions

View file

@ -4345,10 +4345,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/,
@ -4422,9 +4419,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./

View file

@ -1,19 +0,0 @@
call mtr.add_suppression("InnoDB: New log files created, LSN=.*");
call mtr.add_suppression("InnoDB: Creating foreign key constraint system tables.");
call mtr.add_suppression("InnoDB: Error: Table .*");
CREATE TABLE t1 (
pk bigint auto_increment,
col_int int,
col_int_key int,
col_char char(12),
col_char_key char(12),
primary key (pk),
key (`col_int_key` ),
key (`col_char_key` )
) ENGINE=InnoDB;
CREATE TABLE t2 LIKE t1;
INSERT INTO t1 VALUES (NULL,1,1,'foo','foo'),(NULL,2,2,'bar','bar'),(NULL,3,3,'baz','baz'),(NULL,4,4,'qux','qux');
INSERT INTO t2
SELECT NULL, a1.col_int, a1.col_int_key, a1.col_char, a1.col_char_key
FROM t1 a1, t1 a2, t1 a3, t1 a4, t1 a5, t1 a6, t1 a7, t1 a8, t1 a9, t1 a10;
DROP TABLE t1, t2;

View file

@ -1,53 +0,0 @@
create table t1(c1 bigint not null, b char(200), c varchar(200)) engine=innodb encrypted=yes encryption_key_id=1;
show warnings;
Level Code Message
create procedure innodb_insert_proc (repeat_count int)
begin
declare current_num int;
set current_num = 0;
while current_num < repeat_count do
insert into t1 values(current_num, substring(MD5(RAND()), -64), REPEAT('privatejanprivate',10));
set current_num = current_num + 1;
end while;
end//
commit;
begin;
call innodb_insert_proc(2000);
commit;
update t1 set c1 = c1 +1;
select count(*) from t1;
count(*)
2000
# Kill the server
# ibdata1 yes on expecting NOT FOUND
NOT FOUND /privatejanprivate/ in ibdata1
# t1 yes on expecting NOT FOUND
NOT FOUND /privatejanprivate/ in t1.ibd
# log0 yes on expecting NOT FOUND
NOT FOUND /privatejanprivate/ in ib_logfile0
# log1 yes on expecting NOT FOUND
NOT FOUND /privatejanprivate/ in ib_logfile1
# Restart mysqld --innodb_encrypt_log=0
insert into t1 values(5000, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
insert into t1 values(5001, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
insert into t1 values(5002, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
insert into t1 values(5003, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
insert into t1 values(5004, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
# ibdata1 yes on expecting NOT FOUND
NOT FOUND /privatejanprivate/ in ibdata1
# t1 yes on expecting NOT FOUND
NOT FOUND /privatejanprivate/ in t1.ibd
# log0 yes on expecting NOT FOUND
NOT FOUND /privatejanprivate/ in ib_logfile0
# log1 yes on expecting NOT FOUND
NOT FOUND /privatejanprivate/ in ib_logfile1
# ibdata1 yes on expecting NOT FOUND
NOT FOUND /publicmessage/ in ibdata1
# t1 yes on expecting NOT FOUND
NOT FOUND /publicmessage/ in t1.ibd
# log0 no on expecting FOUND/NOTFOUND depending where insert goes
FOUND /publicmessage/ in ib_logfile0
# log1 no on expecting FOUND/NOTFOUND depending where insert goes
NOT FOUND /publicmessage/ in ib_logfile1
drop procedure innodb_insert_proc;
drop table t1;

View file

@ -0,0 +1,65 @@
#
# MDEV-9011: Redo log encryption does not work
#
#
# MDEV-9422 Encrypted redo log checksum errors
# on restart after killing busy server instance
#
SET GLOBAL innodb_log_checksums=0;
Warnings:
Warning 138 innodb_encrypt_log implies innodb_log_checksums
SELECT @@global.innodb_log_checksums;
@@global.innodb_log_checksums
1
CREATE TABLE t0 (
pk bigint auto_increment,
col_int int,
col_int_key int,
col_char char(12),
col_char_key char(12),
primary key (pk),
key (col_int_key),
key (col_char_key)
) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=1;
CREATE TEMPORARY TABLE t LIKE t0;
INSERT INTO t VALUES
(NULL,1,1,'private','secret'),(NULL,2,2,'sacred','success'),
(NULL,3,3,'story','secure'),(NULL,4,4,'security','sacrament');
SET GLOBAL innodb_flush_log_at_trx_commit=1;
INSERT INTO t0
SELECT NULL, t1.col_int, t1.col_int_key, t1.col_char, t1.col_char_key
FROM t t1, t t2, t t3, t t4, t t5;
# Kill the server
# ibdata1 expecting NOT FOUND
NOT FOUND /private|secret|sacr(ed|ament)|success|story|secur(e|ity)/ in ibdata1
# t0.ibd expecting NOT FOUND
NOT FOUND /private|secret|sacr(ed|ament)|success|story|secur(e|ity)/ in t0.ibd
# ib_logfile0 expecting NOT FOUND
NOT FOUND /private|secret|sacr(ed|ament)|success|story|secur(e|ity)/ in ib_logfile0
# ib_logfile1 expecting NOT FOUND
NOT FOUND /private|secret|sacr(ed|ament)|success|story|secur(e|ity)/ in ib_logfile1
# Restart without redo log encryption
SELECT COUNT(*) FROM t0;
COUNT(*)
1024
CHECK TABLE t0;
Table Op Msg_type Msg_text
test.t0 check status OK
SET GLOBAL innodb_flush_log_at_trx_commit=1;
INSERT INTO t0 VALUES(NULL, 5, 5, 'public', 'gossip');
# Kill the server
# ib_logfile0 expecting NOT FOUND
NOT FOUND /private|secret|sacr(ed|ament)|success|story|secur(e|ity)/ in ib_logfile0
# ib_logfile0 expecting FOUND
FOUND /public|gossip/ in ib_logfile0
# ibdata1 expecting NOT FOUND
NOT FOUND /private|secret|sacr(ed|ament)|success|story|secur(e|ity)|public|gossip/ in ibdata1
# t0.ibd expecting NOT FOUND
NOT FOUND /private|secret|sacr(ed|ament)|success|story|secur(e|ity)|public|gossip/ in t0.ibd
SELECT COUNT(*) FROM t0;
COUNT(*)
1025
CHECK TABLE t0;
Table Op Msg_type Msg_text
test.t0 check status OK
DROP TABLE t0;

View file

@ -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

View file

@ -1,6 +0,0 @@
--innodb-encrypt-log=ON
--plugin-load-add=$FILE_KEY_MANAGEMENT_SO
--loose-file-key-management
--loose-file-key-management-filename=$MYSQL_TEST_DIR/std_data/logkey.txt
--file-key-management-encryption-algorithm=aes_cbc
--innodb-buffer-pool-size=128M

View file

@ -1,41 +0,0 @@
-- source include/have_innodb.inc
-- source include/not_embedded.inc
# test takes very long time on debug build
-- source include/not_debug.inc
-- source include/big_test.inc
-- source filekeys_plugin.inc
call mtr.add_suppression("InnoDB: New log files created, LSN=.*");
call mtr.add_suppression("InnoDB: Creating foreign key constraint system tables.");
call mtr.add_suppression("InnoDB: Error: Table .*");
#
# MDEV-9422: Checksum errors on restart when killing busy instance that uses encrypted XtraDB tables
#
CREATE TABLE t1 (
pk bigint auto_increment,
col_int int,
col_int_key int,
col_char char(12),
col_char_key char(12),
primary key (pk),
key (`col_int_key` ),
key (`col_char_key` )
) ENGINE=InnoDB;
CREATE TABLE t2 LIKE t1;
INSERT INTO t1 VALUES (NULL,1,1,'foo','foo'),(NULL,2,2,'bar','bar'),(NULL,3,3,'baz','baz'),(NULL,4,4,'qux','qux');
INSERT INTO t2
SELECT NULL, a1.col_int, a1.col_int_key, a1.col_char, a1.col_char_key
FROM t1 a1, t1 a2, t1 a3, t1 a4, t1 a5, t1 a6, t1 a7, t1 a8, t1 a9, t1 a10;
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--shutdown_server 0
--source include/wait_until_disconnected.inc
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc
DROP TABLE t1, t2;

View file

@ -1,94 +0,0 @@
-- source include/have_innodb.inc
-- source include/not_embedded.inc
-- source filekeys_plugin.inc
#
# MDEV-9011: Redo log encryption does not work
#
create table t1(c1 bigint not null, b char(200), c varchar(200)) engine=innodb encrypted=yes encryption_key_id=1;
show warnings;
delimiter //;
create procedure innodb_insert_proc (repeat_count int)
begin
declare current_num int;
set current_num = 0;
while current_num < repeat_count do
insert into t1 values(current_num, substring(MD5(RAND()), -64), REPEAT('privatejanprivate',10));
set current_num = current_num + 1;
end while;
end//
delimiter ;//
commit;
begin;
call innodb_insert_proc(2000);
commit;
update t1 set c1 = c1 +1;
select count(*) from t1;
--let $MYSQLD_DATADIR=`select @@datadir`
--let ib1_IBD = $MYSQLD_DATADIR/ibdata1
--let t1_IBD = $MYSQLD_DATADIR/test/t1.ibd
--let log0 = $MYSQLD_DATADIR/ib_logfile0
--let log1 = $MYSQLD_DATADIR/ib_logfile1
--let SEARCH_RANGE = 10000000
--let SEARCH_PATTERN=privatejanprivate
-- source include/kill_mysqld.inc
--echo # ibdata1 yes on expecting NOT FOUND
-- let SEARCH_FILE=$ib1_IBD
-- source include/search_pattern_in_file.inc
--echo # t1 yes on expecting NOT FOUND
-- let SEARCH_FILE=$t1_IBD
-- source include/search_pattern_in_file.inc
--echo # log0 yes on expecting NOT FOUND
-- let SEARCH_FILE=$log0
-- source include/search_pattern_in_file.inc
--echo # log1 yes on expecting NOT FOUND
-- let SEARCH_FILE=$log1
-- source include/search_pattern_in_file.inc
--echo # Restart mysqld --innodb_encrypt_log=0
-- let $restart_parameters=--innodb_encrypt_log=0
-- source include/start_mysqld.inc
insert into t1 values(5000, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
insert into t1 values(5001, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
insert into t1 values(5002, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
insert into t1 values(5003, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
insert into t1 values(5004, substring(MD5(RAND()), -64), REPEAT('publicmessage',10));
--let SEARCH_PATTERN=privatejanprivate
--echo # ibdata1 yes on expecting NOT FOUND
-- let SEARCH_FILE=$ib1_IBD
-- source include/search_pattern_in_file.inc
--echo # t1 yes on expecting NOT FOUND
-- let SEARCH_FILE=$t1_IBD
-- source include/search_pattern_in_file.inc
--echo # log0 yes on expecting NOT FOUND
-- let SEARCH_FILE=$log0
-- source include/search_pattern_in_file.inc
--echo # log1 yes on expecting NOT FOUND
-- let SEARCH_FILE=$log1
-- source include/search_pattern_in_file.inc
--let SEARCH_PATTERN=publicmessage
--echo # ibdata1 yes on expecting NOT FOUND
-- let SEARCH_FILE=$ib1_IBD
-- source include/search_pattern_in_file.inc
--echo # t1 yes on expecting NOT FOUND
-- let SEARCH_FILE=$t1_IBD
-- source include/search_pattern_in_file.inc
--echo # log0 no on expecting FOUND/NOTFOUND depending where insert goes
-- let SEARCH_FILE=$log0
-- source include/search_pattern_in_file.inc
--echo # log1 no on expecting FOUND/NOTFOUND depending where insert goes
-- let SEARCH_FILE=$log1
-- source include/search_pattern_in_file.inc
drop procedure innodb_insert_proc;
drop table t1;

View file

@ -0,0 +1,94 @@
-- source include/have_innodb.inc
-- source include/not_embedded.inc
-- source filekeys_plugin.inc
--echo #
--echo # MDEV-9011: Redo log encryption does not work
--echo #
--echo #
--echo # MDEV-9422 Encrypted redo log checksum errors
--echo # on restart after killing busy server instance
--echo #
--let $MYSQLD_DATADIR=`select @@datadir`
SET GLOBAL innodb_log_checksums=0;
SELECT @@global.innodb_log_checksums;
CREATE TABLE t0 (
pk bigint auto_increment,
col_int int,
col_int_key int,
col_char char(12),
col_char_key char(12),
primary key (pk),
key (col_int_key),
key (col_char_key)
) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=1;
CREATE TEMPORARY TABLE t LIKE t0;
INSERT INTO t VALUES
(NULL,1,1,'private','secret'),(NULL,2,2,'sacred','success'),
(NULL,3,3,'story','secure'),(NULL,4,4,'security','sacrament');
# Force a redo log flush at the next commit.
SET GLOBAL innodb_flush_log_at_trx_commit=1;
INSERT INTO t0
SELECT NULL, t1.col_int, t1.col_int_key, t1.col_char, t1.col_char_key
FROM t t1, t t2, t t3, t t4, t t5;
--source include/kill_mysqld.inc
--let SEARCH_RANGE = 10000000
--let SEARCH_PATTERN=private|secret|sacr(ed|ament)|success|story|secur(e|ity)
--echo # ibdata1 expecting NOT FOUND
-- let SEARCH_FILE=$MYSQLD_DATADIR/ibdata1
-- source include/search_pattern_in_file.inc
--echo # t0.ibd expecting NOT FOUND
-- let SEARCH_FILE=$MYSQLD_DATADIR/test/t0.ibd
-- source include/search_pattern_in_file.inc
--echo # ib_logfile0 expecting NOT FOUND
-- let SEARCH_FILE=$MYSQLD_DATADIR/ib_logfile0
-- source include/search_pattern_in_file.inc
--echo # ib_logfile1 expecting NOT FOUND
-- let SEARCH_FILE=$MYSQLD_DATADIR/ib_logfile1
-- source include/search_pattern_in_file.inc
--echo # Restart without redo log encryption
-- let $restart_parameters=--skip-innodb-encrypt-log --innodb-log-files-in-group=1
-- source include/start_mysqld.inc
SELECT COUNT(*) FROM t0;
CHECK TABLE t0;
# Force a redo log flush at the next commit.
SET GLOBAL innodb_flush_log_at_trx_commit=1;
# If we tested with UPDATE, we would get clear-text redo log for
# encrypted undo log written with the old secret values.
INSERT INTO t0 VALUES(NULL, 5, 5, 'public', 'gossip');
--source include/kill_mysqld.inc
--echo # ib_logfile0 expecting NOT FOUND
-- let SEARCH_FILE=$MYSQLD_DATADIR/ib_logfile0
-- source include/search_pattern_in_file.inc
--let SEARCH_PATTERN=public|gossip
--echo # ib_logfile0 expecting FOUND
-- let SEARCH_FILE=$MYSQLD_DATADIR/ib_logfile0
-- source include/search_pattern_in_file.inc
--let SEARCH_PATTERN=private|secret|sacr(ed|ament)|success|story|secur(e|ity)|public|gossip
--echo # ibdata1 expecting NOT FOUND
-- let SEARCH_FILE=$MYSQLD_DATADIR/ibdata1
-- source include/search_pattern_in_file.inc
--echo # t0.ibd expecting NOT FOUND
-- let SEARCH_FILE=$MYSQLD_DATADIR/test/t0.ibd
-- source include/search_pattern_in_file.inc
--let $restart_parameters=
--source include/start_mysqld.inc
SELECT COUNT(*) FROM t0;
CHECK TABLE t0;
DROP TABLE t0;

View file

@ -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

View file

@ -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

View file

@ -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';

View file

@ -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';

View file

@ -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

View file

@ -340,7 +340,7 @@ WHERE engine='innodb'
AND support IN ('YES', 'DEFAULT', 'ENABLED');
1
1
FOUND /Resizing redo log from 1\*\d+ to 3\*\d+ pages, LSN=\d+/ in mysqld.1.err
FOUND /Resizing redo log from 1\*\d+ to 3\*\d+ pages; LSN=\d+/ in mysqld.1.err
# Cleanup
bak_ib_logfile0
bak_ib_logfile1

View file

@ -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`;

View file

@ -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

View file

@ -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';

View file

@ -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: Log scan aborted at LSN");
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

View file

@ -219,7 +219,7 @@ eval $check_no_innodb;
--source include/start_mysqld.inc
eval $check_yes_innodb;
--source include/shutdown_mysqld.inc
--let SEARCH_PATTERN=Resizing redo log from 1\*\d+ to 3\*\d+ pages, LSN=\d+
--let SEARCH_PATTERN=Resizing redo log from 1\*\d+ to 3\*\d+ pages; LSN=\d+
--source include/search_pattern_in_file.inc
--let $restart_parameters=

View file

@ -160,7 +160,7 @@ call mtr.add_suppression("InnoDB: Cannot read first page in datafile: .*test.*ib
call mtr.add_suppression("InnoDB: Datafile '.*test.*ibd' is corrupted");
call mtr.add_suppression("InnoDB: Cannot replay file rename. Remove either file and try again");
call mtr.add_suppression("InnoDB: Cannot rename.*because the target file exists");
call mtr.add_suppression("InnoDB: Found corrupted log");
call mtr.add_suppression("InnoDB: Log scan aborted at LSN");
# The following are for the --innodb-force-recovery=1 with broken u* tables:
call mtr.add_suppression("InnoDB: Header page consists of zero bytes in datafile: .*u1.ibd");
call mtr.add_suppression("InnoDB: The error means the system cannot find the path specified");

View file

@ -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");
@ -100,7 +97,7 @@ let SEARCH_PATTERN= InnoDB: innodb_read_only prevents crash recovery;
SELECT * FROM t1;
let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
let SEARCH_PATTERN= redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
--source include/search_pattern_in_file.inc
--let $restart_parameters= --debug=d,innodb_log_abort_5
@ -109,7 +106,7 @@ let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
SELECT * FROM t1;
let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
let SEARCH_PATTERN= redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
--source include/search_pattern_in_file.inc
--let $restart_parameters= --innodb-read-only
@ -126,7 +123,7 @@ SELECT * FROM t1;
let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records;
--source include/search_pattern_in_file.inc
let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
let SEARCH_PATTERN= redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
--source include/search_pattern_in_file.inc
--let $restart_parameters= --debug=d,innodb_log_abort_7

View file

@ -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)
{

View file

@ -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"
@ -3748,14 +3748,42 @@ static const char* deprecated_use_trim
/** Update log_checksum_algorithm_ptr with a pointer to the function
corresponding to whether checksums are enabled.
@param[in] check whether redo log block checksums are enabled */
static
void
innodb_log_checksums_func_update(bool check)
@param[in,out] thd client session, or NULL if at startup
@param[in] check whether redo log block checksums are enabled
@return whether redo log block checksums are enabled */
static inline
bool
innodb_log_checksums_func_update(THD* thd, bool check)
{
log_checksum_algorithm_ptr = check
? log_block_calc_checksum_crc32
: log_block_calc_checksum_none;
static const char msg[] = "innodb_encrypt_log implies"
" innodb_log_checksums";
ut_ad(!thd == !srv_was_started);
if (!check) {
check = srv_encrypt_log;
if (!check) {
} else if (thd) {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_UNSUPPORTED, msg);
} else {
sql_print_warning(msg);
}
}
if (thd) {
log_mutex_enter();
log_checksum_algorithm_ptr = check
? log_block_calc_checksum_crc32
: log_block_calc_checksum_none;
log_mutex_exit();
} else {
log_checksum_algorithm_ptr = check
? log_block_calc_checksum_crc32
: log_block_calc_checksum_none;
}
return(check);
}
/****************************************************************//**
@ -4244,7 +4272,8 @@ innobase_change_buffering_inited_ok:
srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_NONE;
}
innodb_log_checksums_func_update(innodb_log_checksums);
innodb_log_checksums = innodb_log_checksums_func_update(
NULL, innodb_log_checksums);
#ifdef HAVE_LINUX_LARGE_PAGES
if ((os_use_large_pages = my_use_large_pages)) {
@ -20229,13 +20258,8 @@ innodb_log_checksums_update(
void* var_ptr,
const void* save)
{
my_bool check = *static_cast<my_bool*>(var_ptr)
= *static_cast<const my_bool*>(save);
/* Make sure we are the only log user */
mutex_enter(&log_sys->mutex);
innodb_log_checksums_func_update(check);
mutex_exit(&log_sys->mutex);
*static_cast<my_bool*>(var_ptr) = innodb_log_checksums_func_update(
thd, *static_cast<const my_bool*>(save));
}
static SHOW_VAR innodb_status_variables_export[]= {

View file

@ -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

View file

@ -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 */
log_crypt_read_checkpoint_buf(const byte* buf);
/********************************************************
Encrypt one or more log block before it is flushed to disk */
/** 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_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;
/********************************************************
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 */
/********************************************************
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

View file

@ -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)

View file

@ -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

View file

@ -22,55 +22,46 @@ 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;
typedef union {
uint32_t words[MY_AES_BLOCK_SIZE / sizeof(uint32_t)];
byte bytes[MY_AES_BLOCK_SIZE];
} aes_block_t;
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];
/** random string for encrypting the key */
aes_block_t crypt_msg;
/** the secret key */
aes_block_t crypt_key;
/** a random string for the per-block initialization vector */
union {
uint32_t word;
byte bytes[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,366 +84,167 @@ 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);
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
ut_a(info.key_version);
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
<< "]";
}
}
}
/*********************************************************************//**
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);
#define LOG_CRYPT_HDR_SIZE 4
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);
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
/* 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;
}
& (*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.word;
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));
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);
int rc = encryption_crypt(
buf + LOG_CRYPT_HDR_SIZE, sizeof dst, dst, &dst_len,
const_cast<byte*>(info.crypt_key.bytes),
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);
if (decrypt) {
std::ostringstream s;
ut_print_buf_hex(s, buf + LOG_CRYPT_HDR_SIZE,
OS_FILE_LOG_BLOCK_SIZE
- LOG_CRYPT_HDR_SIZE);
ib::info() << "S: " << s.str();
std::ostringstream d;
ut_print_buf_hex(d, dst,
OS_FILE_LOG_BLOCK_SIZE
- LOG_CRYPT_HDR_SIZE);
ib::info() << "c: " << d.str();
}
return rc;
memcpy(buf + LOG_CRYPT_HDR_SIZE, dst, sizeof dst);
}
}
/*********************************************************************//**
Generate crypt key from crypt msg.
@return true if successfull, false if not. */
/** Generate crypt key from crypt msg.
@param[in,out] info encryption key
@param[in] upgrade whether to use the key in MariaDB 10.1 format
@return whether the operation was successful */
static
bool
init_crypt_key(
/*===========*/
crypt_info_t* info) /*< in/out: crypt info */
init_crypt_key(crypt_info_t* info, bool upgrade = false)
{
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];
uint keylen = sizeof mysqld_key;
compile_time_assert(16 == sizeof info->crypt_key);
if (uint rc = encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY,
info->key_version, mysqld_key,
&keylen)) {
ib::error()
<< "Obtaining redo log encryption key version "
<< info->key_version << " failed (" << rc
<< "). Maybe the key or the required encryption "
<< " key management plugin was not found.";
return false;
}
byte mysqld_key[MY_AES_MAX_KEY_LENGTH] = {0};
uint keylen= sizeof(mysqld_key);
uint rc;
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.";
return false;
if (upgrade) {
while (keylen < sizeof mysqld_key) {
mysqld_key[keylen++] = 0;
}
}
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);
int err= my_aes_crypt(MY_AES_ECB,
ENCRYPTION_FLAG_NOPAD | ENCRYPTION_FLAG_ENCRYPT,
info->crypt_msg.bytes, sizeof info->crypt_msg,
info->crypt_key.bytes, &dst_len,
mysqld_key, keylen, 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)));
info.key_version = encryption_key_get_latest_version(
LOG_DEFAULT_ENCRYPTION_KEY);
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;
}
if (!init_crypt_key(info)) {
if (info.key_version == ENCRYPTION_KEY_VERSION_INVALID) {
ib::error() << "innodb_encrypt_log: cannot get key version";
info.key_version = 0;
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 (my_random_bytes(info.crypt_msg.bytes, sizeof info.crypt_msg)
!= MY_AES_OK
|| my_random_bytes(info.crypt_nonce.bytes, sizeof info.crypt_nonce)
!= MY_AES_OK) {
ib::error() << "innodb_encrypt_log: my_random_bytes() failed";
return false;
}
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);
}
/********************************************************
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 */
{
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;
}
return init_crypt_key(&info);
}
/** Read the MariaDB 10.1 checkpoint crypto (version, msg and iv) info.
@ -467,13 +259,14 @@ 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.bytes, buf + 8, sizeof info.crypt_msg);
memcpy(info.crypt_nonce.bytes, buf + 24,
sizeof info.crypt_nonce);
if (!add_crypt_info(&info, true)) {
if (!init_crypt_key(&info, true)) {
return false;
}
buf += 4 + 4 + 2 * MY_AES_BLOCK_SIZE;
@ -491,7 +284,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 +293,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.bytes, 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.bytes),
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 +329,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;
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);
// 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);
memcpy(buf + LOG_CHECKPOINT_CRYPT_MESSAGE, info.crypt_msg.bytes,
sizeof info.crypt_msg);
memcpy(buf + LOG_CHECKPOINT_CRYPT_NONCE, info.crypt_nonce.bytes,
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 */
log_crypt_read_checkpoint_buf(const byte* buf)
{
info.checkpoint_no = mach_read_from_4(buf + (LOG_CHECKPOINT_NO + 4));
info.key_version = mach_read_from_4(buf + LOG_CHECKPOINT_CRYPT_KEY);
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");
#if MY_AES_BLOCK_SIZE != 16
# error "MY_AES_BLOCK_SIZE != 16; redo log checkpoint format affected"
#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 */
{
ibool maybe_encrypted = FALSE;
const crypt_info_t* crypt_info;
*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;
}
}
return (maybe_encrypted);
}
/********************************************************
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 */
}
compile_time_assert(16 == sizeof info.crypt_msg);
compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE
- LOG_CHECKPOINT_CRYPT_NONCE
== sizeof info.crypt_nonce);
memcpy(info.crypt_msg.bytes, buf + LOG_CHECKPOINT_CRYPT_MESSAGE,
sizeof info.crypt_msg);
memcpy(info.crypt_nonce.bytes, buf + LOG_CHECKPOINT_CRYPT_NONCE,
sizeof info.crypt_nonce);
return init_crypt_key(&info);
}

View file

@ -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)) {
for (log_group_t* group = UT_LIST_GET_FIRST(log_sys->log_groups);
group;
group = UT_LIST_GET_NEXT(log_groups, group)) {
log_group_checkpoint(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

View file

@ -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;
@ -705,14 +797,9 @@ recv_synchronize_groups(void)
const lsn_t start_lsn = ut_uint64_align_down(recovered_lsn,
OS_FILE_LOG_BLOCK_SIZE);
const lsn_t end_lsn = ut_uint64_align_up(recovered_lsn,
OS_FILE_LOG_BLOCK_SIZE);
ut_ad(start_lsn != end_lsn);
log_group_read_log_seg(log_sys->buf,
UT_LIST_GET_FIRST(log_sys->log_groups),
start_lsn, end_lsn);
start_lsn, start_lsn + OS_FILE_LOG_BLOCK_SIZE);
ut_ad(UT_LIST_GET_LEN(log_sys->log_groups) == 1);
@ -730,8 +817,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 +888,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 +1019,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 +1053,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 +1069,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 +1100,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
@ -2300,8 +2370,7 @@ bool
recv_parse_log_recs(
lsn_t checkpoint_lsn,
store_t store,
bool apply,
dberr_t* err)
bool apply)
{
byte* ptr;
byte* end_ptr;
@ -2716,62 +2785,22 @@ recv_scan_log_recs(
lsn_t* contiguous_lsn, /*!< in/out: it is known that all log
groups contain contiguous log data up
to this lsn */
lsn_t* group_scanned_lsn,/*!< out: scanning succeeded up to
lsn_t* group_scanned_lsn)/*!< out: scanning succeeded up to
this lsn */
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);
ut_ad(len >= OS_FILE_LOG_BLOCK_SIZE);
*err = DB_SUCCESS;
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:
@ -2852,7 +2881,6 @@ recv_scan_log_recs(
ib::error()
<< "Set innodb_force_recovery"
" to ignore this error.";
*err = DB_ERROR;
return(true);
}
} else if (!recv_sys->found_corrupt_log) {
@ -2888,25 +2916,13 @@ recv_scan_log_recs(
if (more_data && !recv_sys->found_corrupt_log) {
/* Try to parse more log records */
bool parse_finished = false;
dberr_t parse_err = DB_SUCCESS;
parse_finished = recv_parse_log_recs(checkpoint_lsn,
*store_to_hash, apply,
&parse_err);
if (parse_err != DB_SUCCESS) {
ib::info() << "Parsing more log records failed checkpoint_lsn "
<< checkpoint_lsn << " error " << parse_err;
parse_finished = true;
}
if (parse_finished) {
if (recv_parse_log_recs(checkpoint_lsn,
*store_to_hash, apply)) {
ut_ad(recv_sys->found_corrupt_log
|| recv_sys->found_corrupt_fs
|| recv_sys->mlog_checkpoint_lsn
== recv_sys->recovered_lsn);
*err = parse_err;
return(true);
}
@ -2932,15 +2948,13 @@ Parses and hashes the log records if new data found.
until which all redo log has been scanned
@param[in] last_phase whether changes
can be applied to the tablespaces
@param[out] err DB_SUCCESS or error code
@return whether rescan is needed (not everything was stored) */
static
bool
recv_group_scan_log_recs(
log_group_t* group,
lsn_t* contiguous_lsn,
bool last_phase,
dberr_t* err)
bool last_phase)
{
DBUG_ENTER("recv_group_scan_log_recs");
DBUG_ASSERT(!last_phase || recv_sys->mlog_checkpoint_lsn > 0);
@ -2971,38 +2985,33 @@ recv_group_scan_log_recs(
* (buf_pool_get_n_pages()
- (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 {
if (*err != DB_SUCCESS) {
DBUG_RETURN(false);
}
if (last_phase && store_to_hash == STORE_NO) {
store_to_hash = STORE_IF_EXISTS;
/* We must not allow change buffer
merge here, because it would generate
redo log records before we have
finished the redo log scan. */
*err = recv_apply_hashed_log_recs(FALSE);
if (recv_apply_hashed_log_recs(FALSE) != DB_SUCCESS) {
DBUG_RETURN(false);
}
}
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));
start_lsn, contiguous_lsn, &group->scanned_lsn));
if (recv_sys->found_corrupt_log || recv_sys->found_corrupt_fs) {
ib::error() << "Found corrupted log when looking checkpoint lsn: "
<< contiguous_lsn << " error = " << *err;
DBUG_RETURN(false);
}
@ -3185,8 +3194,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 +3239,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);
@ -3240,7 +3249,7 @@ recv_recovery_from_checkpoint_start(
}
/* Look for MLOG_CHECKPOINT. */
recv_group_scan_log_recs(group, &contiguous_lsn, false, &err);
recv_group_scan_log_recs(group, &contiguous_lsn, false);
/* The first scan should not have stored or applied any records. */
ut_ad(recv_sys->n_addrs == 0);
ut_ad(!recv_sys->found_corrupt_fs);
@ -3252,22 +3261,19 @@ recv_recovery_from_checkpoint_start(
if (recv_sys->found_corrupt_log && !srv_force_recovery) {
log_mutex_exit();
ib::error() << "Found corrupted log when looking checkpoint lsn: "
<< contiguous_lsn << " error = " << err;
ib::warn() << "Log scan aborted at LSN " << contiguous_lsn;
return(DB_ERROR);
}
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;
@ -3275,13 +3281,11 @@ recv_recovery_from_checkpoint_start(
} else {
contiguous_lsn = checkpoint_lsn;
rescan = recv_group_scan_log_recs(
group, &contiguous_lsn, false, &err);
group, &contiguous_lsn, false);
if ((recv_sys->found_corrupt_log && !srv_force_recovery)
|| recv_sys->found_corrupt_fs) {
log_mutex_exit();
ib::error() << "Found corrupted log in lsn:"
<< contiguous_lsn << " err = " << err;
return(DB_ERROR);
}
}
@ -3332,15 +3336,12 @@ recv_recovery_from_checkpoint_start(
if (rescan) {
contiguous_lsn = checkpoint_lsn;
recv_group_scan_log_recs(group, &contiguous_lsn, true, &err);
recv_group_scan_log_recs(group, &contiguous_lsn, true);
if ((recv_sys->found_corrupt_log
&& !srv_force_recovery)
|| recv_sys->found_corrupt_fs) {
log_mutex_exit();
ib::error() << "Found corrupted log in lsn:"
<< contiguous_lsn << " err = " << err;
return(DB_ERROR);
}
}
@ -3380,10 +3381,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 +3404,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);
@ -3462,42 +3458,6 @@ recv_recovery_from_checkpoint_finish(void)
/* Free up the flush_rbt. */
buf_flush_free_flush_rbt();
/* Validate a few system page types that were left uninitialized
by older versions of MySQL. */
mtr_t mtr;
buf_block_t* block;
mtr.start();
mtr.set_sys_modified();
/* Bitmap page types will be reset in buf_dblwr_check_block()
without redo logging. */
block = buf_page_get(
page_id_t(IBUF_SPACE_ID, FSP_IBUF_HEADER_PAGE_NO),
univ_page_size, RW_X_LATCH, &mtr);
fil_block_check_type(block, FIL_PAGE_TYPE_SYS, &mtr);
/* Already MySQL 3.23.53 initialized FSP_IBUF_TREE_ROOT_PAGE_NO
to FIL_PAGE_INDEX. No need to reset that one. */
block = buf_page_get(
page_id_t(TRX_SYS_SPACE, TRX_SYS_PAGE_NO),
univ_page_size, RW_X_LATCH, &mtr);
fil_block_check_type(block, FIL_PAGE_TYPE_TRX_SYS, &mtr);
block = buf_page_get(
page_id_t(TRX_SYS_SPACE, FSP_FIRST_RSEG_PAGE_NO),
univ_page_size, RW_X_LATCH, &mtr);
fil_block_check_type(block, FIL_PAGE_TYPE_SYS, &mtr);
block = buf_page_get(
page_id_t(TRX_SYS_SPACE, FSP_DICT_HDR_PAGE_NO),
univ_page_size, RW_X_LATCH, &mtr);
fil_block_check_type(block, FIL_PAGE_TYPE_SYS, &mtr);
mtr.commit();
/* Roll back any recovered data dictionary transactions, so
that the data dictionary tables will be free of any locks.
The data dictionary latch should guarantee that there is at
most one data dictionary transaction active at a time. */
if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO) {
trx_rollback_or_clean_recovered(FALSE);
}
}
/********************************************************//**
@ -3546,18 +3506,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;

View file

@ -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

View file

@ -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,35 @@ 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) {
if (srv_encrypt_log
== log_sys->is_encrypted()) {
info << (srv_encrypt_log
? "Resizing encrypted"
: "Resizing");
} else if (srv_encrypt_log) {
info << "Encrypting and resizing";
} else {
info << "Removing encryption"
" and resizing";
}
info << " 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. */
@ -2180,6 +2201,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();
@ -2291,6 +2320,115 @@ files_checked:
recv_recovery_from_checkpoint_finish();
/* Upgrade or resize or rebuild the redo logs before
generating any dirty pages, so that the old redo log
files will not be written to. */
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);
DBUG_EXECUTE_IF("innodb_log_abort_1",
return(srv_init_abort(DB_ERROR)););
/* Prohibit redo log writes from any other
threads until creating a log checkpoint at the
end of create_log_files(). */
ut_d(recv_no_log_write = true);
ut_ad(!buf_pool_check_no_pending_io());
DBUG_EXECUTE_IF("innodb_log_abort_3",
return(srv_init_abort(DB_ERROR)););
/* Stamp the LSN to the data files. */
err = fil_write_flushed_lsn(flushed_lsn);
DBUG_EXECUTE_IF("innodb_log_abort_4", err = DB_ERROR;);
if (err != DB_SUCCESS) {
return(srv_init_abort(err));
}
/* Close and free the redo log files, so that
we can replace them. */
fil_close_log_files(true);
DBUG_EXECUTE_IF("innodb_log_abort_5",
return(srv_init_abort(DB_ERROR)););
/* Free the old log file space. */
log_group_close_all();
ib::info() << "Starting to delete and rewrite log"
" files.";
srv_log_file_size = srv_log_file_size_requested;
err = create_log_files(
logfilename, dirnamelen, flushed_lsn,
logfile0);
if (err == DB_SUCCESS) {
err = create_log_files_rename(
logfilename, dirnamelen, flushed_lsn,
logfile0);
}
if (err != DB_SUCCESS) {
return(srv_init_abort(err));
}
}
/* Validate a few system page types that were left
uninitialized by older versions of MySQL. */
if (!high_level_read_only) {
mtr_t mtr;
buf_block_t* block;
mtr.start();
mtr.set_sys_modified();
/* Bitmap page types will be reset in
buf_dblwr_check_block() without redo logging. */
block = buf_page_get(
page_id_t(IBUF_SPACE_ID,
FSP_IBUF_HEADER_PAGE_NO),
univ_page_size, RW_X_LATCH, &mtr);
fil_block_check_type(block, FIL_PAGE_TYPE_SYS, &mtr);
/* Already MySQL 3.23.53 initialized
FSP_IBUF_TREE_ROOT_PAGE_NO to
FIL_PAGE_INDEX. No need to reset that one. */
block = buf_page_get(
page_id_t(TRX_SYS_SPACE, TRX_SYS_PAGE_NO),
univ_page_size, RW_X_LATCH, &mtr);
fil_block_check_type(block, FIL_PAGE_TYPE_TRX_SYS,
&mtr);
block = buf_page_get(
page_id_t(TRX_SYS_SPACE,
FSP_FIRST_RSEG_PAGE_NO),
univ_page_size, RW_X_LATCH, &mtr);
fil_block_check_type(block, FIL_PAGE_TYPE_SYS, &mtr);
block = buf_page_get(
page_id_t(TRX_SYS_SPACE, FSP_DICT_HDR_PAGE_NO),
univ_page_size, RW_X_LATCH, &mtr);
fil_block_check_type(block, FIL_PAGE_TYPE_SYS, &mtr);
mtr.commit();
}
/* Roll back any recovered data dictionary transactions, so
that the data dictionary tables will be free of any locks.
The data dictionary latch should guarantee that there is at
most one data dictionary transaction active at a time. */
if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO) {
trx_rollback_or_clean_recovered(FALSE);
}
/* Fix-up truncate of tables in the system tablespace
if server crashed while truncate was active. The non-
system tables are done after tablespace discovery. Do
@ -2344,71 +2482,6 @@ 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));
}
/* Prepare to delete the old redo log files */
flushed_lsn = srv_prepare_to_delete_redo_log_files(i);
DBUG_EXECUTE_IF("innodb_log_abort_1",
return(srv_init_abort(DB_ERROR)););
/* Prohibit redo log writes from any other
threads until creating a log checkpoint at the
end of create_log_files(). */
ut_d(recv_no_log_write = true);
ut_ad(!buf_pool_check_no_pending_io());
DBUG_EXECUTE_IF("innodb_log_abort_3",
return(srv_init_abort(DB_ERROR)););
/* Stamp the LSN to the data files. */
err = fil_write_flushed_lsn(flushed_lsn);
DBUG_EXECUTE_IF("innodb_log_abort_4", err = DB_ERROR;);
if (err != DB_SUCCESS) {
return(srv_init_abort(err));
}
/* Close and free the redo log files, so that
we can replace them. */
fil_close_log_files(true);
DBUG_EXECUTE_IF("innodb_log_abort_5",
return(srv_init_abort(DB_ERROR)););
/* Free the old log file space. */
log_group_close_all();
ib::warn() << "Starting to delete and rewrite log"
" files.";
srv_log_file_size = srv_log_file_size_requested;
err = create_log_files(
logfilename, dirnamelen, flushed_lsn,
logfile0);
if (err == DB_SUCCESS) {
err = create_log_files_rename(
logfilename, dirnamelen, flushed_lsn,
logfile0);
}
if (err != DB_SUCCESS) {
return(srv_init_abort(err));
}
}
recv_recovery_rollback_active();
/* It is possible that file_format tag has never