Skip to content

Commit c0f47a4

Browse files
Thirunarayanandr-m
authored andcommitted
MDEV-12026: Implement innodb_checksum_algorithm=full_crc32
MariaDB data-at-rest encryption (innodb_encrypt_tables) had repurposed the same unused data field that was repurposed in MySQL 5.7 (and MariaDB 10.2) for the Split Sequence Number (SSN) field of SPATIAL INDEX. Because of this, MariaDB was unable to support encryption on SPATIAL INDEX pages. Furthermore, InnoDB page checksums skipped some bytes, and there are multiple variations and checksum algorithms. By default, InnoDB accepts all variations of all algorithms that ever existed. This unnecessarily weakens the page checksums. We hereby introduce two more innodb_checksum_algorithm variants (full_crc32, strict_full_crc32) that are special in a way: When either setting is active, newly created data files will carry a flag (fil_space_t::full_crc32()) that indicates that all pages of the file will use a full CRC-32C checksum over the entire page contents (excluding the bytes where the checksum is stored, at the very end of the page). Such files will always use that checksum, no matter what the parameter innodb_checksum_algorithm is assigned to. For old files, the old checksum algorithms will continue to be used. The value strict_full_crc32 will be equivalent to strict_crc32 and the value full_crc32 will be equivalent to crc32. ROW_FORMAT=COMPRESSED tables will only use the old format. These tables do not support new features, such as larger innodb_page_size or instant ADD/DROP COLUMN. They may be deprecated in the future. We do not want an unnecessary file format change for them. The new full_crc32() format also cleans up the MariaDB tablespace flags. We will reserve flags to store the page_compressed compression algorithm, and to store the compressed payload length, so that checksum can be computed over the compressed (and possibly encrypted) stream and can be validated without decrypting or decompressing the page. In the full_crc32 format, there no longer are separate before-encryption and after-encryption checksums for pages. The single checksum is computed on the page contents that is written to the file. We do not make the new algorithm the default for two reasons. First, MariaDB 10.4.2 was a beta release, and the default values of parameters should not change after beta. Second, we did not yet implement the full_crc32 format for page_compressed pages. This will be fixed in MDEV-18644. This is joint work with Marko Mäkelä.
1 parent 93984ff commit c0f47a4

File tree

83 files changed

+2337
-535
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+2337
-535
lines changed

extra/innochecksum.cc

Lines changed: 84 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -274,30 +274,27 @@ void print_leaf_stats(
274274
}
275275
}
276276

277-
/** Get the ROW_FORMAT=COMPRESSED size from the filespace header.
278-
@param[in] buf buffer used to read the page.
279-
@return ROW_FORMAT_COMPRESSED page size
280-
@retval 0 if not ROW_FORMAT=COMPRESSED */
281-
static ulint get_zip_size(const byte* buf)
277+
/** Init the page size for the tablespace.
278+
@param[in] buf buffer used to read the page */
279+
static void init_page_size(const byte* buf)
282280
{
283281
const unsigned flags = mach_read_from_4(buf + FIL_PAGE_DATA
284282
+ FSP_SPACE_FLAGS);
285283

284+
if (FSP_FLAGS_FCRC32_HAS_MARKER(flags)) {
285+
srv_page_size = fil_space_t::logical_size(flags);
286+
physical_page_size = srv_page_size;
287+
return;
288+
}
289+
286290
const ulong ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags);
287291

288292
srv_page_size_shift = ssize
289293
? UNIV_ZIP_SIZE_SHIFT_MIN - 1 + ssize
290294
: UNIV_PAGE_SIZE_SHIFT_ORIG;
291295

292-
srv_page_size = 1U << srv_page_size_shift;
293-
ulint zip_size = FSP_FLAGS_GET_ZIP_SSIZE(flags);
294-
if (zip_size) {
295-
zip_size = (UNIV_ZIP_SIZE_MIN >> 1) << zip_size;
296-
physical_page_size = zip_size;
297-
} else {
298-
physical_page_size = srv_page_size;
299-
}
300-
return zip_size;
296+
srv_page_size = fil_space_t::logical_size(flags);
297+
physical_page_size = fil_space_t::physical_size(flags);
301298
}
302299

