Skip to content

Commit 161db7c

Browse files
committed
MDEV-8773: InnoDB innochecksum does not work with encrypted or page compressed tables
1 parent b75c003 commit 161db7c

File tree

11 files changed

+310
-69
lines changed

11 files changed

+310
-69
lines changed

extra/innochecksum.cc

Lines changed: 133 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ The parts not included are excluded by #ifndef UNIV_INNOCHECKSUM. */
6262
#include "fsp0fsp.h" /* fsp_flags_get_page_size() &
6363
fsp_flags_get_zip_size() */
6464
#include "ut0crc32.h" /* ut_crc32_init() */
65+
#include "fsp0pagecompress.h" /* fil_get_compression_alg_name */
6566

6667
#ifdef UNIV_NONINL
6768
# include "fsp0fsp.ic"
@@ -109,6 +110,8 @@ int n_fil_page_type_xdes;
109110
int n_fil_page_type_blob;
110111
int n_fil_page_type_zblob;
111112
int n_fil_page_type_other;
113+
int n_fil_page_type_page_compressed;
114+
int n_fil_page_type_page_compressed_encrypted;
112115

113116
int n_fil_page_max_index_id;
114117

@@ -152,6 +155,8 @@ struct per_index_stats {
152155

153156
std::map<unsigned long long, per_index_stats> index_ids;
154157

158+
bool encrypted = false;
159+
155160
/* Get the page size of the filespace from the filespace header. */
156161
static
157162
my_bool
@@ -197,6 +202,8 @@ get_page_size(
197202
{
198203
compressed= true;
199204
}
205+
206+
200207
return TRUE;
201208
}
202209

@@ -515,6 +522,18 @@ parse_page(
515522
}
516523
n_fil_page_type_zblob++;
517524
break;
525+
case FIL_PAGE_PAGE_COMPRESSED:
526+
if (per_page_details) {
527+
printf("FIL_PAGE_PAGE_COMPRESSED\n");
528+
}
529+
n_fil_page_type_page_compressed++;
530+
break;
531+
case FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED:
532+
if (per_page_details) {
533+
printf("FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED\n");
534+
}
535+
n_fil_page_type_page_compressed_encrypted++;
536+
break;
518537
default:
519538
if (per_page_details) {
520539
printf("FIL_PAGE_TYPE_OTHER\n");
@@ -604,6 +623,8 @@ print_stats()
604623
printf("%d\tFIL_PAGE_TYPE_XDES\n", n_fil_page_type_xdes);
605624
printf("%d\tFIL_PAGE_TYPE_BLOB\n", n_fil_page_type_blob);
606625
printf("%d\tFIL_PAGE_TYPE_ZBLOB\n", n_fil_page_type_zblob);
626+
printf("%d\tFIL_PAGE_PAGE_COMPRESSED\n", n_fil_page_type_page_compressed);
627+
printf("%d\tFIL_PAGE_PAGE_COMPRESSED_ENCRYPTED\n", n_fil_page_type_page_compressed_encrypted);
607628
printf("%d\tother\n", n_fil_page_type_other);
608629
printf("%d\tmax index_id\n", n_fil_page_max_index_id);
609630
printf("undo type: %d insert, %d update, %d other\n",
@@ -791,7 +812,9 @@ int main(int argc, char **argv)
791812
while (!feof(f))
792813
{
793814
int page_ok = 1;
815+
794816
bytes= fread(buf, 1, physical_page_size, f);
817+
795818
if (!bytes && feof(f))
796819
{
797820
print_stats();
@@ -809,58 +832,126 @@ int main(int argc, char **argv)
809832
return 1;
810833
}
811834

812-
if (compressed) {
813-
/* compressed pages */
814-
if (!page_zip_verify_checksum(buf, physical_page_size)) {
815-
fprintf(stderr, "Fail; page %lu invalid (fails compressed page checksum).\n", ct);
816-
if (!skip_corrupt)
817-
{
818-
free(big_buf);
819-
free(big_xdes);
820-
return 1;
821-
}
822-
page_ok = 0;
823-
}
835+
ulint page_type = mach_read_from_2(buf+FIL_PAGE_TYPE);
836+
ulint key_version = mach_read_from_4(buf + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
837+
838+
if (key_version && page_type != FIL_PAGE_PAGE_COMPRESSED) {
839+
encrypted = true;
824840
} else {
841+
encrypted = false;
842+
}
843+
844+
ulint comp_method = 0;
825845

826-
/* check the "stored log sequence numbers" */
827-
logseq= mach_read_from_4(buf + FIL_PAGE_LSN + 4);
828-
logseqfield= mach_read_from_4(buf + logical_page_size - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
846+
if (encrypted) {
847+
comp_method = mach_read_from_2(buf+FIL_PAGE_DATA+FIL_PAGE_COMPRESSED_SIZE);
848+
} else {
849+
comp_method = mach_read_from_8(buf+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
850+
}
851+
852+
ulint comp_size = mach_read_from_2(buf+FIL_PAGE_DATA);
853+
ib_uint32_t encryption_checksum = mach_read_from_4(buf+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4);
854+
855+
856+
if (page_type == FIL_PAGE_PAGE_COMPRESSED) {
857+
/* Page compressed tables do not have any checksum */
829858
if (debug)
830-
printf("page %lu: log sequence number: first = %lu; second = %lu\n", ct, logseq, logseqfield);
831-
if (logseq != logseqfield)
832-
{
833-
fprintf(stderr, "Fail; page %lu invalid (fails log sequence number check)\n", ct);
834-
if (!skip_corrupt)
835-
{
836-
free(big_buf);
837-
free(big_xdes);
838-
return 1;
859+
fprintf(stderr, "Page %lu page compressed with method %s real_size %lu\n", ct,
860+
fil_get_compression_alg_name(comp_method), comp_size);
861+
page_ok = 1;
862+
} else if (compressed) {
863+
/* compressed pages */
864+
ulint crccsum = page_zip_calc_checksum(buf, physical_page_size, SRV_CHECKSUM_ALGORITHM_CRC32);
865+
ulint icsum = page_zip_calc_checksum(buf, physical_page_size, SRV_CHECKSUM_ALGORITHM_INNODB);
866+
867+
if (debug) {
868+
if (key_version != 0) {
869+
fprintf(stderr,
870+
"Page %lu encrypted key_version %lu calculated = %lu; crc32 = %lu; recorded = %u\n",
871+
ct, key_version, icsum, crccsum, encryption_checksum);
872+
}
873+
}
874+
875+
if (encrypted) {
876+
if (encryption_checksum != 0 && crccsum != encryption_checksum && icsum != encryption_checksum) {
877+
if (debug)
878+
fprintf(stderr, "page %lu: compressed: calculated = %lu; crc32 = %lu; recorded = %u\n",
879+
ct, icsum, crccsum, encryption_checksum);
880+
fprintf(stderr, "Fail; page %lu invalid (fails compressed page checksum).\n", ct);
881+
}
882+
} else {
883+
if (!page_zip_verify_checksum(buf, physical_page_size)) {
884+
fprintf(stderr, "Fail; page %lu invalid (fails compressed page checksum).\n", ct);
885+
if (!skip_corrupt)
886+
{
887+
free(big_buf);
888+
free(big_xdes);
889+
return 1;
890+
}
891+
page_ok = 0;
892+
}
893+
}
894+
} else {
895+
if (key_version != 0) {
896+
/* Encrypted page */
897+
if (debug) {
898+
if (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
899+
fprintf(stderr,
900+
"Page %lu page compressed with method %s real_size %lu and encrypted key_version %lu checksum %u\n",
901+
ct, fil_get_compression_alg_name(comp_method), comp_size, key_version, encryption_checksum);
902+
} else {
903+
fprintf(stderr,
904+
"Page %lu encrypted key_version %lu checksum %u\n",
905+
ct, key_version, encryption_checksum);
906+
}
839907
}
840-
page_ok = 0;
841908
}
842909

843-
/* check old method of checksumming */
844-
oldcsum= buf_calc_page_old_checksum(buf);
845-
oldcsumfield= mach_read_from_4(buf + logical_page_size - FIL_PAGE_END_LSN_OLD_CHKSUM);
846-
if (debug)
847-
printf("page %lu: old style: calculated = %lu; recorded = %lu\n", ct, oldcsum, oldcsumfield);
848-
if (oldcsumfield != mach_read_from_4(buf + FIL_PAGE_LSN) && oldcsumfield != oldcsum)
849-
{
850-
fprintf(stderr, "Fail; page %lu invalid (fails old style checksum)\n", ct);
851-
if (!skip_corrupt)
910+
/* Page compressed tables do not contain FIL tailer */
911+
if (page_type != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED && page_type != FIL_PAGE_PAGE_COMPRESSED) {
912+
/* check the "stored log sequence numbers" */
913+
logseq= mach_read_from_4(buf + FIL_PAGE_LSN + 4);
914+
logseqfield= mach_read_from_4(buf + logical_page_size - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
915+
if (debug)
916+
printf("page %lu: log sequence number: first = %lu; second = %lu\n", ct, logseq, logseqfield);
917+
if (logseq != logseqfield)
852918
{
853-
free(big_buf);
854-
free(big_xdes);
855-
return 1;
919+
fprintf(stderr, "Fail; page %lu invalid (fails log sequence number check)\n", ct);
920+
if (!skip_corrupt)
921+
{
922+
free(big_buf);
923+
free(big_xdes);
924+
return 1;
925+
}
926+
page_ok = 0;
927+
}
928+
929+
/* check old method of checksumming */
930+
oldcsum= buf_calc_page_old_checksum(buf);
931+
oldcsumfield= mach_read_from_4(buf + logical_page_size - FIL_PAGE_END_LSN_OLD_CHKSUM);
932+
if (debug)
933+
printf("page %lu: old style: calculated = %lu; recorded = %lu\n", ct, oldcsum, oldcsumfield);
934+
if (oldcsumfield != mach_read_from_4(buf + FIL_PAGE_LSN) && oldcsumfield != oldcsum)
935+
{
936+
fprintf(stderr, "Fail; page %lu invalid (fails old style checksum)\n", ct);
937+
if (!skip_corrupt)
938+
{
939+
free(big_buf);
940+
free(big_xdes);
941+
return 1;
942+
}
943+
page_ok = 0;
856944
}
857-
page_ok = 0;
858945
}
859946

860947
/* now check the new method */
861948
csum= buf_calc_page_new_checksum(buf);
862949
crc32= buf_calc_page_crc32(buf);
863950
csumfield= mach_read_from_4(buf + FIL_PAGE_SPACE_OR_CHKSUM);
951+
952+
if (key_version)
953+
csumfield = encryption_checksum;
954+
864955
if (debug)
865956
printf("page %lu: new style: calculated = %lu; crc32 = %lu; recorded = %lu\n",
866957
ct, csum, crc32, csumfield);
@@ -903,7 +994,10 @@ int main(int argc, char **argv)
903994
continue;
904995
}
905996

906-
parse_page(buf, xdes);
997+
/* Can't parse compressed or/and encrypted pages */
998+
if (page_type != FIL_PAGE_PAGE_COMPRESSED && !encrypted) {
999+
parse_page(buf, xdes);
1000+
}
9071001

9081002
if (verbose)
9091003
{
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
SET GLOBAL innodb_file_format = `Barracuda`;
2+
SET GLOBAL innodb_file_per_table = ON;
3+
set global innodb_compression_algorithm = 1;
4+
# Create and populate a tables
5+
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=4;
6+
CREATE TABLE t2 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED ENCRYPTED=YES ENCRYPTION_KEY_ID=4;
7+
CREATE TABLE t3 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED ENCRYPTED=NO;
8+
CREATE TABLE t4 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB PAGE_COMPRESSED=1;
9+
CREATE TABLE t5 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB PAGE_COMPRESSED=1 ENCRYPTED=YES ENCRYPTION_KEY_ID=4;
10+
# Write file to make mysql-test-run.pl expect the "crash", but don't
11+
# start it until it's told to
12+
# We give 30 seconds to do a clean shutdown because we do not want
13+
# to redo apply the pages of t1.ibd at the time of recovery.
14+
# We want SQL to initiate the first access to t1.ibd.
15+
# Wait until disconnected.
16+
# Run innochecksum on t1
17+
# Run innochecksum on t2
18+
# Run innochecksum on t3
19+
# Run innochecksum on t4
20+
# Run innochecksum on t4
21+
# Write file to make mysql-test-run.pl start up the server again
22+
# Cleanup
23+
DROP TABLE t1, t2, t3, t4, t5;

mysql-test/suite/encryption/r/innodb-bad-key-change.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ SELECT * FROM t2;
4949
ERROR HY000: Got error 192 'Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.' from InnoDB
5050
SHOW WARNINGS;
5151
Level Code Message
52-
Warning 192Table test/t2 in tablespace 7 is encrypted but encryption service or used key_id is not available. Can't continue reading table.
53-
Warning 192Table test/t2 in tablespace 7 is encrypted but encryption service or used key_id is not available. Can't continue reading table.
52+
Warning 192 is encrypted but encryption service or used key_id is not available. Can't continue reading table.
53+
Warning 192 is encrypted but encryption service or used key_id is not available. Can't continue reading table.
5454
Warning 192 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table.
5555
Error 1296 Got error 192 'Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.' from InnoDB
5656
SELECT * FROM t2 where id = 1;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#
2+
# MDEV-8773: InnoDB innochecksum does not work with encrypted or page compressed tables
3+
#
4+
5+
# Don't test under embedded
6+
-- source include/not_embedded.inc
7+
# Require InnoDB
8+
-- source include/have_innodb.inc
9+
-- source include/have_file_key_management_plugin.inc
10+
11+
if (!$INNOCHECKSUM) {
12+
--echo Need innochecksum binary
13+
--die Need innochecksum binary
14+
}
15+
16+
--disable_query_log
17+
let $innodb_compression_algorithm_orig=`SELECT @@innodb_compression_algorithm`;
18+
let $innodb_file_format_orig = `SELECT @@innodb_file_format`;
19+
let $innodb_file_per_table_orig = `SELECT @@innodb_file_per_table`;
20+
--enable_query_log
21+
22+
SET GLOBAL innodb_file_format = `Barracuda`;
23+
SET GLOBAL innodb_file_per_table = ON;
24+
# zlib
25+
set global innodb_compression_algorithm = 1;
26+
27+
--echo # Create and populate a tables
28+
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=4;
29+
CREATE TABLE t2 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED ENCRYPTED=YES ENCRYPTION_KEY_ID=4;
30+
CREATE TABLE t3 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED ENCRYPTED=NO;
31+
CREATE TABLE t4 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB PAGE_COMPRESSED=1;
32+
CREATE TABLE t5 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB PAGE_COMPRESSED=1 ENCRYPTED=YES ENCRYPTION_KEY_ID=4;
33+
34+
--disable_query_log
35+
--let $i = 1000
36+
while ($i)
37+
{
38+
INSERT INTO t1 (b) VALUES (REPEAT('abcdefghijklmnopqrstuvwxyz', 100));
39+
dec $i;
40+
}
41+
INSERT INTO t2 SELECT * FROM t1;
42+
INSERT INTO t3 SELECT * FROM t1;
43+
INSERT INTO t4 SELECT * FROM t1;
44+
INSERT INTO t5 SELECT * FROM t1;
45+
--enable_query_log
46+
47+
let $MYSQLD_DATADIR=`select @@datadir`;
48+
let t1_IBD = $MYSQLD_DATADIR/test/t1.ibd;
49+
let t2_IBD = $MYSQLD_DATADIR/test/t2.ibd;
50+
let t3_IBD = $MYSQLD_DATADIR/test/t3.ibd;
51+
let t4_IBD = $MYSQLD_DATADIR/test/t4.ibd;
52+
let t5_IBD = $MYSQLD_DATADIR/test/t5.ibd;
53+
54+
--echo # Write file to make mysql-test-run.pl expect the "crash", but don't
55+
--echo # start it until it's told to
56+
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
57+
58+
--echo # We give 30 seconds to do a clean shutdown because we do not want
59+
--echo # to redo apply the pages of t1.ibd at the time of recovery.
60+
--echo # We want SQL to initiate the first access to t1.ibd.
61+
shutdown_server 30;
62+
63+
--echo # Wait until disconnected.
64+
--source include/wait_until_disconnected.inc
65+
66+
--echo # Run innochecksum on t1
67+
-- disable_result_log
68+
--exec $INNOCHECKSUM $t1_IBD
69+
70+
--echo # Run innochecksum on t2
71+
72+
--exec $INNOCHECKSUM $t2_IBD
73+
74+
--echo # Run innochecksum on t3
75+
76+
--exec $INNOCHECKSUM $t3_IBD
77+
78+
--echo # Run innochecksum on t4
79+
80+
--exec $INNOCHECKSUM $t4_IBD
81+
82+
--echo # Run innochecksum on t4
83+
84+
--exec $INNOCHECKSUM $t4_IBD
85+
86+
--enable_result_log
87+
88+
--echo # Write file to make mysql-test-run.pl start up the server again
89+
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
90+
--enable_reconnect
91+
--source include/wait_until_connected_again.inc
92+
93+
--echo # Cleanup
94+
DROP TABLE t1, t2, t3, t4, t5;
95+
96+
# reset system
97+
--disable_query_log
98+
EVAL SET GLOBAL innodb_compression_algorithm = $innodb_compression_algorithm_orig;
99+
EVAL SET GLOBAL innodb_file_per_table = $innodb_file_per_table_orig;
100+
EVAL SET GLOBAL innodb_file_format = $innodb_file_format_orig;
101+
--enable_query_log

mysql-test/suite/encryption/t/innodb-bad-key-change.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ INSERT INTO t2 VALUES ('foobar',1,2);
7979

8080
--error ER_GET_ERRMSG
8181
SELECT * FROM t2;
82+
--replace_regex /.*tablespace [0-9]*//
8283
SHOW WARNINGS;
8384
--error ER_GET_ERRMSG
8485
SELECT * FROM t2 where id = 1;

0 commit comments

Comments
 (0)