diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index b2758e1253f..a4825adbbbb 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -59,6 +59,14 @@ Created 11/5/1995 Heikki Tuuri #include "ut0byte.h" #include +#ifdef HAVE_LZO +#include "lzo/lzo1x.h" +#endif + +/* Number of temporary slots used for encryption/compression +memory allocation before/after I/O operations */ +#define BUF_MAX_TMP_SLOTS 200 + /* IMPLEMENTATION OF THE BUFFER POOL ================================= @@ -1001,14 +1009,11 @@ buf_block_init( block->page.state = BUF_BLOCK_NOT_USED; block->page.buf_fix_count = 0; block->page.io_fix = BUF_IO_NONE; - block->page.crypt_buf = NULL; - block->page.crypt_buf_free = NULL; - block->page.comp_buf = NULL; - block->page.comp_buf_free = NULL; block->page.key_version = 0; - block->page.encrypt_later = false; - + block->page.real_size = 0; + block->page.write_size = 0; block->modify_clock = 0; + block->page.slot = NULL; #if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG block->page.file_page_was_freed = FALSE; @@ -1361,6 +1366,11 @@ buf_pool_init_instance( /* Initialize the iterator for single page scan search */ new(&buf_pool->single_scan_itr) LRUItr(buf_pool, &buf_pool->mutex); + /* Initialize the temporal memory array and slots */ + buf_pool->tmp_arr = (buf_tmp_array_t *)mem_zalloc(sizeof(buf_tmp_array_t)); + buf_pool->tmp_arr->n_slots = BUF_MAX_TMP_SLOTS; + buf_pool->tmp_arr->slots = (buf_tmp_buffer_t*)mem_zalloc(sizeof(buf_tmp_buffer_t) * BUF_MAX_TMP_SLOTS); + buf_pool_mutex_exit(buf_pool); return(DB_SUCCESS); @@ -1412,6 +1422,25 @@ buf_pool_free_instance( ha_clear(buf_pool->page_hash); hash_table_free(buf_pool->page_hash); hash_table_free(buf_pool->zip_hash); + + /* Free all used temporary slots */ + for(ulint i = 0; i < buf_pool->tmp_arr->n_slots; i++) { + buf_tmp_buffer_t* slot = &buf_pool->tmp_arr->slots[i]; +#ifdef HAVE_LZO + if (slot->lzo_mem) { + ut_free(slot->lzo_mem); + } +#endif + if (slot->crypt_buf_free) { + ut_free(slot->crypt_buf_free); + } + if (slot->comp_buf_free) { + ut_free(slot->comp_buf_free); + } + } + + mem_free(buf_pool->tmp_arr->slots); + mem_free(buf_pool->tmp_arr); } /********************************************************************//** @@ -3494,12 +3523,9 @@ buf_page_init_low( bpage->newest_modification = 0; bpage->oldest_modification = 0; bpage->write_size = 0; - bpage->crypt_buf = NULL; - bpage->crypt_buf_free = NULL; - bpage->comp_buf = NULL; - bpage->comp_buf_free = NULL; bpage->key_version = 0; - bpage->encrypt_later = false; + bpage->real_size = 0; + bpage->slot = NULL; HASH_INVALIDATE(bpage, hash); #if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG @@ -4366,7 +4392,11 @@ corrupt: } } else { /* io_type == BUF_IO_WRITE */ - buf_page_encrypt_after_write(bpage); + if (bpage->slot) { + /* Mark slot free */ + bpage->slot->reserved = false; + bpage->slot = NULL; + } } buf_pool_mutex_enter(buf_pool); @@ -5602,6 +5632,60 @@ buf_page_init_for_backup_restore( } #endif /* !UNIV_HOTBACKUP */ +/********************************************************************//** +Reserve unused slot from temporary memory array and allocate necessary +temporary memory if not yet allocated. +@return reserved slot */ +buf_tmp_buffer_t* +buf_pool_reserve_tmp_slot( +/*======================*/ + buf_pool_t* buf_pool, /*!< in: buffer pool where to + reserve */ + bool compressed) /*!< in: is file space compressed */ +{ + buf_tmp_buffer_t *free_slot=NULL; + + /* Array is protected by buf_pool mutex */ + buf_pool_mutex_enter(buf_pool); + + for(ulint i = 0; i < buf_pool->tmp_arr->n_slots; i++) { + buf_tmp_buffer_t *slot = &buf_pool->tmp_arr->slots[i]; + + if(slot->reserved == false) { + free_slot = slot; + break; + } + } + + /* We assume that free slot is found */ + ut_a(free_slot != NULL); + free_slot->reserved = true; + /* Now that we have reserved this slot we can release + buf_pool mutex */ + buf_pool_mutex_exit(buf_pool); + + /* Allocate temporary memory for encryption/decryption */ + if (free_slot->crypt_buf_free == NULL) { + free_slot->crypt_buf_free = static_cast(ut_malloc(UNIV_PAGE_SIZE*2)); + free_slot->crypt_buf = static_cast(ut_align(free_slot->crypt_buf_free, UNIV_PAGE_SIZE)); + memset(free_slot->crypt_buf_free, 0, UNIV_PAGE_SIZE *2); + } + + /* For page compressed tables allocate temporary memory for + compression/decompression */ + if (compressed && free_slot->comp_buf_free == NULL) { + free_slot->comp_buf_free = static_cast(ut_malloc(UNIV_PAGE_SIZE*2)); + free_slot->comp_buf = static_cast(ut_align(free_slot->comp_buf_free, UNIV_PAGE_SIZE)); + memset(free_slot->comp_buf_free, 0, UNIV_PAGE_SIZE *2); +#ifdef HAVE_LZO + free_slot->lzo_mem = static_cast(ut_malloc(LZO1X_1_15_MEM_COMPRESS)); + memset(free_slot->lzo_mem, 0, LZO1X_1_15_MEM_COMPRESS); +#endif + } + + return (free_slot); +} + /********************************************************************//** Encrypts a buffer page right before it's flushed to disk */ @@ -5609,11 +5693,17 @@ byte* buf_page_encrypt_before_write( /*==========================*/ buf_page_t* bpage, /*!< in/out: buffer page to be flushed */ - const byte* src_frame) /*!< in: src frame */ + const byte* src_frame, /*!< in: src frame */ + ulint space_id) /*!< in: space id */ { - bpage->encrypt_later = false; + fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id); - if (srv_encrypt_tables == FALSE) { + if (crypt_data != NULL && crypt_data->encryption == FIL_SPACE_ENCRYPTION_OFF) { + /* Encryption is disabled */ + return const_cast(src_frame); + } + + if (!srv_encrypt_tables && (crypt_data == NULL || crypt_data->encryption == FIL_SPACE_ENCRYPTION_DEFAULT)) { /* Encryption is disabled */ return const_cast(src_frame); } @@ -5638,103 +5728,63 @@ buf_page_encrypt_before_write( ulint zip_size = buf_page_get_zip_size(bpage); ulint page_size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; + buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); + bool page_compressed = fil_space_is_page_compressed(bpage->space); - /** - * TODO(jonaso): figure out more clever malloc strategy - * - * This implementation does a malloc/free per iop for encrypted - * tablespaces. Alternative strategies that have been considered are - * - * 1) use buf_block_alloc (i.e alloc from buffer pool) - * this does not work as buf_block_alloc will then be called - * when needing to flush a page, which might be triggered - * due to shortage of memory in buffer pool - * 2) allocate a buffer per fil_node_t - * this would break abstraction layers and has therfore not been - * considered a lot. - */ + /* Find free slot from temporary memory array */ + buf_tmp_buffer_t* slot = buf_pool_reserve_tmp_slot(buf_pool, page_compressed); + bpage->slot = slot; - if (bpage->crypt_buf_free == NULL) { - bpage->crypt_buf_free = (byte*)malloc(page_size*2); - // TODO: Is 4k aligment enough ? - bpage->crypt_buf = (byte *)ut_align(bpage->crypt_buf_free, page_size); - } + byte *dst_frame = slot->crypt_buf; - byte *dst_frame = bpage->crypt_buf; - - if (!fil_space_is_page_compressed(bpage->space)) { - // encrypt page content - fil_space_encrypt(bpage->space, bpage->offset, - bpage->newest_modification, - src_frame, zip_size, dst_frame, 0); + if (!page_compressed) { + /* Encrypt page content */ + fil_space_encrypt(bpage->space, + bpage->offset, + bpage->newest_modification, + src_frame, + zip_size, + dst_frame); unsigned key_version = mach_read_from_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); ut_ad(key_version == 0 || key_version >= bpage->key_version); bpage->key_version = key_version; + bpage->real_size = zip_size; } else { - /** Compression and encryption is done later at os0file.cc */ - bpage->encrypt_later = true; - dst_frame = (byte *)src_frame; + /* First we compress the page content */ + ulint out_len = 0; + ulint block_size = fil_space_get_block_size(bpage->space, bpage->offset, page_size); + + byte *tmp = fil_compress_page(bpage->space, + (byte *)src_frame, + slot->comp_buf, + page_size, + fil_space_get_page_compression_level(bpage->space), + block_size, + &out_len, +#ifdef HAVE_LZO + slot->lzo_mem +#else + NULL +#endif + ); + + bpage->real_size = out_len; + + /* And then we encrypt the page content */ + fil_space_encrypt(bpage->space, + bpage->offset, + bpage->newest_modification, + tmp, + zip_size, + dst_frame); } // return dst_frame which will be written return dst_frame; } -/********************************************************************//** -Release memory after encrypted page has been written to disk -*/ -ibool -buf_page_encrypt_after_write( -/*=========================*/ - buf_page_t* bpage) /*!< in/out: buffer page flushed */ -{ - if (bpage->crypt_buf_free != NULL) { - free(bpage->crypt_buf_free); - bpage->crypt_buf_free = NULL; - bpage->crypt_buf = NULL; - } - - if (bpage->comp_buf_free != NULL) { - free(bpage->comp_buf_free); - bpage->comp_buf_free = NULL; - bpage->comp_buf = NULL; - } - - return (TRUE); -} - -/********************************************************************//** -Allocates memory to read in an encrypted page -*/ -byte* -buf_page_decrypt_before_read( -/*=========================*/ - buf_page_t* bpage, /*!< in/out: buffer page to be read */ - ulint zip_size) /*!< in: compressed page size, or 0 */ -{ - ulint size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; - - /* - Here we only need to allocate space for not header pages - in case of file space encryption. Table encryption is handled - later. - */ - if (!srv_encrypt_tables || bpage->offset == 0 || - fil_space_check_encryption_read(bpage->space) == false) - return zip_size ? bpage->zip.data : ((buf_block_t*) bpage)->frame; - - if (bpage->crypt_buf_free == NULL) - { - // allocate buffer to read data into - bpage->crypt_buf_free = (byte*)malloc(size*2); - // TODO: Is 4K aligment enough ? - bpage->crypt_buf = (byte*)ut_align(bpage->crypt_buf_free, size); - } - return bpage->crypt_buf; -} - /********************************************************************//** Decrypt page after it has been read from disk */ @@ -5752,96 +5802,58 @@ buf_page_decrypt_after_read( if (bpage->offset == 0) { /* File header pages are not encrypted */ - ut_a(bpage->crypt_buf == NULL); return (TRUE); } - - const byte* src_frame = bpage->crypt_buf != NULL ? - bpage->crypt_buf : dst_frame; - unsigned key_version = - mach_read_from_4(src_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); + mach_read_from_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); bool page_compressed = fil_page_is_compressed(dst_frame); + buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); + if (key_version == 0) { /* the page we read is unencrypted */ - if (fil_page_is_compressed(dst_frame)) { - if (bpage->comp_buf_free == NULL) { - bpage->comp_buf_free = (byte *)malloc(UNIV_PAGE_SIZE*2); - // TODO: is 4k aligment enough ? - bpage->comp_buf = (byte*)ut_align(bpage->comp_buf_free, UNIV_PAGE_SIZE); - } + if (page_compressed) { + /* Find free slot from temporary memory array */ + buf_tmp_buffer_t* slot = buf_pool_reserve_tmp_slot(buf_pool, page_compressed); - fil_decompress_page(bpage->comp_buf, dst_frame, size, NULL); - } else { - if (dst_frame != src_frame) { - /* but we had allocated a crypt_buf */ - // TODO: Can this be avoided ? - memcpy(dst_frame, src_frame, size); - } + fil_decompress_page(slot->comp_buf, + dst_frame, + size, + &bpage->write_size); + + /* Mark this slot as free */ + slot->reserved = false; } } else { /* the page we read is encrypted */ - if (dst_frame == src_frame) { - /* but we had NOT allocated a crypt buf - * malloc a buffer, copy page to it - * and then decrypt from that into real page*/ - bpage->crypt_buf_free = (byte *)malloc(UNIV_PAGE_SIZE*2); - // TODO: is 4k aligment enough ? - src_frame = bpage->crypt_buf = (byte*)ut_align(bpage->crypt_buf_free, UNIV_PAGE_SIZE); - memcpy(bpage->crypt_buf, dst_frame, size); - } + /* Find free slot from temporary memory array */ + buf_tmp_buffer_t* slot = buf_pool_reserve_tmp_slot(buf_pool, page_compressed); - /* decrypt from src_frame to dst_frame */ + memcpy(slot->crypt_buf, dst_frame, size); + + /* decrypt from crypt_buf to dst_frame */ fil_space_decrypt(bpage->space, - src_frame, size, dst_frame); + slot->crypt_buf, + size, + dst_frame); /* decompress from dst_frame to comp_buf and then copy to buffer pool */ if (page_compressed) { - if (bpage->comp_buf_free == NULL) { - bpage->comp_buf_free = (byte *)malloc(UNIV_PAGE_SIZE*2); - // TODO: is 4k aligment enough ? - bpage->comp_buf = (byte*)ut_align(bpage->comp_buf_free, UNIV_PAGE_SIZE); - } - - fil_decompress_page(bpage->comp_buf, dst_frame, size, NULL); + fil_decompress_page(slot->comp_buf, + dst_frame, + size, + &bpage->write_size); } + + /* Mark this slot as free */ + slot->reserved = false; } bpage->key_version = key_version; - if (bpage->crypt_buf_free != NULL) { - // free temp page - free(bpage->crypt_buf_free); - bpage->crypt_buf = NULL; - bpage->crypt_buf_free = NULL; - } - - if (bpage->comp_buf_free != NULL) { - // free temp page - free(bpage->comp_buf_free); - bpage->comp_buf = NULL; - bpage->comp_buf_free = NULL; - } - return (TRUE); } - -/********************************************************************//** -Release memory allocated for decryption -*/ -void -buf_page_decrypt_cleanup( -/*=====================*/ - buf_page_t* bpage) /*!< in/out: buffer page */ -{ - if (bpage->crypt_buf != NULL) { - free(bpage->crypt_buf_free); - bpage->crypt_buf = NULL; - bpage->crypt_buf_free = NULL; - } -} diff --git a/storage/innobase/buf/buf0dblwr.cc b/storage/innobase/buf/buf0dblwr.cc index 53ef6ecb316..03681e8fbeb 100644 --- a/storage/innobase/buf/buf0dblwr.cc +++ b/storage/innobase/buf/buf0dblwr.cc @@ -872,7 +872,7 @@ buf_dblwr_write_block_to_datafile( 0, buf_block_get_page_no(block), 0, - UNIV_PAGE_SIZE, + bpage->real_size, frame, (void*) block, (ulint *)&bpage->write_size); @@ -1231,7 +1231,7 @@ retry: TRX_SYS_SPACE, 0, offset, 0, - UNIV_PAGE_SIZE, + bpage->real_size, frame, NULL, 0); diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index b8a34558867..d893d424b02 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -909,7 +909,7 @@ buf_flush_write_block_low( break; } - frame = buf_page_encrypt_before_write(bpage, frame); + frame = buf_page_encrypt_before_write(bpage, frame, space_id); if (!srv_use_doublewrite_buf || !buf_dblwr) { fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER, @@ -918,7 +918,7 @@ buf_flush_write_block_low( zip_size, buf_page_get_page_no(bpage), 0, - zip_size ? zip_size : UNIV_PAGE_SIZE, + zip_size ? zip_size : bpage->real_size, frame, bpage, &bpage->write_size); @@ -938,7 +938,7 @@ buf_flush_write_block_low( zip_size, buf_page_get_page_no(bpage), 0, - zip_size ? zip_size : UNIV_PAGE_SIZE, + zip_size ? zip_size : bpage->real_size, frame, bpage, &bpage->write_size); diff --git a/storage/innobase/buf/buf0rea.cc b/storage/innobase/buf/buf0rea.cc index 5917af7ca9d..b4b474c547f 100644 --- a/storage/innobase/buf/buf0rea.cc +++ b/storage/innobase/buf/buf0rea.cc @@ -176,7 +176,7 @@ buf_read_page_low( ut_ad(buf_page_in_file(bpage)); - byte* frame = buf_page_decrypt_before_read(bpage, zip_size); + byte* frame = zip_size ? bpage->zip.data : ((buf_block_t*) bpage)->frame; if (sync) { thd_wait_begin(NULL, THD_WAIT_DISKIO); @@ -202,7 +202,6 @@ buf_read_page_low( } if (*err != DB_SUCCESS) { - buf_page_decrypt_cleanup(bpage); if (ignore_nonexistent_pages || *err == DB_TABLESPACE_DELETED) { buf_read_page_handle_error(bpage); return(0); diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index aab0b1f5568..07636d27879 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -319,6 +319,7 @@ fil_space_create_crypt_data() &crypt_data->mutex, SYNC_NO_ORDER_CHECK); crypt_data->iv_length = iv_length; my_random_bytes(crypt_data->iv, iv_length); + crypt_data->encryption = FIL_SPACE_ENCRYPTION_DEFAULT; return crypt_data; } @@ -421,6 +422,9 @@ fil_space_read_crypt_data( uint min_key_version = mach_read_from_4 (page + offset + MAGIC_SZ + 2 + iv_length); + fil_encryption_t encryption = (fil_encryption_t)mach_read_from_1( + page + offset + MAGIC_SZ + 2 + iv_length + 4); + const uint sz = sizeof(fil_space_crypt_t) + iv_length; fil_space_crypt_t* crypt_data = static_cast( malloc(sz)); @@ -429,6 +433,7 @@ fil_space_read_crypt_data( crypt_data->type = type; crypt_data->min_key_version = min_key_version; crypt_data->page0_offset = offset; + crypt_data->encryption = encryption; mutex_create(fil_crypt_data_mutex_key, &crypt_data->mutex, SYNC_NO_ORDER_CHECK); crypt_data->iv_length = iv_length; @@ -474,8 +479,9 @@ fil_space_write_crypt_data_low( page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); const uint len = crypt_data->iv_length; const uint min_key_version = crypt_data->min_key_version; + const fil_encryption_t encryption = crypt_data->encryption; crypt_data->page0_offset = offset; - ut_a(2 + len + 4 + MAGIC_SZ < maxsize); + ut_a(2 + len + 4 + 1 + MAGIC_SZ < maxsize); /* redo log this as bytewise updates to page 0 @@ -489,8 +495,10 @@ fil_space_write_crypt_data_low( mtr); mlog_write_ulint(page + offset + MAGIC_SZ + 2 + len, min_key_version, MLOG_4BYTES, mtr); + mlog_write_ulint(page + offset + MAGIC_SZ + 2 + len + 4, encryption, + MLOG_1BYTE, mtr); - byte* log_ptr = mlog_open(mtr, 11 + 12 + len); + byte* log_ptr = mlog_open(mtr, 11 + 13 + len); if (log_ptr != NULL) { log_ptr = mlog_write_initial_log_record_fast( @@ -507,6 +515,8 @@ fil_space_write_crypt_data_low( log_ptr += 1; mach_write_to_4(log_ptr, min_key_version); log_ptr += 4; + mach_write_to_1(log_ptr, encryption); + log_ptr += 1; mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, crypt_data->iv, len); @@ -555,7 +565,8 @@ fil_parse_write_crypt_data( 2 + // size of offset 1 + // size of type 1 + // size of iv-len - 4; // size of min_key_version + 4 + // size of min_key_version + 1; // fil_encryption_t if (end_ptr - ptr < entry_size){ return NULL; @@ -582,9 +593,17 @@ fil_parse_write_crypt_data( return NULL; } + fil_encryption_t encryption = (fil_encryption_t)mach_read_from_1(ptr); + ptr +=1; + + if (end_ptr - ptr < len) { + return NULL; + } + fil_space_crypt_t* crypt_data = fil_space_create_crypt_data(); crypt_data->page0_offset = offset; crypt_data->min_key_version = min_key_version; + crypt_data->encryption = encryption; memcpy(crypt_data->iv, ptr, len); ptr += len; @@ -610,7 +629,8 @@ fil_space_clear_crypt_data( 1 + // type 1 + // len len + // iv - 4; // min key version + 4 + // min key version + 1; // fil_encryption_t memset(page + offset, 0, size); } @@ -674,13 +694,11 @@ fil_space_encrypt( const byte* src_frame, /*!< in: Source page to be encrypted */ ulint zip_size, /*!< in: compressed size if row_format compressed */ - byte* dst_frame, /*!< in: outbut buffer */ - ulint encryption_key) /*!< in: encryption key id if page - encrypted */ + byte* dst_frame) /*!< in: outbut buffer */ { fil_space_crypt_t* crypt_data=NULL; ulint page_size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; - uint key_version = (uint)encryption_key; + uint key_version; unsigned char key[MY_AES_MAX_KEY_LENGTH]; uint key_length=MY_AES_MAX_KEY_LENGTH; uint aes_method; @@ -699,6 +717,7 @@ fil_space_encrypt( /* Get crypt data from file space */ crypt_data = fil_space_get_crypt_data(space); + key_version = crypt_data->keys[0].key_id; if (crypt_data == NULL) { //TODO: Is this really needed ? @@ -808,6 +827,10 @@ fil_space_check_encryption_read( return false; } + if (crypt_data->encryption == FIL_SPACE_ENCRYPTION_OFF) { + return false; + } + return true; } @@ -1352,6 +1375,11 @@ fil_crypt_space_needs_rotation( mutex_enter(&crypt_data->mutex); do { + if (crypt_data->encryption == FIL_SPACE_ENCRYPTION_OFF) { + /* This space is unencrypted by user request */ + break; + } + /* prevent threads from starting to rotate space */ if (crypt_data->rotate_state.starting) { /* recheck this space later */ diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 9d37de6d63b..9d0bb03c0e9 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -5543,6 +5543,74 @@ fil_report_invalid_page_access( (ulong) byte_offset, (ulong) len, (ulong) type); } +/********************************************************************//** +Find correct node from file space +@return node */ +static +fil_node_t* +fil_space_get_node( + fil_space_t* space, /*!< in: file spage */ + ulint space_id, /*!< in: space id */ + ulint* block_offset, /*!< in/out: offset in number of blocks */ + ulint byte_offset, /*!< in: remainder of offset in bytes; in + aio this must be divisible by the OS block + size */ + ulint len) /*!< in: how many bytes to read or write; this + must not cross a file boundary; in aio this + must be a block size multiple */ +{ + fil_node_t* node; + ut_ad(mutex_own(&fil_system->mutex)); + + node = UT_LIST_GET_FIRST(space->chain); + + for (;;) { + if (node == NULL) { + return(NULL); + } else if (fil_is_user_tablespace_id(space->id) + && node->size == 0) { + + /* We do not know the size of a single-table tablespace + before we open the file */ + break; + } else if (node->size > *block_offset) { + /* Found! */ + break; + } else { + *block_offset -= node->size; + node = UT_LIST_GET_NEXT(chain, node); + } + } + + return (node); +} +/********************************************************************//** +Return block size of node in file space +@return file block size */ +UNIV_INTERN +ulint +fil_space_get_block_size( +/*=====================*/ + ulint space_id, + ulint block_offset, + ulint len) +{ + ulint block_size = 512; + fil_space_t* space = fil_space_get_space(space_id); + + if (space) { + mutex_enter(&fil_system->mutex); + fil_node_t* node = fil_space_get_node(space, space_id, &block_offset, 0, len); + mutex_exit(&fil_system->mutex); + + if (node) { + block_size = node->file_block_size; + } + } + + return block_size; +} + /********************************************************************//** Reads or writes data. This operation is asynchronous (aio). @return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do @@ -5589,7 +5657,7 @@ fil_io( ulint is_log; ulint wake_later; os_offset_t offset; - ibool ignore_nonexistent_pages; + bool ignore_nonexistent_pages; is_log = type & OS_FILE_LOG; type = type & ~OS_FILE_LOG; @@ -5674,34 +5742,18 @@ fil_io( ut_ad(mode != OS_AIO_IBUF || space->purpose == FIL_TABLESPACE); - node = UT_LIST_GET_FIRST(space->chain); + node = fil_space_get_node(space, space_id, &block_offset, byte_offset, len); - for (;;) { - if (node == NULL) { - if (ignore_nonexistent_pages) { - mutex_exit(&fil_system->mutex); - return(DB_ERROR); - } - - fil_report_invalid_page_access( + if (!node) { + if (ignore_nonexistent_pages) { + mutex_exit(&fil_system->mutex); + return(DB_ERROR); + } + fil_report_invalid_page_access( block_offset, space_id, space->name, byte_offset, len, type); - ut_error; - - } else if (fil_is_user_tablespace_id(space->id) - && node->size == 0) { - - /* We do not know the size of a single-table tablespace - before we open the file */ - break; - } else if (node->size > block_offset) { - /* Found! */ - break; - } else { - block_offset -= node->size; - node = UT_LIST_GET_NEXT(chain, node); - } + ut_error; } /* Open file if closed */ diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 311fdee4d3b..bd2d49995a0 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -566,10 +566,10 @@ ha_create_table_option innodb_table_option_list[]= HA_TOPTION_NUMBER("PAGE_COMPRESSION_LEVEL", page_compression_level, 0, 1, 9, 1), /* With this option user can enable atomic writes feature for this table */ HA_TOPTION_ENUM("ATOMIC_WRITES", atomic_writes, "DEFAULT,ON,OFF", 0), - /* With this option the user can enable page encryption for the table */ - HA_TOPTION_BOOL("PAGE_ENCRYPTION", page_encryption, 0), + /* With this option the user can enable encryption for the table */ + HA_TOPTION_ENUM("ENCRYPTION", encryption, "DEFAULT, ON, OFF", 0), /* With this option the user defines the key identifier using for the encryption */ - HA_TOPTION_NUMBER("PAGE_ENCRYPTION_KEY", page_encryption_key, 0, 1, 255, 1), + HA_TOPTION_NUMBER("ENCRYPTION_KEY", encryption_key, 0, 1, UINT_MAX32, 1), HA_TOPTION_END }; @@ -11285,20 +11285,21 @@ ha_innobase::check_table_options( enum row_type row_format = table->s->row_type; ha_table_option_struct *options= table->s->option_struct; atomic_writes_t awrites = (atomic_writes_t)options->atomic_writes; + fil_encryption_t encrypt = (fil_encryption_t)options->encryption; - if (options->page_encryption) { + if (encrypt == FIL_SPACE_ENCRYPTION_ON) { if (srv_encrypt_tables) { push_warning( thd, Sql_condition::WARN_LEVEL_WARN, HA_WRONG_CREATE_OPTION, - "InnoDB: PAGE_ENCRYPTION not available if innodb_encrypt_tables=ON"); + "InnoDB: ENCRYPTION not available if innodb_encrypt_tables=ON"); return "INNODB_ENCRYPT_TABLES"; } if (!use_tablespace) { push_warning( thd, Sql_condition::WARN_LEVEL_WARN, HA_WRONG_CREATE_OPTION, - "InnoDB: PAGE_ENCRYPTION requires" + "InnoDB: ENCRYPTION requires" " innodb_file_per_table."); return "PAGE_ENCRYPTION"; } @@ -11307,14 +11308,6 @@ ha_innobase::check_table_options( /* Check page compression requirements */ if (options->page_compressed) { - if (srv_encrypt_tables) { - push_warning( - thd, Sql_condition::WARN_LEVEL_WARN, - HA_WRONG_CREATE_OPTION, - "InnoDB: PAGE_COMPRESSION not available if innodb_encrypt_tables=ON"); - return "PAGE_COMPRESSED"; - } - if (row_format == ROW_TYPE_COMPRESSED) { push_warning( thd, Sql_condition::WARN_LEVEL_WARN, @@ -11384,29 +11377,29 @@ ha_innobase::check_table_options( } } - if (options->page_encryption_key != 0) { - if (options->page_encryption == false) { + if (options->encryption_key != 0) { + if (options->encryption == FIL_SPACE_ENCRYPTION_OFF) { /* ignore this to allow alter table without changing page_encryption_key ...*/ } - if (options->page_encryption_key < 1 || options->page_encryption_key > 255) { + if (options->encryption_key < 1) { push_warning_printf( thd, Sql_condition::WARN_LEVEL_WARN, HA_WRONG_CREATE_OPTION, - "InnoDB: invalid PAGE_ENCRYPTION_KEY = %lu." - " Valid values are [1..255]", - options->page_encryption_key); - return "PAGE_ENCRYPTION_KEY"; + "InnoDB: invalid ENCRYPTION_KEY = %lu." + " Valid values are [1..INT32_MAX]", + options->encryption_key); + return "ENCRYPTION_KEY"; } - if (!has_encryption_key(options->page_encryption_key)) { + if (!has_encryption_key(options->encryption_key)) { push_warning_printf( thd, Sql_condition::WARN_LEVEL_WARN, HA_WRONG_CREATE_OPTION, - "InnoDB: PAGE_ENCRYPTION_KEY encryption key %lu not available", - options->page_encryption_key + "InnoDB: ENCRYPTION_KEY encryption key %lu not available", + options->encryption_key ); - return "PAGE_ENCRYPTION_KEY"; + return "ENCRYPTION_KEY"; } } @@ -11467,6 +11460,11 @@ ha_innobase::create( const char* stmt; size_t stmt_len; + /* Cache table options */ + ha_table_option_struct *options= table->s->option_struct; + fil_encryption_t encrypt = (fil_encryption_t)options->encryption; + ulint key_id = (options->encryption_key == 0) ? srv_default_encryption_key : + options->encryption_key; DBUG_ENTER("ha_innobase::create"); @@ -11706,6 +11704,18 @@ ha_innobase::create( innobase_commit_low(trx); + /* If user has requested that table should be encrypted or table + should remain as unencrypted store crypt data */ + if (encrypt == FIL_SPACE_ENCRYPTION_ON || encrypt == FIL_SPACE_ENCRYPTION_OFF) { + ulint maxsize; + ulint zip_size = fil_space_get_zip_size(innobase_table->space); + fil_space_crypt_t* crypt_data = fil_space_create_crypt_data(); + crypt_data->page0_offset = fsp_header_get_crypt_offset(zip_size, &maxsize); + crypt_data->keys[0].key_id = key_id; + crypt_data->encryption = encrypt; + fil_space_set_crypt_data(innobase_table->space, crypt_data); + } + row_mysql_unlock_data_dictionary(trx); /* Flush the log to reduce probability that the .frm files and @@ -19146,12 +19156,12 @@ static MYSQL_SYSVAR_UINT(encryption_rotation_iops, srv_n_fil_crypt_iops, innodb_encryption_rotation_iops_update, srv_n_fil_crypt_iops, 0, UINT_MAX32, 0); -static MYSQL_SYSVAR_UINT(default_page_encryption_key, srv_default_page_encryption_key, +static MYSQL_SYSVAR_UINT(default_encryption_key, srv_default_encryption_key, PLUGIN_VAR_RQCMDARG, - "Encryption key used for page encryption.", + "Default encryption key used for table encryption.", NULL, NULL, - FIL_DEFAULT_ENCRYPTION_KEY, 1, 255, 0); + FIL_DEFAULT_ENCRYPTION_KEY, 1, UINT_MAX32, 0); static MYSQL_SYSVAR_BOOL(scrub_log, srv_scrub_log, PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY, @@ -19420,7 +19430,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(scrub_log), MYSQL_SYSVAR(scrub_log_interval), MYSQL_SYSVAR(encrypt_log), - MYSQL_SYSVAR(default_page_encryption_key), + MYSQL_SYSVAR(default_encryption_key), /* Scrubing feature */ MYSQL_SYSVAR(immediate_scrub_data_uncompressed), diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 7807c7ca7e6..2b9f1506339 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -70,8 +70,8 @@ struct ha_table_option_struct srv_use_atomic_writes=1. Atomic writes are not used if value OFF.*/ - bool page_encryption; /*!< Flag for an encrypted table */ - int page_encryption_key; /*!< ID of the encryption key */ + uint encryption; /*!< DEFAULT, ON, OFF */ + int encryption_key; /*!< encryption key id*/ }; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 94f17e75358..327e4e92d1b 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -278,8 +278,8 @@ ha_innobase::check_if_supported_inplace_alter( DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } - if (new_options->page_encryption != old_options->page_encryption || - new_options->page_encryption_key != old_options->page_encryption_key) { + if (new_options->encryption != old_options->encryption || + new_options->encryption_key != old_options->encryption_key) { ha_alter_info->unsupported_reason = innobase_get_err_msg( ER_ALTER_OPERATION_NOT_SUPPORTED_REASON); DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 1c84e68167e..d22d344162a 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -1445,16 +1445,19 @@ buf_flush_update_zip_checksum( The hook that is called just before a page is written to disk. The function encrypts the content of the page and returns a pointer to a frame that will be written instead of the real frame. */ +UNIV_INTERN byte* buf_page_encrypt_before_write( /*==========================*/ buf_page_t* page, /*!< in/out: buffer page to be flushed */ - const byte* frame); + const byte* frame, + ulint space_id); /********************************************************************** The hook that is called after page is written to disk. The function releases any resources needed for encryption that was allocated in buf_page_encrypt_before_write */ +UNIV_INTERN ibool buf_page_encrypt_after_write( /*=========================*/ @@ -1464,6 +1467,7 @@ buf_page_encrypt_after_write( The hook that is called just before a page is read from disk. The function allocates memory that is used to temporarily store disk content before getting decrypted */ +UNIV_INTERN byte* buf_page_decrypt_before_read( /*=========================*/ @@ -1474,19 +1478,35 @@ buf_page_decrypt_before_read( The hook that is called just after a page is read from disk. The function decrypt disk content into buf_page_t and releases the temporary buffer that was allocated in buf_page_decrypt_before_read */ +UNIV_INTERN ibool buf_page_decrypt_after_read( /*========================*/ buf_page_t* page); /*!< in/out: buffer page read from disk */ -/********************************************************************//** -Release memory allocated for page decryption. -Only used in scenarios where read fails, e.g due to tablespace being dropped */ -void -buf_page_decrypt_cleanup( -/*=====================*/ - buf_page_t* page); /*!< in/out: buffer page read from disk */ +/** @brief The temporary memory structure. +NOTE! The definition appears here only for other modules of this +directory (buf) to see it. Do not use from outside! */ + +typedef struct { + bool reserved; /*!< true if this slot is reserved + */ +#ifdef HAVE_LZO + byte* lzo_mem; /*!< Temporal memory used by LZO */ +#endif + byte* crypt_buf; /*!< for encryption the data needs to be + copied to a separate buffer before it's + encrypted&written. this as a page can be + read while it's being flushed */ + byte* crypt_buf_free; /*!< for encryption, allocated buffer + that is then alligned */ + byte* comp_buf; /*!< for compression we need + temporal buffer because page + can be read while it's being flushed */ + byte* comp_buf_free; /*!< for compression, allocated + buffer that is then alligned */ +} buf_tmp_buffer_t; /** The common buffer control block structure for compressed and uncompressed frames */ @@ -1559,20 +1579,15 @@ struct buf_page_t{ operation needed. */ unsigned key_version; /*!< key version for this block */ - byte* crypt_buf; /*!< for encryption the data needs to be - copied to a separate buffer before it's - encrypted&written. this as a page can be - read while it's being flushed */ - byte* crypt_buf_free; /*!< for encryption, allocated buffer - that is then alligned */ - byte* comp_buf; /*!< for compression we need - temporal buffer because page - can be read while it's being flushed */ - byte* comp_buf_free; /*!< for compression, allocated - buffer that is then alligned */ - bool encrypt_later; /*!< should we encrypt the page - at os0file.cc ? */ + ulint real_size; /*!< Real size of the page + Normal pages == UNIV_PAGE_SIZE + page compressed pages, payload + size alligned to sector boundary. + */ + buf_tmp_buffer_t* slot; /*!< Slot for temporary memory + used for encryption/compression + or NULL */ #ifndef UNIV_HOTBACKUP buf_page_t* hash; /*!< node used in chaining to buf_pool->page_hash or @@ -2017,6 +2032,17 @@ struct buf_buddy_stat_t { ib_uint64_t relocated_usec; }; +/** @brief The temporary memory array structure. + +NOTE! The definition appears here only for other modules of this +directory (buf) to see it. Do not use from outside! */ + +typedef struct { + ulint n_slots; /*!< Total number of slots */ + buf_tmp_buffer_t *slots; /*!< Pointer to the slots in the + array */ +} buf_tmp_array_t; + /** @brief The buffer pool structure. NOTE! The definition appears here only for other modules of this @@ -2200,6 +2226,10 @@ struct buf_pool_t{ pool watches. Protected by buf_pool->mutex. */ + buf_tmp_array_t* tmp_arr; + /*!< Array for temporal memory + used in compression and encryption */ + #if BUF_BUDDY_LOW > UNIV_ZIP_SIZE_MIN # error "BUF_BUDDY_LOW > UNIV_ZIP_SIZE_MIN" #endif diff --git a/storage/innobase/include/buf0buf.ic b/storage/innobase/include/buf0buf.ic index 842aa6758cd..53e7445d447 100644 --- a/storage/innobase/include/buf0buf.ic +++ b/storage/innobase/include/buf0buf.ic @@ -1464,28 +1464,15 @@ buf_get_nth_chunk_block( return(chunk->blocks); } -/********************************************************************//** -Get crypt buffer. */ -UNIV_INLINE -byte* -buf_page_get_crypt_buffer( -/*=========================*/ - const buf_page_t* bpage) /*!< in: buffer pool page */ -{ - return bpage->crypt_buf; -} - /********************************************************************//** Get buf frame. */ UNIV_INLINE void * buf_page_get_frame( -/*=========================*/ +/*===============*/ const buf_page_t* bpage) /*!< in: buffer pool page */ { - if (bpage->crypt_buf) { - return buf_page_get_crypt_buffer(bpage); - } else if (bpage->zip.data) { + if (bpage->zip.data) { return bpage->zip.data; } else { return ((buf_block_t*) bpage)->frame; diff --git a/storage/innobase/include/fil0crypt.h b/storage/innobase/include/fil0crypt.h index 9d02034e4b2..359533dc50a 100644 --- a/storage/innobase/include/fil0crypt.h +++ b/storage/innobase/include/fil0crypt.h @@ -37,6 +37,14 @@ Created 04/01/2015 Jan Lindström /* This key will be used if nothing else is given */ #define FIL_DEFAULT_ENCRYPTION_KEY 1 +/** Enum values for encryption table option */ +typedef enum { + FIL_SPACE_ENCRYPTION_DEFAULT = 0, /* Tablespace encrypted if + srv_encrypt_tables = ON */ + FIL_SPACE_ENCRYPTION_ON = 1, /* Tablespace is encrypted always */ + FIL_SPACE_ENCRYPTION_OFF = 2 /* Tablespace is not encrypted */ +} fil_encryption_t; + /** * CRYPT_SCHEME_UNENCRYPTED * @@ -67,6 +75,8 @@ struct key_struct { uint key_version; /*!< Key version used as identifier */ + uint key_id; /*1< Key id used as + identifier */ byte key[MY_AES_MAX_KEY_LENGTH]; /*!< Cached L or key */ uint key_length; /*!< Key length */ }; @@ -95,6 +105,7 @@ struct fil_space_crypt_struct key_struct keys[3]; // cached L = AES_ECB(KEY, IV) uint min_key_version; // min key version for this space ulint page0_offset; // byte offset on page 0 for crypt data + fil_encryption_t encryption; // Encryption setup ib_mutex_t mutex; // mutex protecting following variables bool closing; // is tablespace being closed @@ -227,9 +238,7 @@ fil_space_encrypt( lsn_t lsn, /*!< in: page lsn */ const byte* src_frame,/*!< in: page frame */ ulint size, /*!< in: size of data to encrypt */ - byte* dst_frame, /*!< in: where to encrypt to */ - ulint page_encryption_key); /*!< in: page encryption key id if page - encrypted */ + byte* dst_frame); /*!< in: where to encrypt to */ /********************************************************************* Decrypt buffer page */ diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 2c6c5804f65..7f2c00eb168 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -1272,6 +1272,16 @@ ulint fil_get_next_space( ulint id); /*!< in: space id */ +/*******************************************************************//** +Returns the block size of the file space +@return block size */ +UNIV_INTERN +ulint +fil_space_get_block_size( +/*=====================*/ + ulint id, /*!< in: space id */ + ulint offset, /*!< in: page offset */ + ulint len); /*!< in: page len */ #endif diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 31db78ae5ee..314bce63c7b 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -562,8 +562,8 @@ that semaphore times out in InnoDB */ #define DEFAULT_SRV_FATAL_SEMAPHORE_TIMEOUT 600 extern ulong srv_fatal_semaphore_wait_threshold; -/** Default encryption key used for page encryption */ -extern uint srv_default_page_encryption_key; +/** Default encryption key used for tablespace encryption */ +extern uint srv_default_encryption_key; /** Enable semaphore request instrumentation */ extern my_bool srv_instrument_semaphores; diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index 1287ee76819..e17cf161973 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -242,7 +242,6 @@ struct os_aio_slot_t{ int n_bytes; /* bytes written/read. */ int ret; /* AIO return code */ #endif /* WIN_ASYNC_IO */ - byte *lzo_mem; /* Temporal memory used by LZO */ }; /** The asynchronous i/o array structure */ diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 78954725001..d8c921fb612 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -523,8 +523,8 @@ thread ensures that we flush the log files at least once per second. */ static time_t srv_last_log_flush_time; -/** Default encryption key used for page encryption */ -UNIV_INTERN uint srv_default_page_encryption_key = FIL_DEFAULT_ENCRYPTION_KEY; +/** Default encryption key used for tablespace encryption */ +UNIV_INTERN uint srv_default_encryption_key = FIL_DEFAULT_ENCRYPTION_KEY; /** Enable semaphore request instrumentation */ UNIV_INTERN my_bool srv_instrument_semaphores = FALSE; diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc index 56b0245da93..ab8dbadf0fa 100644 --- a/storage/xtradb/buf/buf0buf.cc +++ b/storage/xtradb/buf/buf0buf.cc @@ -95,6 +95,14 @@ _increment_page_get_statistics(buf_block_t* block, trx_t* trx) return; } +#ifdef HAVE_LZO +#include "lzo/lzo1x.h" +#endif + +/* Number of temporary slots used for encryption/compression +memory allocation before/after I/O operations */ +#define BUF_MAX_TMP_SLOTS 200 + /* IMPLEMENTATION OF THE BUFFER POOL ================================= @@ -1077,15 +1085,11 @@ buf_block_init( block->page.state = BUF_BLOCK_NOT_USED; block->page.buf_fix_count = 0; block->page.io_fix = BUF_IO_NONE; - block->page.crypt_buf = NULL; - block->page.crypt_buf_free = NULL; - block->page.comp_buf = NULL; - block->page.comp_buf_free = NULL; block->page.key_version = 0; - block->page.encrypt_later = false; - - + block->page.real_size = 0; + block->page.write_size = 0; block->modify_clock = 0; + block->page.slot = NULL; #if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG block->page.file_page_was_freed = FALSE; @@ -1438,6 +1442,11 @@ buf_pool_init_instance( /* All fields are initialized by mem_zalloc(). */ + /* Initialize the temporal memory array and slots */ + buf_pool->tmp_arr = (buf_tmp_array_t *)mem_zalloc(sizeof(buf_tmp_array_t)); + buf_pool->tmp_arr->n_slots = BUF_MAX_TMP_SLOTS; + buf_pool->tmp_arr->slots = (buf_tmp_buffer_t*)mem_zalloc(sizeof(buf_tmp_buffer_t) * BUF_MAX_TMP_SLOTS); + buf_pool->try_LRU_scan = TRUE; return(DB_SUCCESS); @@ -1489,6 +1498,9 @@ buf_pool_free_instance( ha_clear(buf_pool->page_hash); hash_table_free(buf_pool->page_hash); hash_table_free(buf_pool->zip_hash); + + mem_free(buf_pool->tmp_arr->slots); + mem_free(buf_pool->tmp_arr); } /********************************************************************//** @@ -3574,12 +3586,8 @@ buf_page_init_low( bpage->newest_modification = 0; bpage->oldest_modification = 0; bpage->write_size = 0; - bpage->crypt_buf = NULL; - bpage->crypt_buf_free = NULL; - bpage->comp_buf = NULL; - bpage->comp_buf_free = NULL; bpage->key_version = 0; - bpage->encrypt_later = false; + bpage->real_size = 0; HASH_INVALIDATE(bpage, hash); bpage->is_corrupt = FALSE; @@ -4498,7 +4506,11 @@ corrupt: } } else { /* io_type == BUF_IO_WRITE */ - buf_page_encrypt_after_write(bpage); + if (bpage->slot) { + /* Mark slot free */ + bpage->slot->reserved = false; + bpage->slot = NULL; + } } if (io_type == BUF_IO_WRITE @@ -5778,6 +5790,60 @@ buf_pool_mutex_exit( mutex_exit(&buf_pool->LRU_list_mutex); } +/********************************************************************//** +Reserve unused slot from temporary memory array and allocate necessary +temporary memory if not yet allocated. +@return reserved slot */ +buf_tmp_buffer_t* +buf_pool_reserve_tmp_slot( +/*======================*/ + buf_pool_t* buf_pool, /*!< in: buffer pool where to + reserve */ + bool compressed) /*!< in: is file space compressed */ +{ + buf_tmp_buffer_t *free_slot=NULL; + + /* Array is protected by buf_pool mutex */ + buf_pool_mutex_enter(buf_pool); + + for(ulint i = 0; i < buf_pool->tmp_arr->n_slots; i++) { + buf_tmp_buffer_t *slot = &buf_pool->tmp_arr->slots[i]; + + if(slot->reserved == false) { + free_slot = slot; + break; + } + } + + /* We assume that free slot is found */ + ut_a(free_slot != NULL); + free_slot->reserved = true; + /* Now that we have reserved this slot we can release + buf_pool mutex */ + buf_pool_mutex_exit(buf_pool); + + /* Allocate temporary memory for encryption/decryption */ + if (free_slot->crypt_buf_free == NULL) { + free_slot->crypt_buf_free = static_cast(ut_malloc(UNIV_PAGE_SIZE*2)); + free_slot->crypt_buf = static_cast(ut_align(free_slot->crypt_buf_free, UNIV_PAGE_SIZE)); + memset(free_slot->crypt_buf_free, 0, UNIV_PAGE_SIZE *2); + } + + /* For page compressed tables allocate temporary memory for + compression/decompression */ + if (compressed && free_slot->comp_buf_free == NULL) { + free_slot->comp_buf_free = static_cast(ut_malloc(UNIV_PAGE_SIZE*2)); + free_slot->comp_buf = static_cast(ut_align(free_slot->comp_buf_free, UNIV_PAGE_SIZE)); + memset(free_slot->comp_buf_free, 0, UNIV_PAGE_SIZE *2); +#ifdef HAVE_LZO + free_slot->lzo_mem = static_cast(ut_malloc(LZO1X_1_15_MEM_COMPRESS)); + memset(free_slot->lzo_mem, 0, LZO1X_1_15_MEM_COMPRESS); +#endif + } + + return (free_slot); +} + /********************************************************************//** Encrypts a buffer page right before it's flushed to disk */ @@ -5785,11 +5851,17 @@ byte* buf_page_encrypt_before_write( /*==========================*/ buf_page_t* bpage, /*!< in/out: buffer page to be flushed */ - const byte* src_frame) /*!< in: src frame */ + const byte* src_frame, /*!< in: src frame */ + ulint space_id) /*!< in: space id */ { - bpage->encrypt_later = false; + fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id); - if (srv_encrypt_tables == FALSE) { + if (crypt_data != NULL && crypt_data->encryption == FIL_SPACE_ENCRYPTION_OFF) { + /* Encryption is disabled */ + return const_cast(src_frame); + } + + if (!srv_encrypt_tables && (crypt_data == NULL || crypt_data->encryption == FIL_SPACE_ENCRYPTION_DEFAULT)) { /* Encryption is disabled */ return const_cast(src_frame); } @@ -5814,103 +5886,63 @@ buf_page_encrypt_before_write( ulint zip_size = buf_page_get_zip_size(bpage); ulint page_size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; + buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); + bool page_compressed = fil_space_is_page_compressed(bpage->space); - /** - * TODO(jonaso): figure out more clever malloc strategy - * - * This implementation does a malloc/free per iop for encrypted - * tablespaces. Alternative strategies that have been considered are - * - * 1) use buf_block_alloc (i.e alloc from buffer pool) - * this does not work as buf_block_alloc will then be called - * when needing to flush a page, which might be triggered - * due to shortage of memory in buffer pool - * 2) allocate a buffer per fil_node_t - * this would break abstraction layers and has therfore not been - * considered a lot. - */ + /* Find free slot from temporary memory array */ + buf_tmp_buffer_t* slot = buf_pool_reserve_tmp_slot(buf_pool, page_compressed); + bpage->slot = slot; - if (bpage->crypt_buf_free == NULL) { - bpage->crypt_buf_free = (byte*)malloc(page_size*2); - // TODO: Is 4k aligment enough ? - bpage->crypt_buf = (byte *)ut_align(bpage->crypt_buf_free, page_size); - } + byte *dst_frame = slot->crypt_buf; - byte *dst_frame = bpage->crypt_buf; - - if (!fil_space_is_page_compressed(bpage->space)) { - // encrypt page content - fil_space_encrypt(bpage->space, bpage->offset, - bpage->newest_modification, - src_frame, zip_size, dst_frame, 0); + if (!page_compressed) { + /* Encrypt page content */ + fil_space_encrypt(bpage->space, + bpage->offset, + bpage->newest_modification, + src_frame, + zip_size, + dst_frame); unsigned key_version = mach_read_from_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); ut_ad(key_version == 0 || key_version >= bpage->key_version); bpage->key_version = key_version; + bpage->real_size = zip_size; } else { - /** Compression and encryption is done later at os0file.cc */ - bpage->encrypt_later = true; - dst_frame = (byte *)src_frame; + /* First we compress the page content */ + ulint out_len = 0; + ulint block_size = fil_space_get_block_size(bpage->space, bpage->offset, page_size); + + byte *tmp = fil_compress_page(bpage->space, + (byte *)src_frame, + slot->comp_buf, + page_size, + fil_space_get_page_compression_level(bpage->space), + block_size, + &out_len, +#ifdef HAVE_LZO + slot->lzo_mem +#else + NULL +#endif + ); + + bpage->real_size = out_len; + + /* And then we encrypt the page content */ + fil_space_encrypt(bpage->space, + bpage->offset, + bpage->newest_modification, + tmp, + zip_size, + dst_frame); } // return dst_frame which will be written return dst_frame; } -/********************************************************************//** -Release memory after encrypted page has been written to disk -*/ -ibool -buf_page_encrypt_after_write( -/*=========================*/ - buf_page_t* bpage) /*!< in/out: buffer page flushed */ -{ - if (bpage->crypt_buf_free != NULL) { - free(bpage->crypt_buf_free); - bpage->crypt_buf_free = NULL; - bpage->crypt_buf = NULL; - } - - if (bpage->comp_buf_free != NULL) { - free(bpage->comp_buf_free); - bpage->comp_buf_free = NULL; - bpage->comp_buf = NULL; - } - - return (TRUE); -} - -/********************************************************************//** -Allocates memory to read in an encrypted page -*/ -byte* -buf_page_decrypt_before_read( -/*=========================*/ - buf_page_t* bpage, /*!< in/out: buffer page to be read */ - ulint zip_size) /*!< in: compressed page size, or 0 */ -{ - ulint size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; - - /* - Here we only need to allocate space for not header pages - in case of file space encryption. Table encryption is handled - later. - */ - if (!srv_encrypt_tables || bpage->offset == 0 || - fil_space_check_encryption_read(bpage->space) == false) - return zip_size ? bpage->zip.data : ((buf_block_t*) bpage)->frame; - - if (bpage->crypt_buf_free == NULL) - { - // allocate buffer to read data into - bpage->crypt_buf_free = (byte*)malloc(size*2); - // TODO: Is 4K aligment enough ? - bpage->crypt_buf = (byte*)ut_align(bpage->crypt_buf_free, size); - } - return bpage->crypt_buf; -} - /********************************************************************//** Decrypt page after it has been read from disk */ @@ -5928,92 +5960,51 @@ buf_page_decrypt_after_read( if (bpage->offset == 0) { /* File header pages are not encrypted */ - ut_a(bpage->crypt_buf == NULL); return (TRUE); } - const byte* src_frame = bpage->crypt_buf != NULL ? - bpage->crypt_buf : dst_frame; - unsigned key_version = - mach_read_from_4(src_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); + mach_read_from_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); bool page_compressed = fil_page_is_compressed(dst_frame); + buf_pool_t* buf_pool = buf_pool_from_bpage(bpage); if (key_version == 0) { /* the page we read is unencrypted */ - if (fil_page_is_compressed(dst_frame)) { - if (bpage->comp_buf_free == NULL) { - bpage->comp_buf_free = (byte *)malloc(UNIV_PAGE_SIZE*2); - // TODO: is 4k aligment enough ? - bpage->comp_buf = (byte*)ut_align(bpage->comp_buf_free, UNIV_PAGE_SIZE); - } - - fil_decompress_page(bpage->comp_buf, dst_frame, size, NULL); - } else { - if (dst_frame != src_frame) { - /* but we had allocated a crypt_buf */ - // TODO: Can this be avoided ? - memcpy(dst_frame, src_frame, size); - } - } - } else { - /* the page we read is encrypted */ - if (dst_frame == src_frame) { - /* but we had NOT allocated a crypt buf - * malloc a buffer, copy page to it - * and then decrypt from that into real page*/ - bpage->crypt_buf_free = (byte *)malloc(UNIV_PAGE_SIZE*2); - // TODO: is 4k aligment enough ? - src_frame = bpage->crypt_buf = (byte*)ut_align(bpage->crypt_buf_free, UNIV_PAGE_SIZE); - memcpy(bpage->crypt_buf, dst_frame, size); - } - /* decrypt from src_frame to dst_frame */ - fil_space_decrypt(bpage->space, - src_frame, size, dst_frame); - - /* decompress from dst_frame to comp_buf and then copy to - buffer pool */ if (page_compressed) { - if (bpage->comp_buf_free == NULL) { - bpage->comp_buf_free = (byte *)malloc(UNIV_PAGE_SIZE*2); - // TODO: is 4k aligment enough ? - bpage->comp_buf = (byte*)ut_align(bpage->comp_buf_free, UNIV_PAGE_SIZE); - } + /* Find free slot from temporary memory array */ + buf_tmp_buffer_t* slot = buf_pool_reserve_tmp_slot(buf_pool, page_compressed); - fil_decompress_page(bpage->comp_buf, dst_frame, size, NULL); - } + fil_decompress_page(slot->comp_buf, + dst_frame, + size, + &bpage->write_size); + + /* Mark this slot as free */ + slot->reserved = false; + } + } else { + /* Find free slot from temporary memory array */ + buf_tmp_buffer_t* slot = buf_pool_reserve_tmp_slot(buf_pool, page_compressed); + memcpy(slot->crypt_buf, dst_frame, size); + + /* decrypt from crypt_buf to dst_frame */ + fil_space_decrypt(bpage->space, + slot->crypt_buf, + size, + dst_frame); + + if (page_compressed) { + fil_decompress_page(slot->comp_buf, + dst_frame, + size, + &bpage->write_size); + } + + /* Mark this slot as free */ + slot->reserved = false; } bpage->key_version = key_version; - if (bpage->crypt_buf_free != NULL) { - // free temp page - free(bpage->crypt_buf_free); - bpage->crypt_buf = NULL; - bpage->crypt_buf_free = NULL; - } - - if (bpage->comp_buf_free != NULL) { - // free temp page - free(bpage->comp_buf_free); - bpage->comp_buf = NULL; - bpage->comp_buf_free = NULL; - } - return (TRUE); } - -/********************************************************************//** -Release memory allocated for decryption -*/ -void -buf_page_decrypt_cleanup( -/*=====================*/ - buf_page_t* bpage) /*!< in/out: buffer page */ -{ - if (bpage->crypt_buf != NULL) { - free(bpage->crypt_buf_free); - bpage->crypt_buf = NULL; - bpage->crypt_buf_free = NULL; - } -} diff --git a/storage/xtradb/buf/buf0dblwr.cc b/storage/xtradb/buf/buf0dblwr.cc index ec733dd5a73..b0fdc58e4d2 100644 --- a/storage/xtradb/buf/buf0dblwr.cc +++ b/storage/xtradb/buf/buf0dblwr.cc @@ -871,7 +871,7 @@ buf_dblwr_write_block_to_datafile( 0, buf_block_get_page_no(block), 0, - UNIV_PAGE_SIZE, + bpage->real_size, frame, (void*) block, (ulint *)&bpage->write_size); @@ -1247,7 +1247,8 @@ retry: TRX_SYS_SPACE, 0, offset, - 0, UNIV_PAGE_SIZE, + 0, + bpage->real_size, frame, NULL, 0); diff --git a/storage/xtradb/buf/buf0flu.cc b/storage/xtradb/buf/buf0flu.cc index d023e2ff227..6443043310b 100644 --- a/storage/xtradb/buf/buf0flu.cc +++ b/storage/xtradb/buf/buf0flu.cc @@ -951,7 +951,7 @@ buf_flush_write_block_low( break; } - frame = buf_page_encrypt_before_write(bpage, frame); + frame = buf_page_encrypt_before_write(bpage, frame, space_id); if (!srv_use_doublewrite_buf || !buf_dblwr) { fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER, @@ -960,7 +960,7 @@ buf_flush_write_block_low( zip_size, buf_page_get_page_no(bpage), 0, - zip_size ? zip_size : UNIV_PAGE_SIZE, + zip_size ? zip_size : bpage->real_size, frame, bpage, &bpage->write_size); @@ -979,7 +979,7 @@ buf_flush_write_block_low( zip_size, buf_page_get_page_no(bpage), 0, - zip_size ? zip_size : UNIV_PAGE_SIZE, + zip_size ? zip_size : bpage->real_size, frame, bpage, &bpage->write_size); diff --git a/storage/xtradb/buf/buf0rea.cc b/storage/xtradb/buf/buf0rea.cc index 164aa3c62c1..6c74c30739e 100644 --- a/storage/xtradb/buf/buf0rea.cc +++ b/storage/xtradb/buf/buf0rea.cc @@ -222,7 +222,7 @@ not_to_recover: ut_ad(buf_page_in_file(bpage)); ut_ad(!mutex_own(&buf_pool_from_bpage(bpage)->LRU_list_mutex)); - byte* frame = buf_page_decrypt_before_read(bpage, zip_size); + byte* frame = zip_size ? bpage->zip.data : ((buf_block_t*) bpage)->frame; if (sync) { thd_wait_begin(NULL, THD_WAIT_DISKIO); @@ -247,7 +247,6 @@ not_to_recover: } if (*err != DB_SUCCESS) { - buf_page_decrypt_cleanup(bpage); if (ignore_nonexistent_pages || *err == DB_TABLESPACE_DELETED) { buf_read_page_handle_error(bpage); return(0); diff --git a/storage/xtradb/fil/fil0crypt.cc b/storage/xtradb/fil/fil0crypt.cc index aab0b1f5568..07636d27879 100644 --- a/storage/xtradb/fil/fil0crypt.cc +++ b/storage/xtradb/fil/fil0crypt.cc @@ -319,6 +319,7 @@ fil_space_create_crypt_data() &crypt_data->mutex, SYNC_NO_ORDER_CHECK); crypt_data->iv_length = iv_length; my_random_bytes(crypt_data->iv, iv_length); + crypt_data->encryption = FIL_SPACE_ENCRYPTION_DEFAULT; return crypt_data; } @@ -421,6 +422,9 @@ fil_space_read_crypt_data( uint min_key_version = mach_read_from_4 (page + offset + MAGIC_SZ + 2 + iv_length); + fil_encryption_t encryption = (fil_encryption_t)mach_read_from_1( + page + offset + MAGIC_SZ + 2 + iv_length + 4); + const uint sz = sizeof(fil_space_crypt_t) + iv_length; fil_space_crypt_t* crypt_data = static_cast( malloc(sz)); @@ -429,6 +433,7 @@ fil_space_read_crypt_data( crypt_data->type = type; crypt_data->min_key_version = min_key_version; crypt_data->page0_offset = offset; + crypt_data->encryption = encryption; mutex_create(fil_crypt_data_mutex_key, &crypt_data->mutex, SYNC_NO_ORDER_CHECK); crypt_data->iv_length = iv_length; @@ -474,8 +479,9 @@ fil_space_write_crypt_data_low( page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); const uint len = crypt_data->iv_length; const uint min_key_version = crypt_data->min_key_version; + const fil_encryption_t encryption = crypt_data->encryption; crypt_data->page0_offset = offset; - ut_a(2 + len + 4 + MAGIC_SZ < maxsize); + ut_a(2 + len + 4 + 1 + MAGIC_SZ < maxsize); /* redo log this as bytewise updates to page 0 @@ -489,8 +495,10 @@ fil_space_write_crypt_data_low( mtr); mlog_write_ulint(page + offset + MAGIC_SZ + 2 + len, min_key_version, MLOG_4BYTES, mtr); + mlog_write_ulint(page + offset + MAGIC_SZ + 2 + len + 4, encryption, + MLOG_1BYTE, mtr); - byte* log_ptr = mlog_open(mtr, 11 + 12 + len); + byte* log_ptr = mlog_open(mtr, 11 + 13 + len); if (log_ptr != NULL) { log_ptr = mlog_write_initial_log_record_fast( @@ -507,6 +515,8 @@ fil_space_write_crypt_data_low( log_ptr += 1; mach_write_to_4(log_ptr, min_key_version); log_ptr += 4; + mach_write_to_1(log_ptr, encryption); + log_ptr += 1; mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, crypt_data->iv, len); @@ -555,7 +565,8 @@ fil_parse_write_crypt_data( 2 + // size of offset 1 + // size of type 1 + // size of iv-len - 4; // size of min_key_version + 4 + // size of min_key_version + 1; // fil_encryption_t if (end_ptr - ptr < entry_size){ return NULL; @@ -582,9 +593,17 @@ fil_parse_write_crypt_data( return NULL; } + fil_encryption_t encryption = (fil_encryption_t)mach_read_from_1(ptr); + ptr +=1; + + if (end_ptr - ptr < len) { + return NULL; + } + fil_space_crypt_t* crypt_data = fil_space_create_crypt_data(); crypt_data->page0_offset = offset; crypt_data->min_key_version = min_key_version; + crypt_data->encryption = encryption; memcpy(crypt_data->iv, ptr, len); ptr += len; @@ -610,7 +629,8 @@ fil_space_clear_crypt_data( 1 + // type 1 + // len len + // iv - 4; // min key version + 4 + // min key version + 1; // fil_encryption_t memset(page + offset, 0, size); } @@ -674,13 +694,11 @@ fil_space_encrypt( const byte* src_frame, /*!< in: Source page to be encrypted */ ulint zip_size, /*!< in: compressed size if row_format compressed */ - byte* dst_frame, /*!< in: outbut buffer */ - ulint encryption_key) /*!< in: encryption key id if page - encrypted */ + byte* dst_frame) /*!< in: outbut buffer */ { fil_space_crypt_t* crypt_data=NULL; ulint page_size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; - uint key_version = (uint)encryption_key; + uint key_version; unsigned char key[MY_AES_MAX_KEY_LENGTH]; uint key_length=MY_AES_MAX_KEY_LENGTH; uint aes_method; @@ -699,6 +717,7 @@ fil_space_encrypt( /* Get crypt data from file space */ crypt_data = fil_space_get_crypt_data(space); + key_version = crypt_data->keys[0].key_id; if (crypt_data == NULL) { //TODO: Is this really needed ? @@ -808,6 +827,10 @@ fil_space_check_encryption_read( return false; } + if (crypt_data->encryption == FIL_SPACE_ENCRYPTION_OFF) { + return false; + } + return true; } @@ -1352,6 +1375,11 @@ fil_crypt_space_needs_rotation( mutex_enter(&crypt_data->mutex); do { + if (crypt_data->encryption == FIL_SPACE_ENCRYPTION_OFF) { + /* This space is unencrypted by user request */ + break; + } + /* prevent threads from starting to rotate space */ if (crypt_data->rotate_state.starting) { /* recheck this space later */ diff --git a/storage/xtradb/fil/fil0fil.cc b/storage/xtradb/fil/fil0fil.cc index 6b776edd2f9..1084f90c720 100644 --- a/storage/xtradb/fil/fil0fil.cc +++ b/storage/xtradb/fil/fil0fil.cc @@ -5579,6 +5579,74 @@ fil_report_invalid_page_access( (ulong) byte_offset, (ulong) len, (ulong) type); } +/********************************************************************//** +Find correct node from file space +@return node */ +static +fil_node_t* +fil_space_get_node( + fil_space_t* space, /*!< in: file spage */ + ulint space_id, /*!< in: space id */ + ulint* block_offset, /*!< in/out: offset in number of blocks */ + ulint byte_offset, /*!< in: remainder of offset in bytes; in + aio this must be divisible by the OS block + size */ + ulint len) /*!< in: how many bytes to read or write; this + must not cross a file boundary; in aio this + must be a block size multiple */ +{ + fil_node_t* node; + ut_ad(mutex_own(&fil_system->mutex)); + + node = UT_LIST_GET_FIRST(space->chain); + + for (;;) { + if (node == NULL) { + return(NULL); + } else if (fil_is_user_tablespace_id(space->id) + && node->size == 0) { + + /* We do not know the size of a single-table tablespace + before we open the file */ + break; + } else if (node->size > *block_offset) { + /* Found! */ + break; + } else { + *block_offset -= node->size; + node = UT_LIST_GET_NEXT(chain, node); + } + } + + return (node); +} +/********************************************************************//** +Return block size of node in file space +@return file block size */ +UNIV_INTERN +ulint +fil_space_get_block_size( +/*=====================*/ + ulint space_id, + ulint block_offset, + ulint len) +{ + ulint block_size = 512; + fil_space_t* space = fil_space_get_space(space_id); + + if (space) { + mutex_enter(&fil_system->mutex); + fil_node_t* node = fil_space_get_node(space, space_id, &block_offset, 0, len); + mutex_exit(&fil_system->mutex); + + if (node) { + block_size = node->file_block_size; + } + } + + return block_size; +} + /********************************************************************//** Reads or writes data. This operation is asynchronous (aio). @return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do @@ -5626,7 +5694,7 @@ _fil_io( ulint is_log; ulint wake_later; os_offset_t offset; - ibool ignore_nonexistent_pages; + bool ignore_nonexistent_pages; is_log = type & OS_FILE_LOG; type = type & ~OS_FILE_LOG; @@ -5711,34 +5779,18 @@ _fil_io( ut_ad(mode != OS_AIO_IBUF || space->purpose == FIL_TABLESPACE); - node = UT_LIST_GET_FIRST(space->chain); + node = fil_space_get_node(space, space_id, &block_offset, byte_offset, len); - for (;;) { - if (node == NULL) { - if (ignore_nonexistent_pages) { - mutex_exit(&fil_system->mutex); - return(DB_ERROR); - } - - fil_report_invalid_page_access( + if (!node) { + if (ignore_nonexistent_pages) { + mutex_exit(&fil_system->mutex); + return(DB_ERROR); + } + fil_report_invalid_page_access( block_offset, space_id, space->name, byte_offset, len, type); - ut_error; - - } else if (fil_is_user_tablespace_id(space->id) - && node->size == 0) { - - /* We do not know the size of a single-table tablespace - before we open the file */ - break; - } else if (node->size > block_offset) { - /* Found! */ - break; - } else { - block_offset -= node->size; - node = UT_LIST_GET_NEXT(chain, node); - } + ut_error; } /* Open file if closed */ diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index cb6b7d1a263..9f4f52b4f4a 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -631,10 +631,10 @@ ha_create_table_option innodb_table_option_list[]= HA_TOPTION_NUMBER("PAGE_COMPRESSION_LEVEL", page_compression_level, 0, 1, 9, 1), /* With this option user can enable atomic writes feature for this table */ HA_TOPTION_ENUM("ATOMIC_WRITES", atomic_writes, "DEFAULT,ON,OFF", 0), - /* With this option the user can enable page encryption for the table */ - HA_TOPTION_BOOL("PAGE_ENCRYPTION", page_encryption, 0), + /* With this option the user can enable encryption for the table */ + HA_TOPTION_ENUM("ENCRYPTION", encryption, "DEFAULT, ON, OFF", 0), /* With this option the user defines the key identifier using for the encryption */ - HA_TOPTION_NUMBER("PAGE_ENCRYPTION_KEY", page_encryption_key, 0, 1, 255, 1), + HA_TOPTION_NUMBER("ENCRYPTION_KEY", encryption_key, 0, 1, UINT_MAX32, 1), HA_TOPTION_END }; @@ -11791,13 +11791,14 @@ ha_innobase::check_table_options( enum row_type row_format = table->s->row_type; ha_table_option_struct *options= table->s->option_struct; atomic_writes_t awrites = (atomic_writes_t)options->atomic_writes; + fil_encryption_t encrypt = (fil_encryption_t)options->encryption; - if (options->page_encryption) { + if (encrypt == FIL_SPACE_ENCRYPTION_ON) { if (srv_encrypt_tables) { push_warning( thd, Sql_condition::WARN_LEVEL_WARN, HA_WRONG_CREATE_OPTION, - "InnoDB: PAGE_ENCRYPTION not available if innodb_encrypt_tables=ON"); + "InnoDB: ENCRYPTION not available if innodb_encrypt_tables=ON"); return "INNODB_ENCRYPT_TABLES"; } @@ -11805,7 +11806,7 @@ ha_innobase::check_table_options( push_warning( thd, Sql_condition::WARN_LEVEL_WARN, HA_WRONG_CREATE_OPTION, - "InnoDB: PAGE_ENCRYPTION requires" + "InnoDB: ENCRYPTION requires" " innodb_file_per_table."); return "PAGE_ENCRYPTION"; } @@ -11814,14 +11815,6 @@ ha_innobase::check_table_options( /* Check page compression requirements */ if (options->page_compressed) { - if (srv_encrypt_tables) { - push_warning( - thd, Sql_condition::WARN_LEVEL_WARN, - HA_WRONG_CREATE_OPTION, - "InnoDB: PAGE_COMPRESSION not available if innodb_encrypt_tables=ON"); - return "PAGE_COMPRESSED"; - } - if (row_format == ROW_TYPE_COMPRESSED) { push_warning( thd, Sql_condition::WARN_LEVEL_WARN, @@ -11891,29 +11884,29 @@ ha_innobase::check_table_options( } } - if (options->page_encryption_key != 0) { - if (options->page_encryption == false) { + if (options->encryption_key != 0) { + if (options->encryption == FIL_SPACE_ENCRYPTION_OFF) { /* ignore this to allow alter table without changing page_encryption_key ...*/ } - if (options->page_encryption_key < 1 || options->page_encryption_key > 255) { + if (options->encryption_key < 1) { push_warning_printf( thd, Sql_condition::WARN_LEVEL_WARN, HA_WRONG_CREATE_OPTION, - "InnoDB: invalid PAGE_ENCRYPTION_KEY = %lu." - " Valid values are [1..255]", - options->page_encryption_key); - return "PAGE_ENCRYPTION_KEY"; + "InnoDB: invalid ENCRYPTION_KEY = %lu." + " Valid values are [1..UINTMAX32]", + options->encryption_key); + return "ENCRYPTION_KEY"; } - if (!has_encryption_key(options->page_encryption_key)) { + if (!has_encryption_key(options->encryption_key)) { push_warning_printf( thd, Sql_condition::WARN_LEVEL_WARN, HA_WRONG_CREATE_OPTION, - "InnoDB: PAGE_ENCRYPTION_KEY encryption key %lu not available", - options->page_encryption_key + "InnoDB: ENCRYPTION_KEY %lu not available", + options->encryption_key ); - return "PAGE_ENCRYPTION_KEY"; + return "ENCRYPTION_KEY"; } } @@ -11975,6 +11968,11 @@ ha_innobase::create( const char* stmt; size_t stmt_len; + /* Cache table options */ + ha_table_option_struct *options= table->s->option_struct; + fil_encryption_t encrypt = (fil_encryption_t)options->encryption; + ulint key_id = (options->encryption_key == 0) ? srv_default_encryption_key : + options->encryption_key; DBUG_ENTER("ha_innobase::create"); @@ -12220,6 +12218,18 @@ ha_innobase::create( innobase_commit_low(trx); + /* If user has requested that table should be encrypted or table + should remain as unencrypted store crypt data */ + if (encrypt == FIL_SPACE_ENCRYPTION_ON || encrypt == FIL_SPACE_ENCRYPTION_OFF) { + ulint maxsize; + ulint zip_size = fil_space_get_zip_size(innobase_table->space); + fil_space_crypt_t* crypt_data = fil_space_create_crypt_data(); + crypt_data->page0_offset = fsp_header_get_crypt_offset(zip_size, &maxsize); + crypt_data->keys[0].key_id = key_id; + crypt_data->encryption = encrypt; + fil_space_set_crypt_data(innobase_table->space, crypt_data); + } + row_mysql_unlock_data_dictionary(trx); /* Flush the log to reduce probability that the .frm files and @@ -20329,12 +20339,12 @@ static MYSQL_SYSVAR_UINT(encryption_rotation_iops, srv_n_fil_crypt_iops, innodb_encryption_rotation_iops_update, srv_n_fil_crypt_iops, 0, UINT_MAX32, 0); -static MYSQL_SYSVAR_UINT(default_page_encryption_key, srv_default_page_encryption_key, +static MYSQL_SYSVAR_UINT(default_encryption_key, srv_default_encryption_key, PLUGIN_VAR_RQCMDARG, - "Encryption key used for page encryption.", + "Default encryption key used for table encryption.", NULL, NULL, - FIL_DEFAULT_ENCRYPTION_KEY, 1, 255, 0); + FIL_DEFAULT_ENCRYPTION_KEY, 1, UINT_MAX32, 0); static MYSQL_SYSVAR_BOOL(scrub_log, srv_scrub_log, PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY, @@ -20641,7 +20651,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(scrub_log), MYSQL_SYSVAR(scrub_log_interval), MYSQL_SYSVAR(encrypt_log), - MYSQL_SYSVAR(default_page_encryption_key), + MYSQL_SYSVAR(default_encryption_key), /* Scrubing feature */ MYSQL_SYSVAR(immediate_scrub_data_uncompressed), MYSQL_SYSVAR(background_scrub_data_uncompressed), diff --git a/storage/xtradb/handler/ha_innodb.h b/storage/xtradb/handler/ha_innodb.h index 0acf77da28f..fdc6f9521aa 100644 --- a/storage/xtradb/handler/ha_innodb.h +++ b/storage/xtradb/handler/ha_innodb.h @@ -71,9 +71,8 @@ struct ha_table_option_struct srv_use_atomic_writes=1. Atomic writes are not used if value OFF.*/ - bool page_encryption; /*!< Flag for an encrypted table */ - /* Following can't be unsigned as it's compared with ULINT_UNDEFINED */ - int page_encryption_key; /*!< ID of the encryption key */ + uint encryption; /*!< DEFAULT, ON, OFF */ + int encryption_key; /*!< encryption key id */ }; /** The class defining a handle to an Innodb table */ diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc index 7e8e6e4598e..9fe3cbc2bd1 100644 --- a/storage/xtradb/handler/handler0alter.cc +++ b/storage/xtradb/handler/handler0alter.cc @@ -282,8 +282,8 @@ ha_innobase::check_if_supported_inplace_alter( DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); } - if (new_options->page_encryption != old_options->page_encryption || - new_options->page_encryption_key != old_options->page_encryption_key) { + if (new_options->encryption != old_options->encryption || + new_options->encryption_key != old_options->encryption_key) { ha_alter_info->unsupported_reason = innobase_get_err_msg( ER_ALTER_OPERATION_NOT_SUPPORTED_REASON); DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); diff --git a/storage/xtradb/include/buf0buf.h b/storage/xtradb/include/buf0buf.h index 7eb58216698..912fbb9194f 100644 --- a/storage/xtradb/include/buf0buf.h +++ b/storage/xtradb/include/buf0buf.h @@ -1469,16 +1469,19 @@ buf_own_zip_mutex_for_page( The hook that is called just before a page is written to disk. The function encrypts the content of the page and returns a pointer to a frame that will be written instead of the real frame. */ +UNIV_INTERN byte* buf_page_encrypt_before_write( /*==========================*/ buf_page_t* page, /*!< in/out: buffer page to be flushed */ - const byte* frame); + const byte* frame, + ulint space_id); /********************************************************************** The hook that is called after page is written to disk. The function releases any resources needed for encryption that was allocated in buf_page_encrypt_before_write */ +UNIV_INTERN ibool buf_page_encrypt_after_write( /*=========================*/ @@ -1488,6 +1491,7 @@ buf_page_encrypt_after_write( The hook that is called just before a page is read from disk. The function allocates memory that is used to temporarily store disk content before getting decrypted */ +UNIV_INTERN byte* buf_page_decrypt_before_read( /*=========================*/ @@ -1498,19 +1502,35 @@ buf_page_decrypt_before_read( The hook that is called just after a page is read from disk. The function decrypt disk content into buf_page_t and releases the temporary buffer that was allocated in buf_page_decrypt_before_read */ +UNIV_INTERN ibool buf_page_decrypt_after_read( /*========================*/ buf_page_t* page); /*!< in/out: buffer page read from disk */ -/********************************************************************//** -Release memory allocated for page decryption. -Only used in scenarios where read fails, e.g due to tablespace being dropped */ -void -buf_page_decrypt_cleanup( -/*=====================*/ - buf_page_t* page); /*!< in/out: buffer page read from disk */ +/** @brief The temporary memory structure. +NOTE! The definition appears here only for other modules of this +directory (buf) to see it. Do not use from outside! */ + +typedef struct { + bool reserved; /*!< true if this slot is reserved + */ +#ifdef HAVE_LZO + byte* lzo_mem; /*!< Temporal memory used by LZO */ +#endif + byte* crypt_buf; /*!< for encryption the data needs to be + copied to a separate buffer before it's + encrypted&written. this as a page can be + read while it's being flushed */ + byte* crypt_buf_free; /*!< for encryption, allocated buffer + that is then alligned */ + byte* comp_buf; /*!< for compression we need + temporal buffer because page + can be read while it's being flushed */ + byte* comp_buf_free; /*!< for compression, allocated + buffer that is then alligned */ +} buf_tmp_buffer_t; /** The common buffer control block structure for compressed and uncompressed frames */ @@ -1587,19 +1607,16 @@ struct buf_page_t{ operation needed. */ unsigned key_version; /*!< key version for this block */ - byte* crypt_buf; /*!< for encryption the data needs to be - copied to a separate buffer before it's - encrypted&written. this as a page can be - read while it's being flushed */ - byte* crypt_buf_free; /*!< for encryption, allocated buffer - that is then alligned */ - byte* comp_buf; /*!< for compression we need - temporal buffer because page - can be read while it's being flushed */ - byte* comp_buf_free; /*!< for compression, allocated - buffer that is then alligned */ - bool encrypt_later; /*!< should we encrypt the page - at os0file.cc ? */ + + ulint real_size; /*!< Real size of the page + Normal pages == UNIV_PAGE_SIZE + page compressed pages, payload + size alligned to sector boundary. + */ + + buf_tmp_buffer_t* slot; /*!< Slot for temporary memory + used for encryption/compression + or NULL */ #ifndef UNIV_HOTBACKUP buf_page_t* hash; /*!< node used in chaining to buf_pool->page_hash or @@ -1918,6 +1935,17 @@ struct buf_buddy_stat_t { ib_uint64_t relocated_usec; }; +/** @brief The temporary memory array structure. + +NOTE! The definition appears here only for other modules of this +directory (buf) to see it. Do not use from outside! */ + +typedef struct { + ulint n_slots; /*!< Total number of slots */ + buf_tmp_buffer_t *slots; /*!< Pointer to the slots in the + array */ +} buf_tmp_array_t; + /** @brief The buffer pool structure. NOTE! The definition appears here only for other modules of this @@ -2091,6 +2119,10 @@ struct buf_pool_t{ /*!< Sentinel records for buffer pool watches. */ + buf_tmp_array_t* tmp_arr; + /*!< Array for temporal memory + used in compression and encryption */ + #if BUF_BUDDY_LOW > UNIV_ZIP_SIZE_MIN # error "BUF_BUDDY_LOW > UNIV_ZIP_SIZE_MIN" #endif diff --git a/storage/xtradb/include/buf0buf.ic b/storage/xtradb/include/buf0buf.ic index b4cbba29f3c..7f6cceade36 100644 --- a/storage/xtradb/include/buf0buf.ic +++ b/storage/xtradb/include/buf0buf.ic @@ -925,28 +925,15 @@ buf_block_free( mutex_exit(&block->mutex); } -/********************************************************************//** -Get crypt buffer. */ -UNIV_INLINE -byte* -buf_page_get_crypt_buffer( -/*=========================*/ - const buf_page_t* bpage) /*!< in: buffer pool page */ -{ - return bpage->crypt_buf; -} - /********************************************************************//** Get buf frame. */ UNIV_INLINE void * buf_page_get_frame( -/*=========================*/ +/*===============*/ const buf_page_t* bpage) /*!< in: buffer pool page */ { - if (bpage->crypt_buf) { - return buf_page_get_crypt_buffer(bpage); - } else if (bpage->zip.data) { + if (bpage->zip.data) { return bpage->zip.data; } else { return ((buf_block_t*) bpage)->frame; diff --git a/storage/xtradb/include/fil0crypt.h b/storage/xtradb/include/fil0crypt.h index 9d02034e4b2..359533dc50a 100644 --- a/storage/xtradb/include/fil0crypt.h +++ b/storage/xtradb/include/fil0crypt.h @@ -37,6 +37,14 @@ Created 04/01/2015 Jan Lindström /* This key will be used if nothing else is given */ #define FIL_DEFAULT_ENCRYPTION_KEY 1 +/** Enum values for encryption table option */ +typedef enum { + FIL_SPACE_ENCRYPTION_DEFAULT = 0, /* Tablespace encrypted if + srv_encrypt_tables = ON */ + FIL_SPACE_ENCRYPTION_ON = 1, /* Tablespace is encrypted always */ + FIL_SPACE_ENCRYPTION_OFF = 2 /* Tablespace is not encrypted */ +} fil_encryption_t; + /** * CRYPT_SCHEME_UNENCRYPTED * @@ -67,6 +75,8 @@ struct key_struct { uint key_version; /*!< Key version used as identifier */ + uint key_id; /*1< Key id used as + identifier */ byte key[MY_AES_MAX_KEY_LENGTH]; /*!< Cached L or key */ uint key_length; /*!< Key length */ }; @@ -95,6 +105,7 @@ struct fil_space_crypt_struct key_struct keys[3]; // cached L = AES_ECB(KEY, IV) uint min_key_version; // min key version for this space ulint page0_offset; // byte offset on page 0 for crypt data + fil_encryption_t encryption; // Encryption setup ib_mutex_t mutex; // mutex protecting following variables bool closing; // is tablespace being closed @@ -227,9 +238,7 @@ fil_space_encrypt( lsn_t lsn, /*!< in: page lsn */ const byte* src_frame,/*!< in: page frame */ ulint size, /*!< in: size of data to encrypt */ - byte* dst_frame, /*!< in: where to encrypt to */ - ulint page_encryption_key); /*!< in: page encryption key id if page - encrypted */ + byte* dst_frame); /*!< in: where to encrypt to */ /********************************************************************* Decrypt buffer page */ diff --git a/storage/xtradb/include/fil0fil.h b/storage/xtradb/include/fil0fil.h index 91653c4bb7d..c7ecaf8b628 100644 --- a/storage/xtradb/include/fil0fil.h +++ b/storage/xtradb/include/fil0fil.h @@ -982,6 +982,16 @@ _fil_io( #define fil_io(type, sync, space_id, zip_size, block_offset, byte_offset, len, buf, message, write_size) \ _fil_io(type, sync, space_id, zip_size, block_offset, byte_offset, len, buf, message, write_size, NULL) +/*******************************************************************//** +Returns the block size of the file space +@return block size */ +UNIV_INTERN +ulint +fil_space_get_block_size( +/*=====================*/ + ulint id, /*!< in: space id */ + ulint offset, /*!< in: page offset */ + ulint len); /*!< in: page len */ /**********************************************************************//** Waits for an aio operation to complete. This function is used to write the diff --git a/storage/xtradb/include/srv0srv.h b/storage/xtradb/include/srv0srv.h index de33f767021..7387f11bd6e 100644 --- a/storage/xtradb/include/srv0srv.h +++ b/storage/xtradb/include/srv0srv.h @@ -705,8 +705,8 @@ that semaphore times out in InnoDB */ #define DEFAULT_SRV_FATAL_SEMAPHORE_TIMEOUT 600 extern ulong srv_fatal_semaphore_wait_threshold; -/** Default encryption key used for page encryption */ -extern uint srv_default_page_encryption_key; +/** Default encryption key used for tablespace encryption */ +extern uint srv_default_encryption_key; /** Enable semaphore request instrumentation */ extern my_bool srv_instrument_semaphores; diff --git a/storage/xtradb/srv/srv0srv.cc b/storage/xtradb/srv/srv0srv.cc index 9396a713cad..df3982b98cd 100644 --- a/storage/xtradb/srv/srv0srv.cc +++ b/storage/xtradb/srv/srv0srv.cc @@ -670,8 +670,8 @@ thread ensures that we flush the log files at least once per second. */ static time_t srv_last_log_flush_time; -/** Default encryption key used for page encryption */ -UNIV_INTERN uint srv_default_page_encryption_key = FIL_DEFAULT_ENCRYPTION_KEY; +/** Default encryption key used for tablespace encryption */ +UNIV_INTERN uint srv_default_encryption_key = FIL_DEFAULT_ENCRYPTION_KEY; /** Enable semaphore request instrumentation */ UNIV_INTERN my_bool srv_instrument_semaphores = FALSE;