summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
Diffstat (limited to 'storage')
-rw-r--r--storage/innobase/buf/buf0buf.cc93
-rw-r--r--storage/innobase/buf/buf0checksum.cc45
-rw-r--r--storage/innobase/buf/buf0dblwr.cc76
-rw-r--r--storage/innobase/dict/dict0dict.cc6
-rw-r--r--storage/innobase/fil/fil0crypt.cc17
-rw-r--r--storage/innobase/fil/fil0fil.cc28
-rw-r--r--storage/innobase/fil/fil0pagecompress.cc272
-rw-r--r--storage/innobase/handler/ha_innodb.cc3
-rw-r--r--storage/innobase/include/buf0checksum.h13
-rw-r--r--storage/innobase/include/fil0pagecompress.h58
-rw-r--r--storage/innobase/include/fsp0pagecompress.h4
-rw-r--r--storage/innobase/include/fsp0pagecompress.ic2
-rw-r--r--storage/xtradb/buf/buf0buf.cc81
-rw-r--r--storage/xtradb/buf/buf0checksum.cc45
-rw-r--r--storage/xtradb/buf/buf0dblwr.cc76
-rw-r--r--storage/xtradb/dict/dict0dict.cc6
-rw-r--r--storage/xtradb/fil/fil0crypt.cc17
-rw-r--r--storage/xtradb/fil/fil0fil.cc28
-rw-r--r--storage/xtradb/fil/fil0pagecompress.cc272
-rw-r--r--storage/xtradb/handler/ha_innodb.cc3
-rw-r--r--storage/xtradb/include/buf0checksum.h13
-rw-r--r--storage/xtradb/include/fil0pagecompress.h58
-rw-r--r--storage/xtradb/include/fsp0pagecompress.h4
-rw-r--r--storage/xtradb/include/fsp0pagecompress.ic2
24 files changed, 793 insertions, 429 deletions
diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc
index e3c2337659e..07fee423973 100644
--- a/storage/innobase/buf/buf0buf.cc
+++ b/storage/innobase/buf/buf0buf.cc
@@ -2,7 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
-Copyright (c) 2013, 2017, MariaDB Corporation.
+Copyright (c) 2013, 2018, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -740,25 +740,34 @@ buf_page_is_corrupted(
#ifndef UNIV_INNOCHECKSUM
ulint space_id = mach_read_from_4(read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
#endif
- ulint page_type = mach_read_from_2(read_buf + FIL_PAGE_TYPE);
-
/* We can trust page type if page compression is set on tablespace
flags because page compression flag means file must have been
created with 10.1 (later than 5.5 code base). In 10.1 page
compressed tables do not contain post compression checksum and
- FIL_PAGE_END_LSN_OLD_CHKSUM field stored. Note that space can
- be null if we are in fil_check_first_page() and first page
- is not compressed or encrypted. Page checksum is verified
- after decompression (i.e. normally pages are already
- decompressed at this stage). */
+ FIL_PAGE_END_LSN_OLD_CHKSUM field stored.
+
+ Note that space can be null if we are in fil_check_first_page() but
+ first page (page 0) is not compressed or encrypted.
+
+ Note that space can be null if we are in Datafile::find_space_id()
+ and that point pages are not yet decompressed/decrypted.
+
+ Note that for innochecksum.cc we want to know if page is corrupted
+ using traditional checksum methods even when page type is
+ compressed or compressed and encrypted.
+
+ Page checksum is verified after decompression (i.e. normally pages
+ are already decompressed at this stage). */
+#ifndef UNIV_INNOCHECKSUM
+ ulint page_type = mach_read_from_2(read_buf + FIL_PAGE_TYPE);
if ((page_type == FIL_PAGE_PAGE_COMPRESSED ||
page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED)
-#ifndef UNIV_INNOCHECKSUM
- && space && FSP_FLAGS_HAS_PAGE_COMPRESSION(space->flags)
-#endif
- ) {
+ && (!space
+ || (space && FSP_FLAGS_HAS_PAGE_COMPRESSION(space->flags))))
+ {
return (false);
}
+#endif /* !UNIV_INNOCHECKSUM */
if (!zip_size
&& memcmp(read_buf + FIL_PAGE_LSN + 4,
@@ -1052,12 +1061,14 @@ buf_page_print(const byte* read_buf, ulint zip_size)
size = UNIV_PAGE_SIZE;
}
+#ifdef UNIV_BUG_DEBUG
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Page dump in ascii and hex (" ULINTPF " bytes):\n",
size);
ut_print_buf(stderr, read_buf, size);
fputs("\nInnoDB: End of page dump\n", stderr);
+#endif
if (zip_size) {
/* Print compressed page. */
@@ -3214,8 +3225,8 @@ loop:
/* Try to set table as corrupted instead of
asserting. */
- if (space > TRX_SYS_SPACE &&
- dict_set_corrupted_by_space(space)) {
+ if (space > TRX_SYS_SPACE) {
+ dict_set_corrupted_by_space(space);
return (NULL);
}
@@ -4710,21 +4721,19 @@ buf_page_check_corrupt(buf_page_t* bpage, fil_space_t* space)
still be corrupted if used key does not match. */
still_encrypted = (crypt_data &&
crypt_data->type != CRYPT_SCHEME_UNENCRYPTED &&
- !bpage->encrypted &&
fil_space_verify_crypt_checksum(dst_frame, zip_size,
space, bpage->offset));
- if (!still_encrypted) {
- /* If traditional checksums match, we assume that page is
- not anymore encrypted. */
- corrupted = buf_page_is_corrupted(true, dst_frame, zip_size,
- space);
+ /* If traditional checksums match, we assume that page is
+ not anymore encrypted. */
+ corrupted = buf_page_is_corrupted(true, dst_frame, zip_size,
+ space);
- if (!corrupted) {
- bpage->encrypted = false;
- } else {
- err = DB_PAGE_CORRUPTED;
- }
+ if (!corrupted) {
+ bpage->encrypted = false;
+ still_encrypted = false;
+ } else if (!still_encrypted) {
+ err = DB_PAGE_CORRUPTED;
}
/* Pages that we think are unencrypted but do not match the checksum
@@ -4741,7 +4750,7 @@ buf_page_check_corrupt(buf_page_t* bpage, fil_space_t* space)
", page number=%u]"
" in file %s cannot be decrypted.",
bpage->space, bpage->offset,
- space->name);
+ space->chain.start->name);
ib_logf(IB_LOG_LEVEL_INFO,
"However key management plugin or used key_version " ULINTPF
@@ -4802,7 +4811,11 @@ buf_page_io_complete(buf_page_t* bpage, bool evict)
return(DB_TABLESPACE_DELETED);
}
- buf_page_decrypt_after_read(bpage, space);
+ dberr_t err = DB_SUCCESS;
+
+ if (!buf_page_decrypt_after_read(bpage, space)) {
+ err = DB_PAGE_CORRUPTED;
+ }
if (buf_page_get_zip_size(bpage)) {
frame = bpage->zip.data;
@@ -4810,6 +4823,10 @@ buf_page_io_complete(buf_page_t* bpage, bool evict)
frame = ((buf_block_t*) bpage)->frame;
}
+ if (err != DB_SUCCESS) {
+ goto database_corrupted;
+ }
+
if (buf_page_get_zip_size(bpage)) {
frame = bpage->zip.data;
os_atomic_increment_ulint(&buf_pool->n_pend_unzip, 1);
@@ -4896,7 +4913,7 @@ database_corrupted:
ib_logf(IB_LOG_LEVEL_ERROR,
"Database page corruption on disk"
" or a failed file read of tablespace %s"
- " page [page id: space=%u"
+ " page [page id: space=%u"
", page number=%u]"
". You may have to recover from "
"a backup.",
@@ -6442,10 +6459,14 @@ buf_page_decrypt_after_read(buf_page_t* bpage, fil_space_t* space)
#endif
/* decompress using comp_buf to dst_frame */
- fil_decompress_page(slot->comp_buf,
- dst_frame,
- ulong(size),
- &bpage->write_size);
+ if (!fil_verify_compression_checksum(dst_frame,
+ bpage->space, bpage->offset)
+ || !fil_decompress_page(slot->comp_buf,
+ dst_frame,
+ ulong(size),
+ &bpage->write_size)) {
+ success = false;
+ }
/* Mark this slot as free */
slot->reserved = false;
@@ -6500,10 +6521,14 @@ buf_page_decrypt_after_read(buf_page_t* bpage, fil_space_t* space)
fil_page_type_validate(dst_frame);
#endif
/* decompress using comp_buf to dst_frame */
- fil_decompress_page(slot->comp_buf,
+ if (!fil_verify_compression_checksum(dst_frame,
+ bpage->space, bpage->offset)
+ || !fil_decompress_page(slot->comp_buf,
dst_frame,
ulong(size),
- &bpage->write_size);
+ &bpage->write_size)) {
+ success = false;
+ }
ut_d(fil_page_type_validate(dst_frame));
}
diff --git a/storage/innobase/buf/buf0checksum.cc b/storage/innobase/buf/buf0checksum.cc
index 9e5f1dfe475..f3e5d9b5359 100644
--- a/storage/innobase/buf/buf0checksum.cc
+++ b/storage/innobase/buf/buf0checksum.cc
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -28,6 +29,8 @@ Created Aug 11, 2011 Vasil Dimov
#include "ut0crc32.h" /* ut_crc32() */
#include "ut0rnd.h" /* ut_fold_binary() */
#include "buf0checksum.h"
+#include "mtr0types.h"
+#include "mach0data.h"
#ifndef UNIV_INNOCHECKSUM
@@ -156,3 +159,45 @@ buf_checksum_algorithm_name(
return(NULL);
}
+/** Calculates the CRC32 checksum of a page compressed page. The value is
+stored to the page when it is written to a file and also checked for
+a match when reading from the file. Checksum is calculated from
+actual payload of the compressed page and some header fields.
+
+@param[in] page buffer page (UNIV_PAGE_SIZE bytes)
+@return checksum */
+UNIV_INTERN
+uint32_t
+buf_calc_compressed_crc32(
+ const byte* page)
+{
+ /* In page compressed pages compression method is stored to field
+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, thus add it to checksum.
+ In pages first compressed and then encrypted same field
+ contains key version after compression. */
+
+ ulint page_type = mach_read_from_2(page + FIL_PAGE_TYPE);
+
+ ulint header_len = page_type == FIL_PAGE_PAGE_COMPRESSED ?
+ FIL_PAGE_SPACE_ID - FIL_PAGE_OFFSET :
+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION - FIL_PAGE_OFFSET;
+
+ const uint32_t c1 = ut_crc32(
+ page + FIL_PAGE_OFFSET,
+ header_len);
+
+ /* Calculate checksum from actual payload including stored size
+ field. In encrypted case add also compression method field. */
+ ulint payload_len = mach_read_from_2(page+FIL_PAGE_DATA)+FIL_PAGE_COMPRESSED_SIZE;
+
+ if (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
+ payload_len += FIL_PAGE_COMPRESSION_METHOD_SIZE;
+ }
+
+ const uint32_t c2 = ut_crc32(
+ page + FIL_PAGE_DATA,
+ payload_len);
+
+ return(c1 ^ c2);
+}
+
diff --git a/storage/innobase/buf/buf0dblwr.cc b/storage/innobase/buf/buf0dblwr.cc
index 70422671190..99c7546485a 100644
--- a/storage/innobase/buf/buf0dblwr.cc
+++ b/storage/innobase/buf/buf0dblwr.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2013, 2017, MariaDB Corporation.
+Copyright (c) 2013, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -488,8 +488,45 @@ leave_func:
ut_free(unaligned_read_buf);
}
-/****************************************************************//**
-Process the double write buffer pages. */
+/** Verify post compression checksum and if it matches
+decompress page.
+@param[in,out] read_buf Page compressed buffer. Result of
+ decompression is copied here.
+@param[in] space_id Tablespace identifier
+@param[in] page_no Page offset
+@param[in] page_size Actual page size
+@return true if both stored post compression checksum matches calculated one
+or it is BUF_NO_CHECKSUM_MAGIC and page decompression is succeeds.*/
+static
+bool
+buf_dblwr_decompress(
+ byte * read_buf,
+ ulint space_id,
+ ulint page_no,
+ ulint page_size)
+{
+ bool success = true;
+
+ /* Page is always fist compressed and then encrypted. Here we can
+ only decompress pages if they are not encrypted.
+
+ For compressed pages verify post compression checksum and if it
+ matches decompress the page.
+
+ Note that old datafiles have checksum BUF_NO_CHECKSUM_MAGIC and
+ that value is accepted as correct post compression checksum see
+ fil_verify_compression_checksum().
+
+ In that case we hope that compression algorithm can recover from
+ corrupted input and return a error that is then handled.*/
+ if(!fil_verify_compression_checksum(read_buf, space_id, page_no)
+ || !fil_decompress_page(NULL, read_buf, page_size, NULL)) {
+ return success = false;
+ }
+
+ return success;
+}
+
void
buf_dblwr_process()
/*===============*/
@@ -556,25 +593,24 @@ buf_dblwr_process()
const bool is_all_zero = buf_page_is_zeroes(
read_buf, zip_size);
+ bool success = true;
+
if (is_all_zero) {
/* We will check if the copy in the
doublewrite buffer is valid. If not, we will
ignore this page (there should be redo log
records to initialize it). */
} else {
- if (fil_page_is_compressed_encrypted(read_buf) ||
- fil_page_is_compressed(read_buf)) {
- /* Decompress the page before
- validating the checksum. */
- fil_decompress_page(
- NULL, read_buf, srv_page_size,
- NULL, true);
+ if (fil_page_is_compressed(read_buf)) {
+ success = buf_dblwr_decompress(read_buf,
+ space_id, page_no, srv_page_size);
}
- if (fil_space_verify_crypt_checksum(
+ if (success
+ && (fil_space_verify_crypt_checksum(
read_buf, zip_size, NULL, page_no)
- || !buf_page_is_corrupted(
- true, read_buf, zip_size, space())) {
+ || !buf_page_is_corrupted(
+ true, read_buf, zip_size, space()))) {
/* The page is good; there is no need
to consult the doublewrite buffer. */
continue;
@@ -589,16 +625,14 @@ buf_dblwr_process()
}
/* Next, validate the doublewrite page. */
- if (fil_page_is_compressed_encrypted(page) ||
- fil_page_is_compressed(page)) {
- /* Decompress the page before
- validating the checksum. */
- fil_decompress_page(
- NULL, page, srv_page_size, NULL, true);
+ if (fil_page_is_compressed(page)) {
+ success = buf_dblwr_decompress(page,
+ space_id, page_no, srv_page_size);
}
- if (!fil_space_verify_crypt_checksum(page, zip_size, NULL, page_no)
- && buf_page_is_corrupted(true, page, zip_size, space)) {
+ if (!success
+ || (!fil_space_verify_crypt_checksum(page, zip_size, NULL, page_no)
+ && buf_page_is_corrupted(true, page, zip_size, space))) {
if (!is_all_zero) {
ib_logf(IB_LOG_LEVEL_WARN,
"A doublewrite copy of page "
diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index 7ec4364becc..d13d5a51a77 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -6113,6 +6113,12 @@ dict_set_corrupted_by_space(
table = dict_find_table_by_space(space_id);
if (!table) {
+#ifdef UNIV_DEBUG
+ ib_logf(IB_LOG_LEVEL_INFO,
+ "Can't set space " ULINTPF
+ " corrupted as table is not found dict_sys->table_LRU.", space_id);
+
+#endif
return(FALSE);
}
diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc
index 3095503cfc5..fa0eaabf4dc 100644
--- a/storage/innobase/fil/fil0crypt.cc
+++ b/storage/innobase/fil/fil0crypt.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (C) 2013, 2015, Google Inc. All Rights Reserved.
-Copyright (c) 2014, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2014, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -719,8 +719,8 @@ fil_space_encrypt(
comp_mem = (byte *)malloc(UNIV_PAGE_SIZE);
uncomp_mem = (byte *)malloc(UNIV_PAGE_SIZE);
memcpy(comp_mem, src_frame, UNIV_PAGE_SIZE);
- fil_decompress_page(uncomp_mem, comp_mem,
- srv_page_size, NULL);
+ ut_ad(fil_decompress_page(uncomp_mem, comp_mem,
+ srv_page_size, NULL));
src = uncomp_mem;
}
@@ -730,8 +730,8 @@ fil_space_encrypt(
/* Need to decompress the page if it was also compressed */
if (page_compressed_encrypted) {
memcpy(comp_mem, tmp_mem, UNIV_PAGE_SIZE);
- fil_decompress_page(tmp_mem, comp_mem,
- srv_page_size, NULL);
+ ut_ad(fil_decompress_page(tmp_mem, comp_mem,
+ srv_page_size, NULL));
}
bool corrupted = buf_page_is_corrupted(true, tmp_mem, zip_size, space);
@@ -2605,13 +2605,6 @@ fil_space_verify_crypt_checksum(
return(true);
}
- /* Compressed and encrypted pages do not have checksum. Assume not
- corrupted. Page verification happens after decompression in
- buf_page_io_complete() using buf_page_is_corrupted(). */
- if (mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
- return (true);
- }
-
ib_uint32_t cchecksum1 = 0;
ib_uint32_t cchecksum2 = 0;
diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc
index 8c7762c82c3..c993d188aa1 100644
--- a/storage/innobase/fil/fil0fil.cc
+++ b/storage/innobase/fil/fil0fil.cc
@@ -6516,8 +6516,32 @@ fil_iterate(
/* If the original page is page_compressed, we need
to decompress page before we can update it. */
if (page_compressed) {
- fil_decompress_page(NULL, dst, ulong(size),
- NULL);
+ /* Check post compression checksum before
+ doing actual decompression. */
+ if (fil_verify_compression_checksum(dst,
+ space_id, page_no)) {
+ if (!fil_decompress_page(NULL, dst, ulong(size),
+ NULL)) {
+ err = DB_CORRUPTION;
+ }
+ } else {
+ err = DB_CORRUPTION;
+ }
+
+ if (err != DB_SUCCESS) {
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "Imported page [page id: space=" ULINTPF
+ ", page number=" ULINTPF "]"
+ " is corrupted. Post compression"
+ " checksum " ULINTPF
+ " does not match calculated %u.",
+ space_id, page_no,
+ mach_read_from_4(dst+FIL_PAGE_SPACE_OR_CHKSUM),
+ buf_calc_compressed_crc32(dst));
+
+ return(err);
+ }
+
updated = true;
}
diff --git a/storage/innobase/fil/fil0pagecompress.cc b/storage/innobase/fil/fil0pagecompress.cc
index edc932f36f5..1ff04919121 100644
--- a/storage/innobase/fil/fil0pagecompress.cc
+++ b/storage/innobase/fil/fil0pagecompress.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (C) 2013, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (C) 2013, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -26,15 +26,19 @@ Updated 14/02/2015
#include "fil0fil.h"
#include "fil0pagecompress.h"
+#include "mtr0types.h"
+#include "mach0data.h"
+#include "buf0buf.h"
+#include "buf0checksum.h"
+#include "fsp0pagecompress.h"
+#ifndef UNIV_INNOCHECKSUM
#include <debug_sync.h>
#include <my_dbug.h>
#include "mem0mem.h"
#include "hash0hash.h"
#include "os0file.h"
-#include "mach0data.h"
-#include "buf0buf.h"
#include "buf0flu.h"
#include "log0recv.h"
#include "fsp0fsp.h"
@@ -364,8 +368,6 @@ fil_compress_page(
/* Set up the page header */
memcpy(out_buf, buf, FIL_PAGE_DATA);
- /* Set up the checksum */
- mach_write_to_4(out_buf+FIL_PAGE_SPACE_OR_CHKSUM, BUF_NO_CHECKSUM_MAGIC);
/* Set up the compression algorithm */
mach_write_to_8(out_buf+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, comp_method);
@@ -382,10 +384,13 @@ fil_compress_page(
/* Set up the actual payload lenght */
mach_write_to_2(out_buf+FIL_PAGE_DATA, write_size);
+ /* Set up the checksum */
+ mach_write_to_4(out_buf+FIL_PAGE_SPACE_OR_CHKSUM, buf_calc_compressed_crc32(out_buf));
+
#ifdef UNIV_DEBUG
/* Verify */
ut_ad(fil_page_is_compressed(out_buf) || fil_page_is_compressed_encrypted(out_buf));
- ut_ad(mach_read_from_4(out_buf+FIL_PAGE_SPACE_OR_CHKSUM) == BUF_NO_CHECKSUM_MAGIC);
+ ut_ad(mach_read_from_4(out_buf+FIL_PAGE_SPACE_OR_CHKSUM) == buf_calc_compressed_crc32(out_buf));
ut_ad(mach_read_from_2(out_buf+FIL_PAGE_DATA) == write_size);
ut_ad(mach_read_from_8(out_buf+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION) == (ulint)comp_method ||
mach_read_from_2(out_buf+FIL_PAGE_DATA+FIL_PAGE_COMPRESSED_SIZE) == (ulint)comp_method);
@@ -399,7 +404,7 @@ fil_compress_page(
uncomp_page = static_cast<byte *>(ut_malloc(UNIV_PAGE_SIZE));
memcpy(comp_page, out_buf, UNIV_PAGE_SIZE);
- fil_decompress_page(uncomp_page, comp_page, ulong(len), NULL);
+ ut_ad(fil_decompress_page(uncomp_page, comp_page, ulong(len), NULL));
if (buf_page_is_corrupted(false, uncomp_page, 0, space)) {
buf_page_print(uncomp_page, 0);
@@ -466,22 +471,25 @@ err_exit:
}
-/****************************************************************//**
+/**
For page compressed pages decompress the page after actual read
-operation. */
+operation.
+@param[in,out] page_buf Preallocated temporal buffer where
+ compression is done and then copied
+ to the buffer.
+@param[in,out] buf Compressed page and after suggesful
+ decompression operation uncompressed page
+ is copied here.
+@param[in] len Length of output buffer.
+@param[out] write_size Actual payload size of the compressed data.
+@return true when operation succeeded or false when failed */
UNIV_INTERN
-void
+bool
fil_decompress_page(
-/*================*/
- byte* page_buf, /*!< in: preallocated buffer or NULL */
- byte* buf, /*!< out: buffer from which to read; in aio
- this must be appropriately aligned */
- ulong len, /*!< in: length of output buffer.*/
- ulint* write_size, /*!< in/out: Actual payload size of
- the compressed data. */
- bool return_error) /*!< in: true if only an error should
- be produced when decompression fails.
- By default this parameter is false. */
+ byte* page_buf,
+ byte* buf,
+ ulong len,
+ ulint* write_size)
{
int err = 0;
ulint actual_size = 0;
@@ -503,7 +511,7 @@ fil_decompress_page(
if (ptype != FIL_PAGE_PAGE_COMPRESSED &&
ptype != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED &&
ptype != FIL_PAGE_TYPE_COMPRESSED) {
- return;
+ ut_error;
}
// If no buffer was given, we need to allocate temporal buffer
@@ -514,24 +522,6 @@ fil_decompress_page(
in_buf = page_buf;
}
- /* Before actual decompress, make sure that page type is correct */
-
- if (mach_read_from_4(buf+FIL_PAGE_SPACE_OR_CHKSUM) != BUF_NO_CHECKSUM_MAGIC ||
- (ptype != FIL_PAGE_PAGE_COMPRESSED &&
- ptype != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: We try to uncompress corrupted page"
- " CRC " ULINTPF " type " ULINTPF " len " ULINTPF ".",
- mach_read_from_4(buf+FIL_PAGE_SPACE_OR_CHKSUM),
- mach_read_from_2(buf+FIL_PAGE_TYPE), len);
-
- fflush(stderr);
- if (return_error) {
- goto error_return;
- }
- ut_error;
- }
-
/* Get compression algorithm */
if (ptype == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
compression_alg = mach_read_from_2(buf+FIL_PAGE_DATA+FIL_PAGE_COMPRESSED_SIZE);
@@ -541,17 +531,10 @@ fil_decompress_page(
/* Get the actual size of compressed page */
actual_size = mach_read_from_2(buf+FIL_PAGE_DATA);
+
/* Check if payload size is corrupted */
if (actual_size == 0 || actual_size > UNIV_PAGE_SIZE) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: We try to uncompress corrupted page"
- " actual size " ULINTPF " compression %s.",
- actual_size, fil_get_compression_alg_name(compression_alg));
- fflush(stderr);
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
/* Store actual payload size of the compressed data. This pointer
@@ -570,19 +553,7 @@ fil_decompress_page(
/* If uncompress fails it means that page is corrupted */
if (err != Z_OK) {
-
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but uncompress failed with error %d "
- " size " ULINTPF " len " ULINTPF ".",
- err, actual_size, len);
-
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
break;
@@ -591,18 +562,7 @@ fil_decompress_page(
err = LZ4_decompress_fast((const char *)buf+header_len, (char *)in_buf, len);
if (err != (int)actual_size) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but uncompress failed with error %d "
- " size " ULINTPF " len " ULINTPF ".",
- err, actual_size, len);
-
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
break;
#endif /* HAVE_LZ4 */
@@ -616,18 +576,7 @@ fil_decompress_page(
olen = olen_lzo;
if (err != LZO_E_OK || (olen == 0 || olen > UNIV_PAGE_SIZE)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but uncompress failed with error %d "
- " size " ULINTPF " len " ULINTPF ".",
- err, actual_size, len);
-
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
break;
}
@@ -653,17 +602,7 @@ fil_decompress_page(
if (ret != LZMA_OK || (dst_pos == 0 || dst_pos > UNIV_PAGE_SIZE)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but decompression read only %ld bytes"
- " size " ULINTPF "len " ULINTPF ".",
- dst_pos, actual_size, len);
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
break;
@@ -682,17 +621,8 @@ fil_decompress_page(
0);
if (err != BZ_OK || (dst_pos == 0 || dst_pos > UNIV_PAGE_SIZE)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but decompression read only %du bytes"
- " size " ULINTPF " len " ULINTPF " err %d.",
- dst_pos, actual_size, len, err);
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ len = dst_pos;
+ goto error_return;
}
break;
}
@@ -709,35 +639,17 @@ fil_decompress_page(
(char *)in_buf,
(size_t*)&olen);
- if (cstatus != SNAPPY_OK || olen != UNIV_PAGE_SIZE) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but decompression read only " ULINTPF " bytes"
- " size " ULINTPF " len " ULINTPF " err %d.",
- olen, actual_size, len, (int)cstatus);
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ if (cstatus != SNAPPY_OK || (olen == 0 || olen > UNIV_PAGE_SIZE)) {
+ err = (int)cstatus;
+ len = olen;
+ goto error_return;
}
break;
}
#endif /* HAVE_SNAPPY */
default:
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but compression algorithm %s"
- " is not known."
- ,fil_get_compression_alg_name(compression_alg));
-
- fflush(stderr);
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
break;
}
@@ -747,8 +659,108 @@ fil_decompress_page(
really any other options. */
memcpy(buf, in_buf, len);
+ if (page_buf != in_buf) {
+ ut_free(in_buf);
+ }
+ return true;
+
error_return:
if (page_buf != in_buf) {
ut_free(in_buf);
}
+
+ /* Note that as we have found the page is corrupted, so
+ all this could be incorrect. */
+ ulint space_id = mach_read_from_4(buf+FIL_PAGE_SPACE_ID);
+ fil_space_t* space = fil_space_acquire_for_io(space_id);
+
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "Corruption: Page is marked as compressed"
+ " space : " ULINTPF " name %s "
+ " but uncompress failed with error: %d"
+ " size: " ULINTPF
+ " len: " ULINTPF
+ " compression method %s",
+ space_id,
+ (space ? space->chain.start->name : "(import)"),
+ err,
+ actual_size,
+ len,
+ fil_get_compression_alg_name(compression_alg));
+
+ if (space) {
+ fil_space_release(space);
+ }
+
+ return false;
+}
+#endif /* !UNIV_INNOCHECKSUM */
+
+/**
+Verify that stored post compression checksum matches calculated
+checksum. Note that old format did not have a checksum and
+in that case either original pre-compression page checksum will
+fail after decompression or page decompression fails.
+
+@param[in,out] page page frame
+@param[in] space_id Tablespace identifier
+@param[in] offset Page offset
+@return true if post compression checksum matches, false otherwise */
+#ifndef UNIV_INNOCHECKSUM
+UNIV_INTERN
+#endif
+bool
+fil_verify_compression_checksum(
+ const byte* page,
+ ulint space_id,
+ ulint offset)
+{
+ ut_ad(page &&
+ (mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED
+ || mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED));
+
+ uint32_t checksum = mach_read_from_4(page+FIL_PAGE_SPACE_OR_CHKSUM);
+
+ /* In earlier versions supporting page compression, page compression
+ stored BUF_NO_CHECKSUM_MAGIC to checksum filed. These pages are
+ treaded as not corrupted. */
+ if (checksum == BUF_NO_CHECKSUM_MAGIC) {
+ return (true);
+ }
+
+ uint32_t cchecksum = buf_calc_compressed_crc32(page);
+
+ if (checksum != cchecksum) {
+ ulint comp_method = mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED
+ ? mach_read_from_8(page+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION)
+ : mach_read_from_2(page+FIL_PAGE_DATA+FIL_PAGE_COMPRESSED_SIZE);
+
+#ifdef UNIV_INNOCHECKSUM
+ fprintf(log_file ? log_file : stderr,
+ "Page " ULINTPF ":" ULINTPF " may be corrupted."
+ " Post compression checksum %u"
+ " stored %u compression_method %s\n",
+ space_id, offset, checksum, cchecksum,
+ fil_get_compression_alg_name(comp_method));
+#else /*! UNIV_INNOCHECKSUM */
+ FilSpace space(space_id, true);
+
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "Page [page id: space=" ULINTPF
+ ", page number= " ULINTPF "]"
+ " in file %s"
+ " may be corrupted."
+ " Post compression checksum %u"
+ " stored %u"
+ " compression_method %s",
+ space_id,
+ offset,
+ (space() ? space()->chain.start->name : "(import)"),
+ checksum,
+ cchecksum,
+ fil_get_compression_alg_name(comp_method));
+#endif
+ }
+
+ return (checksum == cchecksum);
}
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 4f76c568f17..8e9a5ed2f55 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1997,6 +1997,7 @@ convert_error_code_to_mysql(
code should be introduced */
case DB_CORRUPTION:
+ case DB_PAGE_CORRUPTED:
return(HA_ERR_CRASHED);
case DB_OUT_OF_FILE_SPACE:
@@ -6032,6 +6033,8 @@ table_opened:
buf, space()->chain.start->name);
ret_err = HA_ERR_DECRYPTION_FAILED;
}
+ } else if (ib_table->corrupted) {
+ ret_err = HA_ERR_CRASHED;
}
dict_table_close(ib_table, FALSE, FALSE);
diff --git a/storage/innobase/include/buf0checksum.h b/storage/innobase/include/buf0checksum.h
index 6818345f965..7834ad4d292 100644
--- a/storage/innobase/include/buf0checksum.h
+++ b/storage/innobase/include/buf0checksum.h
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -80,4 +81,16 @@ buf_checksum_algorithm_name(
extern ulong srv_checksum_algorithm;
+/** Calculates the CRC32 checksum of a page compressed page. The value is
+stored to the page when it is written to a file and also checked for
+a match when reading from the file. Checksum is calculated from
+actual payload of the compressed page and some header fields.
+
+@param[in] page buffer page (UNIV_PAGE_SIZE bytes)
+@return checksum */
+UNIV_INTERN
+uint32_t
+buf_calc_compressed_crc32(
+ const byte* page);
+
#endif /* buf0checksum_h */
diff --git a/storage/innobase/include/fil0pagecompress.h b/storage/innobase/include/fil0pagecompress.h
index 03e16699ce3..bd1395b4835 100644
--- a/storage/innobase/include/fil0pagecompress.h
+++ b/storage/innobase/include/fil0pagecompress.h
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (C) 2013, 2017 MariaDB Corporation. All Rights Reserved.
+Copyright (C) 2013, 2018 MariaDB Corporation. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -19,6 +19,8 @@ this program; if not, write to the Free Software Foundation, Inc.,
#ifndef fil0pagecompress_h
#define fil0pagecompress_h
+#ifndef UNIV_INNOCHECKSUM
+
#include "fsp0fsp.h"
#include "fsp0pagecompress.h"
@@ -68,23 +70,26 @@ fil_compress_page(
ulint* out_len); /*!< out: actual length of compressed
page */
-/****************************************************************//**
+/**
For page compressed pages decompress the page after actual read
-operation. */
+operation.
+@param[in,out] page_buf Preallocated temporal buffer where
+ compression is done and then copied
+ to the buffer.
+@param[in,out] buf Compressed page and after suggesful
+ decompression operation uncompressed page
+ is copied here.
+@param[in] len Length of output buffer.
+@param[out] write_size Actual payload size of the compressed data.
+@return true when operation succeeded or false when failed */
UNIV_INTERN
-void
+bool
fil_decompress_page(
-/*================*/
- byte* page_buf, /*!< in: preallocated buffer or NULL */
- byte* buf, /*!< out: buffer from which to read; in aio
- this must be appropriately aligned */
- ulong len, /*!< in: length of output buffer.*/
- ulint* write_size, /*!< in/out: Actual payload size of
- the compressed data. */
- bool return_error=false);
- /*!< in: true if only an error should
- be produced when decompression fails.
- By default this parameter is false. */
+ byte* page_buf,
+ byte* buf,
+ ulong len,
+ ulint* write_size)
+ MY_ATTRIBUTE((warn_unused_result));
/****************************************************************//**
Get space id from fil node
@@ -129,4 +134,25 @@ ibool
fil_page_is_lzo_compressed(
/*=======================*/
byte* buf); /*!< in: page */
-#endif
+#endif /* !UNIV_INNOCHECKSUM */
+
+/**
+Verify that stored post compression checksum matches calculated
+checksum. Note that old format did not have a checksum and
+in that case either original pre-compression page checksum will
+fail after decompression or page decompression fails.
+
+@param[in,out] page page frame
+@param[in] space_id Tablespace identifier
+@param[in] offset Page offset
+@return true if post compression checksum matches, false otherwise */
+UNIV_INTERN
+bool
+fil_verify_compression_checksum(
+ const byte* page,
+ ulint space_id,
+ ulint offset)
+ MY_ATTRIBUTE((warn_unused_result));
+
+#endif /* fil0pagecompress_h */
+
diff --git a/storage/innobase/include/fsp0pagecompress.h b/storage/innobase/include/fsp0pagecompress.h
index c623d11c326..00645616556 100644
--- a/storage/innobase/include/fsp0pagecompress.h
+++ b/storage/innobase/include/fsp0pagecompress.h
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (C) 2013, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (C) 2013, 2018, MariaDB Corporation. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -38,6 +38,7 @@ Created 11/12/2013 Jan Lindström jan.lindstrom@skysql.com
#define PAGE_SNAPPY_ALGORITHM 6
#define PAGE_ALGORITHM_LAST PAGE_SNAPPY_ALGORITHM
+#ifndef UNIV_INNOCHECKSUM
/**********************************************************************//**
Reads the page compression level from the first page of a tablespace.
@return page compression level, or 0 if uncompressed */
@@ -67,6 +68,7 @@ atomic_writes_t
fsp_flags_get_atomic_writes(
/*========================*/
ulint flags); /*!< in: tablespace flags */
+#endif /* !UNIV_INNOCHECKSUM */
#ifndef UNIV_NONINL
#include "fsp0pagecompress.ic"
diff --git a/storage/innobase/include/fsp0pagecompress.ic b/storage/innobase/include/fsp0pagecompress.ic
index 14f968e319e..0e9a574eef6 100644
--- a/storage/innobase/include/fsp0pagecompress.ic
+++ b/storage/innobase/include/fsp0pagecompress.ic
@@ -25,6 +25,7 @@ Created 11/12/2013 Jan Lindström jan.lindstrom@mariadb.com
***********************************************************************/
+#ifndef UNIV_INNOCHECKSUM
/********************************************************************//**
Determine the tablespace is page compression level from dict_table_t::flags.
@return page compression level or 0 if not compressed*/
@@ -84,6 +85,7 @@ fil_page_is_compressed_encrypted(
{
return(mach_read_from_2(buf+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED);
}
+#endif /*!UNIV_INNOCHECKSUM */
/****************************************************************//**
Get the name of the compression algorithm used for page
diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc
index 83e38fbcc98..3e2e2267bf4 100644
--- a/storage/xtradb/buf/buf0buf.cc
+++ b/storage/xtradb/buf/buf0buf.cc
@@ -2,7 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
-Copyright (c) 2013, 2017, MariaDB Corporation.
+Copyright (c) 2013, 2018, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -739,22 +739,25 @@ buf_page_is_corrupted(
ulint checksum_field2;
ulint space_id = mach_read_from_4(
read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
- ulint page_type = mach_read_from_2(
- read_buf + FIL_PAGE_TYPE);
/* We can trust page type if page compression is set on tablespace
flags because page compression flag means file must have been
created with 10.1 (later than 5.5 code base). In 10.1 page
compressed tables do not contain post compression checksum and
- FIL_PAGE_END_LSN_OLD_CHKSUM field stored. Note that space can
- be null if we are in fil_check_first_page() and first page
- is not compressed or encrypted. Page checksum is verified
- after decompression (i.e. normally pages are already
- decompressed at this stage). */
+ FIL_PAGE_END_LSN_OLD_CHKSUM field stored.
+
+ Note that space can be null if we are in fil_check_first_page() but
+ first page (page 0) is not compressed or encrypted.
+
+ Page checksum is verified after decompression (i.e. normally pages
+ are already decompressed at this stage). */
+ ulint page_type = mach_read_from_2(read_buf + FIL_PAGE_TYPE);
if ((page_type == FIL_PAGE_PAGE_COMPRESSED ||
page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED)
- && space && FSP_FLAGS_HAS_PAGE_COMPRESSION(space->flags)) {
- return (false);
+ && (!space
+ || (space && FSP_FLAGS_HAS_PAGE_COMPRESSION(space->flags))))
+ {
+ return(false);
}
if (!zip_size
@@ -975,12 +978,14 @@ buf_page_print(const byte* read_buf, ulint zip_size)
size = UNIV_PAGE_SIZE;
}
+#ifdef UNIV_BUG_DEBUG
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Page dump in ascii and hex (%lu bytes):\n",
size);
ut_print_buf(stderr, read_buf, size);
fputs("\nInnoDB: End of page dump\n", stderr);
+#endif
if (zip_size) {
/* Print compressed page. */
@@ -3113,8 +3118,8 @@ loop:
/* Try to set table as corrupted instead of
asserting. */
- if (space > TRX_SYS_SPACE &&
- dict_set_corrupted_by_space(space)) {
+ if (space > TRX_SYS_SPACE) {
+ dict_set_corrupted_by_space(space);
return (NULL);
}
@@ -4657,21 +4662,19 @@ buf_page_check_corrupt(buf_page_t* bpage, fil_space_t* space)
still be corrupted if used key does not match. */
still_encrypted = (crypt_data &&
crypt_data->type != CRYPT_SCHEME_UNENCRYPTED &&
- !bpage->encrypted &&
fil_space_verify_crypt_checksum(dst_frame, zip_size,
space, bpage->offset));
- if (!still_encrypted) {
- /* If traditional checksums match, we assume that page is
- not anymore encrypted. */
- corrupted = buf_page_is_corrupted(true, dst_frame, zip_size,
+ /* If traditional checksums match, we assume that page is
+ not anymore encrypted. */
+ corrupted = buf_page_is_corrupted(true, dst_frame, zip_size,
space);
- if (!corrupted) {
- bpage->encrypted = false;
- } else {
- err = DB_PAGE_CORRUPTED;
- }
+ if (!corrupted) {
+ bpage->encrypted = false;
+ still_encrypted = false;
+ } else if (!still_encrypted) {
+ err = DB_PAGE_CORRUPTED;
}
/* Pages that we think are unencrypted but do not match the checksum
@@ -4688,7 +4691,7 @@ buf_page_check_corrupt(buf_page_t* bpage, fil_space_t* space)
", page number=%u]"
" in file %s cannot be decrypted.",
bpage->space, bpage->offset,
- space->name);
+ space->chain.start->name);
ib_logf(IB_LOG_LEVEL_INFO,
"However key management plugin or used key_version " ULINTPF
@@ -4748,7 +4751,11 @@ buf_page_io_complete(buf_page_t* bpage)
return(DB_TABLESPACE_DELETED);
}
- buf_page_decrypt_after_read(bpage, space);
+ dberr_t err = DB_SUCCESS;
+
+ if (!buf_page_decrypt_after_read(bpage, space)) {
+ err = DB_PAGE_CORRUPTED;
+ }
if (buf_page_get_zip_size(bpage)) {
frame = bpage->zip.data;
@@ -4756,6 +4763,10 @@ buf_page_io_complete(buf_page_t* bpage)
frame = ((buf_block_t*) bpage)->frame;
}
+ if (err != DB_SUCCESS) {
+ goto database_corrupted;
+ }
+
if (buf_page_get_zip_size(bpage)) {
frame = bpage->zip.data;
os_atomic_increment_ulint(&buf_pool->n_pend_unzip, 1);
@@ -4846,7 +4857,7 @@ database_corrupted:
ib_logf(IB_LOG_LEVEL_ERROR,
"Database page corruption on disk"
" or a failed file read of tablespace %s"
- " page [page id: space=%u"
+ " page [page id: space=%u"
", page number=%u]"
". You may have to recover from "
"a backup.",
@@ -6449,10 +6460,14 @@ buf_page_decrypt_after_read(buf_page_t* bpage, fil_space_t* space)
#endif
/* decompress using comp_buf to dst_frame */
- fil_decompress_page(slot->comp_buf,
- dst_frame,
- ulong(size),
- &bpage->write_size);
+ if (!fil_verify_compression_checksum(dst_frame,
+ bpage->space, bpage->offset)
+ || !fil_decompress_page(slot->comp_buf,
+ dst_frame,
+ ulong(size),
+ &bpage->write_size)) {
+ success = false;
+ }
/* Mark this slot as free */
slot->reserved = false;
@@ -6507,10 +6522,14 @@ buf_page_decrypt_after_read(buf_page_t* bpage, fil_space_t* space)
fil_page_type_validate(dst_frame);
#endif
/* decompress using comp_buf to dst_frame */
- fil_decompress_page(slot->comp_buf,
+ if (!fil_verify_compression_checksum(dst_frame,
+ bpage->space, bpage->offset)
+ || !fil_decompress_page(slot->comp_buf,
dst_frame,
ulong(size),
- &bpage->write_size);
+ &bpage->write_size)) {
+ success = false;
+ }
ut_d(fil_page_type_validate(dst_frame));
}
diff --git a/storage/xtradb/buf/buf0checksum.cc b/storage/xtradb/buf/buf0checksum.cc
index 01b646a78e0..98abba575ef 100644
--- a/storage/xtradb/buf/buf0checksum.cc
+++ b/storage/xtradb/buf/buf0checksum.cc
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -28,6 +29,7 @@ Created Aug 11, 2011 Vasil Dimov
#include "ut0crc32.h" /* ut_crc32() */
#include "ut0rnd.h" /* ut_fold_binary() */
#include "buf0types.h"
+#include "mach0data.h"
#ifndef UNIV_INNOCHECKSUM
@@ -154,3 +156,46 @@ buf_checksum_algorithm_name(
ut_error;
return(NULL);
}
+
+/** Calculates the CRC32 checksum of a page compressed page. The value is
+stored to the page when it is written to a file and also checked for
+a match when reading from the file. Checksum is calculated from
+actual payload of the compressed page and some header fields.
+
+@param[in] page buffer page (UNIV_PAGE_SIZE bytes)
+@return checksum */
+UNIV_INTERN
+uint32_t
+buf_calc_compressed_crc32(
+ const byte* page)
+{
+ /* In page compressed pages compression method is stored to field
+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, thus add it to checksum.
+ In pages first compressed and then encrypted same field
+ contains key version after compression. */
+
+ ulint page_type = mach_read_from_2(page + FIL_PAGE_TYPE);
+
+ ulint header_len = page_type == FIL_PAGE_PAGE_COMPRESSED ?
+ FIL_PAGE_SPACE_ID - FIL_PAGE_OFFSET :
+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION - FIL_PAGE_OFFSET;
+
+ const uint32_t c1 = ut_crc32(
+ page + FIL_PAGE_OFFSET,
+ header_len);
+
+ /* Calculate checksum from actual payload including stored size
+ field. In encrypted case add also compression method field. */
+ ulint payload_len = mach_read_from_2(page+FIL_PAGE_DATA)+FIL_PAGE_COMPRESSED_SIZE;
+
+ if (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
+ payload_len += FIL_PAGE_COMPRESSION_METHOD_SIZE;
+ }
+
+ const uint32_t c2 = ut_crc32(
+ page + FIL_PAGE_DATA,
+ payload_len);
+
+ return(c1 ^ c2);
+}
+
diff --git a/storage/xtradb/buf/buf0dblwr.cc b/storage/xtradb/buf/buf0dblwr.cc
index ee8c85446e9..369a6a3255e 100644
--- a/storage/xtradb/buf/buf0dblwr.cc
+++ b/storage/xtradb/buf/buf0dblwr.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2013, 2017, MariaDB Corporation.
+Copyright (c) 2013, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -488,8 +488,45 @@ leave_func:
ut_free(unaligned_read_buf);
}
-/****************************************************************//**
-Process the double write buffer pages. */
+/** Verify post compression checksum and if it matches
+decompress page.
+@param[in,out] read_buf Page compressed buffer. Result of
+ decompression is copied here.
+@param[in] space_id Tablespace identifier
+@param[in] page_no Page offset
+@param[in] page_size Actual page size
+@return true if both stored post compression checksum matches calculated one
+or it is BUF_NO_CHECKSUM_MAGIC and page decompression is succeeds.*/
+static
+bool
+buf_dblwr_decompress(
+ byte * read_buf,
+ ulint space_id,
+ ulint page_no,
+ ulint page_size)
+{
+ bool success = true;
+
+ /* Page is always fist compressed and then encrypted. Here we can
+ only decompress pages if they are not encrypted.
+
+ For compressed pages verify post compression checksum and if it
+ matches decompress the page.
+
+ Note that old datafiles have checksum BUF_NO_CHECKSUM_MAGIC and
+ that value is accepted as correct post compression checksum see
+ fil_verify_compression_checksum().
+
+ In that case we hope that compression algorithm can recover from
+ corrupted input and return a error that is then handled.*/
+ if(!fil_verify_compression_checksum(read_buf, space_id, page_no)
+ || !fil_decompress_page(NULL, read_buf, page_size, NULL)) {
+ return success = false;
+ }
+
+ return success;
+}
+
void
buf_dblwr_process()
/*===============*/
@@ -556,25 +593,24 @@ buf_dblwr_process()
const bool is_all_zero = buf_page_is_zeroes(
read_buf, zip_size);
+ bool success = true;
+
if (is_all_zero) {
/* We will check if the copy in the
doublewrite buffer is valid. If not, we will
ignore this page (there should be redo log
records to initialize it). */
} else {
- if (fil_page_is_compressed_encrypted(read_buf) ||
- fil_page_is_compressed(read_buf)) {
- /* Decompress the page before
- validating the checksum. */
- fil_decompress_page(
- NULL, read_buf, srv_page_size,
- NULL, true);
+ if (fil_page_is_compressed(read_buf)) {
+ success = buf_dblwr_decompress(read_buf,
+ space_id, page_no, srv_page_size);
}
- if (fil_space_verify_crypt_checksum(
+ if (success
+ && (fil_space_verify_crypt_checksum(
read_buf, zip_size, NULL, page_no)
- || !buf_page_is_corrupted(
- true, read_buf, zip_size, space())) {
+ || !buf_page_is_corrupted(
+ true, read_buf, zip_size, space()))) {
/* The page is good; there is no need
to consult the doublewrite buffer. */
continue;
@@ -589,16 +625,14 @@ buf_dblwr_process()
}
/* Next, validate the doublewrite page. */
- if (fil_page_is_compressed_encrypted(page) ||
- fil_page_is_compressed(page)) {
- /* Decompress the page before
- validating the checksum. */
- fil_decompress_page(
- NULL, page, srv_page_size, NULL, true);
+ if (fil_page_is_compressed(page)) {
+ success = buf_dblwr_decompress(page,
+ space_id, page_no, srv_page_size);
}
- if (!fil_space_verify_crypt_checksum(page, zip_size, NULL, page_no)
- && buf_page_is_corrupted(true, page, zip_size, space)) {
+ if (!success
+ || (!fil_space_verify_crypt_checksum(page, zip_size, NULL, page_no)
+ && buf_page_is_corrupted(true, page, zip_size, space))) {
if (!is_all_zero) {
ib_logf(IB_LOG_LEVEL_WARN,
"A doublewrite copy of page "
diff --git a/storage/xtradb/dict/dict0dict.cc b/storage/xtradb/dict/dict0dict.cc
index 9257321c7ef..cc0241e6789 100644
--- a/storage/xtradb/dict/dict0dict.cc
+++ b/storage/xtradb/dict/dict0dict.cc
@@ -6120,6 +6120,12 @@ dict_set_corrupted_by_space(
table = dict_find_table_by_space(space_id);
if (!table) {
+#ifdef UNIV_DEBUG
+ ib_logf(IB_LOG_LEVEL_INFO,
+ "Can't set space " ULINTPF
+ " corrupted as table is not found dict_sys->table_LRU.", space_id);
+
+#endif
return(FALSE);
}
diff --git a/storage/xtradb/fil/fil0crypt.cc b/storage/xtradb/fil/fil0crypt.cc
index 3095503cfc5..fa0eaabf4dc 100644
--- a/storage/xtradb/fil/fil0crypt.cc
+++ b/storage/xtradb/fil/fil0crypt.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (C) 2013, 2015, Google Inc. All Rights Reserved.
-Copyright (c) 2014, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2014, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -719,8 +719,8 @@ fil_space_encrypt(
comp_mem = (byte *)malloc(UNIV_PAGE_SIZE);
uncomp_mem = (byte *)malloc(UNIV_PAGE_SIZE);
memcpy(comp_mem, src_frame, UNIV_PAGE_SIZE);
- fil_decompress_page(uncomp_mem, comp_mem,
- srv_page_size, NULL);
+ ut_ad(fil_decompress_page(uncomp_mem, comp_mem,
+ srv_page_size, NULL));
src = uncomp_mem;
}
@@ -730,8 +730,8 @@ fil_space_encrypt(
/* Need to decompress the page if it was also compressed */
if (page_compressed_encrypted) {
memcpy(comp_mem, tmp_mem, UNIV_PAGE_SIZE);
- fil_decompress_page(tmp_mem, comp_mem,
- srv_page_size, NULL);
+ ut_ad(fil_decompress_page(tmp_mem, comp_mem,
+ srv_page_size, NULL));
}
bool corrupted = buf_page_is_corrupted(true, tmp_mem, zip_size, space);
@@ -2605,13 +2605,6 @@ fil_space_verify_crypt_checksum(
return(true);
}
- /* Compressed and encrypted pages do not have checksum. Assume not
- corrupted. Page verification happens after decompression in
- buf_page_io_complete() using buf_page_is_corrupted(). */
- if (mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
- return (true);
- }
-
ib_uint32_t cchecksum1 = 0;
ib_uint32_t cchecksum2 = 0;
diff --git a/storage/xtradb/fil/fil0fil.cc b/storage/xtradb/fil/fil0fil.cc
index 83c17bf113f..1d56450b4bd 100644
--- a/storage/xtradb/fil/fil0fil.cc
+++ b/storage/xtradb/fil/fil0fil.cc
@@ -6861,8 +6861,32 @@ fil_iterate(
/* If the original page is page_compressed, we need
to decompress page before we can update it. */
if (page_compressed) {
- fil_decompress_page(NULL, dst, ulong(size),
- NULL);
+ /* Check post compression checksum before
+ doing actual decompression. */
+ if (fil_verify_compression_checksum(dst,
+ space_id, page_no)) {
+ if (!fil_decompress_page(NULL, dst, ulong(size),
+ NULL)) {
+ err = DB_CORRUPTION;
+ }
+ } else {
+ err = DB_CORRUPTION;
+ }
+
+ if (err != DB_SUCCESS) {
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "Imported page [page id: space=" ULINTPF
+ ", page number=" ULINTPF "]"
+ " is corrupted. Post compression"
+ " checksum " ULINTPF
+ " does not match calculated %u.",
+ space_id, page_no,
+ mach_read_from_4(dst+FIL_PAGE_SPACE_OR_CHKSUM),
+ buf_calc_compressed_crc32(dst));
+
+ return(err);
+ }
+
updated = true;
}
diff --git a/storage/xtradb/fil/fil0pagecompress.cc b/storage/xtradb/fil/fil0pagecompress.cc
index edc932f36f5..1ff04919121 100644
--- a/storage/xtradb/fil/fil0pagecompress.cc
+++ b/storage/xtradb/fil/fil0pagecompress.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (C) 2013, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (C) 2013, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -26,15 +26,19 @@ Updated 14/02/2015
#include "fil0fil.h"
#include "fil0pagecompress.h"
+#include "mtr0types.h"
+#include "mach0data.h"
+#include "buf0buf.h"
+#include "buf0checksum.h"
+#include "fsp0pagecompress.h"
+#ifndef UNIV_INNOCHECKSUM
#include <debug_sync.h>
#include <my_dbug.h>
#include "mem0mem.h"
#include "hash0hash.h"
#include "os0file.h"
-#include "mach0data.h"
-#include "buf0buf.h"
#include "buf0flu.h"
#include "log0recv.h"
#include "fsp0fsp.h"
@@ -364,8 +368,6 @@ fil_compress_page(
/* Set up the page header */
memcpy(out_buf, buf, FIL_PAGE_DATA);
- /* Set up the checksum */
- mach_write_to_4(out_buf+FIL_PAGE_SPACE_OR_CHKSUM, BUF_NO_CHECKSUM_MAGIC);
/* Set up the compression algorithm */
mach_write_to_8(out_buf+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, comp_method);
@@ -382,10 +384,13 @@ fil_compress_page(
/* Set up the actual payload lenght */
mach_write_to_2(out_buf+FIL_PAGE_DATA, write_size);
+ /* Set up the checksum */
+ mach_write_to_4(out_buf+FIL_PAGE_SPACE_OR_CHKSUM, buf_calc_compressed_crc32(out_buf));
+
#ifdef UNIV_DEBUG
/* Verify */
ut_ad(fil_page_is_compressed(out_buf) || fil_page_is_compressed_encrypted(out_buf));
- ut_ad(mach_read_from_4(out_buf+FIL_PAGE_SPACE_OR_CHKSUM) == BUF_NO_CHECKSUM_MAGIC);
+ ut_ad(mach_read_from_4(out_buf+FIL_PAGE_SPACE_OR_CHKSUM) == buf_calc_compressed_crc32(out_buf));
ut_ad(mach_read_from_2(out_buf+FIL_PAGE_DATA) == write_size);
ut_ad(mach_read_from_8(out_buf+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION) == (ulint)comp_method ||
mach_read_from_2(out_buf+FIL_PAGE_DATA+FIL_PAGE_COMPRESSED_SIZE) == (ulint)comp_method);
@@ -399,7 +404,7 @@ fil_compress_page(
uncomp_page = static_cast<byte *>(ut_malloc(UNIV_PAGE_SIZE));
memcpy(comp_page, out_buf, UNIV_PAGE_SIZE);
- fil_decompress_page(uncomp_page, comp_page, ulong(len), NULL);
+ ut_ad(fil_decompress_page(uncomp_page, comp_page, ulong(len), NULL));
if (buf_page_is_corrupted(false, uncomp_page, 0, space)) {
buf_page_print(uncomp_page, 0);
@@ -466,22 +471,25 @@ err_exit:
}
-/****************************************************************//**
+/**
For page compressed pages decompress the page after actual read
-operation. */
+operation.
+@param[in,out] page_buf Preallocated temporal buffer where
+ compression is done and then copied
+ to the buffer.
+@param[in,out] buf Compressed page and after suggesful
+ decompression operation uncompressed page
+ is copied here.
+@param[in] len Length of output buffer.
+@param[out] write_size Actual payload size of the compressed data.
+@return true when operation succeeded or false when failed */
UNIV_INTERN
-void
+bool
fil_decompress_page(
-/*================*/
- byte* page_buf, /*!< in: preallocated buffer or NULL */
- byte* buf, /*!< out: buffer from which to read; in aio
- this must be appropriately aligned */
- ulong len, /*!< in: length of output buffer.*/
- ulint* write_size, /*!< in/out: Actual payload size of
- the compressed data. */
- bool return_error) /*!< in: true if only an error should
- be produced when decompression fails.
- By default this parameter is false. */
+ byte* page_buf,
+ byte* buf,
+ ulong len,
+ ulint* write_size)
{
int err = 0;
ulint actual_size = 0;
@@ -503,7 +511,7 @@ fil_decompress_page(
if (ptype != FIL_PAGE_PAGE_COMPRESSED &&
ptype != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED &&
ptype != FIL_PAGE_TYPE_COMPRESSED) {
- return;
+ ut_error;
}
// If no buffer was given, we need to allocate temporal buffer
@@ -514,24 +522,6 @@ fil_decompress_page(
in_buf = page_buf;
}
- /* Before actual decompress, make sure that page type is correct */
-
- if (mach_read_from_4(buf+FIL_PAGE_SPACE_OR_CHKSUM) != BUF_NO_CHECKSUM_MAGIC ||
- (ptype != FIL_PAGE_PAGE_COMPRESSED &&
- ptype != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: We try to uncompress corrupted page"
- " CRC " ULINTPF " type " ULINTPF " len " ULINTPF ".",
- mach_read_from_4(buf+FIL_PAGE_SPACE_OR_CHKSUM),
- mach_read_from_2(buf+FIL_PAGE_TYPE), len);
-
- fflush(stderr);
- if (return_error) {
- goto error_return;
- }
- ut_error;
- }
-
/* Get compression algorithm */
if (ptype == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
compression_alg = mach_read_from_2(buf+FIL_PAGE_DATA+FIL_PAGE_COMPRESSED_SIZE);
@@ -541,17 +531,10 @@ fil_decompress_page(
/* Get the actual size of compressed page */
actual_size = mach_read_from_2(buf+FIL_PAGE_DATA);
+
/* Check if payload size is corrupted */
if (actual_size == 0 || actual_size > UNIV_PAGE_SIZE) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: We try to uncompress corrupted page"
- " actual size " ULINTPF " compression %s.",
- actual_size, fil_get_compression_alg_name(compression_alg));
- fflush(stderr);
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
/* Store actual payload size of the compressed data. This pointer
@@ -570,19 +553,7 @@ fil_decompress_page(
/* If uncompress fails it means that page is corrupted */
if (err != Z_OK) {
-
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but uncompress failed with error %d "
- " size " ULINTPF " len " ULINTPF ".",
- err, actual_size, len);
-
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
break;
@@ -591,18 +562,7 @@ fil_decompress_page(
err = LZ4_decompress_fast((const char *)buf+header_len, (char *)in_buf, len);
if (err != (int)actual_size) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but uncompress failed with error %d "
- " size " ULINTPF " len " ULINTPF ".",
- err, actual_size, len);
-
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
break;
#endif /* HAVE_LZ4 */
@@ -616,18 +576,7 @@ fil_decompress_page(
olen = olen_lzo;
if (err != LZO_E_OK || (olen == 0 || olen > UNIV_PAGE_SIZE)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but uncompress failed with error %d "
- " size " ULINTPF " len " ULINTPF ".",
- err, actual_size, len);
-
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
break;
}
@@ -653,17 +602,7 @@ fil_decompress_page(
if (ret != LZMA_OK || (dst_pos == 0 || dst_pos > UNIV_PAGE_SIZE)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but decompression read only %ld bytes"
- " size " ULINTPF "len " ULINTPF ".",
- dst_pos, actual_size, len);
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
}
break;
@@ -682,17 +621,8 @@ fil_decompress_page(
0);
if (err != BZ_OK || (dst_pos == 0 || dst_pos > UNIV_PAGE_SIZE)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but decompression read only %du bytes"
- " size " ULINTPF " len " ULINTPF " err %d.",
- dst_pos, actual_size, len, err);
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ len = dst_pos;
+ goto error_return;
}
break;
}
@@ -709,35 +639,17 @@ fil_decompress_page(
(char *)in_buf,
(size_t*)&olen);
- if (cstatus != SNAPPY_OK || olen != UNIV_PAGE_SIZE) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but decompression read only " ULINTPF " bytes"
- " size " ULINTPF " len " ULINTPF " err %d.",
- olen, actual_size, len, (int)cstatus);
- fflush(stderr);
-
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ if (cstatus != SNAPPY_OK || (olen == 0 || olen > UNIV_PAGE_SIZE)) {
+ err = (int)cstatus;
+ len = olen;
+ goto error_return;
}
break;
}
#endif /* HAVE_SNAPPY */
default:
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Corruption: Page is marked as compressed"
- " but compression algorithm %s"
- " is not known."
- ,fil_get_compression_alg_name(compression_alg));
-
- fflush(stderr);
- if (return_error) {
- goto error_return;
- }
- ut_error;
+ goto error_return;
break;
}
@@ -747,8 +659,108 @@ fil_decompress_page(
really any other options. */
memcpy(buf, in_buf, len);
+ if (page_buf != in_buf) {
+ ut_free(in_buf);
+ }
+ return true;
+
error_return:
if (page_buf != in_buf) {
ut_free(in_buf);
}
+
+ /* Note that as we have found the page is corrupted, so
+ all this could be incorrect. */
+ ulint space_id = mach_read_from_4(buf+FIL_PAGE_SPACE_ID);
+ fil_space_t* space = fil_space_acquire_for_io(space_id);
+
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "Corruption: Page is marked as compressed"
+ " space : " ULINTPF " name %s "
+ " but uncompress failed with error: %d"
+ " size: " ULINTPF
+ " len: " ULINTPF
+ " compression method %s",
+ space_id,
+ (space ? space->chain.start->name : "(import)"),
+ err,
+ actual_size,
+ len,
+ fil_get_compression_alg_name(compression_alg));
+
+ if (space) {
+ fil_space_release(space);
+ }
+
+ return false;
+}
+#endif /* !UNIV_INNOCHECKSUM */
+
+/**
+Verify that stored post compression checksum matches calculated
+checksum. Note that old format did not have a checksum and
+in that case either original pre-compression page checksum will
+fail after decompression or page decompression fails.
+
+@param[in,out] page page frame
+@param[in] space_id Tablespace identifier
+@param[in] offset Page offset
+@return true if post compression checksum matches, false otherwise */
+#ifndef UNIV_INNOCHECKSUM
+UNIV_INTERN
+#endif
+bool
+fil_verify_compression_checksum(
+ const byte* page,
+ ulint space_id,
+ ulint offset)
+{
+ ut_ad(page &&
+ (mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED
+ || mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED));
+
+ uint32_t checksum = mach_read_from_4(page+FIL_PAGE_SPACE_OR_CHKSUM);
+
+ /* In earlier versions supporting page compression, page compression
+ stored BUF_NO_CHECKSUM_MAGIC to checksum filed. These pages are
+ treaded as not corrupted. */
+ if (checksum == BUF_NO_CHECKSUM_MAGIC) {
+ return (true);
+ }
+
+ uint32_t cchecksum = buf_calc_compressed_crc32(page);
+
+ if (checksum != cchecksum) {
+ ulint comp_method = mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED
+ ? mach_read_from_8(page+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION)
+ : mach_read_from_2(page+FIL_PAGE_DATA+FIL_PAGE_COMPRESSED_SIZE);
+
+#ifdef UNIV_INNOCHECKSUM
+ fprintf(log_file ? log_file : stderr,
+ "Page " ULINTPF ":" ULINTPF " may be corrupted."
+ " Post compression checksum %u"
+ " stored %u compression_method %s\n",
+ space_id, offset, checksum, cchecksum,
+ fil_get_compression_alg_name(comp_method));
+#else /*! UNIV_INNOCHECKSUM */
+ FilSpace space(space_id, true);
+
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "Page [page id: space=" ULINTPF
+ ", page number= " ULINTPF "]"
+ " in file %s"
+ " may be corrupted."
+ " Post compression checksum %u"
+ " stored %u"
+ " compression_method %s",
+ space_id,
+ offset,
+ (space() ? space()->chain.start->name : "(import)"),
+ checksum,
+ cchecksum,
+ fil_get_compression_alg_name(comp_method));
+#endif
+ }
+
+ return (checksum == cchecksum);
}
diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc
index 9749bcecfe9..c467adc635f 100644
--- a/storage/xtradb/handler/ha_innodb.cc
+++ b/storage/xtradb/handler/ha_innodb.cc
@@ -2329,6 +2329,7 @@ convert_error_code_to_mysql(
code should be introduced */
case DB_CORRUPTION:
+ case DB_PAGE_CORRUPTED:
return(HA_ERR_CRASHED);
case DB_OUT_OF_FILE_SPACE:
@@ -6600,6 +6601,8 @@ table_opened:
buf, space()->chain.start->name);
ret_err = HA_ERR_DECRYPTION_FAILED;
}
+ } else if (ib_table->corrupted) {
+ ret_err = HA_ERR_CRASHED;
}
dict_table_close(ib_table, FALSE, FALSE);
diff --git a/storage/xtradb/include/buf0checksum.h b/storage/xtradb/include/buf0checksum.h
index 6818345f965..7834ad4d292 100644
--- a/storage/xtradb/include/buf0checksum.h
+++ b/storage/xtradb/include/buf0checksum.h
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -80,4 +81,16 @@ buf_checksum_algorithm_name(
extern ulong srv_checksum_algorithm;
+/** Calculates the CRC32 checksum of a page compressed page. The value is
+stored to the page when it is written to a file and also checked for
+a match when reading from the file. Checksum is calculated from
+actual payload of the compressed page and some header fields.
+
+@param[in] page buffer page (UNIV_PAGE_SIZE bytes)
+@return checksum */
+UNIV_INTERN
+uint32_t
+buf_calc_compressed_crc32(
+ const byte* page);
+
#endif /* buf0checksum_h */
diff --git a/storage/xtradb/include/fil0pagecompress.h b/storage/xtradb/include/fil0pagecompress.h
index 03e16699ce3..bd1395b4835 100644
--- a/storage/xtradb/include/fil0pagecompress.h
+++ b/storage/xtradb/include/fil0pagecompress.h
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (C) 2013, 2017 MariaDB Corporation. All Rights Reserved.
+Copyright (C) 2013, 2018 MariaDB Corporation. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -19,6 +19,8 @@ this program; if not, write to the Free Software Foundation, Inc.,
#ifndef fil0pagecompress_h
#define fil0pagecompress_h
+#ifndef UNIV_INNOCHECKSUM
+
#include "fsp0fsp.h"
#include "fsp0pagecompress.h"
@@ -68,23 +70,26 @@ fil_compress_page(
ulint* out_len); /*!< out: actual length of compressed
page */
-/****************************************************************//**
+/**
For page compressed pages decompress the page after actual read
-operation. */
+operation.
+@param[in,out] page_buf Preallocated temporal buffer where
+ compression is done and then copied
+ to the buffer.
+@param[in,out] buf Compressed page and after suggesful
+ decompression operation uncompressed page
+ is copied here.
+@param[in] len Length of output buffer.
+@param[out] write_size Actual payload size of the compressed data.
+@return true when operation succeeded or false when failed */
UNIV_INTERN
-void
+bool
fil_decompress_page(
-/*================*/
- byte* page_buf, /*!< in: preallocated buffer or NULL */
- byte* buf, /*!< out: buffer from which to read; in aio
- this must be appropriately aligned */
- ulong len, /*!< in: length of output buffer.*/
- ulint* write_size, /*!< in/out: Actual payload size of
- the compressed data. */
- bool return_error=false);
- /*!< in: true if only an error should
- be produced when decompression fails.
- By default this parameter is false. */
+ byte* page_buf,
+ byte* buf,
+ ulong len,
+ ulint* write_size)
+ MY_ATTRIBUTE((warn_unused_result));
/****************************************************************//**
Get space id from fil node
@@ -129,4 +134,25 @@ ibool
fil_page_is_lzo_compressed(
/*=======================*/
byte* buf); /*!< in: page */
-#endif
+#endif /* !UNIV_INNOCHECKSUM */
+
+/**
+Verify that stored post compression checksum matches calculated
+checksum. Note that old format did not have a checksum and
+in that case either original pre-compression page checksum will
+fail after decompression or page decompression fails.
+
+@param[in,out] page page frame
+@param[in] space_id Tablespace identifier
+@param[in] offset Page offset
+@return true if post compression checksum matches, false otherwise */
+UNIV_INTERN
+bool
+fil_verify_compression_checksum(
+ const byte* page,
+ ulint space_id,
+ ulint offset)
+ MY_ATTRIBUTE((warn_unused_result));
+
+#endif /* fil0pagecompress_h */
+
diff --git a/storage/xtradb/include/fsp0pagecompress.h b/storage/xtradb/include/fsp0pagecompress.h
index c623d11c326..00645616556 100644
--- a/storage/xtradb/include/fsp0pagecompress.h
+++ b/storage/xtradb/include/fsp0pagecompress.h
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (C) 2013, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (C) 2013, 2018, MariaDB Corporation. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -38,6 +38,7 @@ Created 11/12/2013 Jan Lindström jan.lindstrom@skysql.com
#define PAGE_SNAPPY_ALGORITHM 6
#define PAGE_ALGORITHM_LAST PAGE_SNAPPY_ALGORITHM
+#ifndef UNIV_INNOCHECKSUM
/**********************************************************************//**
Reads the page compression level from the first page of a tablespace.
@return page compression level, or 0 if uncompressed */
@@ -67,6 +68,7 @@ atomic_writes_t
fsp_flags_get_atomic_writes(
/*========================*/
ulint flags); /*!< in: tablespace flags */
+#endif /* !UNIV_INNOCHECKSUM */
#ifndef UNIV_NONINL
#include "fsp0pagecompress.ic"
diff --git a/storage/xtradb/include/fsp0pagecompress.ic b/storage/xtradb/include/fsp0pagecompress.ic
index 14f968e319e..0e9a574eef6 100644
--- a/storage/xtradb/include/fsp0pagecompress.ic
+++ b/storage/xtradb/include/fsp0pagecompress.ic
@@ -25,6 +25,7 @@ Created 11/12/2013 Jan Lindström jan.lindstrom@mariadb.com
***********************************************************************/
+#ifndef UNIV_INNOCHECKSUM
/********************************************************************//**
Determine the tablespace is page compression level from dict_table_t::flags.
@return page compression level or 0 if not compressed*/
@@ -84,6 +85,7 @@ fil_page_is_compressed_encrypted(
{
return(mach_read_from_2(buf+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED);
}
+#endif /*!UNIV_INNOCHECKSUM */
/****************************************************************//**
Get the name of the compression algorithm used for page