diff options
Diffstat (limited to 'storage/xtradb/fil/fil0fil.cc')
-rw-r--r-- | storage/xtradb/fil/fil0fil.cc | 396 |
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); + } +} |