diff options
author | Jan Lindström <jan.lindstrom@mariadb.com> | 2016-12-28 16:14:28 +0200 |
---|---|---|
committer | Jan Lindström <jan.lindstrom@mariadb.com> | 2016-12-28 16:32:45 +0200 |
commit | 283e9cf4cbb34e1325699707068ab72ec3accfff (patch) | |
tree | 40d9660aefc4aae5c01ca7a4215d8f8911e61399 /storage/xtradb | |
parent | d50cf42bc05b1faa5d39c766389ac345e119037e (diff) | |
download | mariadb-git-283e9cf4cbb34e1325699707068ab72ec3accfff.tar.gz |
MDEV-11656: 'Data structure corruption' IMPORT TABLESPACE doesn't work for encrypted InnoDB tables if space_id changed
Problem was that for encryption we use temporary scratch area for
reading and writing tablespace pages. But if page was not really
decrypted the correct updated page was not moved to scratch area
that was then written. This can happen e.g. for page 0 as it is
newer encrypted even if encryption is enabled and as we write
the contents of old page 0 to tablespace it contained naturally
incorrect space_id that is then later noted and error message
was written. Updated page with correct space_id was lost.
If tablespace is encrypted we use additional
temporary scratch area where pages are read
for decrypting readptr == crypt_io_buffer != io_buffer.
Destination for decryption is a buffer pool block
block->frame == dst == io_buffer that is updated.
Pages that did not require decryption even when
tablespace is marked as encrypted are not copied
instead block->frame is set to src == readptr.
If tablespace was encrypted we copy updated page to
writeptr != io_buffer. This fixes above bug.
For encryption we again use temporary scratch area
writeptr != io_buffer == dst
that is then written to the tablespace
(1) For normal tables src == dst == writeptr
ut_ad(!encrypted && !page_compressed ?
src == dst && dst == writeptr + (i * size):1);
(2) For page compressed tables src == dst == writeptr
ut_ad(page_compressed && !encrypted ?
src == dst && dst == writeptr + (i * size):1);
(3) For encrypted tables src != dst != writeptr
ut_ad(encrypted ?
src != dst && dst != writeptr + (i * size):1);
Diffstat (limited to 'storage/xtradb')
-rw-r--r-- | storage/xtradb/fil/fil0fil.cc | 61 |
1 files changed, 53 insertions, 8 deletions
diff --git a/storage/xtradb/fil/fil0fil.cc b/storage/xtradb/fil/fil0fil.cc index 5900bdb2140..e7da4569f0d 100644 --- a/storage/xtradb/fil/fil0fil.cc +++ b/storage/xtradb/fil/fil0fil.cc @@ -6594,6 +6594,7 @@ fil_iterate( for (offset = iter.start; offset < iter.end; offset += n_bytes) { byte* io_buffer = iter.io_buffer; + bool row_compressed = false; block->frame = io_buffer; @@ -6606,6 +6607,7 @@ fil_iterate( /* Zip IO is done in the compressed page buffer. */ io_buffer = block->page.zip.data; + row_compressed = true; } else { io_buffer = iter.io_buffer; } @@ -6646,8 +6648,9 @@ fil_iterate( for (ulint i = 0; i < n_pages_read; ++i) { ulint size = iter.page_size; dberr_t err = DB_SUCCESS; - byte* src = (readptr + (i * size)); - byte* dst = (io_buffer + (i * size)); + byte* src = readptr + (i * size); + byte* dst = io_buffer + (i * size); + bool frame_changed = false; ulint page_type = mach_read_from_2(src+FIL_PAGE_TYPE); @@ -6671,8 +6674,12 @@ fil_iterate( if (decrypted) { updated = true; } else { - /* TODO: remove unnecessary memcpy's */ - memcpy(dst, src, size); + if (!page_compressed && !row_compressed) { + block->frame = src; + frame_changed = true; + } else { + memcpy(dst, src, size); + } } } @@ -6697,7 +6704,45 @@ fil_iterate( buf_block_set_state(block, BUF_BLOCK_NOT_USED); buf_block_set_state(block, BUF_BLOCK_READY_FOR_USE); - src = (io_buffer + (i * size)); + /* If tablespace is encrypted we use additional + temporary scratch area where pages are read + for decrypting readptr == crypt_io_buffer != io_buffer. + + Destination for decryption is a buffer pool block + block->frame == dst == io_buffer that is updated. + Pages that did not require decryption even when + tablespace is marked as encrypted are not copied + instead block->frame is set to src == readptr. + + For encryption we again use temporary scratch area + writeptr != io_buffer == dst + that is then written to the tablespace + + (1) For normal tables io_buffer == dst == writeptr + (2) For only page compressed tables + io_buffer == dst == writeptr + (3) For encrypted (and page compressed) + readptr != io_buffer == dst != writeptr + */ + + ut_ad(!encrypted && !page_compressed ? + src == dst && dst == writeptr + (i * size):1); + ut_ad(page_compressed && !encrypted ? + src == dst && dst == writeptr + (i * size):1); + ut_ad(encrypted ? + src != dst && dst != writeptr + (i * size):1); + + if (encrypted) { + memcpy(writeptr + (i * size), + row_compressed ? block->page.zip.data : + block->frame, size); + } + + if (frame_changed) { + block->frame = dst; + } + + src = io_buffer + (i * size); if (page_compressed) { ulint len = 0; @@ -6718,7 +6763,7 @@ fil_iterate( write it back. Note that we should not encrypt the buffer that is in buffer pool. */ if (decrypted && encrypted) { - unsigned char *dest = (writeptr + (i * size)); + byte *dest = writeptr + (i * size); ulint space = mach_read_from_4( src + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); ulint offset = mach_read_from_4(src + FIL_PAGE_OFFSET); @@ -6900,9 +6945,9 @@ fil_tablespace_iterate( void* crypt_io_buffer = NULL; if (iter.crypt_data != NULL) { crypt_io_buffer = mem_alloc( - iter.n_io_buffers * UNIV_PAGE_SIZE); + (2 + iter.n_io_buffers) * UNIV_PAGE_SIZE); iter.crypt_io_buffer = static_cast<byte*>( - crypt_io_buffer); + ut_align(crypt_io_buffer, UNIV_PAGE_SIZE)); } err = fil_iterate(iter, &block, callback); |