1 /*
2 Copyright (c) 2005, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 /*
26 InnoDB offline file checksum utility. 85% of the code in this utility
27 is included from the InnoDB codebase.
28
29 The final 15% was originally written by Mark Smith of Danga
30 Interactive, Inc. <junior@danga.com>
31
32 Published with a permission.
33 */
34
35 #include <my_config.h>
36 #include <my_global.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <time.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45 #include <my_getopt.h>
46 #include <m_string.h>
47 #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
48 #include "typelib.h"
49 #include "prealloced_array.h"
50
51 /* Only parts of these files are included from the InnoDB codebase.
52 The parts not included are excluded by #ifndef UNIV_INNOCHECKSUM. */
53
54 #include "univ.i" /* include all of this */
55 #include "page0size.h" /* page_size_t */
56 #include "page0zip.h" /* page_zip_calc_checksum() */
57 #include "page0page.h" /* PAGE_* */
58 #include "trx0undo.h" /* TRX_UNDO_* */
59 #include "fut0lst.h" /* FLST_NODE_SIZE */
60 #include "buf0checksum.h" /* buf_calc_page_*() */
61 #include "fil0fil.h" /* FIL_* */
62 #include "os0file.h"
63 #include "fsp0fsp.h" /* fsp_flags_get_page_size() &
64 fsp_flags_get_zip_size() */
65 #include "mach0data.h" /* mach_read_from_4() */
66 #include "ut0crc32.h" /* ut_crc32_init() */
67
68 #ifdef UNIV_NONINL
69 # include "fsp0fsp.ic"
70 # include "mach0data.ic"
71 # include "ut0rnd.ic"
72 #endif
73
74 /* Global variables */
75 static bool verbose;
76 static bool just_count;
77 static uintmax_t start_page;
78 static uintmax_t end_page;
79 static uintmax_t do_page;
80 static bool use_end_page;
81 static bool do_one_page;
82 /* replaces declaration in srv0srv.c */
83 ulong srv_page_size;
84 page_size_t univ_page_size(0, 0, false);
85 extern ulong srv_checksum_algorithm;
86 /* Current page number (0 based). */
87 uintmax_t cur_page_num;
88 /* Skip the checksum verification. */
89 static bool no_check;
90 /* Enabled for strict checksum verification. */
91 bool strict_verify = 0;
92 /* Enabled for rewrite checksum. */
93 static bool do_write;
94 /* Mismatches count allowed (0 by default). */
95 static uintmax_t allow_mismatches;
96 static bool page_type_summary;
97 static bool page_type_dump;
98 /* Store filename for page-type-dump option. */
99 char* page_dump_filename = 0;
100 /* skip the checksum verification & rewrite if page is doublewrite buffer. */
101 static bool skip_page = 0;
102 const char *dbug_setting = "FALSE";
103 char* log_filename = NULL;
104 /* User defined filename for logging. */
105 FILE* log_file = NULL;
106 /* Enabled for log write option. */
107 static bool is_log_enabled = false;
108
109 #ifndef _WIN32
110 /* advisory lock for non-window system. */
111 struct flock lk;
112 #endif /* _WIN32 */
113
114 /* Strict check algorithm name. */
115 static ulong strict_check;
116 /* Rewrite checksum algorithm name. */
117 static ulong write_check;
118
119 /* Innodb page type. */
120 struct innodb_page_type {
121 int n_undo_state_active;
122 int n_undo_state_cached;
123 int n_undo_state_to_free;
124 int n_undo_state_to_purge;
125 int n_undo_state_prepared;
126 int n_undo_state_other;
127 int n_undo_insert;
128 int n_undo_update;
129 int n_undo_other;
130 int n_fil_page_index;
131 int n_fil_page_undo_log;
132 int n_fil_page_inode;
133 int n_fil_page_ibuf_free_list;
134 int n_fil_page_ibuf_bitmap;
135 int n_fil_page_type_sys;
136 int n_fil_page_type_trx_sys;
137 int n_fil_page_type_fsp_hdr;
138 int n_fil_page_type_allocated;
139 int n_fil_page_type_xdes;
140 int n_fil_page_type_blob;
141 int n_fil_page_type_zblob;
142 int n_fil_page_type_other;
143 int n_fil_page_type_zblob2;
144 } page_type;
145
146 /* Possible values for "--strict-check" for strictly verify checksum
147 and "--write" for rewrite checksum. */
148 static const char *innochecksum_algorithms[] = {
149 "crc32",
150 "crc32",
151 "innodb",
152 "innodb",
153 "none",
154 "none",
155 NullS
156 };
157
158 /* Used to define an enumerate type of the "innochecksum algorithm". */
159 static TYPELIB innochecksum_algorithms_typelib = {
160 array_elements(innochecksum_algorithms)-1,"",
161 innochecksum_algorithms, NULL
162 };
163
164 /** Get the page size of the filespace from the filespace header.
165 @param[in] buf buffer used to read the page.
166 @return page size */
167 static
168 const page_size_t
get_page_size(byte * buf)169 get_page_size(
170 byte* buf)
171 {
172 const ulint flags = mach_read_from_4(buf + FIL_PAGE_DATA
173 + FSP_SPACE_FLAGS);
174
175 const ulint ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags);
176
177 if (ssize == 0) {
178 srv_page_size = UNIV_PAGE_SIZE_ORIG;
179 } else {
180 srv_page_size = ((UNIV_ZIP_SIZE_MIN >> 1) << ssize);
181 }
182
183 univ_page_size.copy_from(
184 page_size_t(srv_page_size, srv_page_size, false));
185
186 return(page_size_t(flags));
187 }
188
189 /** Decompress a page
190 @param[in,out] buf Page read from disk, uncompressed data will
191 also be copied to this page
192 @param[in, out] scratch Page to use for temporary decompress
193 @param[in] page_size scratch physical size
194 @return true if decompress succeeded */
195 static
page_decompress(byte * buf,byte * scratch,page_size_t page_size)196 bool page_decompress(
197 byte* buf,
198 byte* scratch,
199 page_size_t page_size)
200 {
201 dberr_t err;
202
203 /* Set the dblwr recover flag to false. */
204 err = os_file_decompress_page(
205 false, buf, scratch, page_size.physical());
206
207 return(err == DB_SUCCESS);
208 }
209
210 #ifdef _WIN32
211 /***********************************************//*
212 @param [in] error error no. from the getLastError().
213
214 @retval error message corresponding to error no.
215 */
216 static
217 char*
error_message(int error)218 error_message(
219 int error)
220 {
221 static char err_msg[1024] = {'\0'};
222 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
223 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
224 (LPTSTR)err_msg, sizeof(err_msg), NULL );
225
226 return (err_msg);
227 }
228 #endif /* _WIN32 */
229
230 /***********************************************//*
231 @param>>_______[in] name>_____name of file.
232 @retval file pointer; file pointer is NULL when error occured.
233 */
234
235 FILE*
open_file(const char * name)236 open_file(
237 const char* name)
238 {
239 int fd; /* file descriptor. */
240 FILE* fil_in;
241 #ifdef _WIN32
242 HANDLE hFile; /* handle to open file. */
243 DWORD access; /* define access control */
244 int flags = 0; /* define the mode for file
245 descriptor */
246
247 if (do_write) {
248 access = GENERIC_READ | GENERIC_WRITE;
249 flags = _O_RDWR | _O_BINARY;
250 } else {
251 access = GENERIC_READ;
252 flags = _O_RDONLY | _O_BINARY;
253 }
254 /* CreateFile() also provide advisory lock with the usage of
255 access and share mode of the file.*/
256 hFile = CreateFile(
257 (LPCTSTR) name, access, 0L, NULL,
258 OPEN_EXISTING, NULL, NULL);
259
260 if (hFile == INVALID_HANDLE_VALUE) {
261 /* print the error message. */
262 fprintf(stderr, "Filename::%s %s\n", name,
263 error_message(GetLastError()));
264
265 return (NULL);
266 }
267
268 /* get the file descriptor. */
269 fd= _open_osfhandle((intptr_t)hFile, flags);
270 #else /* _WIN32 */
271
272 int create_flag;
273 /* define the advisory lock and open file mode. */
274 if (do_write) {
275 create_flag = O_RDWR;
276 lk.l_type = F_WRLCK;
277 }
278 else {
279 create_flag = O_RDONLY;
280 lk.l_type = F_RDLCK;
281 }
282
283 fd = open(name, create_flag);
284
285 lk.l_whence = SEEK_SET;
286 lk.l_start = lk.l_len = 0;
287
288 if (fcntl(fd, F_SETLK, &lk) == -1) {
289 fprintf(stderr, "Error: Unable to lock file::"
290 " %s\n", name);
291 perror("fcntl");
292 return (NULL);
293 }
294 #endif /* _WIN32 */
295
296 if (do_write) {
297 fil_in = fdopen(fd, "rb+");
298 } else {
299 fil_in = fdopen(fd, "rb");
300 }
301
302 return (fil_in);
303 }
304
305 /************************************************************//*
306 Read the content of file
307
308 @param [in,out] buf read the file in buffer
309 @param [in] partial_page_read enable when to read the
310 remaining buffer for first page.
311 @param [in] physical_page_size Physical/Commpressed page size.
312 @param [in,out] fil_in file pointer created for the
313 tablespace.
314 @retval no. of bytes read.
315 */
read_file(byte * buf,bool partial_page_read,ulong physical_page_size,FILE * fil_in)316 ulong read_file(
317 byte* buf,
318 bool partial_page_read,
319 ulong physical_page_size,
320 FILE* fil_in)
321 {
322 ulong bytes = 0;
323
324 assert(physical_page_size >= UNIV_ZIP_SIZE_MIN);
325
326 if (partial_page_read) {
327 buf += UNIV_ZIP_SIZE_MIN;
328 physical_page_size -= UNIV_ZIP_SIZE_MIN;
329 bytes = UNIV_ZIP_SIZE_MIN;
330 }
331
332 bytes += ulong(fread(buf, 1, physical_page_size, fil_in));
333
334 return bytes;
335 }
336
337 /** Check if page is corrupted or not.
338 @param[in] buf page frame
339 @param[in] page_size page size
340 @retval true if page is corrupted otherwise false. */
341 static
342 bool
is_page_corrupted(const byte * buf,const page_size_t & page_size)343 is_page_corrupted(
344 const byte* buf,
345 const page_size_t& page_size)
346 {
347
348 /* enable if page is corrupted. */
349 bool is_corrupted;
350 /* use to store LSN values. */
351 ulint logseq;
352 ulint logseqfield;
353
354 if (!page_size.is_compressed()) {
355 /* check the stored log sequence numbers
356 for uncompressed tablespace. */
357 logseq = mach_read_from_4(buf + FIL_PAGE_LSN + 4);
358 logseqfield = mach_read_from_4(
359 buf + page_size.logical() -
360 FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
361
362 if (is_log_enabled) {
363 fprintf(log_file,
364 "page::%" PRIuMAX
365 "; log sequence number:first = " ULINTPF
366 "; second = " ULINTPF "\n",
367 cur_page_num, logseq, logseqfield);
368 if (logseq != logseqfield) {
369 fprintf(log_file,
370 "Fail; page %" PRIuMAX
371 " invalid (fails log "
372 "sequence number check)\n",
373 cur_page_num);
374 }
375 }
376 }
377
378 is_corrupted = buf_page_is_corrupted(
379 true, buf, page_size, false, cur_page_num, strict_verify,
380 is_log_enabled, log_file);
381
382 return(is_corrupted);
383 }
384
385 /********************************************//*
386 Check if page is doublewrite buffer or not.
387 @param [in] page buffer page
388
389 @retval true if page is doublewrite buffer otherwise false.
390 */
391 static
392 bool
is_page_doublewritebuffer(const byte * page)393 is_page_doublewritebuffer(
394 const byte* page)
395 {
396 if ((cur_page_num >= FSP_EXTENT_SIZE)
397 && (cur_page_num < FSP_EXTENT_SIZE * 3)) {
398 /* page is doublewrite buffer. */
399 return (true);
400 }
401
402 return (false);
403 }
404
405 /*******************************************************//*
406 Check if page is empty or not.
407 @param [in] page page to checked for empty.
408 @param [in] len size of page.
409
410 @retval true if page is empty.
411 @retval false if page is not empty.
412 */
413 static
414 bool
is_page_empty(const byte * page,size_t len)415 is_page_empty(
416 const byte* page,
417 size_t len)
418 {
419 while (len--) {
420 if (*page++) {
421 return (false);
422 }
423 }
424 return (true);
425 }
426
427 /********************************************************************//**
428 Rewrite the checksum for the page.
429 @param [in/out] page page buffer
430 @param [in] physical_page_size page size in bytes on disk.
431 @param [in] iscompressed Is compressed/Uncompressed Page.
432
433 @retval true : do rewrite
434 @retval false : skip the rewrite as checksum stored match with
435 calculated or page is doublwrite buffer.
436 */
437
438 bool
update_checksum(byte * page,ulong physical_page_size,bool iscompressed)439 update_checksum(
440 byte* page,
441 ulong physical_page_size,
442 bool iscompressed)
443 {
444 ib_uint32_t checksum = 0;
445 byte stored1[4]; /* get FIL_PAGE_SPACE_OR_CHKSUM field checksum */
446 byte stored2[4]; /* get FIL_PAGE_END_LSN_OLD_CHKSUM field checksum */
447
448 ut_ad(page);
449 /* If page is doublewrite buffer, skip the rewrite of checksum. */
450 if (skip_page) {
451 return (false);
452 }
453
454 memcpy(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4);
455 memcpy(stored2, page + physical_page_size -
456 FIL_PAGE_END_LSN_OLD_CHKSUM, 4);
457
458 /* Check if page is empty, exclude the checksum field */
459 if (is_page_empty(page + 4, physical_page_size - 12)
460 && is_page_empty(page + physical_page_size - 4, 4)) {
461
462 memset(page + FIL_PAGE_SPACE_OR_CHKSUM, 0, 4);
463 memset(page + physical_page_size -
464 FIL_PAGE_END_LSN_OLD_CHKSUM, 0, 4);
465
466 goto func_exit;
467 }
468
469 if (iscompressed) {
470 /* page is compressed */
471 checksum = page_zip_calc_checksum(
472 page, physical_page_size,
473 static_cast<srv_checksum_algorithm_t>(write_check));
474
475 mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, checksum);
476 if (is_log_enabled) {
477 fprintf(log_file, "page::%" PRIuMAX "; Updated checksum ="
478 " %u\n", cur_page_num, checksum);
479 }
480
481 } else {
482 /* page is uncompressed. */
483
484 /* Store the new formula checksum */
485 switch ((srv_checksum_algorithm_t) write_check) {
486
487 case SRV_CHECKSUM_ALGORITHM_CRC32:
488 case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
489 checksum = buf_calc_page_crc32(page);
490 break;
491
492 case SRV_CHECKSUM_ALGORITHM_INNODB:
493 case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
494 checksum = (ib_uint32_t)
495 buf_calc_page_new_checksum(page);
496 break;
497
498 case SRV_CHECKSUM_ALGORITHM_NONE:
499 case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
500 checksum = BUF_NO_CHECKSUM_MAGIC;
501 break;
502 /* no default so the compiler will emit a warning if new
503 enum is added and not handled here */
504 }
505
506 mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, checksum);
507 if (is_log_enabled) {
508 fprintf(log_file, "page::%" PRIuMAX "; Updated checksum field1"
509 " = %u\n", cur_page_num, checksum);
510 }
511
512 if (write_check == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB
513 || write_check == SRV_CHECKSUM_ALGORITHM_INNODB) {
514 checksum = (ib_uint32_t)
515 buf_calc_page_old_checksum(page);
516 }
517
518 mach_write_to_4(page + physical_page_size -
519 FIL_PAGE_END_LSN_OLD_CHKSUM,checksum);
520
521 if (is_log_enabled) {
522 fprintf(log_file, "page::%" PRIuMAX "; Updated checksum "
523 "field2 = %u\n", cur_page_num, checksum);
524 }
525
526 }
527
528 func_exit:
529 /* The following code is to check the stored checksum with the
530 calculated checksum. If it matches, then return FALSE to skip
531 the rewrite of checksum, otherwise return TRUE. */
532 if (iscompressed) {
533 if (!memcmp(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4)) {
534 return (false);
535 }
536 return (true);
537 }
538
539 if (!memcmp(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4)
540 && !memcmp(stored2, page + physical_page_size -
541 FIL_PAGE_END_LSN_OLD_CHKSUM, 4)) {
542 return (false);
543
544 }
545
546 return (true);
547 }
548
549 /**
550 Write the content to the file
551 @param[in] filename name of the file.
552 @param[in,out] file file pointer where content
553 have to be written
554 @param[in] buf file buffer read
555 @param[in] compressed Enabled if tablespace is
556 compressed.
557 @param[in,out] pos current file position.
558 @param[in] page_size page size in bytes on disk.
559
560 @retval true if successfully written
561 @retval false if a non-recoverable error occurred
562 */
563 static
564 bool
write_file(const char * filename,FILE * file,byte * buf,bool compressed,fpos_t * pos,ulong page_size)565 write_file(
566 const char* filename,
567 FILE* file,
568 byte* buf,
569 bool compressed,
570 fpos_t* pos,
571 ulong page_size)
572 {
573 bool do_update;
574
575 do_update = update_checksum(buf, page_size, compressed);
576
577 if (file != stdin) {
578 if (do_update) {
579 /* Set the previous file pointer position
580 saved in pos to current file position. */
581 if (0 != fsetpos(file, pos)) {
582 perror("fsetpos");
583 return(false);
584 }
585 } else {
586 /* Store the current file position in pos */
587 if (0 != fgetpos(file, pos)) {
588 perror("fgetpos");
589 return(false);
590 }
591 return(true);
592 }
593 }
594
595 if (page_size
596 != fwrite(buf, 1, page_size, file == stdin ? stdout : file)) {
597 fprintf(stderr, "Failed to write page %" PRIuMAX " to %s: %s\n",
598 cur_page_num, filename, strerror(errno));
599
600 return(false);
601 }
602 if (file != stdin) {
603 fflush(file);
604 /* Store the current file position in pos */
605 if (0 != fgetpos(file, pos)) {
606 perror("fgetpos");
607 return(false);
608 }
609 }
610
611 return(true);
612 }
613
614 /*
615 Parse the page and collect/dump the information about page type
616 @param [in] page buffer page
617 @param [in] file file for diagnosis.
618 */
619 void
parse_page(const byte * page,FILE * file)620 parse_page(
621 const byte* page,
622 FILE* file)
623 {
624 unsigned long long id;
625 ulint undo_page_type;
626 char str[20]={'\0'};
627
628 /* Check whether page is doublewrite buffer. */
629 if(skip_page) {
630 strcpy(str, "Double_write_buffer");
631 } else {
632 strcpy(str, "-");
633 }
634
635 switch (mach_read_from_2(page + FIL_PAGE_TYPE)) {
636
637 case FIL_PAGE_INDEX:
638 page_type.n_fil_page_index++;
639 id = mach_read_from_8(page + PAGE_HEADER + PAGE_INDEX_ID);
640 if (page_type_dump) {
641 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tIndex page\t\t\t|"
642 "\tindex id=%llu,", cur_page_num, id);
643
644 fprintf(file,
645 " page level=" ULINTPF
646 ", No. of records=" ULINTPF
647 ", garbage=" ULINTPF ", %s\n",
648 page_header_get_field(page, PAGE_LEVEL),
649 page_header_get_field(page, PAGE_N_RECS),
650 page_header_get_field(page, PAGE_GARBAGE), str);
651 }
652 break;
653
654 case FIL_PAGE_UNDO_LOG:
655 page_type.n_fil_page_undo_log++;
656 undo_page_type = mach_read_from_2(page +
657 TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE);
658 if (page_type_dump) {
659 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tUndo log page\t\t\t|",
660 cur_page_num);
661 }
662 if (undo_page_type == TRX_UNDO_INSERT) {
663 page_type.n_undo_insert++;
664 if (page_type_dump) {
665 fprintf(file, "\t%s",
666 "Insert Undo log page");
667 }
668
669 } else if (undo_page_type == TRX_UNDO_UPDATE) {
670 page_type.n_undo_update++;
671 if (page_type_dump) {
672 fprintf(file, "\t%s",
673 "Update undo log page");
674 }
675 }
676
677 undo_page_type = mach_read_from_2(page + TRX_UNDO_SEG_HDR +
678 TRX_UNDO_STATE);
679 switch (undo_page_type) {
680 case TRX_UNDO_ACTIVE:
681 page_type.n_undo_state_active++;
682 if (page_type_dump) {
683 fprintf(file, ", %s", "Undo log of "
684 "an active transaction");
685 }
686 break;
687
688 case TRX_UNDO_CACHED:
689 page_type.n_undo_state_cached++;
690 if (page_type_dump) {
691 fprintf(file, ", %s", "Page is "
692 "cached for quick reuse");
693 }
694 break;
695
696 case TRX_UNDO_TO_FREE:
697 page_type.n_undo_state_to_free++;
698 if (page_type_dump) {
699 fprintf(file, ", %s", "Insert undo "
700 "segment that can be freed");
701 }
702 break;
703
704 case TRX_UNDO_TO_PURGE:
705 page_type.n_undo_state_to_purge++;
706 if (page_type_dump) {
707 fprintf(file, ", %s", "Will be "
708 "freed in purge when all undo"
709 "data in it is removed");
710 }
711 break;
712
713 case TRX_UNDO_PREPARED:
714 page_type.n_undo_state_prepared++;
715 if (page_type_dump) {
716 fprintf(file, ", %s", "Undo log of "
717 "an prepared transaction");
718 }
719 break;
720
721 default:
722 page_type.n_undo_state_other++;
723 break;
724 }
725 if(page_type_dump) {
726 fprintf(file, ", %s\n", str);
727 }
728 break;
729
730 case FIL_PAGE_INODE:
731 page_type.n_fil_page_inode++;
732 if (page_type_dump) {
733 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tInode page\t\t\t|"
734 "\t%s\n",cur_page_num, str);
735 }
736 break;
737
738 case FIL_PAGE_IBUF_FREE_LIST:
739 page_type.n_fil_page_ibuf_free_list++;
740 if (page_type_dump) {
741 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tInsert buffer free list"
742 " page\t|\t%s\n", cur_page_num, str);
743 }
744 break;
745
746 case FIL_PAGE_TYPE_ALLOCATED:
747 page_type.n_fil_page_type_allocated++;
748 if (page_type_dump) {
749 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tFreshly allocated "
750 "page\t\t|\t%s\n", cur_page_num, str);
751 }
752 break;
753
754 case FIL_PAGE_IBUF_BITMAP:
755 page_type.n_fil_page_ibuf_bitmap++;
756 if (page_type_dump) {
757 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tInsert Buffer "
758 "Bitmap\t\t|\t%s\n", cur_page_num, str);
759 }
760 break;
761
762 case FIL_PAGE_TYPE_SYS:
763 page_type.n_fil_page_type_sys++;
764 if (page_type_dump) {
765 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tSystem page\t\t\t|"
766 "\t%s\n",cur_page_num, str);
767 }
768 break;
769
770 case FIL_PAGE_TYPE_TRX_SYS:
771 page_type.n_fil_page_type_trx_sys++;
772 if (page_type_dump) {
773 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tTransaction system "
774 "page\t\t|\t%s\n", cur_page_num, str);
775 }
776 break;
777
778 case FIL_PAGE_TYPE_FSP_HDR:
779 page_type.n_fil_page_type_fsp_hdr++;
780 if (page_type_dump) {
781 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tFile Space "
782 "Header\t\t|\t%s\n", cur_page_num, str);
783 }
784 break;
785
786 case FIL_PAGE_TYPE_XDES:
787 page_type.n_fil_page_type_xdes++;
788 if (page_type_dump) {
789 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tExtent descriptor "
790 "page\t\t|\t%s\n", cur_page_num, str);
791 }
792 break;
793
794 case FIL_PAGE_TYPE_BLOB:
795 page_type.n_fil_page_type_blob++;
796 if (page_type_dump) {
797 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tBLOB page\t\t\t|\t%s\n",
798 cur_page_num, str);
799 }
800 break;
801
802 case FIL_PAGE_TYPE_ZBLOB:
803 page_type.n_fil_page_type_zblob++;
804 if (page_type_dump) {
805 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tCompressed BLOB "
806 "page\t\t|\t%s\n", cur_page_num, str);
807 }
808 break;
809
810 case FIL_PAGE_TYPE_ZBLOB2:
811 page_type.n_fil_page_type_zblob2++;
812 if (page_type_dump) {
813 fprintf(file, "#::%8" PRIuMAX "\t\t|\t\tSubsequent Compressed "
814 "BLOB page\t|\t%s\n", cur_page_num, str);
815 }
816 break;
817
818 default:
819 page_type.n_fil_page_type_other++;
820 break;
821 }
822 }
823 /**
824 @param [in/out] file_name name of the filename
825
826 @retval FILE pointer if successfully created else NULL when error occured.
827 */
828 FILE*
create_file(char * file_name)829 create_file(
830 char* file_name)
831 {
832 FILE* file = NULL;
833
834 #ifndef _WIN32
835 file = fopen(file_name, "wb");
836 if (file == NULL) {
837 fprintf(stderr, "Failed to create file: %s: %s\n",
838 file_name, strerror(errno));
839 return(NULL);
840 }
841 #else
842 HANDLE hFile; /* handle to open file. */
843 int fd = 0;
844 hFile = CreateFile((LPCTSTR) file_name,
845 GENERIC_READ | GENERIC_WRITE,
846 FILE_SHARE_READ | FILE_SHARE_DELETE,
847 NULL, CREATE_NEW, NULL, NULL);
848
849 if (hFile == INVALID_HANDLE_VALUE) {
850 /* print the error message. */
851 fprintf(stderr, "Filename::%s %s\n",
852 file_name,
853 error_message(GetLastError()));
854
855 return(NULL);
856 }
857
858 /* get the file descriptor. */
859 fd= _open_osfhandle((intptr_t)hFile, _O_RDWR | _O_BINARY);
860 file = fdopen(fd, "wb");
861 #endif /* _WIN32 */
862
863 return(file);
864 }
865
866 /*
867 Print the page type count of a tablespace.
868 @param [in] fil_out stream where the output goes.
869 */
870 void
print_summary(FILE * fil_out)871 print_summary(
872 FILE* fil_out)
873 {
874 fprintf(fil_out, "\n================PAGE TYPE SUMMARY==============\n");
875 fprintf(fil_out, "#PAGE_COUNT\tPAGE_TYPE");
876 fprintf(fil_out, "\n===============================================\n");
877 fprintf(fil_out, "%8d\tIndex page\n",
878 page_type.n_fil_page_index);
879 fprintf(fil_out, "%8d\tUndo log page\n",
880 page_type.n_fil_page_undo_log);
881 fprintf(fil_out, "%8d\tInode page\n",
882 page_type.n_fil_page_inode);
883 fprintf(fil_out, "%8d\tInsert buffer free list page\n",
884 page_type.n_fil_page_ibuf_free_list);
885 fprintf(fil_out, "%8d\tFreshly allocated page\n",
886 page_type.n_fil_page_type_allocated);
887 fprintf(fil_out, "%8d\tInsert buffer bitmap\n",
888 page_type.n_fil_page_ibuf_bitmap);
889 fprintf(fil_out, "%8d\tSystem page\n",
890 page_type.n_fil_page_type_sys);
891 fprintf(fil_out, "%8d\tTransaction system page\n",
892 page_type.n_fil_page_type_trx_sys);
893 fprintf(fil_out, "%8d\tFile Space Header\n",
894 page_type.n_fil_page_type_fsp_hdr);
895 fprintf(fil_out, "%8d\tExtent descriptor page\n",
896 page_type.n_fil_page_type_xdes);
897 fprintf(fil_out, "%8d\tBLOB page\n",
898 page_type.n_fil_page_type_blob);
899 fprintf(fil_out, "%8d\tCompressed BLOB page\n",
900 page_type.n_fil_page_type_zblob);
901 fprintf(fil_out, "%8d\tOther type of page",
902 page_type.n_fil_page_type_other);
903 fprintf(fil_out, "\n===============================================\n");
904 fprintf(fil_out, "Additional information:\n");
905 fprintf(fil_out, "Undo page type: %d insert, %d update, %d other\n",
906 page_type.n_undo_insert,
907 page_type.n_undo_update,
908 page_type.n_undo_other);
909 fprintf(fil_out, "Undo page state: %d active, %d cached, %d to_free, %d"
910 " to_purge, %d prepared, %d other\n",
911 page_type.n_undo_state_active,
912 page_type.n_undo_state_cached,
913 page_type.n_undo_state_to_free,
914 page_type.n_undo_state_to_purge,
915 page_type.n_undo_state_prepared,
916 page_type.n_undo_state_other);
917 }
918
919 /* command line argument for innochecksum tool. */
920 static struct my_option innochecksum_options[] = {
921 {"help", '?', "Displays this help and exits.",
922 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
923 {"info", 'I', "Synonym for --help.",
924 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
925 {"version", 'V', "Displays version information and exits.",
926 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
927 {"verbose", 'v', "Verbose (prints progress every 5 seconds).",
928 &verbose, &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
929 #ifndef NDEBUG
930 {"debug", '#', "Output debug log. See " REFMAN "dbug-package.html",
931 &dbug_setting, &dbug_setting, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
932 #endif /* !NDEBUG */
933 {"count", 'c', "Print the count of pages in the file and exits.",
934 &just_count, &just_count, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
935 {"start_page", 's', "Start on this page number (0 based).",
936 &start_page, &start_page, 0, GET_ULL, REQUIRED_ARG,
937 0, 0, ULLONG_MAX, 0, 1, 0},
938 {"end_page", 'e', "End at this page number (0 based).",
939 &end_page, &end_page, 0, GET_ULL, REQUIRED_ARG,
940 0, 0, ULLONG_MAX, 0, 1, 0},
941 {"page", 'p', "Check only this page (0 based).",
942 &do_page, &do_page, 0, GET_ULL, REQUIRED_ARG,
943 0, 0, ULLONG_MAX, 0, 1, 0},
944 {"strict-check", 'C', "Specify the strict checksum algorithm by the user.",
945 &strict_check, &strict_check, &innochecksum_algorithms_typelib,
946 GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
947 {"no-check", 'n', "Ignore the checksum verification.",
948 &no_check, &no_check, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
949 {"allow-mismatches", 'a', "Maximum checksum mismatch allowed.",
950 &allow_mismatches, &allow_mismatches, 0,
951 GET_ULL, REQUIRED_ARG, 0, 0, ULLONG_MAX, 0, 1, 0},
952 {"write", 'w', "Rewrite the checksum algorithm by the user.",
953 &write_check, &write_check, &innochecksum_algorithms_typelib,
954 GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
955 {"page-type-summary", 'S', "Display a count of each page type "
956 "in a tablespace.", &page_type_summary, &page_type_summary, 0,
957 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
958 {"page-type-dump", 'D', "Dump the page type info for each page in a "
959 "tablespace.", &page_dump_filename, &page_dump_filename, 0,
960 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
961 {"log", 'l', "log output.",
962 &log_filename, &log_filename, 0,
963 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
964
965 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
966 };
967
968 /* Print out the Innodb version and machine information. */
print_version(void)969 static void print_version(void)
970 {
971 #ifdef NDEBUG
972 printf("%s Ver %s, for %s (%s)\n",
973 my_progname, INNODB_VERSION_STR,
974 SYSTEM_TYPE, MACHINE_TYPE);
975 #else
976 printf("%s-debug Ver %s, for %s (%s)\n",
977 my_progname, INNODB_VERSION_STR,
978 SYSTEM_TYPE, MACHINE_TYPE);
979 #endif /* NDEBUG */
980 }
981
usage(void)982 static void usage(void)
983 {
984 print_version();
985 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
986 printf("InnoDB offline file checksum utility.\n");
987 printf("Usage: %s [-c] [-s <start page>] [-e <end page>] "
988 "[-p <page>] [-v] [-a <allow mismatches>] [-n] "
989 "[-C <strict-check>] [-w <write>] [-S] [-D <page type dump>] "
990 "[-l <log>] <filename or [-]>\n", my_progname);
991 printf("See " REFMAN "innochecksum.html for usage hints.\n");
992 my_print_help(innochecksum_options);
993 my_print_variables(innochecksum_options);
994 }
995
996 extern "C" my_bool
innochecksum_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument MY_ATTRIBUTE ((unused)))997 innochecksum_get_one_option(
998 int optid,
999 const struct my_option *opt MY_ATTRIBUTE((unused)),
1000 char *argument MY_ATTRIBUTE((unused)))
1001 {
1002 switch (optid) {
1003 #ifndef NDEBUG
1004 case '#':
1005 dbug_setting = argument
1006 ? argument
1007 : IF_WIN("d:O,innochecksum.trace",
1008 "d:o,/tmp/innochecksum.trace");
1009 DBUG_PUSH(dbug_setting);
1010 break;
1011 #endif /* !NDEBUG */
1012 case 'e':
1013 use_end_page = true;
1014 break;
1015 case 'p':
1016 end_page = start_page = do_page;
1017 use_end_page = true;
1018 do_one_page = true;
1019 break;
1020 case 'V':
1021 print_version();
1022 exit(EXIT_SUCCESS);
1023 break;
1024 case 'C':
1025 strict_verify = true;
1026 switch ((srv_checksum_algorithm_t) strict_check) {
1027
1028 case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
1029 case SRV_CHECKSUM_ALGORITHM_CRC32:
1030 srv_checksum_algorithm =
1031 SRV_CHECKSUM_ALGORITHM_STRICT_CRC32;
1032 break;
1033
1034 case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
1035 case SRV_CHECKSUM_ALGORITHM_INNODB:
1036 srv_checksum_algorithm =
1037 SRV_CHECKSUM_ALGORITHM_STRICT_INNODB;
1038 break;
1039
1040 case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
1041 case SRV_CHECKSUM_ALGORITHM_NONE:
1042 srv_checksum_algorithm =
1043 SRV_CHECKSUM_ALGORITHM_STRICT_NONE;
1044 break;
1045 default:
1046 return(true);
1047 }
1048 break;
1049 case 'n':
1050 no_check = true;
1051 break;
1052 case 'a':
1053 case 'S':
1054 break;
1055 case 'w':
1056 do_write = true;
1057 break;
1058 case 'D':
1059 page_type_dump = true;
1060 break;
1061 case 'l':
1062 is_log_enabled = true;
1063 break;
1064 case 'I':
1065 case '?':
1066 usage();
1067 exit(EXIT_SUCCESS);
1068 break;
1069 }
1070
1071 return(false);
1072 }
1073
1074 static
1075 bool
get_options(int * argc,char *** argv)1076 get_options(
1077 int *argc,
1078 char ***argv)
1079 {
1080 if (handle_options(argc, argv, innochecksum_options,
1081 innochecksum_get_one_option))
1082 exit(true);
1083
1084 /* The next arg must be the filename */
1085 if (!*argc) {
1086 usage();
1087 return (true);
1088 }
1089
1090 return (false);
1091 }
1092
main(int argc,char ** argv)1093 int main(
1094 int argc,
1095 char **argv)
1096 {
1097 /* our input file. */
1098 FILE* fil_in = NULL;
1099 /* our input filename. */
1100 char* filename;
1101 /* Buffer to store pages read. */
1102 Prealloced_array<byte, 1> buf(PSI_NOT_INSTRUMENTED);
1103 /* bytes read count */
1104 ulong bytes;
1105 /* Buffer to decompress page.*/
1106 byte* tbuf = NULL;
1107 /* current time */
1108 time_t now;
1109 /* last time */
1110 time_t lastt;
1111 /* stat, to get file size. */
1112 #ifdef _WIN32
1113 struct _stat64 st;
1114 #else
1115 struct stat st;
1116 #endif /* _WIN32 */
1117
1118 /* size of file (has to be 64 bits) */
1119 unsigned long long int size = 0;
1120 /* number of pages in file */
1121 ulint pages;
1122
1123 off_t offset = 0;
1124 /* count the no. of page corrupted. */
1125 ulint mismatch_count = 0;
1126 /* Variable to ack the page is corrupted or not. */
1127 bool is_corrupted = false;
1128
1129 bool partial_page_read = false;
1130 /* Enabled when read from stdin is done. */
1131 bool read_from_stdin = false;
1132 FILE* fil_page_type = NULL;
1133 fpos_t pos;
1134
1135 /* Use to check the space id of given file. If space_id is zero,
1136 then check whether page is doublewrite buffer.*/
1137 ulint space_id = 0UL;
1138 /* enable when space_id of given file is zero. */
1139 bool is_system_tablespace = false;
1140
1141 ut_crc32_init();
1142 MY_INIT(argv[0]);
1143 DBUG_ENTER("main");
1144 DBUG_PROCESS(argv[0]);
1145
1146 if (get_options(&argc,&argv)) {
1147 DBUG_RETURN(1);
1148 }
1149
1150 if (strict_verify && no_check) {
1151 fprintf(stderr, "Error: --strict-check option cannot be used "
1152 "together with --no-check option.\n");
1153 DBUG_RETURN(1);
1154 }
1155
1156 if (no_check && !do_write) {
1157 fprintf(stderr, "Error: --no-check must be associated with "
1158 "--write option.\n");
1159 DBUG_RETURN(1);
1160 }
1161
1162 if (page_type_dump) {
1163 fil_page_type = create_file(page_dump_filename);
1164 if (!fil_page_type) {
1165 DBUG_RETURN(1);
1166 }
1167 }
1168
1169 if (is_log_enabled) {
1170 log_file = create_file(log_filename);
1171 if (!log_file) {
1172 DBUG_RETURN(1);
1173 }
1174 fprintf(log_file, "InnoDB File Checksum Utility.\n");
1175 }
1176
1177 if (verbose) {
1178 my_print_variables_ex(innochecksum_options, stderr);
1179 }
1180
1181
1182 buf.reserve(UNIV_PAGE_SIZE_MAX * 2);
1183 tbuf = buf.begin() + UNIV_PAGE_SIZE_MAX;
1184
1185 /* The file name is not optional. */
1186 for (int i = 0; i < argc; ++i) {
1187 /* Reset parameters for each file. */
1188 filename = argv[i];
1189 memset(&page_type, 0, sizeof(innodb_page_type));
1190 is_corrupted = false;
1191 partial_page_read = false;
1192 skip_page = false;
1193
1194 if (is_log_enabled) {
1195 fprintf(log_file, "Filename = %s\n", filename);
1196 }
1197
1198 if (*filename == '-') {
1199 /* read from stdin. */
1200 fil_in = stdin;
1201 read_from_stdin = true;
1202
1203 }
1204
1205 /* stat the file to get size and page count. */
1206 if (!read_from_stdin &&
1207 #ifdef _WIN32
1208 _stat64(filename, &st)) {
1209 #else
1210 stat(filename, &st)) {
1211 #endif /* _WIN32 */
1212 fprintf(stderr, "Error: %s cannot be found\n",
1213 filename);
1214
1215 DBUG_RETURN(1);
1216 }
1217
1218 if (!read_from_stdin) {
1219 size = st.st_size;
1220 fil_in = open_file(filename);
1221 /*If fil_in is NULL, terminate as some error encountered */
1222 if(fil_in == NULL) {
1223 DBUG_RETURN(1);
1224 }
1225 /* Save the current file pointer in pos variable.*/
1226 if (0 != fgetpos(fil_in, &pos)) {
1227 perror("fgetpos");
1228 DBUG_RETURN(1);
1229 }
1230 }
1231
1232 /* Testing for lock mechanism. The innochecksum
1233 acquire lock on given file. So other tools accessing the same
1234 file for processsing must fail. */
1235 #ifdef _WIN32
1236 DBUG_EXECUTE_IF("innochecksum_cause_mysqld_crash",
1237 ut_ad(page_dump_filename);
1238 while((_access( page_dump_filename, 0)) == 0) {
1239 sleep(1);
1240 }
1241 DBUG_RETURN(0); );
1242 #else
1243 DBUG_EXECUTE_IF("innochecksum_cause_mysqld_crash",
1244 ut_ad(page_dump_filename);
1245 struct stat status_buf;
1246 while(stat(page_dump_filename, &status_buf) == 0) {
1247 sleep(1);
1248 }
1249 DBUG_RETURN(0); );
1250 #endif /* _WIN32 */
1251
1252 /* Read the minimum page size. */
1253 bytes = ulong(fread(buf.begin(), 1, UNIV_ZIP_SIZE_MIN, fil_in));
1254 partial_page_read = true;
1255
1256 if (bytes != UNIV_ZIP_SIZE_MIN) {
1257 fprintf(stderr, "Error: Was not able to read the "
1258 "minimum page size ");
1259 fprintf(stderr, "of %d bytes. Bytes read was %lu\n",
1260 UNIV_ZIP_SIZE_MIN, bytes);
1261
1262 DBUG_RETURN(1);
1263 }
1264
1265 /* enable variable is_system_tablespace when space_id of given
1266 file is zero. Use to skip the checksum verification and rewrite
1267 for doublewrite pages. */
1268 is_system_tablespace = (!memcmp(&space_id, buf.begin() +
1269 FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 4))
1270 ? true : false;
1271
1272 const page_size_t& page_size = get_page_size(buf.begin());
1273
1274 pages = (ulint) (size / page_size.physical());
1275
1276 if (just_count) {
1277 if (read_from_stdin) {
1278 fprintf(stderr, "Number of pages:" ULINTPF "\n", pages);
1279 } else {
1280 printf("Number of pages:" ULINTPF "\n", pages);
1281 }
1282 continue;
1283 } else if (verbose && !read_from_stdin) {
1284 if (is_log_enabled) {
1285 fprintf(log_file, "file %s = %llu bytes "
1286 "(" ULINTPF " pages)\n", filename, size, pages);
1287 if (do_one_page) {
1288 fprintf(log_file, "Innochecksum: "
1289 "checking page %" PRIuMAX "\n",
1290 do_page);
1291 }
1292 }
1293 } else {
1294 if (is_log_enabled) {
1295 fprintf(log_file, "Innochecksum: checking "
1296 "pages in range %" PRIuMAX " to %" PRIuMAX "\n",
1297 start_page, use_end_page ?
1298 end_page : (pages - 1));
1299 }
1300 }
1301
1302 /* seek to the necessary position */
1303 if (start_page) {
1304 if (!read_from_stdin) {
1305 /* If read is not from stdin, we can use
1306 fseeko() to position the file pointer to
1307 the desired page. */
1308 partial_page_read = false;
1309
1310 offset = (off_t) start_page
1311 * (off_t) page_size.physical();
1312 #ifdef _WIN32
1313 if (_fseeki64(fil_in, offset, SEEK_SET)) {
1314 #else
1315 if (fseeko(fil_in, offset, SEEK_SET)) {
1316 #endif /* _WIN32 */
1317 perror("Error: Unable to seek to "
1318 "necessary offset");
1319
1320 DBUG_RETURN(1);
1321 }
1322 /* Save the current file pointer in
1323 pos variable. */
1324 if (0 != fgetpos(fil_in, &pos)) {
1325 perror("fgetpos");
1326
1327 DBUG_RETURN(1);
1328 }
1329 } else {
1330
1331 ulong count = 0;
1332
1333 while (!feof(fil_in)) {
1334 if (start_page == count) {
1335 break;
1336 }
1337 /* We read a part of page to find the
1338 minimum page size. We cannot reset
1339 the file pointer to the beginning of
1340 the page if we are reading from stdin
1341 (fseeko() on stdin doesn't work). So
1342 read only the remaining part of page,
1343 if partial_page_read is enable. */
1344 bytes = read_file(buf.begin(),
1345 partial_page_read,
1346 static_cast<ulong>(
1347 page_size.physical()),
1348 fil_in);
1349
1350 partial_page_read = false;
1351 count++;
1352
1353 if (!bytes || feof(fil_in)) {
1354 fprintf(stderr, "Error: Unable "
1355 "to seek to necessary "
1356 "offset");
1357
1358 DBUG_RETURN(1);
1359 }
1360 }
1361 }
1362 }
1363
1364 if (page_type_dump) {
1365 fprintf(fil_page_type,
1366 "\n\nFilename::%s\n", filename);
1367 fprintf(fil_page_type,
1368 "========================================"
1369 "======================================\n");
1370 fprintf(fil_page_type,
1371 "\tPAGE_NO\t\t|\t\tPAGE_TYPE\t\t"
1372 "\t|\tEXTRA INFO\n");
1373 fprintf(fil_page_type,
1374 "========================================"
1375 "======================================\n");
1376 }
1377
1378 /* main checksumming loop */
1379 cur_page_num = start_page;
1380 lastt = 0;
1381 while (!feof(fil_in)) {
1382
1383 bytes = read_file(buf.begin(), partial_page_read,
1384 static_cast<ulong>(
1385 page_size.physical()), fil_in);
1386 partial_page_read = false;
1387
1388 if (!bytes && feof(fil_in)) {
1389 break;
1390 }
1391
1392 if (ferror(fil_in)) {
1393 fprintf(stderr, "Error reading " ULINTPF " bytes",
1394 page_size.physical());
1395 perror(" ");
1396
1397 DBUG_RETURN(1);
1398 }
1399
1400 if (bytes != page_size.physical()) {
1401 fprintf(stderr, "Error: bytes read (%lu) "
1402 "doesn't match page size (" ULINTPF ")\n",
1403 bytes, page_size.physical());
1404 DBUG_RETURN(1);
1405 }
1406
1407 if (is_system_tablespace) {
1408 /* enable when page is double write buffer.*/
1409 skip_page = is_page_doublewritebuffer(buf.begin());
1410 } else {
1411 skip_page = false;
1412
1413 if (!page_decompress(buf.begin(), tbuf, page_size)) {
1414
1415 fprintf(stderr,
1416 "Page decompress failed");
1417
1418 DBUG_RETURN(1);
1419 }
1420 }
1421
1422 /* If no-check is enabled, skip the
1423 checksum verification.*/
1424 if (!no_check) {
1425 /* Checksum verification */
1426 if (!skip_page) {
1427 is_corrupted = is_page_corrupted(
1428 buf.begin(), page_size);
1429
1430 if (is_corrupted) {
1431 fprintf(stderr, "Fail: page "
1432 "%" PRIuMAX " invalid\n",
1433 cur_page_num);
1434
1435 mismatch_count++;
1436
1437 if(mismatch_count > allow_mismatches) {
1438 fprintf(stderr,
1439 "Exceeded the "
1440 "maximum allowed "
1441 "checksum mismatch "
1442 "count::%" PRIuMAX "\n",
1443 allow_mismatches);
1444
1445 DBUG_RETURN(1);
1446 }
1447 }
1448 }
1449 }
1450
1451 /* Rewrite checksum */
1452 if (do_write
1453 && !write_file(filename, fil_in, buf.begin(),
1454 page_size.is_compressed(), &pos,
1455 static_cast<ulong>(page_size.physical()))) {
1456
1457 DBUG_RETURN(1);
1458 }
1459
1460 /* end if this was the last page we were supposed to check */
1461 if (use_end_page && (cur_page_num >= end_page)) {
1462 break;
1463 }
1464
1465 if (page_type_summary || page_type_dump) {
1466 parse_page(buf.begin(), fil_page_type);
1467 }
1468
1469 /* do counter increase and progress printing */
1470 cur_page_num++;
1471 if (verbose && !read_from_stdin) {
1472 if ((cur_page_num % 64) == 0) {
1473 now = time(0);
1474 if (!lastt) {
1475 lastt= now;
1476 }
1477 if (now - lastt >= 1
1478 && is_log_enabled) {
1479 fprintf(log_file, "page %" PRIuMAX " "
1480 "okay: %.3f%% done\n",
1481 (cur_page_num - 1),
1482 (float) cur_page_num / pages * 100);
1483 lastt = now;
1484 }
1485 }
1486 }
1487 }
1488
1489 if (!read_from_stdin) {
1490 /* flcose() will flush the data and release the lock if
1491 any acquired. */
1492 fclose(fil_in);
1493 }
1494
1495 /* Enabled for page type summary. */
1496 if (page_type_summary) {
1497 if (!read_from_stdin) {
1498 fprintf(stdout, "\nFile::%s",filename);
1499 print_summary(stdout);
1500 } else {
1501 print_summary(stderr);
1502 }
1503 }
1504 }
1505
1506 if (is_log_enabled) {
1507 fclose(log_file);
1508 }
1509
1510 DBUG_RETURN(0);
1511 }
1512