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