303300
#ifdef _WIN32
@@ -429,19 +426,16 @@ ulint read_file(
429426

430427
/** Check if page is corrupted or not.
431428
@param[in] buf page frame
432-
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
433429
@param[in] is_encrypted true if page0 contained cryp_data
434430
with crypt_scheme encrypted
435-
@param[in] is_compressed true if page0 fsp_flags contained
436-
page compression flag
431+
@param[in] flags tablespace flags
437432
@retval true if page is corrupted otherwise false. */
438433
static
439434
bool
440435
is_page_corrupted(
441436
byte* buf,
442-
ulint zip_size,
443437
bool is_encrypted,
444-
bool is_compressed)
438+
ulint flags)
445439
{
446440

447441
/* enable if page is corrupted. */
@@ -450,9 +444,12 @@ is_page_corrupted(
450444
ulint logseq;
451445
ulint logseqfield;
452446
ulint page_type = mach_read_from_2(buf+FIL_PAGE_TYPE);
453-
uint key_version = mach_read_from_4(buf+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
447+
uint key_version = buf_page_get_key_version(buf, flags);
454448
ulint space_id = mach_read_from_4(
455449
buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
450+
ulint zip_size = fil_space_t::zip_size(flags);
451+
ulint is_compressed = fil_space_t::is_compressed(flags);
452+
const bool use_full_crc32 = fil_space_t::full_crc32(flags);
456453

457454
/* We can't trust only a page type, thus we take account
458455
also fsp_flags or crypt_data on page 0 */
@@ -468,9 +465,11 @@ is_page_corrupted(
468465
/* check the stored log sequence numbers
469466
for uncompressed tablespace. */
470467
logseq = mach_read_from_4(buf + FIL_PAGE_LSN + 4);
471-
logseqfield = mach_read_from_4(
472-
buf + srv_page_size -
473-
FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
468+
logseqfield = use_full_crc32
469+
? mach_read_from_4(buf + srv_page_size
470+
- FIL_PAGE_FCRC32_END_LSN)
471+
: mach_read_from_4(buf + srv_page_size
472+
- FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
474473

475474
if (is_log_enabled) {
476475
fprintf(log_file,
@@ -498,23 +497,22 @@ is_page_corrupted(
498497
so if crypt checksum does not match we verify checksum using
499498
normal method. */
500499
if (is_encrypted && key_version != 0) {
501-
is_corrupted = !fil_space_verify_crypt_checksum(buf, zip_size);
500+
is_corrupted = use_full_crc32
501+
? buf_page_is_corrupted(true, buf, flags)
502+
: !fil_space_verify_crypt_checksum(buf, zip_size);
503+
502504
if (is_corrupted && log_file) {
503505
fprintf(log_file,
504506
"Page " ULINTPF ":%llu may be corrupted;"
505507
" key_version=%u\n",
506-
space_id, cur_page_num,
507-
mach_read_from_4(
508-
FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
509-
+ buf));
508+
space_id, cur_page_num, key_version);
510509
}
511510
} else {
512511
is_corrupted = true;
513512
}
514513

515514
if (is_corrupted) {
516-
is_corrupted = buf_page_is_corrupted(
517-
true, buf, zip_size, NULL);
515+
is_corrupted = buf_page_is_corrupted(true, buf, flags);
518516
}
519517

520518
return(is_corrupted);
@@ -566,17 +564,13 @@ is_page_empty(
566564
/********************************************************************//**
567565
Rewrite the checksum for the page.
568566
@param [in/out] page page buffer
569-
@param [in] iscompressed Is compressed/Uncompressed Page.
567+
@param [in] flags tablespace flags
570568
571569
@retval true : do rewrite
572570
@retval false : skip the rewrite as checksum stored match with
573571
calculated or page is doublwrite buffer.
574572
*/
575-
576-
bool
577-
update_checksum(
578-
byte* page,
579-
bool iscompressed)
573+
static bool update_checksum(byte* page, ulint flags)
580574
{
581575
ib_uint32_t checksum = 0;
582576
byte stored1[4]; /* get FIL_PAGE_SPACE_OR_CHKSUM field checksum */
@@ -588,6 +582,9 @@ update_checksum(
588582
return (false);
589583
}
590584

585+
const bool use_full_crc32 = fil_space_t::full_crc32(flags);
586+
const bool iscompressed = fil_space_t::zip_size(flags);
587+
591588
memcpy(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4);
592589
memcpy(stored2, page + physical_page_size -
593590
FIL_PAGE_END_LSN_OLD_CHKSUM, 4);
@@ -615,12 +612,24 @@ update_checksum(
615612
" %u\n", cur_page_num, checksum);
616613
}
617614

615+
} else if (use_full_crc32) {
616+
checksum = buf_calc_page_full_crc32(page);
617+
byte* c = page + physical_page_size - FIL_PAGE_FCRC32_CHECKSUM;
618+
if (mach_read_from_4(c) == checksum) return false;
619+
mach_write_to_4(c, checksum);
620+
if (is_log_enabled) {
621+
fprintf(log_file, "page::%llu; Updated checksum"
622+
" = %u\n", cur_page_num, checksum);
623+
}
624+
return true;
618625
} else {
619626
/* page is uncompressed. */
620627

621628
/* Store the new formula checksum */
622629
switch ((srv_checksum_algorithm_t) write_check) {
623630

631+
case SRV_CHECKSUM_ALGORITHM_FULL_CRC32:
632+
case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32:
624633
case SRV_CHECKSUM_ALGORITHM_CRC32:
625634
case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
626635
checksum = buf_calc_page_crc32(page);
@@ -636,6 +645,7 @@ update_checksum(
636645
case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
637646
checksum = BUF_NO_CHECKSUM_MAGIC;
638647
break;
648+
639649
/* no default so the compiler will emit a warning if new
640650
enum is added and not handled here */
641651
}
@@ -689,8 +699,7 @@ update_checksum(
689699
@param[in,out] file file pointer where content
690700
have to be written
691701
@param[in] buf file buffer read
692-
@param[in] compressed Enabled if tablespace is
693-
compressed.
702+
@param[in] flags tablespace flags
694703
@param[in,out] pos current file position.
695704
696705
@retval true if successfully written
@@ -702,12 +711,12 @@ write_file(
702711
const char* filename,
703712
FILE* file,
704713
byte* buf,
705-
bool compressed,
714+
ulint flags,
706715
fpos_t* pos)
707716
{
708717
bool do_update;
709718

710-
do_update = update_checksum(buf, compressed);
719+
do_update = update_checksum(buf, flags);
711720

712721
if (file != stdin) {
713722
if (do_update) {
@@ -1322,6 +1331,13 @@ innochecksum_get_one_option(
13221331
srv_checksum_algorithm =
13231332
SRV_CHECKSUM_ALGORITHM_STRICT_NONE;
13241333
break;
1334+
1335+
case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32:
1336+
case SRV_CHECKSUM_ALGORITHM_FULL_CRC32:
1337+
srv_checksum_algorithm =
1338+
SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32;
1339+
break;
1340+
13251341
default:
13261342
return(true);
13271343
}
@@ -1415,30 +1431,21 @@ static bool check_encryption(const char* filename, const byte* page)
14151431
return (type == CRYPT_SCHEME_1);
14161432
}
14171433

1418-
/**
1419-
Verify page checksum.
1434+
/** Verify page checksum.
14201435
@param[in] buf page to verify
14211436
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
14221437
@param[in] is_encrypted true if tablespace is encrypted
1423-
@param[in] is_compressed true if tablespace is page compressed
14241438
@param[in,out] mismatch_count Number of pages failed in checksum verify
1425-
@retval 0 if page checksum matches or 1 if it does not match
1426-
*/
1427-
static
1428-
int verify_checksum(
1429-
byte* buf,
1430-
ulint zip_size,
1431-
bool is_encrypted,
1432-
bool is_compressed,
1433-
unsigned long long* mismatch_count)
1439+
@param[in] flags tablespace flags
1440+
@retval 0 if page checksum matches or 1 if it does not match */
1441+
static int verify_checksum(
1442+
byte* buf,
1443+
bool is_encrypted,
1444+
unsigned long long* mismatch_count,
1445+
ulint flags)
14341446
{
14351447
int exit_status = 0;
1436-
bool is_corrupted = false;
1437-
1438-
is_corrupted = is_page_corrupted(
1439-
buf, zip_size, is_encrypted, is_compressed);
1440-
1441-
if (is_corrupted) {
1448+
if (is_page_corrupted(buf, is_encrypted, flags)) {
14421449
fprintf(stderr, "Fail: page::%llu invalid\n",
14431450
cur_page_num);
14441451

@@ -1464,35 +1471,26 @@ int verify_checksum(
14641471
@param[in] filename File name
14651472
@param[in] fil_in File pointer
14661473
@param[in] buf page
1467-
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
14681474
@param[in] pos File position
14691475
@param[in] is_encrypted true if tablespace is encrypted
1470-
@param[in] is_compressed true if tablespace is page compressed
1476+
@param[in] flags tablespace flags
14711477
@retval 0 if checksum rewrite was successful, 1 if error was detected */
14721478
static
14731479
int
14741480
rewrite_checksum(
14751481
const char* filename,
14761482
FILE* fil_in,
14771483
byte* buf,
1478-
ulint zip_size,
14791484
fpos_t* pos,
1480-
bool is_encrypted,
1481-
bool is_compressed)
1485+
bool is_encrypted,
1486+
ulint flags)
14821487
{
1483-
int exit_status = 0;
1488+
bool is_compressed = fil_space_t::is_compressed(flags);
1489+
14841490
/* Rewrite checksum. Note that for encrypted and
14851491
page compressed tables this is not currently supported. */
1486-
if (do_write &&
1487-
!is_encrypted &&
1488-
!is_compressed
1489-
&& !write_file(filename, fil_in, buf,
1490-
zip_size, pos)) {
1491-
1492-
exit_status = 1;
1493-
}
1494-
1495-
return (exit_status);
1492+
return do_write && !is_encrypted && !is_compressed
1493+
&& !write_file(filename, fil_in, buf, flags, pos);
14961494
}
14971495

14981496
int main(
@@ -1668,10 +1666,9 @@ int main(
16681666

16691667
/* Determine page size, zip_size and page compression
16701668
from fsp_flags and encryption metadata from page 0 */
1671-
ulint zip_size = get_zip_size(buf);
1669+
init_page_size(buf);
16721670

16731671
ulint flags = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + buf);
1674-
bool is_compressed = FSP_FLAGS_HAS_PAGE_COMPRESSION(flags);
16751672

16761673
if (physical_page_size > UNIV_ZIP_SIZE_MIN) {
16771674
/* Read rest of the page 0 to determine crypt_data */
@@ -1698,7 +1695,8 @@ int main(
16981695
unsigned long long tmp_allow_mismatches = allow_mismatches;
16991696
allow_mismatches = 0;
17001697

1701-
exit_status = verify_checksum(buf, zip_size, is_encrypted, is_compressed, &mismatch_count);
1698+
exit_status = verify_checksum(buf, is_encrypted,
1699+
&mismatch_count, flags);
17021700

17031701
if (exit_status) {
17041702
fprintf(stderr, "Error: Page 0 checksum mismatch, can't continue. \n");
@@ -1707,8 +1705,9 @@ int main(
17071705
allow_mismatches = tmp_allow_mismatches;
17081706
}
17091707

1710-
if ((exit_status = rewrite_checksum(filename, fil_in, buf,
1711-
zip_size, &pos, is_encrypted, is_compressed))) {
1708+
if ((exit_status = rewrite_checksum(
1709+
filename, fil_in, buf,
1710+
&pos, is_encrypted, flags))) {
17121711
goto my_exit;
17131712
}
17141713

@@ -1874,13 +1873,15 @@ int main(
18741873
checksum verification.*/
18751874
if (!no_check
18761875
&& !skip_page
1877-
&& (exit_status = verify_checksum(buf, zip_size,
1878-
is_encrypted, is_compressed, &mismatch_count))) {
1876+
&& (exit_status = verify_checksum(
1877+
buf, is_encrypted,
1878+
&mismatch_count, flags))) {
18791879
goto my_exit;
18801880
}
18811881

1882-
if ((exit_status = rewrite_checksum(filename, fil_in, buf,
1883-
zip_size, &pos, is_encrypted, is_compressed))) {
1882+
if ((exit_status = rewrite_checksum(
1883+
filename, fil_in, buf,
1884+
&pos, is_encrypted, flags))) {
18841885
goto my_exit;
18851886
}
18861887

extra/mariabackup/fil_cur.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ static bool page_is_corrupted(const byte *page, ulint page_no,
312312
return false;
313313
}
314314

315+
if (space->full_crc32()) {
316+
return buf_page_is_corrupted(true, page, space->flags);
317+
}
318+
315319
/* Validate encrypted pages. The first page is never encrypted.
316320
In the system tablespace, the first page would be written with
317321
FIL_PAGE_FILE_FLUSH_LSN at shutdown, and if the LSN exceeds
@@ -344,7 +348,8 @@ static bool page_is_corrupted(const byte *page, ulint page_no,
344348
}
345349

346350
if (page_type != FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
347-
return buf_page_is_corrupted(true, tmp_page, 0, space);
351+
return buf_page_is_corrupted(true, tmp_page,
352+
space->flags);
348353
}
349354
}
350355

@@ -363,10 +368,10 @@ static bool page_is_corrupted(const byte *page, ulint page_no,
363368
|| page_type == FIL_PAGE_PAGE_COMPRESSED
364369
|| page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
365370
|| buf_page_is_corrupted(true, tmp_page,
366-
space->zip_size(), space));
371+
space->flags));
367372
}
368373

369-
return buf_page_is_corrupted(true, page, space->zip_size(), space);
374+
return buf_page_is_corrupted(true, page, space->flags);
370375
}
371376

372377
/************************************************************************

0 commit comments

Comments
 (0)