summaryrefslogtreecommitdiff
path: root/storage/xtradb/fil/fil0fil.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/xtradb/fil/fil0fil.cc')
-rw-r--r--storage/xtradb/fil/fil0fil.cc396
1 files changed, 373 insertions, 23 deletions
diff --git a/storage/xtradb/fil/fil0fil.cc b/storage/xtradb/fil/fil0fil.cc
index 08487f595ed..e4be4f6910c 100644
--- a/storage/xtradb/fil/fil0fil.cc
+++ b/storage/xtradb/fil/fil0fil.cc
@@ -56,6 +56,10 @@ Created 10/25/1995 Heikki Tuuri
static ulint srv_data_read, srv_data_written;
#endif /* !UNIV_HOTBACKUP */
#include "fil0pagecompress.h"
+
+#include "fil0pageencryption.h"
+#include "fsp0pageencryption.h"
+
#include "zlib.h"
#ifdef __linux__
#include <linux/fs.h>
@@ -645,8 +649,23 @@ fil_node_open_file(
success = os_file_read(node->handle, page, 0, UNIV_PAGE_SIZE,
space->flags);
+ if (fil_page_encryption_status(page)) {
+ /* if page is (still) encrypted, write an error and return.
+ * Otherwise the server would crash if decrypting is not possible.
+ * This may be the case, if the key file could not be
+ * opened on server startup.
+ */
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "InnoDB: can not decrypt page, because "
+ "keys could not be read.\n"
+ );
+ return false;
+
+ }
+
space_id = fsp_header_get_space_id(page);
flags = fsp_header_get_flags(page);
+
page_size = fsp_flags_get_page_size(flags);
atomic_writes = fsp_flags_get_atomic_writes(flags);
@@ -1157,7 +1176,8 @@ fil_space_create(
const char* name, /*!< in: space name */
ulint id, /*!< in: space id */
ulint flags, /*!< in: tablespace flags */
- ulint purpose)/*!< in: FIL_TABLESPACE, or FIL_LOG if log */
+ ulint purpose,/*!< in: FIL_TABLESPACE, or FIL_LOG if log */
+ fil_space_crypt_t* crypt_data) /*!< in: crypt data */
{
fil_space_t* space;
@@ -1165,6 +1185,21 @@ fil_space_create(
ut_a(fil_system);
+ if (fsp_flags_is_page_encrypted(flags)) {
+ if (!HasCryptoKey(fsp_flags_get_page_encryption_key(flags))) {
+ /* by returning here it should be avoided that
+ * the server crashes, if someone tries to access an
+ * encrypted table and the encryption key is not available.
+ * The the table is treaded as non-existent.
+ */
+ ib_logf(IB_LOG_LEVEL_WARN,
+ "Tablespace '%s' can not be opened, because "
+ " encryption key can not be found (space id: %lu, key %lu)\n"
+ , name, (ulong) id, fsp_flags_get_page_encryption_key(flags));
+ return (FALSE);
+ }
+ }
+
/* Look for a matching tablespace and if found free it. */
do {
mutex_enter(&fil_system->mutex);
@@ -1253,6 +1288,8 @@ fil_space_create(
UT_LIST_ADD_LAST(space_list, fil_system->space_list, space);
+ space->crypt_data = crypt_data;
+
mutex_exit(&fil_system->mutex);
return(TRUE);
@@ -1387,6 +1424,8 @@ fil_space_free(
rw_lock_free(&(space->latch));
+ fil_space_destroy_crypt_data(&(space->crypt_data));
+
mem_free(space->name);
mem_free(space);
@@ -1620,6 +1659,8 @@ fil_init(
UT_LIST_INIT(fil_system->LRU);
fil_system->max_n_open = max_n_open;
+
+ fil_space_crypt_init();
}
/*******************************************************************//**
@@ -1827,7 +1868,8 @@ fil_write_lsn_and_arch_no_to_file(
err = fil_read(TRUE, space, 0, sum_of_sizes, 0,
UNIV_PAGE_SIZE, buf, NULL, 0);
if (err == DB_SUCCESS) {
- mach_write_to_8(buf + FIL_PAGE_FILE_FLUSH_LSN, lsn);
+ mach_write_to_8(buf + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION,
+ lsn);
err = fil_write(TRUE, space, 0, sum_of_sizes, 0,
UNIV_PAGE_SIZE, buf, NULL, 0);
@@ -1909,6 +1951,7 @@ fil_check_first_page(
{
ulint space_id;
ulint flags;
+ ulint page_is_encrypted;
if (srv_force_recovery >= SRV_FORCE_IGNORE_CORRUPT) {
return(NULL);
@@ -1916,12 +1959,23 @@ fil_check_first_page(
space_id = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + page);
flags = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page);
-
- if (UNIV_PAGE_SIZE != fsp_flags_get_page_size(flags)) {
- fprintf(stderr, "InnoDB: Error: Current page size %lu != page size on page %lu\n",
- UNIV_PAGE_SIZE, fsp_flags_get_page_size(flags));
-
- return("innodb-page-size mismatch");
+ /* Note: the 1st page is usually not encrypted. If the Key Provider
+ or the encryption key is not available, the
+ check for reading the first page should intentionally fail
+ with "can not decrypt" message. */
+ page_is_encrypted = fil_page_encryption_status(page);
+ if (page_is_encrypted == PAGE_ENCRYPTION_KEY_MISSING && page_is_encrypted) {
+ page_is_encrypted = 1;
+ } else {
+ page_is_encrypted = 0;
+ if (UNIV_PAGE_SIZE != fsp_flags_get_page_size(flags)) {
+ fprintf(stderr,
+ "InnoDB: Error: Current page size %lu != "
+ " page size on page %lu\n",
+ UNIV_PAGE_SIZE, fsp_flags_get_page_size(flags));
+
+ return("innodb-page-size mismatch");
+ }
}
if (!space_id && !flags) {
@@ -1937,9 +1991,17 @@ fil_check_first_page(
}
}
- if (buf_page_is_corrupted(
+ if (!page_is_encrypted && buf_page_is_corrupted(
false, page, fsp_flags_get_zip_size(flags))) {
return("checksum mismatch");
+ } else {
+ if (page_is_encrypted) {
+ /* this error message is interpreted by the calling method, which is
+ * executed if the server starts in recovery mode.
+ */
+ return(MSG_CANNOT_DECRYPT);
+
+ }
}
if (page_get_space_id(page) == space_id
@@ -1969,8 +2031,9 @@ fil_read_first_page(
lsn values in data files */
lsn_t* max_flushed_lsn, /*!< out: max of flushed
lsn values in data files */
- ulint orig_space_id) /*!< in: original file space
+ ulint orig_space_id, /*!< in: original file space
id */
+ fil_space_crypt_t** crypt_data) /*< out: crypt data */
{
byte* buf;
byte* page;
@@ -2008,7 +2071,16 @@ fil_read_first_page(
check_msg = fil_check_first_page(page);
}
- flushed_lsn = mach_read_from_8(page + FIL_PAGE_FILE_FLUSH_LSN);
+ flushed_lsn = mach_read_from_8(page +
+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
+
+ if (crypt_data) {
+ ulint space = fsp_header_get_space_id(page);
+ ulint offset =
+ fsp_header_get_crypt_offset(
+ fsp_flags_get_zip_size(*flags), NULL);
+ *crypt_data = fil_space_read_crypt_data(space, page, offset);
+ }
ut_free(buf);
@@ -2487,6 +2559,9 @@ fil_check_pending_operations(
*space = 0;
+ /* Wait for crypt threads to stop accessing space */
+ fil_space_crypt_close_tablespace(id);
+
mutex_enter(&fil_system->mutex);
fil_space_t* sp = fil_space_get_by_id(id);
if (sp) {
@@ -3468,7 +3543,8 @@ fil_create_new_single_table_tablespace(
}
}
- success = fil_space_create(tablename, space_id, flags, FIL_TABLESPACE);
+ success = fil_space_create(tablename, space_id, flags, FIL_TABLESPACE,
+ fil_space_create_crypt_data());
if (!success || !fil_node_create(path, size, space_id, FALSE)) {
err = DB_ERROR;
goto error_exit_1;
@@ -3596,6 +3672,7 @@ fil_open_single_table_tablespace(
ulint tablespaces_found = 0;
ulint valid_tablespaces_found = 0;
ulint atomic_writes = 0;
+ fil_space_crypt_t* crypt_data = NULL;
#ifdef UNIV_SYNC_DEBUG
ut_ad(!fix_dict || rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
@@ -3694,7 +3771,7 @@ fil_open_single_table_tablespace(
if (def.success) {
def.check_msg = fil_read_first_page(
def.file, FALSE, &def.flags, &def.id,
- &def.lsn, &def.lsn, id);
+ &def.lsn, &def.lsn, id, &def.crypt_data);
def.valid = !def.check_msg;
/* Validate this single-table-tablespace with SYS_TABLES,
@@ -3716,7 +3793,7 @@ fil_open_single_table_tablespace(
if (remote.success) {
remote.check_msg = fil_read_first_page(
remote.file, FALSE, &remote.flags, &remote.id,
- &remote.lsn, &remote.lsn, id);
+ &remote.lsn, &remote.lsn, id, &remote.crypt_data);
remote.valid = !remote.check_msg;
/* Validate this single-table-tablespace with SYS_TABLES,
@@ -3739,7 +3816,7 @@ fil_open_single_table_tablespace(
if (dict.success) {
dict.check_msg = fil_read_first_page(
dict.file, FALSE, &dict.flags, &dict.id,
- &dict.lsn, &dict.lsn, id);
+ &dict.lsn, &dict.lsn, id, &dict.crypt_data);
dict.valid = !dict.check_msg;
/* Validate this single-table-tablespace with SYS_TABLES,
@@ -3892,9 +3969,17 @@ fil_open_single_table_tablespace(
}
skip_validate:
+ if (remote.success)
+ crypt_data = remote.crypt_data;
+ else if (dict.success)
+ crypt_data = dict.crypt_data;
+ else if (def.success)
+ crypt_data = def.crypt_data;
+
if (err != DB_SUCCESS) {
; // Don't load the tablespace into the cache
- } else if (!fil_space_create(tablename, id, flags, FIL_TABLESPACE)) {
+ } else if (!fil_space_create(tablename, id, flags, FIL_TABLESPACE,
+ crypt_data)) {
err = DB_ERROR;
} else {
/* We do not measure the size of the file, that is why
@@ -3914,15 +3999,25 @@ cleanup_and_exit:
if (remote.filepath) {
mem_free(remote.filepath);
}
+ if (remote.crypt_data && remote.crypt_data != crypt_data) {
+ fil_space_destroy_crypt_data(&remote.crypt_data);
+ }
if (dict.success) {
os_file_close(dict.file);
}
if (dict.filepath) {
mem_free(dict.filepath);
}
+ if (dict.crypt_data && dict.crypt_data != crypt_data) {
+ fil_space_destroy_crypt_data(&dict.crypt_data);
+ }
if (def.success) {
os_file_close(def.file);
}
+ if (def.crypt_data && def.crypt_data != crypt_data) {
+ fil_space_destroy_crypt_data(&def.crypt_data);
+ }
+
mem_free(def.filepath);
return(err);
@@ -4139,13 +4234,22 @@ fil_validate_single_table_tablespace(
check_first_page:
fsp->success = TRUE;
+ fsp->encryption_error = 0;
if (const char* check_msg = fil_read_first_page(
fsp->file, FALSE, &fsp->flags, &fsp->id,
- &fsp->lsn, &fsp->lsn, ULINT_UNDEFINED)) {
+ &fsp->lsn, &fsp->lsn, ULINT_UNDEFINED, &fsp->crypt_data)) {
ib_logf(IB_LOG_LEVEL_ERROR,
"%s in tablespace %s (table %s)",
check_msg, fsp->filepath, tablename);
fsp->success = FALSE;
+ if (strncmp(check_msg, MSG_CANNOT_DECRYPT, strlen(check_msg))==0) {
+ /* by returning here, it should be avoided, that the server crashes,
+ * if started in recovery mode and can not decrypt tables, if
+ * the key file can not be read.
+ */
+ fsp->encryption_error = 1;
+ return;
+ }
}
if (!fsp->success) {
@@ -4299,6 +4403,14 @@ fil_load_single_table_tablespace(
}
if (!def.success && !remote.success) {
+
+ if (def.encryption_error || remote.encryption_error) {
+ fprintf(stderr,
+ "InnoDB: Error: could not open single-table"
+ " tablespace file %s. Encryption error!\n", def.filepath);
+ return;
+ }
+
/* The following call prints an error message */
os_file_get_last_error(true);
fprintf(stderr,
@@ -4482,7 +4594,8 @@ will_not_choose:
mutex_exit(&fil_system->mutex);
#endif /* UNIV_HOTBACKUP */
ibool file_space_create_success = fil_space_create(
- tablename, fsp->id, fsp->flags, FIL_TABLESPACE);
+ tablename, fsp->id, fsp->flags, FIL_TABLESPACE,
+ fsp->crypt_data);
if (!file_space_create_success) {
if (srv_force_recovery > 0) {
@@ -5133,7 +5246,7 @@ retry:
success = os_aio(OS_FILE_WRITE, OS_AIO_SYNC,
node->name, node->handle, buf,
offset, page_size * n_pages,
- node, NULL, space_id, NULL, 0, 0, 0);
+ node, NULL, space_id, NULL, 0, 0, 0, 0, 0);
#endif /* UNIV_HOTBACKUP */
if (success) {
os_has_said_disk_full = FALSE;
@@ -5526,6 +5639,8 @@ _fil_io(
ibool ignore_nonexistent_pages;
ibool page_compressed = FALSE;
ulint page_compression_level = 0;
+ ibool page_encrypted;
+ ulint page_encryption_key;
is_log = type & OS_FILE_LOG;
type = type & ~OS_FILE_LOG;
@@ -5595,6 +5710,11 @@ _fil_io(
page_compressed = fsp_flags_is_page_compressed(space->flags);
page_compression_level = fsp_flags_get_page_compression_level(space->flags);
+
+ page_encrypted = fsp_flags_is_page_encrypted(space->flags);
+ page_encryption_key = fsp_flags_get_page_encryption_key(space->flags);
+
+
/* If we are deleting a tablespace we don't allow any read
operations on that. However, we do allow write operations. */
if (space == 0 || (type == OS_FILE_READ && space->stop_new_ops)) {
@@ -5739,9 +5859,23 @@ _fil_io(
}
/* Queue the aio request */
- ret = os_aio(type, mode | wake_later, node->name, node->handle, buf,
- offset, len, node, message, space_id, trx,
- page_compressed, page_compression_level, write_size);
+ ret = os_aio(
+ type,
+ mode | wake_later,
+ node->name,
+ node->handle,
+ buf,
+ offset,
+ len,
+ node,
+ message,
+ space_id,
+ trx,
+ page_compressed,
+ page_compression_level,
+ write_size,
+ page_encrypted,
+ page_encryption_key);
#else
/* In mysqlbackup do normal i/o, not aio */
@@ -6180,6 +6314,8 @@ void
fil_close(void)
/*===========*/
{
+ fil_space_crypt_cleanup();
+
#ifndef UNIV_HOTBACKUP
/* The mutex should already have been freed. */
ut_ad(fil_system->mutex.magic_n == 0);
@@ -6229,6 +6365,8 @@ struct fil_iterator_t {
ulint n_io_buffers; /*!< Number of pages to use
for IO */
byte* io_buffer; /*!< Buffer to use for IO */
+ fil_space_crypt_t *crypt_data; /*!< Crypt data (if encrypted) */
+ byte* crypt_io_buffer; /*!< IO buffer when encrypted */
};
/********************************************************************//**
@@ -6291,7 +6429,12 @@ fil_iterate(
ut_ad(n_bytes > 0);
ut_ad(!(n_bytes % iter.page_size));
- if (!os_file_read(iter.file, io_buffer, offset,
+ byte* readptr = io_buffer;
+ if (iter.crypt_data != NULL) {
+ readptr = iter.crypt_io_buffer;
+ }
+
+ if (!os_file_read(iter.file, readptr, offset,
(ulint) n_bytes,
fil_space_is_page_compressed(space_id))) {
@@ -6306,6 +6449,18 @@ fil_iterate(
for (ulint i = 0; i < n_pages_read; ++i) {
+ if (iter.crypt_data != NULL) {
+ bool decrypted = fil_space_decrypt(
+ iter.crypt_data,
+ readptr + i * iter.page_size, // src
+ iter.page_size,
+ io_buffer + i * iter.page_size); // dst
+ if (decrypted) {
+ /* write back unencrypted page */
+ updated = true;
+ }
+ }
+
buf_block_set_file_page(block, space_id, page_no++);
dberr_t err;
@@ -6448,6 +6603,13 @@ fil_tablespace_iterate(
iter.n_io_buffers = n_io_buffers;
iter.page_size = callback.get_page_size();
+ ulint crypt_data_offset = fsp_header_get_crypt_offset(
+ callback.get_zip_size(), 0);
+
+ /* read (optional) crypt data */
+ iter.crypt_data = fil_space_read_crypt_data(
+ 0, page, crypt_data_offset);
+
/* Compressed pages can't be optimised for block IO for now.
We do the IMPORT page by page. */
@@ -6456,6 +6618,14 @@ fil_tablespace_iterate(
ut_a(iter.page_size == callback.get_zip_size());
}
+ /** If tablespace is encrypted, it needs extra buffers */
+ if (iter.crypt_data != NULL) {
+ /* decrease io buffers so that memory
+ * consumption doesnt double
+ * note: the +1 is to avoid n_io_buffers getting down to 0 */
+ iter.n_io_buffers = (iter.n_io_buffers + 1) / 2;
+ }
+
/** Add an extra page for compressed page scratch area. */
void* io_buffer = mem_alloc(
@@ -6464,9 +6634,45 @@ fil_tablespace_iterate(
iter.io_buffer = static_cast<byte*>(
ut_align(io_buffer, UNIV_PAGE_SIZE));
+ void* crypt_io_buffer = NULL;
+ if (iter.crypt_data != NULL) {
+ crypt_io_buffer = mem_alloc(
+ iter.n_io_buffers * UNIV_PAGE_SIZE);
+ iter.crypt_io_buffer = static_cast<byte*>(
+ crypt_io_buffer);
+ }
+
err = fil_iterate(iter, &block, callback);
mem_free(io_buffer);
+
+ if (iter.crypt_data != NULL) {
+ /* clear crypt data from page 0 and write it back */
+ os_file_read(file, page, 0, UNIV_PAGE_SIZE, 0);
+ fil_space_clear_crypt_data(page, crypt_data_offset);
+ lsn_t lsn = mach_read_from_8(page + FIL_PAGE_LSN);
+ if (callback.get_zip_size() == 0) {
+ buf_flush_init_for_writing(
+ page, 0, lsn);
+ } else {
+ buf_flush_update_zip_checksum(
+ page, callback.get_zip_size(), lsn);
+ }
+
+ if (!os_file_write(
+ iter.filepath, iter.file, page,
+ 0, iter.page_size)) {
+
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "os_file_write() failed");
+
+ return(DB_IO_ERROR);
+ }
+
+ mem_free(crypt_io_buffer);
+ iter.crypt_io_buffer = NULL;
+ fil_space_destroy_crypt_data(&iter.crypt_data);
+ }
}
if (err == DB_SUCCESS) {
@@ -6700,6 +6906,16 @@ fil_space_name(
}
/*******************************************************************//**
+Return space flags */
+ulint
+fil_space_flags(
+/*===========*/
+ fil_space_t* space) /*!< in: space */
+{
+ return (space->flags);
+}
+
+/*******************************************************************//**
Return page type name */
const char*
fil_get_page_type_name(
@@ -6752,3 +6968,137 @@ fil_node_get_block_size(
{
return (node->file_block_size);
}
+
+/******************************************************************
+Get id of first tablespace or ULINT_UNDEFINED if none */
+UNIV_INTERN
+ulint
+fil_get_first_space()
+{
+ ulint out_id = ULINT_UNDEFINED;
+ fil_space_t* space;
+
+ mutex_enter(&fil_system->mutex);
+
+ space = UT_LIST_GET_FIRST(fil_system->space_list);
+ if (space != NULL) {
+ do
+ {
+ if (!space->stop_new_ops) {
+ out_id = space->id;
+ break;
+ }
+ space = UT_LIST_GET_NEXT(space_list, space);
+ } while (space != NULL);
+ }
+
+ mutex_exit(&fil_system->mutex);
+
+ return out_id;
+}
+
+/******************************************************************
+Get id of next tablespace or ULINT_UNDEFINED if none */
+UNIV_INTERN
+ulint
+fil_get_next_space(ulint id)
+{
+ bool found;
+ fil_space_t* space;
+ ulint out_id = ULINT_UNDEFINED;
+
+ mutex_enter(&fil_system->mutex);
+
+ space = fil_space_get_by_id(id);
+ if (space == NULL) {
+ /* we didn't find it...search for space with space->id > id */
+ found = false;
+ space = UT_LIST_GET_FIRST(fil_system->space_list);
+ } else {
+ /* we found it, take next available space */
+ found = true;
+ }
+
+ while ((space = UT_LIST_GET_NEXT(space_list, space)) != NULL) {
+
+ if (!found && space->id <= id)
+ continue;
+
+ if (!space->stop_new_ops) {
+ /* inc reference to prevent drop */
+ out_id = space->id;
+ break;
+ }
+ }
+
+ mutex_exit(&fil_system->mutex);
+
+ return out_id;
+}
+
+/******************************************************************
+Get crypt data for a tablespace */
+UNIV_INTERN
+fil_space_crypt_t*
+fil_space_get_crypt_data(
+/*==================*/
+ ulint id) /*!< in: space id */
+{
+ fil_space_t* space;
+ fil_space_crypt_t* crypt_data = NULL;
+
+ ut_ad(fil_system);
+
+ mutex_enter(&fil_system->mutex);
+
+ space = fil_space_get_by_id(id);
+ if (space != NULL) {
+ crypt_data = space->crypt_data;
+ }
+
+ mutex_exit(&fil_system->mutex);
+
+ return(crypt_data);
+}
+
+/******************************************************************
+Get crypt data for a tablespace */
+UNIV_INTERN
+void
+fil_space_set_crypt_data(
+/*==================*/
+ ulint id, /*!< in: space id */
+ fil_space_crypt_t* crypt_data) /*!< in: crypt data */
+{
+ fil_space_t* space;
+ fil_space_crypt_t* old_crypt_data = NULL;
+
+ ut_ad(fil_system);
+
+ mutex_enter(&fil_system->mutex);
+
+ space = fil_space_get_by_id(id);
+ if (space != NULL) {
+
+ if (space->crypt_data != NULL) {
+ ut_a(!fil_space_crypt_compare(crypt_data,
+ space->crypt_data));
+ old_crypt_data = space->crypt_data;
+ }
+
+ space->crypt_data = crypt_data;
+ } else {
+ /* there is a small risk that tablespace has been deleted */
+ old_crypt_data = crypt_data;
+ }
+
+ mutex_exit(&fil_system->mutex);
+
+ if (old_crypt_data != NULL) {
+ /* first assign space->crypt_data
+ * then destroy old_crypt_data when no new references to
+ * it can be created.
+ */
+ fil_space_destroy_crypt_data(&old_crypt_data);
+ }
+}