diff --git a/mysql-test/suite/encryption/r/innodb-bad-key-change.result b/mysql-test/suite/encryption/r/innodb-bad-key-change.result index 47bd7f913a3..e829104b86a 100644 --- a/mysql-test/suite/encryption/r/innodb-bad-key-change.result +++ b/mysql-test/suite/encryption/r/innodb-bad-key-change.result @@ -1,6 +1,11 @@ call mtr.add_suppression("Plugin 'file_key_management' init function returned error"); call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*"); call mtr.add_suppression("Plugin 'file_key_management' registration.*failed"); +call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); +call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); +call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); +call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); +call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); # Start server with keys2.txt SET GLOBAL innodb_file_format = `Barracuda`; @@ -33,3 +38,98 @@ Warning 1812 Tablespace is missing for table 'test/t1' Warning 155 Table test/t1 is encrypted but encryption service or used key_id 2 is not available. Can't continue reading table. Error 1932 Table 'test.t1' doesn't exist in engine DROP TABLE t1; +# Start server with keys.txt +CREATE TABLE t2 (c VARCHAR(8), id int not null primary key, b int, key(b)) ENGINE=InnoDB ENCRYPTED=YES; +INSERT INTO t2 VALUES ('foobar',1,2); + +# Restart server with keys2.txt +SELECT * FROM t2; +Got one of the listed errors +SHOW WARNINGS; +Level Code Message +Warning 155 Table test/t2 in tablespace 7 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Warning 155 Table test/t2 in tablespace 7 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +SELECT * FROM t2 where id = 1; +Got one of the listed errors +SHOW WARNINGS; +Level Code Message +Warning 1812 Tablespace is missing for table 'test/t2' +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +SELECT * FROM t2 where b = 1; +Got one of the listed errors +SHOW WARNINGS; +Level Code Message +Warning 1812 Tablespace is missing for table 'test/t2' +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +INSERT INTO t2 VALUES ('tmp',3,3); +ERROR 42S02: Table 'test.t2' doesn't exist in engine +SHOW WARNINGS; +Level Code Message +Warning 1812 Tablespace is missing for table 'test/t2' +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +DELETE FROM t2 where b = 3; +Got one of the listed errors +SHOW WARNINGS; +Level Code Message +Warning 1812 Tablespace is missing for table 'test/t2' +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +DELETE FROM t2 where id = 3; +Got one of the listed errors +SHOW WARNINGS; +Level Code Message +Warning 1812 Tablespace is missing for table 'test/t2' +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +UPDATE t2 set b = b +1; +Got one of the listed errors +SHOW WARNINGS; +Level Code Message +Warning 1812 Tablespace is missing for table 'test/t2' +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +OPTIMIZE TABLE t2; +Table Op Msg_type Msg_text +test.t2 optimize Warning Tablespace is missing for table 'test/t2' +test.t2 optimize Warning Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +test.t2 optimize Error Table 'test.t2' doesn't exist in engine +test.t2 optimize status Operation failed +SHOW WARNINGS; +Level Code Message +ALTER TABLE t2 ADD COLUMN c INT; +ERROR 42S02: Table 'test.t2' doesn't exist in engine +SHOW WARNINGS; +Level Code Message +Warning 1812 Tablespace is missing for table 'test/t2' +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +ANALYZE TABLE t2; +Table Op Msg_type Msg_text +test.t2 analyze Warning Tablespace is missing for table 'test/t2' +test.t2 analyze Warning Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +test.t2 analyze Error Table 'test.t2' doesn't exist in engine +test.t2 analyze status Operation failed +SHOW WARNINGS; +Level Code Message +TRUNCATE TABLE t2; +ERROR 42S02: Table 'test.t2' doesn't exist in engine +SHOW WARNINGS; +Level Code Message +Warning 1812 Tablespace is missing for table 'test/t2' +Warning 155 Table test/t2 is encrypted but encryption service or used key_id is not available. Can't continue reading table. +Error 1932 Table 'test.t2' doesn't exist in engine +DROP TABLE t2; +ERROR HY000: Table 't2' is read only +SHOW WARNINGS; +Level Code Message +Error 1036 Table 't2' is read only + +# Restart server with keys.txt +DROP TABLE t2; +SHOW WARNINGS; +Level Code Message diff --git a/mysql-test/suite/encryption/t/innodb-bad-key-change.test b/mysql-test/suite/encryption/t/innodb-bad-key-change.test index fe8d807e3c0..316f21c7453 100644 --- a/mysql-test/suite/encryption/t/innodb-bad-key-change.test +++ b/mysql-test/suite/encryption/t/innodb-bad-key-change.test @@ -2,10 +2,24 @@ -- source include/have_file_key_management_plugin.inc # embedded does not support restart -- source include/not_embedded.inc +-- source include/not_valgrind.inc +# Avoid CrashReporter popup on Mac +-- source include/not_crashrep.inc +-- source include/not_windows.inc + +# +# MDEV-8588: Assertion failure in file ha_innodb.cc line 21140 if at least one encrypted +# table exists and encryption service is not available. +# call mtr.add_suppression("Plugin 'file_key_management' init function returned error"); call mtr.add_suppression("InnoDB: Database page corruption on disk or a failed.*"); call mtr.add_suppression("Plugin 'file_key_management' registration.*failed"); +call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted"); +call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match."); +call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file."); +call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem."); +call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*"); --echo --echo # Start server with keys2.txt @@ -43,4 +57,64 @@ SHOW WARNINGS; -- let $restart_parameters=--file-key-management-filename=$MYSQL_TEST_DIR/std_data/keysbad3.txt -- source include/restart_mysqld.inc -DROP TABLE t1; \ No newline at end of file +DROP TABLE t1; + +# +# MDEV-8591: Database page corruption on disk or a failed space, Assertion failure in file buf0buf.cc +# line 2856 on querying a table using wrong default encryption key +# + +--echo # Start server with keys.txt +-- let $restart_parameters=--file-key-management-filename=$MYSQL_TEST_DIR/std_data/keys.txt +-- source include/restart_mysqld.inc + +CREATE TABLE t2 (c VARCHAR(8), id int not null primary key, b int, key(b)) ENGINE=InnoDB ENCRYPTED=YES; +INSERT INTO t2 VALUES ('foobar',1,2); + +--echo +--echo # Restart server with keys2.txt +-- let $restart_parameters=--file-key-management-filename=$MYSQL_TEST_DIR/std_data/keys2.txt +-- source include/restart_mysqld.inc + +--error 1932,1712 +SELECT * FROM t2; +SHOW WARNINGS; +--error 1932,1712 +SELECT * FROM t2 where id = 1; +SHOW WARNINGS; +--error 1932,1712 +SELECT * FROM t2 where b = 1; +SHOW WARNINGS; +--error 1932 +INSERT INTO t2 VALUES ('tmp',3,3); +SHOW WARNINGS; +--error 1932,1712 +DELETE FROM t2 where b = 3; +SHOW WARNINGS; +--error 1932,1712 +DELETE FROM t2 where id = 3; +SHOW WARNINGS; +--error 1932,1712 +UPDATE t2 set b = b +1; +SHOW WARNINGS; +OPTIMIZE TABLE t2; +SHOW WARNINGS; +--error 1932 +ALTER TABLE t2 ADD COLUMN c INT; +SHOW WARNINGS; +ANALYZE TABLE t2; +SHOW WARNINGS; +--error 1932 +TRUNCATE TABLE t2; +SHOW WARNINGS; +--error 1036 +DROP TABLE t2; +SHOW WARNINGS; + +--echo +--echo # Restart server with keys.txt +-- let $restart_parameters=--file-key-management-filename=$MYSQL_TEST_DIR/std_data/keys.txt +-- source include/restart_mysqld.inc + +DROP TABLE t2; +SHOW WARNINGS; diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 92539ce1524..d271dc4dd60 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -2,6 +2,7 @@ Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2014, 2015, MariaDB Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -734,6 +735,20 @@ btr_root_block_get( root_page_no = dict_index_get_page(index); block = btr_block_get(space, zip_size, root_page_no, mode, index, mtr); + + if (!block) { + index->table->is_encrypted = TRUE; + index->table->corrupted = FALSE; + + ib_push_warning(index->table->thd, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name, space); + + return NULL; + } + btr_assert_not_corrupted(block, index); #ifdef UNIV_BTR_DEBUG if (!dict_index_is_ibuf(index)) { @@ -759,8 +774,10 @@ btr_root_get( const dict_index_t* index, /*!< in: index tree */ mtr_t* mtr) /*!< in: mtr */ { - return(buf_block_get_frame(btr_root_block_get(index, RW_X_LATCH, - mtr))); + buf_block_t* root = btr_root_block_get(index, RW_X_LATCH, + mtr); + + return(root ? buf_block_get_frame(root) : NULL); } /**************************************************************//** @@ -775,7 +792,7 @@ btr_height_get( dict_index_t* index, /*!< in: index tree */ mtr_t* mtr) /*!< in/out: mini-transaction */ { - ulint height; + ulint height=0; buf_block_t* root_block; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), @@ -786,13 +803,15 @@ btr_height_get( /* S latches the page */ root_block = btr_root_block_get(index, RW_S_LATCH, mtr); - height = btr_page_get_level(buf_block_get_frame(root_block), mtr); + if (root_block) { + height = btr_page_get_level(buf_block_get_frame(root_block), mtr); - /* Release the S latch on the root page. */ - mtr_memo_release(mtr, root_block, MTR_MEMO_PAGE_S_FIX); + /* Release the S latch on the root page. */ + mtr_memo_release(mtr, root_block, MTR_MEMO_PAGE_S_FIX); #ifdef UNIV_SYNC_DEBUG - sync_thread_reset_level(&root_block->lock); + sync_thread_reset_level(&root_block->lock); #endif /* UNIV_SYNC_DEBUG */ + } return(height); } @@ -1240,7 +1259,7 @@ btr_get_size_and_reserved( { fseg_header_t* seg_header; page_t* root; - ulint n; + ulint n=ULINT_UNDEFINED; ulint dummy; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), @@ -1254,17 +1273,21 @@ btr_get_size_and_reserved( } root = btr_root_get(index, mtr); + *used = 0; - seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; + if (root) { - n = fseg_n_reserved_pages(seg_header, used, mtr); + seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; - if (flag == BTR_TOTAL_SIZE) { - seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; + n = fseg_n_reserved_pages(seg_header, used, mtr); - n += fseg_n_reserved_pages(seg_header, &dummy, mtr); - *used += dummy; + if (flag == BTR_TOTAL_SIZE) { + seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; + n += fseg_n_reserved_pages(seg_header, &dummy, mtr); + *used += dummy; + + } } return(n); diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 1a4eb347cd2..acf9ffc45eb 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -3,6 +3,7 @@ Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -347,7 +348,7 @@ search tuple should be performed in the B-tree. InnoDB does an insert immediately after the cursor. Thus, the cursor may end up on a user record, or on a page infimum record. */ UNIV_INTERN -void +dberr_t btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ @@ -397,6 +398,7 @@ btr_cur_search_to_nth_level( page_cur_t* page_cursor; btr_op_t btr_op; ulint root_height = 0; /* remove warning */ + dberr_t err = DB_SUCCESS; #ifdef BTR_CUR_ADAPT btr_search_t* info; @@ -513,7 +515,7 @@ btr_cur_search_to_nth_level( || mode != PAGE_CUR_LE); btr_cur_n_sea++; - return; + return err; } # endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_ADAPT */ @@ -609,7 +611,21 @@ search_loop: retry_page_get: block = buf_page_get_gen( space, zip_size, page_no, rw_latch, guess, buf_mode, - file, line, mtr); + file, line, mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + goto func_exit; + } if (block == NULL) { /* This must be a search to perform an insert/delete @@ -822,12 +838,14 @@ func_exit: rw_lock_s_lock(&btr_search_latch); } + + return err; } /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN -void +dberr_t btr_cur_open_at_index_side_func( /*============================*/ bool from_left, /*!< in: true if open to the low end, @@ -853,6 +871,8 @@ btr_cur_open_at_index_side_func( mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; + dberr_t err = DB_SUCCESS; + rec_offs_init(offsets_); estimate = latch_mode & BTR_ESTIMATE; @@ -890,11 +910,26 @@ btr_cur_open_at_index_side_func( height = ULINT_UNDEFINED; for (;;) { - buf_block_t* block; - page_t* page; + buf_block_t* block=NULL; + page_t* page=NULL; + block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, - file, line, mtr); + file, line, mtr, &err); + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + goto exit_loop; + } + page = buf_block_get_frame(block); ut_ad(fil_page_get_type(page) == FIL_PAGE_INDEX); ut_ad(index->id == btr_page_get_index_id(page)); @@ -979,9 +1014,12 @@ btr_cur_open_at_index_side_func( page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } + exit_loop: if (heap) { mem_heap_free(heap); } + + return err; } /**********************************************************************//** @@ -1029,10 +1067,25 @@ btr_cur_open_at_rnd_pos_func( for (;;) { buf_block_t* block; page_t* page; + dberr_t err=DB_SUCCESS; block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, - file, line, mtr); + file, line, mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + goto exit_loop; + } + page = buf_block_get_frame(block); ut_ad(fil_page_get_type(page) == FIL_PAGE_INDEX); ut_ad(index->id == btr_page_get_index_id(page)); @@ -1066,6 +1119,7 @@ btr_cur_open_at_rnd_pos_func( page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } + exit_loop: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } @@ -3562,6 +3616,7 @@ btr_estimate_n_rows_in_range_on_level( mtr_t mtr; page_t* page; buf_block_t* block; + dberr_t err=DB_SUCCESS; mtr_start(&mtr); @@ -3572,7 +3627,23 @@ btr_estimate_n_rows_in_range_on_level( silence a debug assertion about this. */ block = buf_page_get_gen(space, zip_size, page_no, RW_S_LATCH, NULL, BUF_GET_POSSIBLY_FREED, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + mtr_commit(&mtr); + goto inexact; + } + page = buf_block_get_frame(block); diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 81e6839b500..bbe93dada4f 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -56,6 +56,7 @@ Created 11/5/1995 Heikki Tuuri #include "srv0mon.h" #include "buf0checksum.h" #include "fil0pagecompress.h" +#include "ha_prototypes.h" #include "ut0byte.h" #include @@ -312,6 +313,18 @@ on the io_type */ ? (counter##_READ) \ : (counter##_WRITTEN)) +/********************************************************************//** +Check if page is maybe compressed, encrypted or both when we encounter +corrupted page. Note that we can't be 100% sure if page is corrupted +or decrypt/decompress just failed. +*/ +static +ibool +buf_page_check_corrupt( +/*===================*/ + buf_page_t* bpage); /*!< in/out: buffer page read from + disk */ + /********************************************************************//** Gets the smallest oldest_modification lsn for any page in the pool. Returns zero if all modified pages have been flushed to disk. @@ -1052,6 +1065,9 @@ buf_block_init( block->page.key_version = 0; block->page.page_encrypted = false; block->page.page_compressed = false; + block->page.encrypted = false; + block->page.stored_checksum = BUF_NO_CHECKSUM_MAGIC; + block->page.calculated_checksum = BUF_NO_CHECKSUM_MAGIC; block->page.real_size = 0; block->page.write_size = 0; block->modify_clock = 0; @@ -2243,7 +2259,7 @@ lookup: /* Page not in buf_pool: needs to be read from file */ ut_ad(!hash_lock); - buf_read_page(space, zip_size, offset); + buf_read_page(space, zip_size, offset, NULL); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); @@ -2718,7 +2734,8 @@ buf_page_get_gen( BUF_GET_IF_IN_POOL_OR_WATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr) /*!< in: mini-transaction */ + mtr_t* mtr, /*!< in: mini-transaction */ + dberr_t* err) /*!< out: error code */ { buf_block_t* block; ulint fold; @@ -2735,6 +2752,11 @@ buf_page_get_gen( ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH) || (rw_latch == RW_NO_LATCH)); + + if (err) { + *err = DB_SUCCESS; + } + #ifdef UNIV_DEBUG switch (mode) { case BUF_GET_NO_LATCH: @@ -2795,6 +2817,8 @@ loop: } if (block == NULL) { + buf_page_t* bpage=NULL; + /* Page not in buf_pool: needs to be read from file */ if (mode == BUF_GET_IF_IN_POOL_OR_WATCH) { @@ -2827,36 +2851,77 @@ loop: return(NULL); } - if (buf_read_page(space, zip_size, offset)) { + if (buf_read_page(space, zip_size, offset, &bpage)) { buf_read_ahead_random(space, zip_size, offset, ibuf_inside(mtr)); retries = 0; } else if (retries < BUF_PAGE_READ_MAX_RETRIES) { ++retries; + + bool corrupted = true; + + if (bpage) { + corrupted = buf_page_check_corrupt(bpage); + } + + /* Do not try again for encrypted pages */ + if (!corrupted) { + ib_mutex_t* pmutex = buf_page_get_mutex(bpage); + buf_pool_mutex_enter(buf_pool); + mutex_enter(pmutex); + buf_page_set_io_fix(bpage, BUF_IO_NONE); + buf_pool_mutex_exit(buf_pool); + mutex_exit(pmutex); + + if (err) { + *err = DB_ENCRYPTED_DECRYPT_FAILED; + } + return (NULL); + } + DBUG_EXECUTE_IF( "innodb_page_corruption_retries", retries = BUF_PAGE_READ_MAX_RETRIES; ); } else { + bool corrupted = true; - fprintf(stderr, "InnoDB: Error: Unable" - " to read tablespace %lu page no" - " %lu into the buffer pool after" - " %lu attempts\n" - "InnoDB: The most probable cause" - " of this error may be that the" - " table has been corrupted.\n" - "InnoDB: You can try to fix this" - " problem by using" - " innodb_force_recovery.\n" - "InnoDB: Please see reference manual" - " for more details.\n" - "InnoDB: Aborting...\n", - space, offset, - BUF_PAGE_READ_MAX_RETRIES); + if (bpage) { + corrupted = buf_page_check_corrupt(bpage); + } - ut_error; + if (corrupted) { + fprintf(stderr, "InnoDB: Error: Unable" + " to read tablespace %lu page no" + " %lu into the buffer pool after" + " %lu attempts\n" + "InnoDB: The most probable cause" + " of this error may be that the" + " table has been corrupted.\n" + "InnoDB: You can try to fix this" + " problem by using" + " innodb_force_recovery.\n" + "InnoDB: Please see reference manual" + " for more details.\n" + "InnoDB: Aborting...\n", + space, offset, + BUF_PAGE_READ_MAX_RETRIES); + + ut_error; + } else { + ib_mutex_t* pmutex = buf_page_get_mutex(bpage); + buf_pool_mutex_enter(buf_pool); + mutex_enter(pmutex); + buf_page_set_io_fix(bpage, BUF_IO_NONE); + buf_pool_mutex_exit(buf_pool); + mutex_exit(pmutex); + + if (err) { + *err = DB_ENCRYPTED_DECRYPT_FAILED; + } + return (NULL); + } } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG @@ -3591,8 +3656,11 @@ buf_page_init_low( bpage->oldest_modification = 0; bpage->write_size = 0; bpage->key_version = 0; + bpage->stored_checksum = BUF_NO_CHECKSUM_MAGIC; + bpage->calculated_checksum = BUF_NO_CHECKSUM_MAGIC; bpage->page_encrypted = false; bpage->page_compressed = false; + bpage->encrypted = false; bpage->real_size = 0; bpage->slot = NULL; @@ -4245,34 +4313,40 @@ buf_mark_space_corrupt( ulint space = bpage->space; ibool ret = TRUE; - /* First unfix and release lock on the bpage */ - buf_pool_mutex_enter(buf_pool); - mutex_enter(buf_page_get_mutex(bpage)); - ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); - ut_ad(bpage->buf_fix_count == 0); + if (!bpage->encrypted) { + /* First unfix and release lock on the bpage */ + buf_pool_mutex_enter(buf_pool); + mutex_enter(buf_page_get_mutex(bpage)); + ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); + ut_ad(bpage->buf_fix_count == 0); - /* Set BUF_IO_NONE before we remove the block from LRU list */ - buf_page_set_io_fix(bpage, BUF_IO_NONE); + /* Set BUF_IO_NONE before we remove the block from LRU list */ + buf_page_set_io_fix(bpage, BUF_IO_NONE); - if (uncompressed) { - rw_lock_x_unlock_gen( - &((buf_block_t*) bpage)->lock, - BUF_IO_READ); + if (uncompressed) { + rw_lock_x_unlock_gen( + &((buf_block_t*) bpage)->lock, + BUF_IO_READ); + } + + mutex_exit(buf_page_get_mutex(bpage)); } - mutex_exit(buf_page_get_mutex(bpage)); - /* Find the table with specified space id, and mark it corrupted */ if (dict_set_corrupted_by_space(space)) { - buf_LRU_free_one_page(bpage); + if (!bpage->encrypted) { + buf_LRU_free_one_page(bpage); + } } else { ret = FALSE; } - ut_ad(buf_pool->n_pend_reads > 0); - buf_pool->n_pend_reads--; + if (!bpage->encrypted) { + ut_ad(buf_pool->n_pend_reads > 0); + buf_pool->n_pend_reads--; - buf_pool_mutex_exit(buf_pool); + buf_pool_mutex_exit(buf_pool); + } return(ret); } @@ -4283,42 +4357,77 @@ corrupted page. Note that we can't be 100% sure if page is corrupted or decrypt/decompress just failed. */ static -void +ibool buf_page_check_corrupt( /*===================*/ - const buf_page_t* bpage) /*!< in/out: buffer page read from disk */ + buf_page_t* bpage) /*!< in/out: buffer page read from disk */ { ulint zip_size = buf_page_get_zip_size(bpage); byte* dst_frame = (zip_size) ? bpage->zip.data : ((buf_block_t*) bpage)->frame; unsigned key_version = bpage->key_version; bool page_compressed = bpage->page_encrypted; + ulint stored_checksum = bpage->stored_checksum; + ulint calculated_checksum = bpage->stored_checksum; bool page_compressed_encrypted = bpage->page_compressed; ulint space_id = mach_read_from_4( dst_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id); fil_space_t* space = fil_space_found_by_id(space_id); + bool corrupted = true; + + if (key_version != 0 || page_compressed_encrypted) { + bpage->encrypted = true; + } if (key_version != 0 || (crypt_data && crypt_data->type != CRYPT_SCHEME_UNENCRYPTED) || page_compressed || page_compressed_encrypted) { - ib_logf(IB_LOG_LEVEL_ERROR, - "Maybe corruption: Block space_id %lu in file %s maybe corrupted.", - space_id, space ? space->name : "NULL"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Page based on contents %s encrypted.", - (key_version == 0 && page_compressed_encrypted == false) ? "not" : "maybe"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Reason could be that key_version %u in page " - "or in crypt_data %p could not be found.", - key_version, crypt_data); - ib_logf(IB_LOG_LEVEL_ERROR, - "Reason could be also that key management plugin is not found or" - "used encryption algorithm or method does not match."); - ib_logf(IB_LOG_LEVEL_ERROR, - "Based on page page compressed %d, compressed and encrypted %d.", - page_compressed, page_compressed_encrypted); + + /* Page is really corrupted if post encryption stored + checksum does not match calculated checksum after page was + read. For pages compressed and then encrypted, there is no + checksum. */ + corrupted = (!page_compressed_encrypted && stored_checksum != calculated_checksum); + + if (corrupted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "%s: Block in space_id %lu in file %s corrupted.", + page_compressed_encrypted ? "Maybe corruption" : "Corruption", + space_id, space ? space->name : "NULL"); + ib_logf(IB_LOG_LEVEL_ERROR, + "Page based on contents %s encrypted.", + (key_version == 0 && page_compressed_encrypted == false) ? "not" : "maybe"); + if (stored_checksum != BUF_NO_CHECKSUM_MAGIC || calculated_checksum != BUF_NO_CHECKSUM_MAGIC) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Page stored checksum %lu but calculated checksum %lu.", + stored_checksum, calculated_checksum); + } + ib_logf(IB_LOG_LEVEL_ERROR, + "Reason could be that key_version %u in page " + "or in crypt_data %p could not be found.", + key_version, crypt_data); + ib_logf(IB_LOG_LEVEL_ERROR, + "Reason could be also that key management plugin is not found or" + " used encryption algorithm or method does not match."); + ib_logf(IB_LOG_LEVEL_ERROR, + "Based on page page compressed %d, compressed and encrypted %d.", + page_compressed, page_compressed_encrypted); + } else { + ib_logf(IB_LOG_LEVEL_ERROR, + "Block in space_id %lu in file %s encrypted.", + space_id, space ? space->name : "NULL"); + ib_logf(IB_LOG_LEVEL_ERROR, + "However key management plugin or used key_id %u is not found or" + " used encryption algorithm or method does not match.", + key_version); + ib_logf(IB_LOG_LEVEL_ERROR, + "Marking tablespace as missing. You may drop this table or" + " install correct key management plugin and key file."); + } } + + return corrupted; } /********************************************************************//** @@ -4437,44 +4546,46 @@ buf_page_io_complete( goto page_not_corrupt; ;); corrupt: - fil_system_enter(); - space = fil_space_get_by_id(bpage->space); - fil_system_exit(); + bool corrupted = buf_page_check_corrupt(bpage); - ib_logf(IB_LOG_LEVEL_ERROR, - "Database page corruption on disk" - " or a failed"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Space %lu file %s read of page %lu.", - (ulint)bpage->space, - space ? space->name : "NULL", - (ulong) bpage->offset); - ib_logf(IB_LOG_LEVEL_ERROR, - "You may have to recover" - " from a backup."); + if (corrupted) { + fil_system_enter(); + space = fil_space_get_by_id(bpage->space); + fil_system_exit(); + ib_logf(IB_LOG_LEVEL_ERROR, + "Database page corruption on disk" + " or a failed"); + ib_logf(IB_LOG_LEVEL_ERROR, + "Space %lu file %s read of page %lu.", + (ulint)bpage->space, + space ? space->name : "NULL", + (ulong) bpage->offset); + ib_logf(IB_LOG_LEVEL_ERROR, + "You may have to recover" + " from a backup."); - buf_page_check_corrupt(bpage); - buf_page_print(frame, buf_page_get_zip_size(bpage), - BUF_PAGE_PRINT_NO_CRASH); + buf_page_print(frame, buf_page_get_zip_size(bpage), + BUF_PAGE_PRINT_NO_CRASH); - ib_logf(IB_LOG_LEVEL_ERROR, - "It is also possible that your operating" - "system has corrupted its own file cache."); - ib_logf(IB_LOG_LEVEL_ERROR, - "and rebooting your computer removes the error."); - ib_logf(IB_LOG_LEVEL_ERROR, - "If the corrupt page is an index page you can also try to"); - ib_logf(IB_LOG_LEVEL_ERROR, - "fix the corruption by dumping, dropping, and reimporting"); - ib_logf(IB_LOG_LEVEL_ERROR, - "the corrupt table. You can use CHECK"); - ib_logf(IB_LOG_LEVEL_ERROR, - "TABLE to scan your table for corruption."); - ib_logf(IB_LOG_LEVEL_ERROR, - "See also " - REFMAN "forcing-innodb-recovery.html" - " about forcing recovery."); + ib_logf(IB_LOG_LEVEL_ERROR, + "It is also possible that your operating" + "system has corrupted its own file cache."); + ib_logf(IB_LOG_LEVEL_ERROR, + "and rebooting your computer removes the error."); + ib_logf(IB_LOG_LEVEL_ERROR, + "If the corrupt page is an index page you can also try to"); + ib_logf(IB_LOG_LEVEL_ERROR, + "fix the corruption by dumping, dropping, and reimporting"); + ib_logf(IB_LOG_LEVEL_ERROR, + "the corrupt table. You can use CHECK"); + ib_logf(IB_LOG_LEVEL_ERROR, + "TABLE to scan your table for corruption."); + ib_logf(IB_LOG_LEVEL_ERROR, + "See also " + REFMAN "forcing-innodb-recovery.html" + " about forcing recovery."); + } if (srv_force_recovery < SRV_FORCE_IGNORE_CORRUPT) { /* If page space id is larger than TRX_SYS_SPACE @@ -4484,12 +4595,30 @@ corrupt: && buf_mark_space_corrupt(bpage)) { return(false); } else { - buf_page_check_corrupt(bpage); + corrupted = buf_page_check_corrupt(bpage); - ib_logf(IB_LOG_LEVEL_ERROR, - "Ending processing because of a corrupt database page."); + if (corrupted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Ending processing because of a corrupt database page."); - ut_error; + ut_error; + } + + ib_push_warning((void *)NULL, DB_ENCRYPTED_DECRYPT_FAILED, + "Table in tablespace %lu encrypted." + "However key management plugin or used key_id %lu is not found or" + " used encryption algorithm or method does not match." + " Can't continue opening the table.", + bpage->key_version); + + if (bpage->space > TRX_SYS_SPACE) { + if (corrupted) { + buf_mark_space_corrupt(bpage); + } + } else { + ut_error; + } + return(false); } } } @@ -4630,11 +4759,13 @@ buf_all_freed_instance( const buf_block_t* block = buf_chunk_not_freed(chunk); if (UNIV_LIKELY_NULL(block)) { - fprintf(stderr, - "Page %lu %lu still fixed or dirty\n", - (ulong) block->page.space, - (ulong) block->page.offset); - ut_error; + if (block->page.key_version == 0) { + fprintf(stderr, + "Page %lu %lu still fixed or dirty\n", + (ulong) block->page.space, + (ulong) block->page.offset); + ut_error; + } } } @@ -5950,6 +6081,11 @@ buf_page_decrypt_after_read( bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame); buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); + /* If page is encrypted read post-encryption checksum */ + if (!page_compressed_encrypted && key_version != 0) { + bpage->stored_checksum = mach_read_from_4(dst_frame + + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4); + } + ut_ad(bpage->key_version == 0); if (bpage->offset == 0) { @@ -5994,6 +6130,13 @@ buf_page_decrypt_after_read( #ifdef UNIV_DEBUG fil_page_type_validate(dst_frame); #endif + + /* Calculate checksum before decrypt, this will be + used later to find out if incorrect key was used. */ + if (!page_compressed_encrypted) { + bpage->calculated_checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); + } + /* decrypt using crypt_buf to dst_frame */ fil_space_decrypt(bpage->space, slot->crypt_buf, diff --git a/storage/innobase/buf/buf0rea.cc b/storage/innobase/buf/buf0rea.cc index b4b474c547f..285fc465160 100644 --- a/storage/innobase/buf/buf0rea.cc +++ b/storage/innobase/buf/buf0rea.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015. MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -105,21 +106,22 @@ ulint buf_read_page_low( /*==============*/ dberr_t* err, /*!< out: DB_SUCCESS or DB_TABLESPACE_DELETED if we are - trying to read from a non-existent tablespace, or a - tablespace which is just now being dropped */ - bool sync, /*!< in: true if synchronous aio is desired */ - ulint mode, /*!< in: BUF_READ_IBUF_PAGES_ONLY, ..., - ORed to OS_AIO_SIMULATED_WAKE_LATER (see below - at read-ahead functions) */ - ulint space, /*!< in: space id */ - ulint zip_size,/*!< in: compressed page size, or 0 */ - ibool unzip, /*!< in: TRUE=request uncompressed page */ - ib_int64_t tablespace_version, /*!< in: if the space memory object has - this timestamp different from what we are giving here, - treat the tablespace as dropped; this is a timestamp we - use to stop dangling page reads from a tablespace - which we have DISCARDed + IMPORTed back */ - ulint offset) /*!< in: page number */ + trying to read from a non-existent tablespace, or a + tablespace which is just now being dropped */ + bool sync, /*!< in: true if synchronous aio is desired */ + ulint mode, /*!< in: BUF_READ_IBUF_PAGES_ONLY, ..., + ORed to OS_AIO_SIMULATED_WAKE_LATER (see below + at read-ahead functions) */ + ulint space, /*!< in: space id */ + ulint zip_size,/*!< in: compressed page size, or 0 */ + ibool unzip, /*!< in: TRUE=request uncompressed page */ + ib_int64_t tablespace_version, /*!< in: if the space memory object has + this timestamp different from what we are giving here, + treat the tablespace as dropped; this is a timestamp we + use to stop dangling page reads from a tablespace + which we have DISCARDed + IMPORTed back */ + ulint offset, /*!< in: page number */ + buf_page_t** rbpage) /*!< out: page */ { buf_page_t* bpage; ulint wake_later; @@ -214,10 +216,17 @@ buf_read_page_low( /* The i/o is already completed when we arrive from fil_read */ if (!buf_page_io_complete(bpage)) { + if (rbpage) { + *rbpage = bpage; + } return(0); } } + if (rbpage) { + *rbpage = bpage; + } + return(1); } @@ -348,7 +357,7 @@ read_ahead: &err, false, ibuf_mode | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, FALSE, - tablespace_version, i); + tablespace_version, i, NULL); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); fprintf(stderr, @@ -398,7 +407,8 @@ buf_read_page( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint offset) /*!< in: page number */ + ulint offset, /*!< in: page number */ + buf_page_t** bpage) /*!< out: page */ { ib_int64_t tablespace_version; ulint count; @@ -411,7 +421,7 @@ buf_read_page( count = buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space, zip_size, FALSE, - tablespace_version, offset); + tablespace_version, offset, bpage); srv_stats.buf_pool_reads.add(count); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); @@ -459,7 +469,7 @@ buf_read_page_async( | OS_AIO_SIMULATED_WAKE_LATER | BUF_READ_IGNORE_NONEXISTENT_PAGES, space, zip_size, FALSE, - tablespace_version, offset); + tablespace_version, offset, NULL); srv_stats.buf_pool_reads.add(count); /* We do not increment number of I/O operations used for LRU policy @@ -718,7 +728,7 @@ buf_read_ahead_linear( count += buf_read_page_low( &err, false, ibuf_mode, - space, zip_size, FALSE, tablespace_version, i); + space, zip_size, FALSE, tablespace_version, i, NULL); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); fprintf(stderr, @@ -808,7 +818,7 @@ buf_read_ibuf_merge_pages( buf_read_page_low(&err, sync && (i + 1 == n_stored), BUF_READ_ANY_PAGE, space_ids[i], zip_size, TRUE, space_versions[i], - page_nos[i]); + page_nos[i], NULL); if (UNIV_UNLIKELY(err == DB_TABLESPACE_DELETED)) { tablespace_deleted: @@ -903,12 +913,12 @@ buf_read_recv_pages( if ((i + 1 == n_stored) && sync) { buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space, zip_size, TRUE, tablespace_version, - page_nos[i]); + page_nos[i], NULL); } else { buf_read_page_low(&err, false, BUF_READ_ANY_PAGE | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, TRUE, - tablespace_version, page_nos[i]); + tablespace_version, page_nos[i], NULL); } } diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 2633bd96360..32fe58750e1 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -650,31 +650,8 @@ fil_space_encrypt( /* handle post encryption checksum */ ib_uint32_t checksum = 0; - srv_checksum_algorithm_t algorithm = - static_cast(srv_checksum_algorithm); - if (zip_size == 0) { - switch (algorithm) { - case SRV_CHECKSUM_ALGORITHM_CRC32: - case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: - checksum = buf_calc_page_crc32(dst_frame); - break; - case SRV_CHECKSUM_ALGORITHM_INNODB: - case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: - checksum = (ib_uint32_t) buf_calc_page_new_checksum( - dst_frame); - break; - case SRV_CHECKSUM_ALGORITHM_NONE: - case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: - checksum = BUF_NO_CHECKSUM_MAGIC; - break; - /* no default so the compiler will emit a warning - * if new enum is added and not handled here */ - } - } else { - checksum = page_zip_calc_checksum(dst_frame, zip_size, - algorithm); - } + checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); // store the post-encryption checksum after the key-version mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum); @@ -818,6 +795,47 @@ fil_space_decrypt( return src_frame; } +/****************************************************************** +Calculate post encryption checksum +@return page checksum or BUF_NO_CHECKSUM_MAGIC +not needed. */ +UNIV_INTERN +ulint +fil_crypt_calculate_checksum( +/*=========================*/ + ulint zip_size, /*!< in: zip_size or 0 */ + byte* dst_frame) /*!< in: page where to calculate */ +{ + ib_uint32_t checksum = 0; + srv_checksum_algorithm_t algorithm = + static_cast(srv_checksum_algorithm); + + if (zip_size == 0) { + switch (algorithm) { + case SRV_CHECKSUM_ALGORITHM_CRC32: + case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: + checksum = buf_calc_page_crc32(dst_frame); + break; + case SRV_CHECKSUM_ALGORITHM_INNODB: + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: + checksum = (ib_uint32_t) buf_calc_page_new_checksum( + dst_frame); + break; + case SRV_CHECKSUM_ALGORITHM_NONE: + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + checksum = BUF_NO_CHECKSUM_MAGIC; + break; + /* no default so the compiler will emit a warning + * if new enum is added and not handled here */ + } + } else { + checksum = page_zip_calc_checksum(dst_frame, zip_size, + algorithm); + } + + return checksum; +} + /********************************************************************* Verify checksum for a page (iff it's encrypted) NOTE: currently this function can only be run in single threaded mode diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 6722ee85428..a49669230a9 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1747,6 +1747,7 @@ convert_error_code_to_mysql( case DB_TABLESPACE_DELETED: case DB_TABLE_NOT_FOUND: + case DB_ENCRYPTED_DECRYPT_FAILED: return(HA_ERR_NO_SUCH_TABLE); case DB_TABLESPACE_NOT_FOUND: @@ -5592,7 +5593,14 @@ table_opened: innobase_copy_frm_flags_from_table_share(ib_table, table->s); - dict_stats_init(ib_table); + ib_table->thd = (void*)thd; + + /* No point to init any statistics if tablespace is still encrypted. */ + if (!ib_table->is_encrypted) { + dict_stats_init(ib_table); + } else { + ib_table->stat_initialized = 1; + } MONITOR_INC(MONITOR_TABLE_OPEN); @@ -5621,6 +5629,11 @@ table_opened: file, best to play it safe. */ no_tablespace = true; + } else if (ib_table->is_encrypted) { + /* This means that tablespace was found but we could not + decrypt encrypted page. */ + no_tablespace = true; + ib_table->ibd_file_missing = true; } else { no_tablespace = false; } @@ -5632,9 +5645,9 @@ table_opened: /* If table has no talespace but it has crypt data, check is tablespace made unaccessible because encryption service or used key_id is not available. */ - if (ib_table && ib_table->crypt_data) { + if (ib_table) { fil_space_crypt_t* crypt_data = ib_table->crypt_data; - if ((crypt_data->encryption == FIL_SPACE_ENCRYPTION_ON) || + if ((crypt_data && crypt_data->encryption == FIL_SPACE_ENCRYPTION_ON) || (srv_encrypt_tables && crypt_data && crypt_data->encryption == FIL_SPACE_ENCRYPTION_DEFAULT)) { @@ -5646,6 +5659,13 @@ table_opened: " Can't continue reading table.", ib_table->name, crypt_data->key_id); } + } else if (ib_table->is_encrypted) { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + HA_ERR_NO_SUCH_TABLE, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + ib_table->name); } } @@ -20212,3 +20232,59 @@ static void innodb_remember_check_sysvar_funcs() ut_ad((MYSQL_SYSVAR_NAME(checksum_algorithm).flags & 0x1FF) == PLUGIN_VAR_ENUM); check_sysvar_enum = MYSQL_SYSVAR_NAME(checksum_algorithm).check; } + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)trx->mysql_thd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + convert_error_code_to_mysql((dberr_t)error, 0, thd), + buf); + my_free(buf); + va_end(args); +} + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + void* ithd, /*!< in: thd */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)ithd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + if (ithd == NULL) { + thd = current_thd; + } + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + convert_error_code_to_mysql((dberr_t)error, 0, thd), + buf); + my_free(buf); + va_end(args); +} diff --git a/storage/innobase/include/btr0btr.ic b/storage/innobase/include/btr0btr.ic index 40b468b200a..6604ac6a6f0 100644 --- a/storage/innobase/include/btr0btr.ic +++ b/storage/innobase/include/btr0btr.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -57,13 +58,15 @@ btr_block_get_func( buf_block_t* block; block = buf_page_get_gen(space, zip_size, page_no, mode, - NULL, BUF_GET, file, line, mtr); + NULL, BUF_GET, file, line, mtr); - if (mode != RW_NO_LATCH) { + if (block) { + if (mode != RW_NO_LATCH) { - buf_block_dbg_add_level( - block, index != NULL && dict_index_is_ibuf(index) - ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); + buf_block_dbg_add_level( + block, index != NULL && dict_index_is_ibuf(index) + ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); + } } return(block); diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index d0fd5c2158a..88e3b84e04b 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -136,7 +136,7 @@ Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. If mode is PAGE_CUR_GE, then up_match will a have a sensible value. */ UNIV_INTERN -void +dberr_t btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ @@ -173,7 +173,7 @@ btr_cur_search_to_nth_level( /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN -void +dberr_t btr_cur_open_at_index_side_func( /*============================*/ bool from_left, /*!< in: true if open to the low end, diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h index cfbaacf4de3..d8e7cf6b283 100644 --- a/storage/innobase/include/btr0pcur.h +++ b/storage/innobase/include/btr0pcur.h @@ -114,7 +114,7 @@ btr_pcur_open_low( Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE -void +dberr_t btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ @@ -143,7 +143,7 @@ btr_pcur_open_with_no_init_func( /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE -void +dberr_t btr_pcur_open_at_index_side( /*========================*/ bool from_left, /*!< in: true if open to the low end, diff --git a/storage/innobase/include/btr0pcur.ic b/storage/innobase/include/btr0pcur.ic index 7e355d3709d..1cd13824542 100644 --- a/storage/innobase/include/btr0pcur.ic +++ b/storage/innobase/include/btr0pcur.ic @@ -447,7 +447,7 @@ btr_pcur_open_low( Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE -void +dberr_t btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ @@ -472,6 +472,7 @@ btr_pcur_open_with_no_init_func( mtr_t* mtr) /*!< in: mtr */ { btr_cur_t* btr_cursor; + dberr_t err = DB_SUCCESS; cursor->latch_mode = latch_mode; cursor->search_mode = mode; @@ -480,20 +481,21 @@ btr_pcur_open_with_no_init_func( btr_cursor = btr_pcur_get_btr_cur(cursor); - btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, - btr_cursor, has_search_latch, - file, line, mtr); + err = btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, + btr_cursor, has_search_latch, + file, line, mtr); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; cursor->trx_if_known = NULL; + return err; } /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE -void +dberr_t btr_pcur_open_at_index_side( /*========================*/ bool from_left, /*!< in: true if open to the low end, @@ -506,6 +508,8 @@ btr_pcur_open_at_index_side( (0=leaf) */ mtr_t* mtr) /*!< in/out: mini-transaction */ { + dberr_t err = DB_SUCCESS; + pcur->latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); pcur->search_mode = from_left ? PAGE_CUR_G : PAGE_CUR_L; @@ -514,13 +518,15 @@ btr_pcur_open_at_index_side( btr_pcur_init(pcur); } - btr_cur_open_at_index_side(from_left, index, latch_mode, - btr_pcur_get_btr_cur(pcur), level, mtr); + err = btr_cur_open_at_index_side(from_left, index, latch_mode, + btr_pcur_get_btr_cur(pcur), level, mtr); pcur->pos_state = BTR_PCUR_IS_POSITIONED; pcur->old_stored = BTR_PCUR_OLD_NOT_STORED; pcur->trx_if_known = NULL; + + return (err); } /**********************************************************************//** diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 11cf7ebc8cc..6c8e8e00507 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -430,7 +430,8 @@ buf_page_get_gen( BUF_GET_IF_IN_POOL_OR_WATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr); /*!< in: mini-transaction */ + mtr_t* mtr, /*!< in: mini-transaction */ + dberr_t* err = NULL); /*!< out: error code */ /********************************************************************//** Initializes a page to the buffer buf_pool. The page is usually not read from a file even if it cannot be found in the buffer buf_pool. This is one @@ -1582,9 +1583,14 @@ struct buf_page_t{ operation needed. */ unsigned key_version; /*!< key version for this block */ - bool page_encrypted; /*!< page is encrypted */ + bool page_encrypted; /*!< page is page encrypted */ bool page_compressed;/*!< page is page compressed */ - + ulint stored_checksum;/*!< stored page checksum if page + encrypted */ + bool encrypted; /*!< page is still encrypted */ + ulint calculated_checksum; + /*!< calculated checksum if page + encrypted */ ulint real_size; /*!< Real size of the page Normal pages == UNIV_PAGE_SIZE page compressed pages, payload diff --git a/storage/innobase/include/buf0buf.ic b/storage/innobase/include/buf0buf.ic index 6c128b097b0..a80a4c0a391 100644 --- a/storage/innobase/include/buf0buf.ic +++ b/storage/innobase/include/buf0buf.ic @@ -663,13 +663,18 @@ buf_block_get_frame( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { - ut_ad(block); + if (!block) { + return NULL; + } switch (buf_block_get_state(block)) { case BUF_BLOCK_POOL_WATCH: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: case BUF_BLOCK_NOT_USED: + if (block->page.encrypted) { + goto ok; + } ut_error; break; case BUF_BLOCK_FILE_PAGE: diff --git a/storage/innobase/include/buf0rea.h b/storage/innobase/include/buf0rea.h index d2a1f264ff5..10714031710 100644 --- a/storage/innobase/include/buf0rea.h +++ b/storage/innobase/include/buf0rea.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -41,7 +42,8 @@ buf_read_page( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint offset);/*!< in: page number */ + ulint offset, /*!< in: page number */ + buf_page_t** bpage);/*!< out: page */ /********************************************************************//** High-level function which reads a page asynchronously from a file to the buffer buf_pool if it is not already there. Sets the io_fix flag and sets diff --git a/storage/innobase/include/db0err.h b/storage/innobase/include/db0err.h index 1e87ce3fdb8..24889a9b9c5 100644 --- a/storage/innobase/include/db0err.h +++ b/storage/innobase/include/db0err.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -132,6 +133,11 @@ enum dberr_t { /*< Too many words in a phrase */ DB_TOO_BIG_FOR_REDO, /* Record length greater than 10% of redo log */ + DB_ENCRYPTED_DECRYPT_FAILED, /* Tablespace encrypted and + decrypt operaton failed because + of missing key management plugin, + or missing or incorrect key or + incorret AES method or algorithm. */ /* The following are partial failure codes */ DB_FAIL = 1000, DB_OVERFLOW, diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 059ba2208e3..53a5d6cb08b 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -2,7 +2,7 @@ Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -1017,6 +1017,7 @@ struct dict_table_t{ table_id_t id; /*!< id of the table */ mem_heap_t* heap; /*!< memory heap */ char* name; /*!< table name */ + void* thd; /*!< thd */ fil_space_crypt_t *crypt_data; /*!< crypt data if present */ const char* dir_path_of_temp_table;/*!< NULL or the directory path where a TEMPORARY table that was explicitly @@ -1330,6 +1331,7 @@ struct dict_table_t{ locks; /*!< list of locks on the table; protected by lock_sys->mutex */ #endif /* !UNIV_HOTBACKUP */ + ibool is_encrypted; #ifdef UNIV_DEBUG ulint magic_n;/*!< magic number */ diff --git a/storage/innobase/include/fil0crypt.h b/storage/innobase/include/fil0crypt.h index 34452badc9f..30db095775f 100644 --- a/storage/innobase/include/fil0crypt.h +++ b/storage/innobase/include/fil0crypt.h @@ -382,6 +382,17 @@ fil_crypt_set_encrypt_tables( uint val); /*!< in: New srv_encrypt_tables setting */ +/****************************************************************** +Calculate post encryption checksum +@return page checksum or BUF_NO_CHECKSUM_MAGIC +not needed. */ +UNIV_INTERN +ulint +fil_crypt_calculate_checksum( +/*=========================*/ + ulint zip_size, /*!< in: zip_size or 0 */ + byte* dst_frame); /*!< in: page where to calculate */ + #ifndef UNIV_NONINL #include "fil0crypt.ic" #endif diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index 73f154abdf7..59abb0863d9 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -634,5 +634,13 @@ ib_push_warning( ulint error, /*!< in: error code to push as warning */ const char *format,/*!< in: warning message */ ...); - +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + void* ithd, /*!< in: thd */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...); #endif /* HA_INNODB_PROTOTYPES_H */ diff --git a/storage/innobase/include/page0cur.ic b/storage/innobase/include/page0cur.ic index 028d33b17aa..6e068d9f739 100644 --- a/storage/innobase/include/page0cur.ic +++ b/storage/innobase/include/page0cur.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -39,7 +40,10 @@ page_cur_get_page( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } return(page_align(cur->rec)); } @@ -54,7 +58,11 @@ page_cur_get_block( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } + return(cur->block); } @@ -80,7 +88,10 @@ page_cur_get_rec( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } return(cur->rec); } diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 6a5dff50952..4f891e0f114 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -2338,7 +2338,7 @@ row_ins_clust_index_entry_low( { btr_cur_t cursor; ulint* offsets = NULL; - dberr_t err; + dberr_t err = DB_SUCCESS; big_rec_t* big_rec = NULL; mtr_t mtr; mem_heap_t* offsets_heap = NULL; @@ -2361,9 +2361,16 @@ row_ins_clust_index_entry_low( the function will return in both low_match and up_match of the cursor sensible values */ - btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode, + err = btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode, &cursor, 0, __FILE__, __LINE__, &mtr); + if (err != DB_SUCCESS) { + index->table->is_encrypted = true; + index->table->ibd_file_missing = true; + mtr_commit(&mtr); + goto func_exit; + } + #ifdef UNIV_DEBUG { page_t* page = btr_cur_get_page(&cursor); @@ -2669,9 +2676,22 @@ row_ins_sec_index_entry_low( search_mode |= BTR_IGNORE_SEC_UNIQUE; } - btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, - search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + err = btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, + search_mode, + &cursor, 0, __FILE__, __LINE__, &mtr); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning(trx->mysql_thd, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + goto func_exit; + } if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) { /* The insert was buffered during the search: we are done */ diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 9ed14ffc9cb..d46e2b1c3ef 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -623,6 +623,8 @@ handle_new_error: case DB_FTS_INVALID_DOCID: case DB_INTERRUPTED: case DB_DICT_CHANGED: + case DB_TABLE_NOT_FOUND: + case DB_ENCRYPTED_DECRYPT_FAILED: if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ @@ -1315,7 +1317,13 @@ row_insert_for_mysql( prebuilt->table->name); return(DB_TABLESPACE_NOT_FOUND); - + } else if (prebuilt->table->is_encrypted) { + ib_push_warning(trx, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu encrypted." + "However key management plugin or used key_id is not found or" + " used encryption algorithm or method does not match.", + prebuilt->table->name, prebuilt->table->space); + return(DB_ENCRYPTED_DECRYPT_FAILED); } else if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" @@ -1710,6 +1718,13 @@ row_update_for_mysql( "InnoDB: how you can resolve the problem.\n", prebuilt->table->name); return(DB_ERROR); + } else if (prebuilt->table->is_encrypted) { + ib_push_warning(trx, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu encrypted." + "However key management plugin or used key_id is not found or" + " used encryption algorithm or method does not match.", + prebuilt->table->name, prebuilt->table->space); + return (DB_TABLE_NOT_FOUND); } if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) { @@ -3919,6 +3934,19 @@ row_drop_table_for_mysql( goto funct_exit; } + /* If table is encrypted and table page encryption failed + mark this table read only. */ + if (table->is_encrypted) { + + if (table->can_be_evicted) { + dict_table_move_from_lru_to_non_lru(table); + } + + dict_table_close(table, TRUE, FALSE); + err = DB_READ_ONLY; + goto funct_exit; + } + /* Turn on this drop bit before we could release the dictionary latch */ table->to_be_dropped = true; diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 9c7c45ed481..53ec30b1f42 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -2,6 +2,7 @@ Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. +Copyright (c) 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -3717,6 +3718,9 @@ row_search_for_mysql( return(DB_TABLESPACE_NOT_FOUND); + } else if (prebuilt->table->is_encrypted) { + + return(DB_ENCRYPTED_DECRYPT_FAILED); } else if (!prebuilt->index_usable) { return(DB_MISSING_HISTORY); @@ -4143,9 +4147,14 @@ wait_table_again: } else if (dtuple_get_n_fields(search_tuple) > 0) { - btr_pcur_open_with_no_init(index, search_tuple, mode, - BTR_SEARCH_LEAF, - pcur, 0, &mtr); + err = btr_pcur_open_with_no_init(index, search_tuple, mode, + BTR_SEARCH_LEAF, + pcur, 0, &mtr); + + if (err != DB_SUCCESS) { + rec = NULL; + goto lock_wait_or_error; + } pcur->trx_if_known = trx; @@ -4179,9 +4188,23 @@ wait_table_again: } } } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_L) { - btr_pcur_open_at_index_side( + err = btr_pcur_open_at_index_side( mode == PAGE_CUR_G, index, BTR_SEARCH_LEAF, pcur, false, 0, &mtr); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning(trx->mysql_thd, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + prebuilt->table->name); + index->table->is_encrypted = true; + } + rec = NULL; + goto lock_wait_or_error; + } } rec_loop: @@ -4196,6 +4219,12 @@ rec_loop: /* PHASE 4: Look for matching records in a loop */ rec = btr_pcur_get_rec(pcur); + + if (!rec) { + err = DB_ENCRYPTED_DECRYPT_FAILED; + goto lock_wait_or_error; + } + ut_ad(!!page_rec_is_comp(rec) == comp); #ifdef UNIV_SEARCH_DEBUG /* @@ -5113,7 +5142,9 @@ lock_wait_or_error: /*-------------------------------------------------------------*/ - btr_pcur_store_position(pcur, &mtr); + if (rec) { + btr_pcur_store_position(pcur, &mtr); + } lock_table_wait: mtr_commit(&mtr); diff --git a/storage/xtradb/btr/btr0btr.cc b/storage/xtradb/btr/btr0btr.cc index 2f248c98651..86e70109db5 100644 --- a/storage/xtradb/btr/btr0btr.cc +++ b/storage/xtradb/btr/btr0btr.cc @@ -2,6 +2,7 @@ Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2014, 2015, MariaDB Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -741,6 +742,19 @@ btr_root_block_get( block = btr_block_get(space, zip_size, root_page_no, mode, index, mtr); + if (!block) { + index->table->is_encrypted = TRUE; + index->table->corrupted = FALSE; + + ib_push_warning(index->table->thd, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name, space); + + return NULL; + } + SRV_CORRUPT_TABLE_CHECK(block, return(0);); btr_assert_not_corrupted(block, index); @@ -779,8 +793,10 @@ btr_root_get( const dict_index_t* index, /*!< in: index tree */ mtr_t* mtr) /*!< in: mtr */ { - return(buf_block_get_frame(btr_root_block_get(index, RW_X_LATCH, - mtr))); + buf_block_t* root = btr_root_block_get(index, RW_X_LATCH, + mtr); + + return(root ? buf_block_get_frame(root) : NULL); } /**************************************************************//** @@ -795,7 +811,7 @@ btr_height_get( dict_index_t* index, /*!< in: index tree */ mtr_t* mtr) /*!< in/out: mini-transaction */ { - ulint height; + ulint height=0; buf_block_t* root_block; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), @@ -806,13 +822,16 @@ btr_height_get( /* S latches the page */ root_block = btr_root_block_get(index, RW_S_LATCH, mtr); - height = btr_page_get_level(buf_block_get_frame_fast(root_block), mtr); + if (root_block) { - /* Release the S latch on the root page. */ - mtr_memo_release(mtr, root_block, MTR_MEMO_PAGE_S_FIX); + height = btr_page_get_level(buf_block_get_frame_fast(root_block), mtr); + + /* Release the S latch on the root page. */ + mtr_memo_release(mtr, root_block, MTR_MEMO_PAGE_S_FIX); #ifdef UNIV_SYNC_DEBUG - sync_thread_reset_level(&root_block->lock); + sync_thread_reset_level(&root_block->lock); #endif /* UNIV_SYNC_DEBUG */ + } return(height); } @@ -1260,7 +1279,7 @@ btr_get_size_and_reserved( { fseg_header_t* seg_header; page_t* root; - ulint n; + ulint n=ULINT_UNDEFINED; ulint dummy; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), @@ -1274,17 +1293,21 @@ btr_get_size_and_reserved( } root = btr_root_get(index, mtr); + *used = 0; - seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; + if (root) { - n = fseg_n_reserved_pages(seg_header, used, mtr); + seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; - if (flag == BTR_TOTAL_SIZE) { - seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; + n = fseg_n_reserved_pages(seg_header, used, mtr); - n += fseg_n_reserved_pages(seg_header, &dummy, mtr); - *used += dummy; + if (flag == BTR_TOTAL_SIZE) { + seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; + n += fseg_n_reserved_pages(seg_header, &dummy, mtr); + *used += dummy; + + } } return(n); diff --git a/storage/xtradb/btr/btr0cur.cc b/storage/xtradb/btr/btr0cur.cc index 4e5e6a713ed..316271077ae 100644 --- a/storage/xtradb/btr/btr0cur.cc +++ b/storage/xtradb/btr/btr0cur.cc @@ -3,6 +3,7 @@ Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -386,7 +387,7 @@ search tuple should be performed in the B-tree. InnoDB does an insert immediately after the cursor. Thus, the cursor may end up on a user record, or on a page infimum record. */ UNIV_INTERN -void +dberr_t btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ @@ -436,6 +437,7 @@ btr_cur_search_to_nth_level( page_cur_t* page_cursor; btr_op_t btr_op; ulint root_height = 0; /* remove warning */ + dberr_t err = DB_SUCCESS; #ifdef BTR_CUR_ADAPT btr_search_t* info; @@ -553,7 +555,7 @@ btr_cur_search_to_nth_level( || mode != PAGE_CUR_LE); btr_cur_n_sea++; - return; + return err; } # endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_ADAPT */ @@ -649,7 +651,21 @@ search_loop: retry_page_get: block = buf_page_get_gen( space, zip_size, page_no, rw_latch, guess, buf_mode, - file, line, mtr); + file, line, mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + goto func_exit; + } if (block == NULL) { SRV_CORRUPT_TABLE_CHECK(buf_mode == BUF_GET_IF_IN_POOL || @@ -889,12 +905,14 @@ func_exit: rw_lock_s_lock(btr_search_get_latch(cursor->index)); } + + return err; } /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN -void +dberr_t btr_cur_open_at_index_side_func( /*============================*/ bool from_left, /*!< in: true if open to the low end, @@ -920,6 +938,8 @@ btr_cur_open_at_index_side_func( mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; + dberr_t err = DB_SUCCESS; + rec_offs_init(offsets_); estimate = latch_mode & BTR_ESTIMATE; @@ -957,11 +977,26 @@ btr_cur_open_at_index_side_func( height = ULINT_UNDEFINED; for (;;) { - buf_block_t* block; - page_t* page; + buf_block_t* block=NULL; + page_t* page=NULL; + block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, - file, line, mtr); + file, line, mtr, &err); + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + goto exit_loop; + } + page = buf_block_get_frame(block); SRV_CORRUPT_TABLE_CHECK(page, @@ -1066,6 +1101,8 @@ exit_loop: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } + + return err; } /**********************************************************************//** @@ -1113,10 +1150,25 @@ btr_cur_open_at_rnd_pos_func( for (;;) { buf_block_t* block; page_t* page; + dberr_t err=DB_SUCCESS; block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, - file, line, mtr); + file, line, mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + goto exit_loop; + } + page = buf_block_get_frame(block); SRV_CORRUPT_TABLE_CHECK(page, @@ -3749,6 +3801,7 @@ btr_estimate_n_rows_in_range_on_level( mtr_t mtr; page_t* page; buf_block_t* block; + dberr_t err=DB_SUCCESS; mtr_start(&mtr); @@ -3759,7 +3812,23 @@ btr_estimate_n_rows_in_range_on_level( silence a debug assertion about this. */ block = buf_page_get_gen(space, zip_size, page_no, RW_S_LATCH, NULL, BUF_GET_POSSIBLY_FREED, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, &err); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning((void *)NULL, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + + mtr_commit(&mtr); + goto inexact; + } + page = buf_block_get_frame(block); diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc index cf63bab0d5f..31157aa737f 100644 --- a/storage/xtradb/buf/buf0buf.cc +++ b/storage/xtradb/buf/buf0buf.cc @@ -59,11 +59,24 @@ Created 11/5/1995 Heikki Tuuri #include "srv0start.h" #include "ut0byte.h" #include "fil0pagecompress.h" +#include "ha_prototypes.h" /* prototypes for new functions added to ha_innodb.cc */ trx_t* innobase_get_trx(); +/********************************************************************//** +Check if page is maybe compressed, encrypted or both when we encounter +corrupted page. Note that we can't be 100% sure if page is corrupted +or decrypt/decompress just failed. +*/ +static +ibool +buf_page_check_corrupt( +/*===================*/ + buf_page_t* bpage); /*!< in/out: buffer page read from + disk */ + static inline void _increment_page_get_statistics(buf_block_t* block, trx_t* trx) @@ -1128,6 +1141,9 @@ buf_block_init( block->page.key_version = 0; block->page.page_encrypted = false; block->page.page_compressed = false; + block->page.encrypted = false; + block->page.stored_checksum = BUF_NO_CHECKSUM_MAGIC; + block->page.calculated_checksum = BUF_NO_CHECKSUM_MAGIC; block->page.real_size = 0; block->page.write_size = 0; block->modify_clock = 0; @@ -2234,7 +2250,7 @@ lookup: /* Page not in buf_pool: needs to be read from file */ ut_ad(!hash_lock); - buf_read_page(space, zip_size, offset, trx); + buf_read_page(space, zip_size, offset, trx, NULL); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); @@ -2753,7 +2769,8 @@ buf_page_get_gen( BUF_GET_IF_IN_POOL_OR_WATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr) /*!< in: mini-transaction */ + mtr_t* mtr, /*!< in: mini-transaction */ + dberr_t* err) /*!< out: error code */ { buf_block_t* block; ulint fold; @@ -2771,6 +2788,11 @@ buf_page_get_gen( ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH) || (rw_latch == RW_NO_LATCH)); + + if (err) { + *err = DB_SUCCESS; + } + #ifdef UNIV_DEBUG switch (mode) { case BUF_GET_NO_LATCH: @@ -2834,6 +2856,8 @@ loop: } if (block == NULL) { + buf_page_t* bpage=NULL; + /* Page not in buf_pool: needs to be read from file */ if (mode == BUF_GET_IF_IN_POOL_OR_WATCH) { @@ -2868,36 +2892,77 @@ loop: return(NULL); } - if (buf_read_page(space, zip_size, offset, trx)) { + if (buf_read_page(space, zip_size, offset, trx, &bpage)) { buf_read_ahead_random(space, zip_size, offset, ibuf_inside(mtr), trx); retries = 0; } else if (retries < BUF_PAGE_READ_MAX_RETRIES) { ++retries; + + bool corrupted = true; + + if (bpage) { + corrupted = buf_page_check_corrupt(bpage); + } + + /* Do not try again for encrypted pages */ + if (!corrupted) { + ib_mutex_t* pmutex = buf_page_get_mutex(bpage); + mutex_enter(&buf_pool->LRU_list_mutex); + mutex_enter(pmutex); + buf_page_set_io_fix(bpage, BUF_IO_NONE); + buf_LRU_free_page(bpage, zip_size ? true : false); + mutex_exit(pmutex); + + if (err) { + *err = DB_ENCRYPTED_DECRYPT_FAILED; + } + return (NULL); + } + DBUG_EXECUTE_IF( "innodb_page_corruption_retries", retries = BUF_PAGE_READ_MAX_RETRIES; ); } else { + bool corrupted = true; - fprintf(stderr, "InnoDB: Error: Unable" - " to read tablespace %lu page no" - " %lu into the buffer pool after" - " %lu attempts\n" - "InnoDB: The most probable cause" - " of this error may be that the" - " table has been corrupted.\n" - "InnoDB: You can try to fix this" - " problem by using" - " innodb_force_recovery.\n" - "InnoDB: Please see reference manual" - " for more details.\n" - "InnoDB: Aborting...\n", - space, offset, - BUF_PAGE_READ_MAX_RETRIES); + if (bpage) { + corrupted = buf_page_check_corrupt(bpage); + } - ut_error; + if (corrupted) { + fprintf(stderr, "InnoDB: Error: Unable" + " to read tablespace %lu page no" + " %lu into the buffer pool after" + " %lu attempts\n" + "InnoDB: The most probable cause" + " of this error may be that the" + " table has been corrupted.\n" + "InnoDB: You can try to fix this" + " problem by using" + " innodb_force_recovery.\n" + "InnoDB: Please see reference manual" + " for more details.\n" + "InnoDB: Aborting...\n", + space, offset, + BUF_PAGE_READ_MAX_RETRIES); + + ut_error; + } else { + ib_mutex_t* pmutex = buf_page_get_mutex(bpage); + mutex_enter(&buf_pool->LRU_list_mutex); + mutex_enter(pmutex); + buf_page_set_io_fix(bpage, BUF_IO_NONE); + buf_LRU_free_page(bpage, zip_size ? true : false); + mutex_exit(pmutex); + + if (err) { + *err = DB_ENCRYPTED_DECRYPT_FAILED; + } + return (NULL); + } } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG @@ -3670,8 +3735,11 @@ buf_page_init_low( bpage->oldest_modification = 0; bpage->write_size = 0; bpage->key_version = 0; + bpage->stored_checksum = BUF_NO_CHECKSUM_MAGIC; + bpage->calculated_checksum = BUF_NO_CHECKSUM_MAGIC; bpage->page_encrypted = false; bpage->page_compressed = false; + bpage->encrypted = false; bpage->real_size = 0; HASH_INVALIDATE(bpage, hash); @@ -4341,33 +4409,41 @@ buf_mark_space_corrupt( /* First unfix and release lock on the bpage */ ut_ad(!mutex_own(&buf_pool->LRU_list_mutex)); - mutex_enter(&buf_pool->LRU_list_mutex); - rw_lock_x_lock(hash_lock); - mutex_enter(buf_page_get_mutex(bpage)); - ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); - ut_ad(bpage->buf_fix_count == 0); - /* Set BUF_IO_NONE before we remove the block from LRU list */ - buf_page_set_io_fix(bpage, BUF_IO_NONE); + if (!bpage->encrypted) { + mutex_enter(&buf_pool->LRU_list_mutex); + rw_lock_x_lock(hash_lock); + mutex_enter(buf_page_get_mutex(bpage)); + ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_READ); + ut_ad(bpage->buf_fix_count == 0); - if (uncompressed) { - rw_lock_x_unlock_gen( - &((buf_block_t*) bpage)->lock, - BUF_IO_READ); + /* Set BUF_IO_NONE before we remove the block from LRU list */ + buf_page_set_io_fix(bpage, BUF_IO_NONE); + + if (uncompressed) { + rw_lock_x_unlock_gen( + &((buf_block_t*) bpage)->lock, + BUF_IO_READ); + } } /* Find the table with specified space id, and mark it corrupted */ if (dict_set_corrupted_by_space(space)) { - buf_LRU_free_one_page(bpage); + if (!bpage->encrypted) { + buf_LRU_free_one_page(bpage); + } } else { - mutex_exit(buf_page_get_mutex(bpage)); + if (!bpage->encrypted) { + mutex_exit(buf_page_get_mutex(bpage)); + } ret = FALSE; } - mutex_exit(&buf_pool->LRU_list_mutex); - - ut_ad(buf_pool->n_pend_reads > 0); - os_atomic_decrement_ulint(&buf_pool->n_pend_reads, 1); + if(!bpage->encrypted) { + mutex_exit(&buf_pool->LRU_list_mutex); + ut_ad(buf_pool->n_pend_reads > 0); + os_atomic_decrement_ulint(&buf_pool->n_pend_reads, 1); + } return(ret); } @@ -4378,42 +4454,77 @@ corrupted page. Note that we can't be 100% sure if page is corrupted or decrypt/decompress just failed. */ static -void +ibool buf_page_check_corrupt( /*===================*/ - const buf_page_t* bpage) /*!< in/out: buffer page read from disk */ + buf_page_t* bpage) /*!< in/out: buffer page read from disk */ { ulint zip_size = buf_page_get_zip_size(bpage); byte* dst_frame = (zip_size) ? bpage->zip.data : ((buf_block_t*) bpage)->frame; unsigned key_version = bpage->key_version; bool page_compressed = bpage->page_encrypted; + ulint stored_checksum = bpage->stored_checksum; + ulint calculated_checksum = bpage->stored_checksum; bool page_compressed_encrypted = bpage->page_compressed; ulint space_id = mach_read_from_4( dst_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id); fil_space_t* space = fil_space_found_by_id(space_id); + bool corrupted = true; + + if (key_version != 0 || page_compressed_encrypted) { + bpage->encrypted = true; + } if (key_version != 0 || (crypt_data && crypt_data->type != CRYPT_SCHEME_UNENCRYPTED) || page_compressed || page_compressed_encrypted) { - ib_logf(IB_LOG_LEVEL_ERROR, - "Maybe corruption: Block space_id %lu in file %s maybe corrupted.", - space_id, space ? space->name : "NULL"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Page based on contents %s encrypted.", - (key_version == 0 && page_compressed_encrypted == false) ? "not" : "maybe"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Reason could be that key_version %u in page " - "or in crypt_data %p could not be found.", - key_version, crypt_data); - ib_logf(IB_LOG_LEVEL_ERROR, - "Reason could be also that key management plugin is not found or" - "used encryption algorithm or method does not match."); - ib_logf(IB_LOG_LEVEL_ERROR, - "Based on page page compressed %d, compressed and encrypted %d.", - page_compressed, page_compressed_encrypted); + + /* Page is really corrupted if post encryption stored + checksum does not match calculated checksum after page was + read. For pages compressed and then encrypted, there is no + checksum. */ + corrupted = (!page_compressed_encrypted && stored_checksum != calculated_checksum); + + if (corrupted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "%s: Block in space_id %lu in file %s corrupted.", + page_compressed_encrypted ? "Maybe corruption" : "Corruption", + space_id, space ? space->name : "NULL"); + ib_logf(IB_LOG_LEVEL_ERROR, + "Page based on contents %s encrypted.", + (key_version == 0 && page_compressed_encrypted == false) ? "not" : "maybe"); + if (stored_checksum != BUF_NO_CHECKSUM_MAGIC || calculated_checksum != BUF_NO_CHECKSUM_MAGIC) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Page stored checksum %lu but calculated checksum %lu.", + stored_checksum, calculated_checksum); + } + ib_logf(IB_LOG_LEVEL_ERROR, + "Reason could be that key_version %u in page " + "or in crypt_data %p could not be found.", + key_version, crypt_data); + ib_logf(IB_LOG_LEVEL_ERROR, + "Reason could be also that key management plugin is not found or" + " used encryption algorithm or method does not match."); + ib_logf(IB_LOG_LEVEL_ERROR, + "Based on page page compressed %d, compressed and encrypted %d.", + page_compressed, page_compressed_encrypted); + } else { + ib_logf(IB_LOG_LEVEL_ERROR, + "Block in space_id %lu in file %s encrypted.", + space_id, space ? space->name : "NULL"); + ib_logf(IB_LOG_LEVEL_ERROR, + "However key management plugin or used key_id %u is not found or" + " used encryption algorithm or method does not match.", + key_version); + ib_logf(IB_LOG_LEVEL_ERROR, + "Marking tablespace as missing. You may drop this table or" + " install correct key management plugin and key file."); + } } + + return corrupted; } /********************************************************************//** @@ -4534,42 +4645,46 @@ buf_page_io_complete( ;); corrupt: - fil_system_enter(); - space = fil_space_get_by_id(bpage->space); - fil_system_exit(); - ib_logf(IB_LOG_LEVEL_ERROR, - "Database page corruption on disk" - " or a failed"); - ib_logf(IB_LOG_LEVEL_ERROR, - "Space %lu file %s read of page %lu.", - (ulint)bpage->space, - space ? space->name : "NULL", - (ulong) bpage->offset); - ib_logf(IB_LOG_LEVEL_ERROR, - "You may have to recover" - " from a backup."); + bool corrupted = buf_page_check_corrupt(bpage); - buf_page_check_corrupt(bpage); + if (corrupted) { + fil_system_enter(); + space = fil_space_get_by_id(bpage->space); + fil_system_exit(); + ib_logf(IB_LOG_LEVEL_ERROR, + "Database page corruption on disk" + " or a failed"); + ib_logf(IB_LOG_LEVEL_ERROR, + "Space %lu file %s read of page %lu.", + (ulint)bpage->space, + space ? space->name : "NULL", + (ulong) bpage->offset); + ib_logf(IB_LOG_LEVEL_ERROR, + "You may have to recover" + " from a backup."); - buf_page_print(frame, buf_page_get_zip_size(bpage), - BUF_PAGE_PRINT_NO_CRASH); - ib_logf(IB_LOG_LEVEL_ERROR, - "It is also possible that your operating" - "system has corrupted its own file cache."); - ib_logf(IB_LOG_LEVEL_ERROR, - "and rebooting your computer removes the error."); - ib_logf(IB_LOG_LEVEL_ERROR, - "If the corrupt page is an index page you can also try to"); - ib_logf(IB_LOG_LEVEL_ERROR, - "fix the corruption by dumping, dropping, and reimporting"); - ib_logf(IB_LOG_LEVEL_ERROR, - "the corrupt table. You can use CHECK"); - ib_logf(IB_LOG_LEVEL_ERROR, - "TABLE to scan your table for corruption."); - ib_logf(IB_LOG_LEVEL_ERROR, - "See also " - REFMAN "forcing-innodb-recovery.html" - " about forcing recovery."); + + buf_page_print(frame, buf_page_get_zip_size(bpage), + BUF_PAGE_PRINT_NO_CRASH); + + ib_logf(IB_LOG_LEVEL_ERROR, + "It is also possible that your operating" + "system has corrupted its own file cache."); + ib_logf(IB_LOG_LEVEL_ERROR, + "and rebooting your computer removes the error."); + ib_logf(IB_LOG_LEVEL_ERROR, + "If the corrupt page is an index page you can also try to"); + ib_logf(IB_LOG_LEVEL_ERROR, + "fix the corruption by dumping, dropping, and reimporting"); + ib_logf(IB_LOG_LEVEL_ERROR, + "the corrupt table. You can use CHECK"); + ib_logf(IB_LOG_LEVEL_ERROR, + "TABLE to scan your table for corruption."); + ib_logf(IB_LOG_LEVEL_ERROR, + "See also " + REFMAN "forcing-innodb-recovery.html" + " about forcing recovery."); + } if (srv_pass_corrupt_table && bpage->space != 0 && bpage->space < SRV_LOG_SPACE_FIRST_ID) { @@ -4597,12 +4712,30 @@ corrupt: && buf_mark_space_corrupt(bpage)) { return(false); } else { - buf_page_check_corrupt(bpage); + corrupted = buf_page_check_corrupt(bpage); - ib_logf(IB_LOG_LEVEL_ERROR, - "Ending processing because of a corrupt database page."); + if (corrupted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Ending processing because of a corrupt database page."); - ut_error; + ut_error; + } + + ib_push_warning(innobase_get_trx(), DB_ENCRYPTED_DECRYPT_FAILED, + "Table in tablespace %lu encrypted." + "However key management plugin or used key_id %lu is not found or" + " used encryption algorithm or method does not match." + " Can't continue opening the table.", + bpage->key_version); + + if (bpage->space > TRX_SYS_SPACE) { + if (corrupted) { + buf_mark_space_corrupt(bpage); + } + } else { + ut_error; + } + return(false); } } } @@ -4780,11 +4913,13 @@ buf_all_freed_instance( mutex_exit(&buf_pool->LRU_list_mutex); if (UNIV_LIKELY_NULL(block)) { - fprintf(stderr, - "Page %lu %lu still fixed or dirty\n", - (ulong) block->page.space, - (ulong) block->page.offset); - ut_error; + if (block->page.key_version == 0) { + fprintf(stderr, + "Page %lu %lu still fixed or dirty\n", + (ulong) block->page.space, + (ulong) block->page.offset); + ut_error; + } } } @@ -6124,6 +6259,11 @@ buf_page_decrypt_after_read( bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame); buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); + /* If page is encrypted read post-encryption checksum */ + if (!page_compressed_encrypted && key_version != 0) { + bpage->stored_checksum = mach_read_from_4(dst_frame + + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4); + } + ut_ad(bpage->key_version == 0); if (bpage->offset == 0) { @@ -6168,6 +6308,13 @@ buf_page_decrypt_after_read( #ifdef UNIV_DEBUG fil_page_type_validate(dst_frame); #endif + + /* Calculate checksum before decrypt, this will be + used later to find out if incorrect key was used. */ + if (!page_compressed_encrypted) { + bpage->calculated_checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); + } + /* decrypt using crypt_buf to dst_frame */ fil_space_decrypt(bpage->space, slot->crypt_buf, diff --git a/storage/xtradb/buf/buf0rea.cc b/storage/xtradb/buf/buf0rea.cc index 6c74c30739e..15465699726 100644 --- a/storage/xtradb/buf/buf0rea.cc +++ b/storage/xtradb/buf/buf0rea.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2014, MariaDB Corporation. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -124,7 +124,8 @@ buf_read_page_low( use to stop dangling page reads from a tablespace which we have DISCARDed + IMPORTed back */ ulint offset, /*!< in: page number */ - trx_t* trx) + trx_t* trx, /*!< in: trx */ + buf_page_t** rbpage) /*!< out: page */ { buf_page_t* bpage; ulint wake_later; @@ -259,10 +260,17 @@ not_to_recover: /* The i/o is already completed when we arrive from fil_read */ if (!buf_page_io_complete(bpage)) { + if (rbpage) { + *rbpage = bpage; + } return(0); } } + if (rbpage) { + *rbpage = bpage; + } + return(1); } @@ -398,7 +406,7 @@ read_ahead: &err, false, ibuf_mode | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, FALSE, - tablespace_version, i, trx); + tablespace_version, i, trx, NULL); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); fprintf(stderr, @@ -446,10 +454,11 @@ UNIV_INTERN ibool buf_read_page( /*==========*/ - ulint space, /*!< in: space id */ - ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint offset, /*!< in: page number */ - trx_t* trx) + ulint space, /*!< in: space id */ + ulint zip_size, /*!< in: compressed page size in bytes, or 0 */ + ulint offset, /*!< in: page number */ + trx_t* trx, /*!< in: trx */ + buf_page_t** bpage) /*!< out: page */ { ib_int64_t tablespace_version; ulint count; @@ -462,7 +471,7 @@ buf_read_page( count = buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space, zip_size, FALSE, - tablespace_version, offset, trx); + tablespace_version, offset, trx, bpage); srv_stats.buf_pool_reads.add(count); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); @@ -510,7 +519,7 @@ buf_read_page_async( | OS_AIO_SIMULATED_WAKE_LATER | BUF_READ_IGNORE_NONEXISTENT_PAGES, space, zip_size, FALSE, - tablespace_version, offset, NULL); + tablespace_version, offset, NULL,NULL); srv_stats.buf_pool_reads.add(count); /* We do not increment number of I/O operations used for LRU policy @@ -778,7 +787,7 @@ buf_read_ahead_linear( count += buf_read_page_low( &err, false, ibuf_mode, - space, zip_size, FALSE, tablespace_version, i, trx); + space, zip_size, FALSE, tablespace_version, i, trx, NULL); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(stderr); fprintf(stderr, @@ -868,7 +877,7 @@ buf_read_ibuf_merge_pages( buf_read_page_low(&err, sync && (i + 1 == n_stored), BUF_READ_ANY_PAGE, space_ids[i], zip_size, TRUE, space_versions[i], - page_nos[i], NULL); + page_nos[i], NULL, NULL); if (UNIV_UNLIKELY(err == DB_TABLESPACE_DELETED)) { tablespace_deleted: @@ -1008,12 +1017,12 @@ not_to_recover: if ((i + 1 == n_stored) && sync) { buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space, zip_size, TRUE, tablespace_version, - page_nos[i], NULL); + page_nos[i], NULL, NULL); } else { buf_read_page_low(&err, false, BUF_READ_ANY_PAGE | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, TRUE, - tablespace_version, page_nos[i], NULL); + tablespace_version, page_nos[i], NULL, NULL); } } diff --git a/storage/xtradb/fil/fil0crypt.cc b/storage/xtradb/fil/fil0crypt.cc index 289c4df60bf..f60a4fb3fcf 100644 --- a/storage/xtradb/fil/fil0crypt.cc +++ b/storage/xtradb/fil/fil0crypt.cc @@ -650,31 +650,8 @@ fil_space_encrypt( /* handle post encryption checksum */ ib_uint32_t checksum = 0; - srv_checksum_algorithm_t algorithm = - static_cast(srv_checksum_algorithm); - if (zip_size == 0) { - switch (algorithm) { - case SRV_CHECKSUM_ALGORITHM_CRC32: - case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: - checksum = buf_calc_page_crc32(dst_frame); - break; - case SRV_CHECKSUM_ALGORITHM_INNODB: - case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: - checksum = (ib_uint32_t) buf_calc_page_new_checksum( - dst_frame); - break; - case SRV_CHECKSUM_ALGORITHM_NONE: - case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: - checksum = BUF_NO_CHECKSUM_MAGIC; - break; - /* no default so the compiler will emit a warning - * if new enum is added and not handled here */ - } - } else { - checksum = page_zip_calc_checksum(dst_frame, zip_size, - algorithm); - } + checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); // store the post-encryption checksum after the key-version mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum); @@ -818,6 +795,47 @@ fil_space_decrypt( return src_frame; } +/****************************************************************** +Calculate post encryption checksum +@return page checksum or BUF_NO_CHECKSUM_MAGIC +not needed. */ +UNIV_INTERN +ulint +fil_crypt_calculate_checksum( +/*=========================*/ + ulint zip_size, /*!< in: zip_size or 0 */ + byte* dst_frame) /*!< in: page where to calculate */ +{ + ib_uint32_t checksum = 0; + srv_checksum_algorithm_t algorithm = + static_cast(srv_checksum_algorithm); + + if (zip_size == 0) { + switch (algorithm) { + case SRV_CHECKSUM_ALGORITHM_CRC32: + case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: + checksum = buf_calc_page_crc32(dst_frame); + break; + case SRV_CHECKSUM_ALGORITHM_INNODB: + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: + checksum = (ib_uint32_t) buf_calc_page_new_checksum( + dst_frame); + break; + case SRV_CHECKSUM_ALGORITHM_NONE: + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + checksum = BUF_NO_CHECKSUM_MAGIC; + break; + /* no default so the compiler will emit a warning + * if new enum is added and not handled here */ + } + } else { + checksum = page_zip_calc_checksum(dst_frame, zip_size, + algorithm); + } + + return checksum; +} + /********************************************************************* Verify checksum for a page (iff it's encrypted) NOTE: currently this function can only be run in single threaded mode diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index f27c1f77027..f868d727328 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -2006,6 +2006,7 @@ convert_error_code_to_mysql( case DB_TABLESPACE_DELETED: case DB_TABLE_NOT_FOUND: + case DB_ENCRYPTED_DECRYPT_FAILED: return(HA_ERR_NO_SUCH_TABLE); case DB_TABLESPACE_NOT_FOUND: @@ -6043,7 +6044,14 @@ table_opened: innobase_copy_frm_flags_from_table_share(ib_table, table->s); - dict_stats_init(ib_table); + ib_table->thd = (void*)thd; + + /* No point to init any statistics if tablespace is still encrypted. */ + if (!ib_table->is_encrypted) { + dict_stats_init(ib_table); + } else { + ib_table->stat_initialized = 1; + } MONITOR_INC(MONITOR_TABLE_OPEN); @@ -6072,6 +6080,11 @@ table_opened: file, best to play it safe. */ no_tablespace = true; + } else if (ib_table->is_encrypted) { + /* This means that tablespace was found but we could not + decrypt encrypted page. */ + no_tablespace = true; + ib_table->ibd_file_missing = true; } else { no_tablespace = false; } @@ -6083,9 +6096,9 @@ table_opened: /* If table has no talespace but it has crypt data, check is tablespace made unaccessible because encryption service or used key_id is not available. */ - if (ib_table && ib_table->crypt_data) { + if (ib_table) { fil_space_crypt_t* crypt_data = ib_table->crypt_data; - if ((crypt_data->encryption == FIL_SPACE_ENCRYPTION_ON) || + if ((crypt_data && crypt_data->encryption == FIL_SPACE_ENCRYPTION_ON) || (srv_encrypt_tables && crypt_data && crypt_data->encryption == FIL_SPACE_ENCRYPTION_DEFAULT)) { @@ -6097,6 +6110,13 @@ table_opened: " Can't continue reading table.", ib_table->name, crypt_data->key_id); } + } else if (ib_table->is_encrypted) { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + HA_ERR_NO_SUCH_TABLE, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + ib_table->name); } } @@ -21402,3 +21422,59 @@ static void innodb_remember_check_sysvar_funcs() ut_ad((MYSQL_SYSVAR_NAME(checksum_algorithm).flags & 0x1FF) == PLUGIN_VAR_ENUM); check_sysvar_enum = MYSQL_SYSVAR_NAME(checksum_algorithm).check; } + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + trx_t* trx, /*!< in: trx */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)trx->mysql_thd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + convert_error_code_to_mysql((dberr_t)error, 0, thd), + buf); + my_free(buf); + va_end(args); +} + +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + void* ithd, /*!< in: thd */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...) +{ + va_list args; + THD *thd = (THD *)ithd; + char *buf; +#define MAX_BUF_SIZE 4*1024 + + if (ithd == NULL) { + thd = current_thd; + } + + va_start(args, format); + buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME)); + vsprintf(buf,format, args); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + convert_error_code_to_mysql((dberr_t)error, 0, thd), + buf); + my_free(buf); + va_end(args); +} diff --git a/storage/xtradb/include/btr0btr.ic b/storage/xtradb/include/btr0btr.ic index 40b468b200a..6604ac6a6f0 100644 --- a/storage/xtradb/include/btr0btr.ic +++ b/storage/xtradb/include/btr0btr.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -57,13 +58,15 @@ btr_block_get_func( buf_block_t* block; block = buf_page_get_gen(space, zip_size, page_no, mode, - NULL, BUF_GET, file, line, mtr); + NULL, BUF_GET, file, line, mtr); - if (mode != RW_NO_LATCH) { + if (block) { + if (mode != RW_NO_LATCH) { - buf_block_dbg_add_level( - block, index != NULL && dict_index_is_ibuf(index) - ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); + buf_block_dbg_add_level( + block, index != NULL && dict_index_is_ibuf(index) + ? SYNC_IBUF_TREE_NODE : SYNC_TREE_NODE); + } } return(block); diff --git a/storage/xtradb/include/btr0cur.h b/storage/xtradb/include/btr0cur.h index f4b91b08fc5..2c60f3ad64c 100644 --- a/storage/xtradb/include/btr0cur.h +++ b/storage/xtradb/include/btr0cur.h @@ -136,7 +136,7 @@ Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. If mode is PAGE_CUR_GE, then up_match will a have a sensible value. */ UNIV_INTERN -void +dberr_t btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ @@ -173,7 +173,7 @@ btr_cur_search_to_nth_level( /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN -void +dberr_t btr_cur_open_at_index_side_func( /*============================*/ bool from_left, /*!< in: true if open to the low end, diff --git a/storage/xtradb/include/btr0pcur.h b/storage/xtradb/include/btr0pcur.h index cfbaacf4de3..d8e7cf6b283 100644 --- a/storage/xtradb/include/btr0pcur.h +++ b/storage/xtradb/include/btr0pcur.h @@ -114,7 +114,7 @@ btr_pcur_open_low( Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE -void +dberr_t btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ @@ -143,7 +143,7 @@ btr_pcur_open_with_no_init_func( /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE -void +dberr_t btr_pcur_open_at_index_side( /*========================*/ bool from_left, /*!< in: true if open to the low end, diff --git a/storage/xtradb/include/btr0pcur.ic b/storage/xtradb/include/btr0pcur.ic index 7e355d3709d..1cd13824542 100644 --- a/storage/xtradb/include/btr0pcur.ic +++ b/storage/xtradb/include/btr0pcur.ic @@ -447,7 +447,7 @@ btr_pcur_open_low( Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE -void +dberr_t btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ @@ -472,6 +472,7 @@ btr_pcur_open_with_no_init_func( mtr_t* mtr) /*!< in: mtr */ { btr_cur_t* btr_cursor; + dberr_t err = DB_SUCCESS; cursor->latch_mode = latch_mode; cursor->search_mode = mode; @@ -480,20 +481,21 @@ btr_pcur_open_with_no_init_func( btr_cursor = btr_pcur_get_btr_cur(cursor); - btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, - btr_cursor, has_search_latch, - file, line, mtr); + err = btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, + btr_cursor, has_search_latch, + file, line, mtr); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; cursor->trx_if_known = NULL; + return err; } /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE -void +dberr_t btr_pcur_open_at_index_side( /*========================*/ bool from_left, /*!< in: true if open to the low end, @@ -506,6 +508,8 @@ btr_pcur_open_at_index_side( (0=leaf) */ mtr_t* mtr) /*!< in/out: mini-transaction */ { + dberr_t err = DB_SUCCESS; + pcur->latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode); pcur->search_mode = from_left ? PAGE_CUR_G : PAGE_CUR_L; @@ -514,13 +518,15 @@ btr_pcur_open_at_index_side( btr_pcur_init(pcur); } - btr_cur_open_at_index_side(from_left, index, latch_mode, - btr_pcur_get_btr_cur(pcur), level, mtr); + err = btr_cur_open_at_index_side(from_left, index, latch_mode, + btr_pcur_get_btr_cur(pcur), level, mtr); pcur->pos_state = BTR_PCUR_IS_POSITIONED; pcur->old_stored = BTR_PCUR_OLD_NOT_STORED; pcur->trx_if_known = NULL; + + return (err); } /**********************************************************************//** diff --git a/storage/xtradb/include/buf0buf.h b/storage/xtradb/include/buf0buf.h index 5a0bd203185..b35ab9df338 100644 --- a/storage/xtradb/include/buf0buf.h +++ b/storage/xtradb/include/buf0buf.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -428,7 +428,8 @@ buf_page_get_gen( BUF_GET_IF_IN_POOL_OR_WATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ - mtr_t* mtr); /*!< in: mini-transaction */ + mtr_t* mtr, /*!< in: mini-transaction */ + dberr_t* err = NULL); /*!< out: error code */ /********************************************************************//** Initializes a page to the buffer buf_pool. The page is usually not read from a file even if it cannot be found in the buffer buf_pool. This is one @@ -1611,8 +1612,14 @@ struct buf_page_t{ operation needed. */ unsigned key_version; /*!< key version for this block */ - bool page_encrypted; /*!< page is encrypted */ + bool page_encrypted; /*!< page is page encrypted */ bool page_compressed;/*!< page is page compressed */ + ulint stored_checksum;/*!< stored page checksum if page + encrypted */ + bool encrypted; /*!< page is still encrypted */ + ulint calculated_checksum; + /*!< calculated checksum if page + encrypted */ ulint real_size; /*!< Real size of the page Normal pages == UNIV_PAGE_SIZE diff --git a/storage/xtradb/include/buf0buf.ic b/storage/xtradb/include/buf0buf.ic index 89c35caa815..8c8a3c9f975 100644 --- a/storage/xtradb/include/buf0buf.ic +++ b/storage/xtradb/include/buf0buf.ic @@ -689,6 +689,10 @@ buf_block_get_frame( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { + if (!block) { + return NULL; + } + SRV_CORRUPT_TABLE_CHECK(block, return(0);); switch (buf_block_get_state(block)) { @@ -696,6 +700,9 @@ buf_block_get_frame( case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: case BUF_BLOCK_NOT_USED: + if (block->page.encrypted) { + goto ok; + } ut_error; break; case BUF_BLOCK_FILE_PAGE: diff --git a/storage/xtradb/include/buf0rea.h b/storage/xtradb/include/buf0rea.h index 9adeaa7455a..f0652b5d2cd 100644 --- a/storage/xtradb/include/buf0rea.h +++ b/storage/xtradb/include/buf0rea.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -39,10 +40,12 @@ UNIV_INTERN ibool buf_read_page( /*==========*/ - ulint space, /*!< in: space id */ - ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint offset, /*!< in: page number */ - trx_t* trx); + ulint space, /*!< in: space id */ + ulint zip_size, /*!< in: compressed page size in bytes, or 0 */ + ulint offset, /*!< in: page number */ + trx_t* trx, /*!< in: trx */ + buf_page_t** bpage /*!< out: page */ +); /********************************************************************//** High-level function which reads a page asynchronously from a file to the buffer buf_pool if it is not already there. Sets the io_fix flag and sets diff --git a/storage/xtradb/include/db0err.h b/storage/xtradb/include/db0err.h index dab917e18db..bb206dde72b 100644 --- a/storage/xtradb/include/db0err.h +++ b/storage/xtradb/include/db0err.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -132,6 +133,11 @@ enum dberr_t { /*< Too many words in a phrase */ DB_TOO_BIG_FOR_REDO, /* Record length greater than 10% of redo log */ + DB_ENCRYPTED_DECRYPT_FAILED, /* Tablespace encrypted and + decrypt operaton failed because + of missing key management plugin, + or missing or incorrect key or + incorret AES method or algorithm. */ /* The following are partial failure codes */ DB_FAIL = 1000, DB_OVERFLOW, diff --git a/storage/xtradb/include/dict0mem.h b/storage/xtradb/include/dict0mem.h index 310891162ce..9c43829cecf 100644 --- a/storage/xtradb/include/dict0mem.h +++ b/storage/xtradb/include/dict0mem.h @@ -2,7 +2,7 @@ Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -1033,6 +1033,7 @@ struct dict_table_t{ table_id_t id; /*!< id of the table */ mem_heap_t* heap; /*!< memory heap */ char* name; /*!< table name */ + void* thd; /*!< thd */ fil_space_crypt_t *crypt_data; /*!< crypt data if present */ const char* dir_path_of_temp_table;/*!< NULL or the directory path where a TEMPORARY table that was explicitly @@ -1346,6 +1347,7 @@ struct dict_table_t{ locks; /*!< list of locks on the table; protected by lock_sys->mutex */ ibool is_corrupt; + ibool is_encrypted; #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG diff --git a/storage/xtradb/include/fil0crypt.h b/storage/xtradb/include/fil0crypt.h index 4fd41d4b789..a9491e3131f 100644 --- a/storage/xtradb/include/fil0crypt.h +++ b/storage/xtradb/include/fil0crypt.h @@ -381,6 +381,17 @@ fil_crypt_set_encrypt_tables( /*=========================*/ uint val); /*!< in: New srv_encrypt_tables setting */ +/****************************************************************** +Calculate post encryption checksum +@return page checksum or BUF_NO_CHECKSUM_MAGIC +not needed. */ +UNIV_INTERN +ulint +fil_crypt_calculate_checksum( +/*=========================*/ + ulint zip_size, /*!< in: zip_size or 0 */ + byte* dst_frame); /*!< in: page where to calculate */ + #ifndef UNIV_NONINL #include "fil0crypt.ic" #endif diff --git a/storage/xtradb/include/ha_prototypes.h b/storage/xtradb/include/ha_prototypes.h index 6008332dae3..724453cb4f6 100644 --- a/storage/xtradb/include/ha_prototypes.h +++ b/storage/xtradb/include/ha_prototypes.h @@ -636,5 +636,13 @@ ib_push_warning( ulint error, /*!< in: error code to push as warning */ const char *format,/*!< in: warning message */ ...); - +/********************************************************************//** +Helper function to push warnings from InnoDB internals to SQL-layer. */ +UNIV_INTERN +void +ib_push_warning( + void* ithd, /*!< in: thd */ + ulint error, /*!< in: error code to push as warning */ + const char *format,/*!< in: warning message */ + ...); #endif /* HA_INNODB_PROTOTYPES_H */ diff --git a/storage/xtradb/include/page0cur.ic b/storage/xtradb/include/page0cur.ic index 028d33b17aa..6e068d9f739 100644 --- a/storage/xtradb/include/page0cur.ic +++ b/storage/xtradb/include/page0cur.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2015, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -39,7 +40,10 @@ page_cur_get_page( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } return(page_align(cur->rec)); } @@ -54,7 +58,11 @@ page_cur_get_block( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } + return(cur->block); } @@ -80,7 +88,10 @@ page_cur_get_rec( page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); - ut_ad(page_align(cur->rec) == cur->block->frame); + + if (cur->rec) { + ut_ad(page_align(cur->rec) == cur->block->frame); + } return(cur->rec); } diff --git a/storage/xtradb/row/row0ins.cc b/storage/xtradb/row/row0ins.cc index b1291404176..3f22a7d422d 100644 --- a/storage/xtradb/row/row0ins.cc +++ b/storage/xtradb/row/row0ins.cc @@ -2350,7 +2350,7 @@ row_ins_clust_index_entry_low( { btr_cur_t cursor; ulint* offsets = NULL; - dberr_t err; + dberr_t err = DB_SUCCESS; big_rec_t* big_rec = NULL; mtr_t mtr; mem_heap_t* offsets_heap = NULL; @@ -2380,9 +2380,16 @@ row_ins_clust_index_entry_low( the function will return in both low_match and up_match of the cursor sensible values */ - btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode, + err = btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode, &cursor, 0, __FILE__, __LINE__, &mtr); + if (err != DB_SUCCESS) { + index->table->is_encrypted = true; + index->table->ibd_file_missing = true; + mtr_commit(&mtr); + goto func_exit; + } + #ifdef UNIV_DEBUG { page_t* page = btr_cur_get_page(&cursor); @@ -2696,9 +2703,22 @@ row_ins_sec_index_entry_low( search_mode |= BTR_IGNORE_SEC_UNIQUE; } - btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, - search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + err = btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, + search_mode, + &cursor, 0, __FILE__, __LINE__, &mtr); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning(trx->mysql_thd, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + index->table->name); + index->table->is_encrypted = true; + } + goto func_exit; + } if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) { /* The insert was buffered during the search: we are done */ diff --git a/storage/xtradb/row/row0mysql.cc b/storage/xtradb/row/row0mysql.cc index 8a4536488fe..64c6783f1e4 100644 --- a/storage/xtradb/row/row0mysql.cc +++ b/storage/xtradb/row/row0mysql.cc @@ -622,6 +622,8 @@ handle_new_error: case DB_FTS_INVALID_DOCID: case DB_INTERRUPTED: case DB_DICT_CHANGED: + case DB_TABLE_NOT_FOUND: + case DB_ENCRYPTED_DECRYPT_FAILED: if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ @@ -1314,7 +1316,13 @@ row_insert_for_mysql( prebuilt->table->name); return(DB_TABLESPACE_NOT_FOUND); - + } else if (prebuilt->table->is_encrypted) { + ib_push_warning(trx, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu encrypted." + "However key management plugin or used key_id is not found or" + " used encryption algorithm or method does not match.", + prebuilt->table->name, prebuilt->table->space); + return(DB_ENCRYPTED_DECRYPT_FAILED); } else if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { fprintf(stderr, "InnoDB: Error: trying to free a corrupt\n" @@ -1713,6 +1721,13 @@ row_update_for_mysql( "InnoDB: how you can resolve the problem.\n", prebuilt->table->name); return(DB_ERROR); + } else if (prebuilt->table->is_encrypted) { + ib_push_warning(trx, DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s in tablespace %lu encrypted." + "However key management plugin or used key_id is not found or" + " used encryption algorithm or method does not match.", + prebuilt->table->name, prebuilt->table->space); + return (DB_TABLE_NOT_FOUND); } if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) { @@ -3932,6 +3947,19 @@ row_drop_table_for_mysql( goto funct_exit; } + /* If table is encrypted and table page encryption failed + mark this table read only. */ + if (table->is_encrypted) { + + if (table->can_be_evicted) { + dict_table_move_from_lru_to_non_lru(table); + } + + dict_table_close(table, TRUE, FALSE); + err = DB_READ_ONLY; + goto funct_exit; + } + /* Turn on this drop bit before we could release the dictionary latch */ table->to_be_dropped = true; diff --git a/storage/xtradb/row/row0sel.cc b/storage/xtradb/row/row0sel.cc index b1d64c16c60..a1edbca757c 100644 --- a/storage/xtradb/row/row0sel.cc +++ b/storage/xtradb/row/row0sel.cc @@ -2,6 +2,7 @@ Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. +Copyright (c) 2015, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -3726,6 +3727,9 @@ row_search_for_mysql( return(DB_TABLESPACE_NOT_FOUND); + } else if (prebuilt->table->is_encrypted) { + + return(DB_ENCRYPTED_DECRYPT_FAILED); } else if (!prebuilt->index_usable) { return(DB_MISSING_HISTORY); @@ -4137,9 +4141,14 @@ wait_table_again: } else if (dtuple_get_n_fields(search_tuple) > 0) { - btr_pcur_open_with_no_init(index, search_tuple, mode, - BTR_SEARCH_LEAF, - pcur, 0, &mtr); + err = btr_pcur_open_with_no_init(index, search_tuple, mode, + BTR_SEARCH_LEAF, + pcur, 0, &mtr); + + if (err != DB_SUCCESS) { + rec = NULL; + goto lock_wait_or_error; + } pcur->trx_if_known = trx; @@ -4173,9 +4182,23 @@ wait_table_again: } } } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_L) { - btr_pcur_open_at_index_side( + err = btr_pcur_open_at_index_side( mode == PAGE_CUR_G, index, BTR_SEARCH_LEAF, pcur, false, 0, &mtr); + + if (err != DB_SUCCESS) { + if (err == DB_ENCRYPTED_DECRYPT_FAILED) { + ib_push_warning(trx->mysql_thd, + DB_ENCRYPTED_DECRYPT_FAILED, + "Table %s is encrypted but encryption service or" + " used key_id is not available. " + " Can't continue reading table.", + prebuilt->table->name); + index->table->is_encrypted = true; + } + rec = NULL; + goto lock_wait_or_error; + } } rec_loop: @@ -4191,6 +4214,11 @@ rec_loop: rec = btr_pcur_get_rec(pcur); + if (!rec) { + err = DB_ENCRYPTED_DECRYPT_FAILED; + goto lock_wait_or_error; + } + SRV_CORRUPT_TABLE_CHECK(rec, { err = DB_CORRUPTION; @@ -5132,7 +5160,9 @@ lock_wait_or_error: /*-------------------------------------------------------------*/ - btr_pcur_store_position(pcur, &mtr); + if (rec) { + btr_pcur_store_position(pcur, &mtr); + } lock_table_wait: mtr_commit(&mtr);