Skip to content

Commit 9f4a4cb

Browse files
committed
Cleanup recent mariabackup validation patches.
- Refactor code to isolate page validation in page_is_corrupted() function. - Introduce --extended-validation parameter(default OFF) for mariabackup --backup to enable decryption of encrypted uncompressed pages during backup. - mariabackup would still always check checksum on encrypted data, it is needed to detect partially written pages.
1 parent ed36fc3 commit 9f4a4cb

File tree

4 files changed

+100
-74
lines changed

4 files changed

+100
-74
lines changed

extra/mariabackup/fil_cur.cc

Lines changed: 82 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,87 @@ xb_fil_cur_open(
265265
return(XB_FIL_CUR_SUCCESS);
266266
}
267267

268+
static bool page_is_corrupted(byte *page, ulint page_no, xb_fil_cur_t *cursor, fil_space_t *space)
269+
{
270+
byte tmp_frame[UNIV_PAGE_SIZE_MAX];
271+
byte tmp_page[UNIV_PAGE_SIZE_MAX];
272+
273+
ulint page_type = mach_read_from_2(page + FIL_PAGE_TYPE);
274+
275+
/* We ignore the doublewrite buffer pages.*/
276+
if (cursor->space_id == TRX_SYS_SPACE
277+
&& page_no >= FSP_EXTENT_SIZE
278+
&& page_no < FSP_EXTENT_SIZE * 3) {
279+
return false;
280+
}
281+
282+
/* Validate page number. */
283+
if (mach_read_from_4(page + FIL_PAGE_OFFSET) != page_no
284+
&& space->id != TRX_SYS_SPACE) {
285+
/* On pages that are not all zero, the
286+
page number must match.
287+
288+
There may be a mismatch on tablespace ID,
289+
because files may be renamed during backup.
290+
We disable the page number check
291+
on the system tablespace, because it may consist
292+
of multiple files, and here we count the pages
293+
from the start of each file.)
294+
295+
The first 38 and last 8 bytes are never encrypted. */
296+
const ulint* p = reinterpret_cast<ulint*>(page);
297+
const ulint* const end = reinterpret_cast<ulint*>(
298+
page + cursor->page_size);
299+
do {
300+
if (*p++) {
301+
return true;
302+
}
303+
} while (p != end);
304+
305+
/* Whole zero page is valid. */
306+
return false;
307+
}
308+
309+
/* Validate encrypted pages. */
310+
if (mach_read_from_4(page + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION) &&
311+
(space->crypt_data && space->crypt_data->type!= CRYPT_SCHEME_UNENCRYPTED)) {
312+
313+
if (!fil_space_verify_crypt_checksum(page, cursor->zip_size))
314+
return true;
315+
316+
/* Compressed encrypted need to be unencryped and uncompressed for verification. */
317+
if (page_type != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED && !opt_extended_validation)
318+
return false;
319+
320+
memcpy(tmp_page, page, cursor->page_size);
321+
322+
bool decrypted = false;
323+
if (!fil_space_decrypt(space, tmp_frame,tmp_page, &decrypted)) {
324+
return true;
325+
}
326+
327+
if (page_type != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
328+
return buf_page_is_corrupted(true, tmp_page, cursor->zip_size, space);
329+
}
330+
}
331+
332+
if (page_type == FIL_PAGE_PAGE_COMPRESSED) {
333+
memcpy(tmp_page, page, cursor->page_size);
334+
}
335+
336+
if (page_type == FIL_PAGE_PAGE_COMPRESSED || page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
337+
ulint decomp = fil_page_decompress(tmp_frame, tmp_page);
338+
page_type = mach_read_from_2(tmp_page + FIL_PAGE_TYPE);
339+
340+
return (!decomp
341+
|| (decomp != srv_page_size && cursor->zip_size)
342+
|| page_type == FIL_PAGE_PAGE_COMPRESSED
343+
|| page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
344+
|| buf_page_is_corrupted(true, tmp_page, cursor->zip_size, space));
345+
}
346+
347+
return buf_page_is_corrupted(true, page, cursor->zip_size, space);
348+
}
268349
/************************************************************************
269350
Reads and verifies the next block of pages from the source
270351
file. Positions the cursor after the last read non-corrupted page.
@@ -284,8 +365,6 @@ xb_fil_cur_read(
284365
xb_fil_cur_result_t ret;
285366
ib_int64_t offset;
286367
ib_int64_t to_read;
287-
byte tmp_frame[UNIV_PAGE_SIZE_MAX];
288-
byte tmp_page[UNIV_PAGE_SIZE_MAX];
289368

290369
cursor->read_filter->get_next_batch(&cursor->read_filter_ctxt,
291370
&offset, &to_read);
@@ -347,78 +426,8 @@ xb_fil_cur_read(
347426
for (page = cursor->buf, i = 0; i < npages;
348427
page += cursor->page_size, i++) {
349428
ulint page_no = cursor->buf_page_no + i;
350-
ulint page_type = mach_read_from_2(page + FIL_PAGE_TYPE);
351-
352-
if (cursor->space_id == TRX_SYS_SPACE
353-
&& page_no >= FSP_EXTENT_SIZE
354-
&& page_no < FSP_EXTENT_SIZE * 3) {
355-
/* We ignore the doublewrite buffer pages */
356-
} else if (mach_read_from_4(page + FIL_PAGE_OFFSET) != page_no
357-
&& space->id != TRX_SYS_SPACE) {
358-
/* On pages that are not all zero, the
359-
page number must match.
360-
361-
There may be a mismatch on tablespace ID,
362-
because files may be renamed during backup.
363-
We disable the page number check
364-
on the system tablespace, because it may consist
365-
of multiple files, and here we count the pages
366-
from the start of each file.)
367-
368-
The first 38 and last 8 bytes are never encrypted. */
369-
const ulint* p = reinterpret_cast<ulint*>(page);
370-
const ulint* const end = reinterpret_cast<ulint*>(
371-
page + cursor->page_size);
372-
do {
373-
if (*p++) {
374-
goto corrupted;
375-
}
376-
} while (p != end);
377-
} else if (mach_read_from_4(
378-
page
379-
+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION)
380-
&& space->crypt_data
381-
&& space->crypt_data->type
382-
!= CRYPT_SCHEME_UNENCRYPTED
383-
&& fil_space_verify_crypt_checksum(
384-
page, cursor->zip_size)) {
385-
bool decrypted = false;
386-
387-
memcpy(tmp_page, page, cursor->page_size);
388-
389-
if (!fil_space_decrypt(space, tmp_frame,
390-
tmp_page, &decrypted)) {
391-
goto corrupted;
392-
}
393-
394-
if (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
395-
goto page_decomp;
396-
}
397-
398-
if (buf_page_is_corrupted(
399-
true, tmp_page, cursor->zip_size, space)) {
400-
goto corrupted;
401-
}
402-
} else if (page_type == FIL_PAGE_PAGE_COMPRESSED) {
403-
memcpy(tmp_page, page, cursor->page_size);
404-
page_decomp:
405-
ulint decomp = fil_page_decompress(tmp_frame, tmp_page);
406-
page_type = mach_read_from_2(tmp_page + FIL_PAGE_TYPE);
407-
408-
if (!decomp
409-
|| (decomp != srv_page_size && cursor->zip_size)
410-
|| page_type == FIL_PAGE_PAGE_COMPRESSED
411-
|| page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
412-
|| buf_page_is_corrupted(true, tmp_page,
413-
cursor->zip_size,
414-
space)) {
415-
goto corrupted;
416-
}
417429

418-
} else if (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
419-
|| buf_page_is_corrupted(true, page,
420-
cursor->zip_size, space)) {
421-
corrupted:
430+
if (page_is_corrupted(page, page_no, cursor, space)){
422431
retry_count--;
423432

424433
if (retry_count == 0) {

extra/mariabackup/xtrabackup.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ char* log_ignored_opt = NULL;
206206

207207
extern my_bool opt_use_ssl;
208208
my_bool opt_ssl_verify_server_cert;
209+
my_bool opt_extended_validation;
209210

210211
/* === metadata of backup === */
211212
#define XTRABACKUP_METADATA_FILENAME "xtrabackup_checkpoints"
@@ -510,6 +511,7 @@ enum options_xtrabackup
510511
OPT_XTRA_DATABASES_FILE,
511512
OPT_XTRA_CREATE_IB_LOGFILE,
512513
OPT_XTRA_PARALLEL,
514+
OPT_XTRA_EXTENDED_VALIDATION,
513515
OPT_XTRA_STREAM,
514516
OPT_XTRA_COMPRESS,
515517
OPT_XTRA_COMPRESS_THREADS,
@@ -976,6 +978,14 @@ struct my_option xb_server_options[] =
976978
(G_PTR*) &xtrabackup_parallel, (G_PTR*) &xtrabackup_parallel, 0, GET_INT,
977979
REQUIRED_ARG, 1, 1, INT_MAX, 0, 0, 0},
978980

981+
{"extended_validation", OPT_XTRA_EXTENDED_VALIDATION,
982+
"Enable extended validation for Innodb data pages during backup phase."
983+
"Will slow down backup considerably, in case encryption is used.",
984+
(G_PTR*)&opt_extended_validation,
985+
(G_PTR*)&opt_extended_validation,
986+
0, GET_BOOL, NO_ARG, FALSE, 0, 0, 0, 0, 0},
987+
988+
979989
{"log", OPT_LOG, "Ignored option for MySQL option compatibility",
980990
(G_PTR*) &log_ignored_opt, (G_PTR*) &log_ignored_opt, 0,
981991
GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},

extra/mariabackup/xtrabackup.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ extern my_bool opt_noversioncheck;
128128
extern my_bool opt_no_backup_locks;
129129
extern my_bool opt_decompress;
130130
extern my_bool opt_remove_original;
131+
extern my_bool opt_extended_validation;
131132

132133
extern char *opt_incremental_history_name;
133134
extern char *opt_incremental_history_uuid;

mysql-test/suite/mariabackup/encrypted_page_corruption.test

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,20 @@ let $backuplog=$MYSQLTEST_VARDIR/tmp/backup.log;
6060

6161
--disable_result_log
6262
--error 1
63-
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir > $backuplog;
63+
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --extended-validation --target-dir=$targetdir > $backuplog;
6464
--enable_result_log
6565

6666

6767
--let SEARCH_PATTERN=Database page corruption detected
6868
--let SEARCH_FILE=$backuplog
6969
--source include/search_pattern_in_file.inc
7070
remove_file $backuplog;
71+
rmdir $targetdir;
72+
73+
# Due to very constructed nature of the "corruption" (faking checksums), the "corruption" won't be found without --extended-validation
74+
--disable_result_log
75+
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir;
76+
--enable_result_log
7177

7278
drop table t1;
7379
rmdir $targetdir;

0 commit comments

Comments
 (0)