1 /*
2 Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
3 Copyright (c) 2014, 2021, MariaDB Corporation.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
17 */
18
19 /*
20 InnoDB offline file checksum utility. 85% of the code in this utility
21 is included from the InnoDB codebase.
22
23 The final 15% was originally written by Mark Smith of Danga
24 Interactive, Inc. <junior@danga.com>
25
26 Published with a permission.
27 */
28
29 #include <my_global.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <time.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #ifndef __WIN__
36 # include <unistd.h>
37 #endif
38 #include <my_getopt.h>
39 #include <m_string.h>
40 #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
41
42 /* Only parts of these files are included from the InnoDB codebase.
43 The parts not included are excluded by #ifndef UNIV_INNOCHECKSUM. */
44
45 typedef void fil_space_t;
46
47 #include "page0size.h"
48
49 #define FLST_BASE_NODE_SIZE (4 + 2 * FIL_ADDR_SIZE)
50 #define FLST_NODE_SIZE (2 * FIL_ADDR_SIZE)
51 #define FSEG_PAGE_DATA FIL_PAGE_DATA
52 #define FSEG_HEADER_SIZE 10
53 #define UT_BITS_IN_BYTES(b) (((b) + 7) / 8)
54
55 #include "ut0ut.h"
56 #include "ut0byte.h"
57 #include "mtr0types.h"
58 #include "mach0data.h"
59 #include "fsp0types.h"
60 #include "rem0rec.h"
61 #include "buf0checksum.h" /* buf_calc_page_*() */
62 #include "buf0buf.h" /* buf_page_is_corrupted */
63 #include "fil0fil.h" /* FIL_* */
64 #include "page0page.h" /* PAGE_* */
65 #include "page0zip.h" /* page_zip_*() */
66 #include "trx0undo.h" /* TRX_* */
67 #include "fsp0fsp.h" /* fsp_flags_get_page_size() &
68 fsp_flags_get_zip_size() */
69 #include "ut0crc32.h" /* ut_crc32_init() */
70 #include "fsp0pagecompress.h" /* fil_get_compression_alg_name */
71 #include "fil0crypt.h" /* fil_space_verify_crypt_checksum */
72
73 #include <string.h>
74
75 #ifdef UNIV_NONINL
76 # include "fsp0fsp.inl"
77 # include "mach0data.inl"
78 # include "ut0rnd.inl"
79 #endif
80
81 #ifndef PRIuMAX
82 #define PRIuMAX "llu"
83 #endif
84
85 /* Global variables */
86 static bool verbose;
87 static bool just_count;
88 static uint32_t start_page;
89 static uint32_t end_page;
90 static uint32_t do_page;
91 static bool use_end_page;
92 static bool do_one_page;
93 static my_bool do_leaf;
94 static my_bool per_page_details;
95 static ulint n_merge;
96 extern ulong srv_checksum_algorithm;
97 static ulint physical_page_size; /* Page size in bytes on disk. */
98 static ulint logical_page_size; /* Page size when uncompressed. */
99 ulong srv_page_size;
100 ulong srv_page_size_shift;
101 page_size_t univ_page_size(0, 0, false);
102 /* Current page number (0 based). */
103 uint32_t cur_page_num;
104 /* Current space. */
105 uint32_t cur_space;
106 /* Skip the checksum verification. */
107 static bool no_check;
108 /* Enabled for strict checksum verification. */
109 bool strict_verify = 0;
110 /* Enabled for rewrite checksum. */
111 static bool do_write;
112 /* Mismatches count allowed (0 by default). */
113 static unsigned long long allow_mismatches=0;
114 static bool page_type_summary;
115 static bool page_type_dump;
116 /* Store filename for page-type-dump option. */
117 char* page_dump_filename = 0;
118 /* skip the checksum verification & rewrite if page is doublewrite buffer. */
119 static bool skip_page = 0;
120 const char *dbug_setting = "FALSE";
121 char* log_filename = NULL;
122 /* User defined filename for logging. */
123 FILE* log_file = NULL;
124 /* Enabled for log write option. */
125 static bool is_log_enabled = false;
126
127 #ifndef _WIN32
128 /* advisory lock for non-window system. */
129 struct flock lk;
130 #endif /* _WIN32 */
131
132 /* Strict check algorithm name. */
133 static ulong strict_check;
134 /* Rewrite checksum algorithm name. */
135 static ulong write_check;
136
137 /* Innodb page type. */
138 struct innodb_page_type {
139 int n_undo_state_active;
140 int n_undo_state_cached;
141 int n_undo_state_to_purge;
142 int n_undo_state_prepared;
143 int n_undo_state_other;
144 int n_undo;
145 int n_fil_page_index;
146 int n_fil_page_undo_log;
147 int n_fil_page_inode;
148 int n_fil_page_ibuf_free_list;
149 int n_fil_page_ibuf_bitmap;
150 int n_fil_page_type_sys;
151 int n_fil_page_type_trx_sys;
152 int n_fil_page_type_fsp_hdr;
153 int n_fil_page_type_allocated;
154 int n_fil_page_type_xdes;
155 int n_fil_page_type_blob;
156 int n_fil_page_type_zblob;
157 int n_fil_page_type_other;
158 int n_fil_page_type_zblob2;
159 int n_fil_page_type_page_compressed;
160 int n_fil_page_type_page_compressed_encrypted;
161 } page_type;
162
163 /* Possible values for "--strict-check" for strictly verify checksum
164 and "--write" for rewrite checksum. */
165 static const char *innochecksum_algorithms[] = {
166 "crc32",
167 "crc32",
168 "innodb",
169 "innodb",
170 "none",
171 "none",
172 NullS
173 };
174
175 /* Used to define an enumerate type of the "innochecksum algorithm". */
176 static TYPELIB innochecksum_algorithms_typelib = {
177 array_elements(innochecksum_algorithms)-1,"",
178 innochecksum_algorithms, NULL
179 };
180
181 #define SIZE_RANGES_FOR_PAGE 10
182 #define NUM_RETRIES 3
183 #define DEFAULT_RETRY_DELAY 1000000
184
185 struct per_page_stats {
186 ulint n_recs;
187 ulint data_size;
188 ulint left_page_no;
189 ulint right_page_no;
per_page_statsper_page_stats190 per_page_stats(ulint n, ulint data, ulint left, ulint right) :
191 n_recs(n), data_size(data), left_page_no(left), right_page_no(right) {}
per_page_statsper_page_stats192 per_page_stats() : n_recs(0), data_size(0), left_page_no(0), right_page_no(0) {}
193 };
194
195 struct per_index_stats {
196 unsigned long long pages;
197 unsigned long long leaf_pages;
198 ulint first_leaf_page;
199 ulint count;
200 ulint free_pages;
201 ulint max_data_size;
202 unsigned long long total_n_recs;
203 unsigned long long total_data_bytes;
204
205 /*!< first element for empty pages,
206 last element for pages with more than logical_page_size */
207 unsigned long long pages_in_size_range[SIZE_RANGES_FOR_PAGE+2];
208
209 std::map<unsigned long long, per_page_stats> leaves;
210
per_index_statsper_index_stats211 per_index_stats():pages(0), leaf_pages(0), first_leaf_page(0),
212 count(0), free_pages(0), max_data_size(0), total_n_recs(0),
213 total_data_bytes(0)
214 {
215 memset(pages_in_size_range, 0, sizeof(pages_in_size_range));
216 }
217 };
218
219 std::map<unsigned long long, per_index_stats> index_ids;
220
print_index_leaf_stats(unsigned long long id,const per_index_stats & index,FILE * fil_out)221 void print_index_leaf_stats(
222 unsigned long long id,
223 const per_index_stats& index,
224 FILE* fil_out)
225
226 {
227 ulint page_no = index.first_leaf_page;
228 std::map<unsigned long long, per_page_stats>::const_iterator it_page = index.leaves.find(page_no);
229 fprintf(fil_out, "\nindex: %llu leaf page stats: n_pages = %llu\n",
230 id, index.leaf_pages);
231 fprintf(fil_out, "page_no\tdata_size\tn_recs\n");
232 while (it_page != index.leaves.end()) {
233 const per_page_stats& stat = it_page->second;
234 fprintf(fil_out, "%llu\t" ULINTPF "\t" ULINTPF "\n", it_page->first, stat.data_size, stat.n_recs);
235 page_no = stat.right_page_no;
236 it_page = index.leaves.find(page_no);
237 }
238 }
239
defrag_analysis(unsigned long long id,const per_index_stats & index,FILE * fil_out)240 void defrag_analysis(
241 unsigned long long id,
242 const per_index_stats& index,
243 FILE* fil_out)
244 {
245 // TODO: make it work for compressed pages too
246 std::map<unsigned long long, per_page_stats>::const_iterator it = index.leaves.find(index.first_leaf_page);
247 ulint n_pages = 0;
248 ulint n_leaf_pages = 0;
249 while (it != index.leaves.end()) {
250 ulint data_size_total = 0;
251 for (ulong i = 0; i < n_merge; i++) {
252 const per_page_stats& stat = it->second;
253 n_leaf_pages ++;
254 data_size_total += stat.data_size;
255 it = index.leaves.find(stat.right_page_no);
256 if (it == index.leaves.end()) {
257 break;
258 }
259 }
260
261 if (index.max_data_size) {
262 n_pages += data_size_total / index.max_data_size;
263 if (data_size_total % index.max_data_size != 0) {
264 n_pages += 1;
265 }
266 }
267 }
268
269 if (index.leaf_pages) {
270 fprintf(fil_out, "count = " ULINTPF " free = " ULINTPF "\n", index.count, index.free_pages);
271 }
272
273 if (!n_leaf_pages) {
274 n_leaf_pages = 1;
275 }
276
277 fprintf(fil_out, "%llu\t\t%llu\t\t" ULINTPF "\t\t" ULINTPF "\t\t" ULINTPF "\t\t%.2f\t" ULINTPF "\n",
278 id, index.leaf_pages, n_leaf_pages, n_merge, n_pages,
279 1.0 - (double)n_pages / (double)n_leaf_pages, index.max_data_size);
280 }
281
print_leaf_stats(FILE * fil_out)282 void print_leaf_stats(
283 FILE* fil_out)
284 {
285 fprintf(fil_out, "\n**************************************************\n");
286 fprintf(fil_out, "index_id\t#leaf_pages\t#actual_leaf_pages\tn_merge\t"
287 "#leaf_after_merge\tdefrag\n");
288 for (std::map<unsigned long long, per_index_stats>::const_iterator it = index_ids.begin();
289 it != index_ids.end(); it++) {
290 const per_index_stats& index = it->second;
291
292 if (verbose) {
293 print_index_leaf_stats(it->first, index, fil_out);
294 }
295
296 if (n_merge) {
297 defrag_analysis(it->first, index, fil_out);
298 }
299 }
300 }
301
302 /** Get the page size of the filespace from the filespace header.
303 @param[in] buf buffer used to read the page.
304 @return page size */
305 static
306 const page_size_t
get_page_size(byte * buf)307 get_page_size(
308 byte* buf)
309 {
310 const unsigned flags = mach_read_from_4(buf + FIL_PAGE_DATA
311 + FSP_SPACE_FLAGS);
312
313 const ulong ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags);
314
315 srv_page_size_shift = ssize
316 ? UNIV_ZIP_SIZE_SHIFT_MIN - 1 + ssize
317 : UNIV_PAGE_SIZE_SHIFT_ORIG;
318
319 srv_page_size = 1U << srv_page_size_shift;
320
321 univ_page_size.copy_from(
322 page_size_t(srv_page_size, srv_page_size, false));
323
324 return(page_size_t(flags));
325 }
326
327 #ifdef _WIN32
328 /***********************************************//*
329 @param [in] error error no. from the getLastError().
330
331 @retval error message corresponding to error no.
332 */
333 static
334 char*
error_message(int error)335 error_message(
336 int error)
337 {
338 static char err_msg[1024] = {'\0'};
339 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
340 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
341 (LPTSTR)err_msg, sizeof(err_msg), NULL );
342
343 return (err_msg);
344 }
345 #endif /* _WIN32 */
346
347 /***********************************************//*
348 @param>>_______[in] name>_____name of file.
349 @retval file pointer; file pointer is NULL when error occurred.
350 */
351
352 FILE*
open_file(const char * name)353 open_file(
354 const char* name)
355 {
356 int fd; /* file descriptor. */
357 FILE* fil_in;
358 #ifdef _WIN32
359 HANDLE hFile; /* handle to open file. */
360 DWORD access; /* define access control */
361 int flags = 0; /* define the mode for file
362 descriptor */
363
364 if (do_write) {
365 access = GENERIC_READ | GENERIC_WRITE;
366 flags = _O_RDWR | _O_BINARY;
367 } else {
368 access = GENERIC_READ;
369 flags = _O_RDONLY | _O_BINARY;
370 }
371
372 /* CreateFile() also provide advisory lock with the usage of
373 access and share mode of the file.*/
374 hFile = CreateFile(
375 (LPCTSTR) name, access, 0L, NULL,
376 OPEN_EXISTING, NULL, NULL);
377
378 if (hFile == INVALID_HANDLE_VALUE) {
379 /* print the error message. */
380 fprintf(stderr, "Filename::%s %s\n", name,
381 error_message(GetLastError()));
382
383 return (NULL);
384 }
385
386 /* get the file descriptor. */
387 fd= _open_osfhandle((intptr_t)hFile, flags);
388 #else /* _WIN32 */
389
390 int create_flag;
391 /* define the advisory lock and open file mode. */
392 if (do_write) {
393 create_flag = O_RDWR;
394 lk.l_type = F_WRLCK;
395 } else {
396 create_flag = O_RDONLY;
397 lk.l_type = F_RDLCK;
398 }
399
400 fd = open(name, create_flag);
401
402 lk.l_whence = SEEK_SET;
403 lk.l_start = lk.l_len = 0;
404
405 if (fcntl(fd, F_SETLK, &lk) == -1) {
406 fprintf(stderr, "Error: Unable to lock file::"
407 " %s\n", name);
408 perror("fcntl");
409 return (NULL);
410 }
411 #endif /* _WIN32 */
412
413 if (do_write) {
414 fil_in = fdopen(fd, "rb+");
415 } else {
416 fil_in = fdopen(fd, "rb");
417 }
418
419 return (fil_in);
420 }
421
422 /************************************************************//*
423 Read the content of file
424
425 @param [in,out] buf read the file in buffer
426 @param [in] partial_page_read enable when to read the
427 remaining buffer for first page.
428 @param [in] physical_page_size Physical/Commpressed page size.
429 @param [in,out] fil_in file pointer created for the
430 tablespace.
431 @retval no. of bytes read.
432 */
read_file(byte * buf,bool partial_page_read,ulint physical_page_size,FILE * fil_in)433 ulint read_file(
434 byte* buf,
435 bool partial_page_read,
436 ulint physical_page_size,
437 FILE* fil_in)
438 {
439 ulint bytes = 0;
440
441 DBUG_ASSERT(physical_page_size >= UNIV_ZIP_SIZE_MIN);
442
443 if (partial_page_read) {
444 buf += UNIV_ZIP_SIZE_MIN;
445 physical_page_size -= UNIV_ZIP_SIZE_MIN;
446 bytes = UNIV_ZIP_SIZE_MIN;
447 }
448
449 bytes += ulint(fread(buf, 1, physical_page_size, fil_in));
450
451 return bytes;
452 }
453
454 /** Check whether the page contains all zeroes.
455 @param[in] buf page
456 @param[in] size physical size of the page
457 @return true if the page is all zeroes; else false */
is_page_all_zeroes(byte * buf,ulint size)458 static bool is_page_all_zeroes(
459 byte* buf,
460 ulint size)
461 {
462 /* On pages that are not all zero, the page number
463 must match. */
464 const ulint* p = reinterpret_cast<const ulint*>(buf);
465 const ulint* const end = reinterpret_cast<const ulint*>(buf + size);
466 do {
467 if (*p++) {
468 return false;
469 }
470 } while (p != end);
471
472 return true;
473 }
474
475 /** Check if page is corrupted or not.
476 @param[in] buf page frame
477 @param[in] page_size page size
478 @param[in] is_encrypted true if page0 contained cryp_data
479 with crypt_scheme encrypted
480 @param[in] is_compressed true if page0 fsp_flags contained
481 page compression flag
482 @retval true if page is corrupted otherwise false. */
483 static
484 bool
is_page_corrupted(byte * buf,const page_size_t & page_size,bool is_encrypted,bool is_compressed)485 is_page_corrupted(
486 byte* buf,
487 const page_size_t& page_size,
488 bool is_encrypted,
489 bool is_compressed)
490 {
491
492 /* enable if page is corrupted. */
493 bool is_corrupted;
494 /* use to store LSN values. */
495 uint32_t logseq;
496 uint32_t logseqfield;
497 ulint page_type = mach_read_from_2(buf+FIL_PAGE_TYPE);
498 uint32_t key_version = mach_read_from_4(buf+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
499 uint32_t space_id = mach_read_from_4(
500 buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
501
502 if (mach_read_from_4(buf + FIL_PAGE_OFFSET) != cur_page_num
503 || space_id != cur_space) {
504 /* On pages that are not all zero, the page number
505 must match. */
506 if (is_page_all_zeroes(buf, page_size.physical())) {
507 return false;
508 }
509
510 if (is_log_enabled) {
511 fprintf(log_file,
512 "page id mismatch space::" UINT32PF
513 " page::" UINT32PF " \n",
514 space_id, cur_page_num);
515 }
516
517 return true;
518 }
519
520 /* We can't trust only a page type, thus we take account
521 also fsp_flags or crypt_data on page 0 */
522 if ((page_type == FIL_PAGE_PAGE_COMPRESSED && is_compressed) ||
523 (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED &&
524 is_compressed && is_encrypted)) {
525 /* Page compressed tables do not contain post compression
526 checksum. */
527 return (false);
528 }
529
530 if (page_size.is_compressed()) {
531 /* check the stored log sequence numbers
532 for uncompressed tablespace. */
533 logseq = mach_read_from_4(buf + FIL_PAGE_LSN + 4);
534 logseqfield = mach_read_from_4(
535 buf + page_size.logical() -
536 FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
537
538 if (is_log_enabled) {
539 fprintf(log_file,
540 "space::" UINT32PF " page::" UINT32PF
541 "; log sequence number:first = " UINT32PF
542 "; second = " UINT32PF "\n",
543 space_id, cur_page_num, logseq, logseqfield);
544 if (logseq != logseqfield) {
545 fprintf(log_file,
546 "Fail; space::" UINT32PF
547 " page::" UINT32PF
548 " invalid (fails log "
549 "sequence number check)\n",
550 space_id, cur_page_num);
551 }
552 }
553 }
554
555 /* Again we can't trust only FIL_PAGE_FILE_FLUSH_LSN field
556 now repurposed as FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION,
557 we need to check also crypt_data contents.
558
559 If page is encrypted, use different checksum calculation
560 as innochecksum can't decrypt pages. Note that some old InnoDB
561 versions did not initialize FIL_PAGE_FILE_FLUSH_LSN field
562 so if crypt checksum does not match we verify checksum using
563 normal method. */
564 if (is_encrypted && key_version != 0) {
565 is_corrupted = !fil_space_verify_crypt_checksum(buf,
566 page_size);
567 if (is_corrupted && log_file) {
568 fprintf(log_file,
569 "[page id: space=" UINT32PF
570 ", page_number=" UINT32PF "] may be corrupted;"
571 " key_version=" UINT32PF "\n",
572 space_id, cur_page_num,
573 mach_read_from_4(
574 FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
575 + buf));
576 }
577 } else {
578 is_corrupted = true;
579 }
580
581 if (is_corrupted) {
582 is_corrupted = buf_page_is_corrupted(
583 true, buf, page_size, NULL);
584 }
585
586 return(is_corrupted);
587 }
588
589 /********************************************//*
590 Check if page is doublewrite buffer or not.
591 @param [in] page buffer page
592
593 @retval true if page is doublewrite buffer otherwise false.
594 */
595 static
596 bool
is_page_doublewritebuffer(const byte * page)597 is_page_doublewritebuffer(
598 const byte* page)
599 {
600 if ((cur_page_num >= FSP_EXTENT_SIZE)
601 && (cur_page_num < FSP_EXTENT_SIZE * 3)) {
602 /* page is doublewrite buffer. */
603 return (true);
604 }
605
606 return (false);
607 }
608
609 /*******************************************************//*
610 Check if page is empty or not.
611 @param [in] page page to checked for empty.
612 @param [in] len size of page.
613
614 @retval true if page is empty.
615 @retval false if page is not empty.
616 */
617 static
618 bool
is_page_empty(const byte * page,size_t len)619 is_page_empty(
620 const byte* page,
621 size_t len)
622 {
623 while (len--) {
624 if (*page++) {
625 return (false);
626 }
627 }
628
629 return (true);
630 }
631
632 /********************************************************************//**
633 Rewrite the checksum for the page.
634 @param [in/out] page page buffer
635 @param [in] physical_page_size page size in bytes on disk.
636 @param [in] iscompressed Is compressed/Uncompressed Page.
637
638 @retval true : do rewrite
639 @retval false : skip the rewrite as checksum stored match with
640 calculated or page is doublwrite buffer.
641 */
642
643 bool
update_checksum(byte * page,ulong physical_page_size,bool iscompressed)644 update_checksum(
645 byte* page,
646 ulong physical_page_size,
647 bool iscompressed)
648 {
649 ib_uint32_t checksum = 0;
650 byte stored1[4]; /* get FIL_PAGE_SPACE_OR_CHKSUM field checksum */
651 byte stored2[4]; /* get FIL_PAGE_END_LSN_OLD_CHKSUM field checksum */
652
653 ut_ad(page);
654 /* If page is doublewrite buffer, skip the rewrite of checksum. */
655 if (skip_page) {
656 return (false);
657 }
658
659 memcpy(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4);
660 memcpy(stored2, page + physical_page_size -
661 FIL_PAGE_END_LSN_OLD_CHKSUM, 4);
662
663 /* Check if page is empty, exclude the checksum field */
664 if (is_page_empty(page + 4, physical_page_size - 12)
665 && is_page_empty(page + physical_page_size - 4, 4)) {
666
667 memset(page + FIL_PAGE_SPACE_OR_CHKSUM, 0, 4);
668 memset(page + physical_page_size -
669 FIL_PAGE_END_LSN_OLD_CHKSUM, 0, 4);
670
671 goto func_exit;
672 }
673
674 if (iscompressed) {
675 /* page is compressed */
676 checksum = page_zip_calc_checksum(
677 page, physical_page_size,
678 static_cast<srv_checksum_algorithm_t>(write_check));
679
680 mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, checksum);
681 if (is_log_enabled) {
682 fprintf(log_file, "page::" UINT32PF "; Updated checksum ="
683 " " UINT32PF "\n", cur_page_num, checksum);
684 }
685
686 } else {
687 /* page is uncompressed. */
688
689 /* Store the new formula checksum */
690 switch ((srv_checksum_algorithm_t) write_check) {
691
692 case SRV_CHECKSUM_ALGORITHM_CRC32:
693 case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
694 checksum = buf_calc_page_crc32(page);
695 break;
696
697 case SRV_CHECKSUM_ALGORITHM_INNODB:
698 case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
699 checksum = (ib_uint32_t)
700 buf_calc_page_new_checksum(page);
701 break;
702
703 case SRV_CHECKSUM_ALGORITHM_NONE:
704 case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
705 checksum = BUF_NO_CHECKSUM_MAGIC;
706 break;
707 /* no default so the compiler will emit a warning if new
708 enum is added and not handled here */
709 }
710
711 mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, checksum);
712 if (is_log_enabled) {
713 fprintf(log_file, "page::" UINT32PF "; Updated checksum field1"
714 " = " UINT32PF "\n", cur_page_num, checksum);
715 }
716
717 if (write_check == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB
718 || write_check == SRV_CHECKSUM_ALGORITHM_INNODB) {
719 checksum = (ib_uint32_t)
720 buf_calc_page_old_checksum(page);
721 }
722
723 mach_write_to_4(page + physical_page_size -
724 FIL_PAGE_END_LSN_OLD_CHKSUM,checksum);
725
726 if (is_log_enabled) {
727 fprintf(log_file, "page::" UINT32PF "; Updated checksum "
728 "field2 = " UINT32PF "\n", cur_page_num, checksum);
729 }
730
731 }
732
733 func_exit:
734 /* The following code is to check the stored checksum with the
735 calculated checksum. If it matches, then return FALSE to skip
736 the rewrite of checksum, otherwise return TRUE. */
737 if (iscompressed) {
738 if (!memcmp(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4)) {
739 return (false);
740 }
741 return (true);
742 }
743
744 if (!memcmp(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4)
745 && !memcmp(stored2, page + physical_page_size -
746 FIL_PAGE_END_LSN_OLD_CHKSUM, 4)) {
747 return (false);
748
749 }
750
751 return (true);
752 }
753
754 /**
755 Write the content to the file
756 @param[in] filename name of the file.
757 @param[in,out] file file pointer where content
758 have to be written
759 @param[in] buf file buffer read
760 @param[in] compressed Enabled if tablespace is
761 compressed.
762 @param[in,out] pos current file position.
763 @param[in] page_size page size in bytes on disk.
764
765 @retval true if successfully written
766 @retval false if a non-recoverable error occurred
767 */
768 static
769 bool
write_file(const char * filename,FILE * file,byte * buf,bool compressed,fpos_t * pos,ulong page_size)770 write_file(
771 const char* filename,
772 FILE* file,
773 byte* buf,
774 bool compressed,
775 fpos_t* pos,
776 ulong page_size)
777 {
778 bool do_update;
779
780 do_update = update_checksum(buf, page_size, compressed);
781
782 if (file != stdin) {
783 if (do_update) {
784 /* Set the previous file pointer position
785 saved in pos to current file position. */
786 if (0 != fsetpos(file, pos)) {
787 perror("fsetpos");
788 return(false);
789 }
790 } else {
791 /* Store the current file position in pos */
792 if (0 != fgetpos(file, pos)) {
793 perror("fgetpos");
794 return(false);
795 }
796 return(true);
797 }
798 }
799
800 if (page_size
801 != fwrite(buf, 1, page_size, file == stdin ? stdout : file)) {
802 fprintf(stderr, "Failed to write page::" UINT32PF " to %s: %s\n",
803 cur_page_num, filename, strerror(errno));
804
805 return(false);
806 }
807 if (file != stdin) {
808 fflush(file);
809 /* Store the current file position in pos */
810 if (0 != fgetpos(file, pos)) {
811 perror("fgetpos");
812 return(false);
813 }
814 }
815
816 return(true);
817 }
818
819 // checks using current xdes page whether the page is free
is_page_free(const byte * xdes,page_size_t page_size,uint32_t page_no)820 static inline bool is_page_free(const byte *xdes, page_size_t page_size,
821 uint32_t page_no)
822 {
823 const byte *des=
824 xdes + XDES_ARR_OFFSET +
825 XDES_SIZE * ((page_no & (page_size.physical() - 1)) / FSP_EXTENT_SIZE);
826 return xdes_get_bit(des, XDES_FREE_BIT, page_no % FSP_EXTENT_SIZE);
827 }
828
829 /*
830 Parse the page and collect/dump the information about page type
831 @param [in] page buffer page
832 @param [out] xdes extend descriptor page
833 @param [in] file file for diagnosis.
834 @param [in] page_size page_size
835 @param [in] is_encrypted tablespace is encrypted
836 */
837 void
parse_page(const byte * page,byte * xdes,FILE * file,const page_size_t & page_size,bool is_encrypted)838 parse_page(
839 const byte* page,
840 byte* xdes,
841 FILE* file,
842 const page_size_t& page_size,
843 bool is_encrypted)
844 {
845 unsigned long long id;
846 uint16_t undo_page_type;
847 char str[20]={'\0'};
848 ulint n_recs;
849 uint32_t page_no, left_page_no, right_page_no;
850 ulint data_bytes;
851 bool is_leaf;
852 ulint size_range_id;
853
854 /* Check whether page is doublewrite buffer. */
855 if(skip_page) {
856 strcpy(str, "Double_write_buffer");
857 } else {
858 strcpy(str, "-");
859 }
860
861 switch (mach_read_from_2(page + FIL_PAGE_TYPE)) {
862
863 case FIL_PAGE_INDEX: {
864 uint32_t key_version = mach_read_from_4(page + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
865 page_type.n_fil_page_index++;
866
867 /* If page is encrypted we can't read index header */
868 if (!is_encrypted) {
869 id = mach_read_from_8(page + PAGE_HEADER + PAGE_INDEX_ID);
870 n_recs = mach_read_from_2(page + PAGE_HEADER + PAGE_N_RECS);
871 page_no = mach_read_from_4(page + FIL_PAGE_OFFSET);
872 left_page_no = mach_read_from_4(page + FIL_PAGE_PREV);
873 right_page_no = mach_read_from_4(page + FIL_PAGE_NEXT);
874 ulint is_comp = mach_read_from_2(page + PAGE_HEADER + PAGE_N_HEAP) & 0x8000;
875 ulint level = mach_read_from_2(page + PAGE_HEADER + PAGE_LEVEL);
876 ulint garbage = mach_read_from_2(page + PAGE_HEADER + PAGE_GARBAGE);
877
878
879 data_bytes = (ulint)(mach_read_from_2(page + PAGE_HEADER + PAGE_HEAP_TOP)
880 - (is_comp
881 ? PAGE_NEW_SUPREMUM_END
882 : PAGE_OLD_SUPREMUM_END)
883 - garbage);
884
885 is_leaf = (!*(const uint16*) (page + (PAGE_HEADER + PAGE_LEVEL)));
886
887 if (page_type_dump) {
888 fprintf(file, "#::" UINT32PF "\t\t|\t\tIndex page\t\t\t|"
889 "\tindex id=%llu,", cur_page_num, id);
890
891 fprintf(file,
892 " page level=" ULINTPF
893 ", No. of records=" ULINTPF
894 ", garbage=" ULINTPF ", %s\n",
895 level, n_recs, garbage, str);
896 }
897
898 size_range_id = (data_bytes * SIZE_RANGES_FOR_PAGE
899 + page_size.logical() - 1) /
900 page_size.logical();
901
902 if (size_range_id > SIZE_RANGES_FOR_PAGE + 1) {
903 /* data_bytes is bigger than logical_page_size */
904 size_range_id = SIZE_RANGES_FOR_PAGE + 1;
905 }
906 if (per_page_details) {
907 printf("index id=%llu page " UINT32PF " leaf %d n_recs " ULINTPF " data_bytes " ULINTPF
908 "\n", id, page_no, is_leaf, n_recs, data_bytes);
909 }
910 /* update per-index statistics */
911 {
912 per_index_stats &index = index_ids[id];
913 if (is_page_free(xdes, page_size, page_no)) {
914 index.free_pages++;
915 return;
916 }
917
918 index.pages++;
919
920 if (is_leaf) {
921 index.leaf_pages++;
922 if (data_bytes > index.max_data_size) {
923 index.max_data_size = data_bytes;
924 }
925 struct per_page_stats pp(n_recs, data_bytes,
926 left_page_no, right_page_no);
927
928 index.leaves[page_no] = pp;
929
930 if (left_page_no == ULINT32_UNDEFINED) {
931 index.first_leaf_page = page_no;
932 index.count++;
933 }
934 }
935
936 index.total_n_recs += n_recs;
937 index.total_data_bytes += data_bytes;
938 index.pages_in_size_range[size_range_id] ++;
939 }
940 } else {
941 fprintf(file, "#::" UINT32PF "\t\t|\t\tEncrypted Index page\t\t\t|"
942 "\tkey_version " UINT32PF ",%s\n", cur_page_num, key_version, str);
943 }
944
945 break;
946 }
947 case FIL_PAGE_UNDO_LOG:
948 page_type.n_fil_page_undo_log++;
949 undo_page_type = mach_read_from_2(page +
950 TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE);
951 if (page_type_dump) {
952 fprintf(file, "#::" UINT32PF "\t\t|\t\tUndo log page\t\t\t|",
953 cur_page_num);
954 }
955 page_type.n_undo++;
956 undo_page_type = mach_read_from_2(page + TRX_UNDO_SEG_HDR +
957 TRX_UNDO_STATE);
958 switch (undo_page_type) {
959 case TRX_UNDO_ACTIVE:
960 page_type.n_undo_state_active++;
961 if (page_type_dump) {
962 fprintf(file, ", %s", "Undo log of "
963 "an active transaction");
964 }
965 break;
966
967 case TRX_UNDO_CACHED:
968 page_type.n_undo_state_cached++;
969 if (page_type_dump) {
970 fprintf(file, ", %s", "Page is "
971 "cached for quick reuse");
972 }
973 break;
974
975 case TRX_UNDO_TO_PURGE:
976 page_type.n_undo_state_to_purge++;
977 if (page_type_dump) {
978 fprintf(file, ", %s", "Will be "
979 "freed in purge when all undo"
980 "data in it is removed");
981 }
982 break;
983
984 case TRX_UNDO_PREPARED:
985 page_type.n_undo_state_prepared++;
986 if (page_type_dump) {
987 fprintf(file, ", %s", "Undo log of "
988 "an prepared transaction");
989 }
990 break;
991
992 default:
993 page_type.n_undo_state_other++;
994 break;
995 }
996 if(page_type_dump) {
997 fprintf(file, ", %s\n", str);
998 }
999 break;
1000
1001 case FIL_PAGE_INODE:
1002 page_type.n_fil_page_inode++;
1003 if (page_type_dump) {
1004 fprintf(file, "#::" UINT32PF "\t\t|\t\tInode page\t\t\t|"
1005 "\t%s\n",cur_page_num, str);
1006 }
1007 break;
1008
1009 case FIL_PAGE_IBUF_FREE_LIST:
1010 page_type.n_fil_page_ibuf_free_list++;
1011 if (page_type_dump) {
1012 fprintf(file, "#::" UINT32PF "\t\t|\t\tInsert buffer free list"
1013 " page\t|\t%s\n", cur_page_num, str);
1014 }
1015 break;
1016
1017 case FIL_PAGE_TYPE_ALLOCATED:
1018 page_type.n_fil_page_type_allocated++;
1019 if (page_type_dump) {
1020 fprintf(file, "#::" UINT32PF "\t\t|\t\tFreshly allocated "
1021 "page\t\t|\t%s\n", cur_page_num, str);
1022 }
1023 break;
1024
1025 case FIL_PAGE_IBUF_BITMAP:
1026 page_type.n_fil_page_ibuf_bitmap++;
1027 if (page_type_dump) {
1028 fprintf(file, "#::" UINT32PF "\t\t|\t\tInsert Buffer "
1029 "Bitmap\t\t|\t%s\n", cur_page_num, str);
1030 }
1031 break;
1032
1033 case FIL_PAGE_TYPE_SYS:
1034 page_type.n_fil_page_type_sys++;
1035 if (page_type_dump) {
1036 fprintf(file, "#::" UINT32PF "\t\t|\t\tSystem page\t\t\t|"
1037 "\t%s\n", cur_page_num, str);
1038 }
1039 break;
1040
1041 case FIL_PAGE_TYPE_TRX_SYS:
1042 page_type.n_fil_page_type_trx_sys++;
1043 if (page_type_dump) {
1044 fprintf(file, "#::" UINT32PF "\t\t|\t\tTransaction system "
1045 "page\t\t|\t%s\n", cur_page_num, str);
1046 }
1047 break;
1048
1049 case FIL_PAGE_TYPE_FSP_HDR:
1050 page_type.n_fil_page_type_fsp_hdr++;
1051 if (page_type_dump) {
1052 fprintf(file, "#::" UINT32PF "\t\t|\t\tFile Space "
1053 "Header\t\t|\t%s\n", cur_page_num, str);
1054 }
1055 break;
1056
1057 case FIL_PAGE_TYPE_XDES:
1058 page_type.n_fil_page_type_xdes++;
1059 if (page_type_dump) {
1060 fprintf(file, "#::" UINT32PF "\t\t|\t\tExtent descriptor "
1061 "page\t\t|\t%s\n", cur_page_num, str);
1062 }
1063 break;
1064
1065 case FIL_PAGE_TYPE_BLOB:
1066 page_type.n_fil_page_type_blob++;
1067 if (page_type_dump) {
1068 fprintf(file, "#::" UINT32PF "\t\t|\t\tBLOB page\t\t\t|\t%s\n",
1069 cur_page_num, str);
1070 }
1071 break;
1072
1073 case FIL_PAGE_TYPE_ZBLOB:
1074 page_type.n_fil_page_type_zblob++;
1075 if (page_type_dump) {
1076 fprintf(file, "#::" UINT32PF "\t\t|\t\tCompressed BLOB "
1077 "page\t\t|\t%s\n", cur_page_num, str);
1078 }
1079 break;
1080
1081 case FIL_PAGE_TYPE_ZBLOB2:
1082 page_type.n_fil_page_type_zblob2++;
1083 if (page_type_dump) {
1084 fprintf(file, "#::" UINT32PF "\t\t|\t\tSubsequent Compressed "
1085 "BLOB page\t|\t%s\n", cur_page_num, str);
1086 }
1087 break;
1088
1089 case FIL_PAGE_PAGE_COMPRESSED:
1090 page_type.n_fil_page_type_page_compressed++;
1091 if (page_type_dump) {
1092 fprintf(file, "#::" UINT32PF "\t\t|\t\tPage compressed "
1093 "page\t|\t%s\n", cur_page_num, str);
1094 }
1095 break;
1096
1097 case FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED:
1098 page_type.n_fil_page_type_page_compressed_encrypted++;
1099 if (page_type_dump) {
1100 fprintf(file, "#::" UINT32PF "\t\t|\t\tPage compressed encrypted "
1101 "page\t|\t%s\n", cur_page_num, str);
1102 }
1103 break;
1104 default:
1105 page_type.n_fil_page_type_other++;
1106 break;
1107 }
1108 }
1109 /**
1110 @param [in/out] file_name name of the filename
1111
1112 @retval FILE pointer if successfully created else NULL when error occurred.
1113 */
1114 FILE*
create_file(char * file_name)1115 create_file(
1116 char* file_name)
1117 {
1118 FILE* file = NULL;
1119
1120 #ifndef _WIN32
1121 file = fopen(file_name, "wb");
1122 if (file == NULL) {
1123 fprintf(stderr, "Failed to create file: %s: %s\n",
1124 file_name, strerror(errno));
1125 return(NULL);
1126 }
1127 #else
1128 HANDLE hFile; /* handle to open file. */
1129 int fd = 0;
1130 hFile = CreateFile((LPCTSTR) file_name,
1131 GENERIC_READ | GENERIC_WRITE,
1132 FILE_SHARE_READ | FILE_SHARE_DELETE,
1133 NULL, CREATE_NEW, NULL, NULL);
1134
1135 if (hFile == INVALID_HANDLE_VALUE) {
1136 /* print the error message. */
1137 fprintf(stderr, "Filename::%s %s\n",
1138 file_name,
1139 error_message(GetLastError()));
1140
1141 return(NULL);
1142 }
1143
1144 /* get the file descriptor. */
1145 fd= _open_osfhandle((intptr_t)hFile, _O_RDWR | _O_BINARY);
1146 file = fdopen(fd, "wb");
1147 #endif /* _WIN32 */
1148
1149 return(file);
1150 }
1151
1152 /*
1153 Print the page type count of a tablespace.
1154 @param [in] fil_out stream where the output goes.
1155 */
1156 void
print_summary(FILE * fil_out)1157 print_summary(
1158 FILE* fil_out)
1159 {
1160 fprintf(fil_out, "\n================PAGE TYPE SUMMARY==============\n");
1161 fprintf(fil_out, "#PAGE_COUNT\tPAGE_TYPE");
1162 fprintf(fil_out, "\n===============================================\n");
1163 fprintf(fil_out, "%8d\tIndex page\n",
1164 page_type.n_fil_page_index);
1165 fprintf(fil_out, "%8d\tUndo log page\n",
1166 page_type.n_fil_page_undo_log);
1167 fprintf(fil_out, "%8d\tInode page\n",
1168 page_type.n_fil_page_inode);
1169 fprintf(fil_out, "%8d\tInsert buffer free list page\n",
1170 page_type.n_fil_page_ibuf_free_list);
1171 fprintf(fil_out, "%8d\tFreshly allocated page\n",
1172 page_type.n_fil_page_type_allocated);
1173 fprintf(fil_out, "%8d\tInsert buffer bitmap\n",
1174 page_type.n_fil_page_ibuf_bitmap);
1175 fprintf(fil_out, "%8d\tSystem page\n",
1176 page_type.n_fil_page_type_sys);
1177 fprintf(fil_out, "%8d\tTransaction system page\n",
1178 page_type.n_fil_page_type_trx_sys);
1179 fprintf(fil_out, "%8d\tFile Space Header\n",
1180 page_type.n_fil_page_type_fsp_hdr);
1181 fprintf(fil_out, "%8d\tExtent descriptor page\n",
1182 page_type.n_fil_page_type_xdes);
1183 fprintf(fil_out, "%8d\tBLOB page\n",
1184 page_type.n_fil_page_type_blob);
1185 fprintf(fil_out, "%8d\tCompressed BLOB page\n",
1186 page_type.n_fil_page_type_zblob);
1187 fprintf(fil_out, "%8d\tPage compressed page\n",
1188 page_type.n_fil_page_type_page_compressed);
1189 fprintf(fil_out, "%8d\tPage compressed encrypted page\n",
1190 page_type.n_fil_page_type_page_compressed_encrypted);
1191 fprintf(fil_out, "%8d\tOther type of page\n",
1192 page_type.n_fil_page_type_other);
1193
1194 fprintf(fil_out, "\n===============================================\n");
1195 fprintf(fil_out, "Additional information:\n");
1196 fprintf(fil_out, "Undo page type: %d\n", page_type.n_undo);
1197 fprintf(fil_out, "Undo page state: %d active, %d cached, %d"
1198 " to_purge, %d prepared, %d other\n",
1199 page_type.n_undo_state_active,
1200 page_type.n_undo_state_cached,
1201 page_type.n_undo_state_to_purge,
1202 page_type.n_undo_state_prepared,
1203 page_type.n_undo_state_other);
1204
1205 fprintf(fil_out, "index_id\t#pages\t\t#leaf_pages\t#recs_per_page"
1206 "\t#bytes_per_page\n");
1207
1208 for (std::map<unsigned long long, per_index_stats>::const_iterator it = index_ids.begin();
1209 it != index_ids.end(); it++) {
1210 const per_index_stats& index = it->second;
1211 fprintf(fil_out, "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",
1212 it->first, index.pages, index.leaf_pages,
1213 index.total_n_recs / index.pages,
1214 index.total_data_bytes / index.pages);
1215 }
1216
1217 fprintf(fil_out, "\n");
1218 fprintf(fil_out, "index_id\tpage_data_bytes_histgram(empty,...,oversized)\n");
1219
1220 for (std::map<unsigned long long, per_index_stats>::const_iterator it = index_ids.begin();
1221 it != index_ids.end(); it++) {
1222 fprintf(fil_out, "%lld\t", it->first);
1223 const per_index_stats& index = it->second;
1224 for (ulint i = 0; i < SIZE_RANGES_FOR_PAGE+2; i++) {
1225 fprintf(fil_out, "\t%lld", index.pages_in_size_range[i]);
1226 }
1227 fprintf(fil_out, "\n");
1228 }
1229
1230 if (do_leaf) {
1231 print_leaf_stats(fil_out);
1232 }
1233 }
1234
1235 /* command line argument for innochecksum tool. */
1236 static struct my_option innochecksum_options[] = {
1237 {"help", '?', "Displays this help and exits.",
1238 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1239 {"info", 'I', "Synonym for --help.",
1240 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1241 {"version", 'V', "Displays version information and exits.",
1242 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1243 {"verbose", 'v', "Verbose (prints progress every 5 seconds).",
1244 &verbose, &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1245 #ifndef DBUG_OFF
1246 {"debug", '#', "Output debug log. See https://mariadb.com/kb/en/library/creating-a-trace-file/",
1247 &dbug_setting, &dbug_setting, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1248 #endif /* !DBUG_OFF */
1249 {"count", 'c', "Print the count of pages in the file and exits.",
1250 &just_count, &just_count, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1251 {"start_page", 's', "Start on this page number (0 based).",
1252 &start_page, &start_page, 0, GET_UINT, REQUIRED_ARG,
1253 0, 0, FIL_NULL, 0, 1, 0},
1254 {"end_page", 'e', "End at this page number (0 based).",
1255 &end_page, &end_page, 0, GET_UINT, REQUIRED_ARG,
1256 0, 0, FIL_NULL, 0, 1, 0},
1257 {"page", 'p', "Check only this page (0 based).",
1258 &do_page, &do_page, 0, GET_UINT, REQUIRED_ARG,
1259 0, 0, FIL_NULL, 0, 1, 0},
1260 {"strict-check", 'C', "Specify the strict checksum algorithm by the user.",
1261 &strict_check, &strict_check, &innochecksum_algorithms_typelib,
1262 GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1263 {"no-check", 'n', "Ignore the checksum verification.",
1264 &no_check, &no_check, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1265 {"allow-mismatches", 'a', "Maximum checksum mismatch allowed.",
1266 &allow_mismatches, &allow_mismatches, 0,
1267 GET_ULL, REQUIRED_ARG, 0, 0, ULLONG_MAX, 0, 1, 0},
1268 {"write", 'w', "Rewrite the checksum algorithm by the user.",
1269 &write_check, &write_check, &innochecksum_algorithms_typelib,
1270 GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1271 {"page-type-summary", 'S', "Display a count of each page type "
1272 "in a tablespace.", &page_type_summary, &page_type_summary, 0,
1273 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1274 {"page-type-dump", 'D', "Dump the page type info for each page in a "
1275 "tablespace.", &page_dump_filename, &page_dump_filename, 0,
1276 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1277 {"per-page-details", 'i', "Print out per-page detail information.",
1278 &per_page_details, &per_page_details, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1279 {"log", 'l', "log output.",
1280 &log_filename, &log_filename, 0,
1281 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1282 {"leaf", 'f', "Examine leaf index pages",
1283 &do_leaf, &do_leaf, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1284 {"merge", 'm', "leaf page count if merge given number of consecutive pages",
1285 &n_merge, &n_merge, 0, GET_ULONG, REQUIRED_ARG, 0, 0, (longlong)10L, 0, 1, 0},
1286
1287 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
1288 };
1289
1290 /* Print out the Innodb version and machine information. */
print_version(void)1291 static void print_version(void)
1292 {
1293 #ifdef DBUG_OFF
1294 printf("%s Ver %s, for %s (%s)\n",
1295 my_progname, INNODB_VERSION_STR,
1296 SYSTEM_TYPE, MACHINE_TYPE);
1297 #else
1298 printf("%s-debug Ver %s, for %s (%s)\n",
1299 my_progname, INNODB_VERSION_STR,
1300 SYSTEM_TYPE, MACHINE_TYPE);
1301 #endif /* DBUG_OFF */
1302 }
1303
usage(void)1304 static void usage(void)
1305 {
1306 print_version();
1307 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
1308 printf("InnoDB offline file checksum utility.\n");
1309 printf("Usage: %s [-c] [-s <start page>] [-e <end page>] "
1310 "[-p <page>] [-i] [-v] [-a <allow mismatches>] [-n] "
1311 "[-C <strict-check>] [-w <write>] [-S] [-D <page type dump>] "
1312 "[-l <log>] [-l] [-m <merge pages>] <filename or [-]>\n", my_progname);
1313 printf("See https://mariadb.com/kb/en/library/innochecksum/"
1314 " for usage hints.\n");
1315 my_print_help(innochecksum_options);
1316 my_print_variables(innochecksum_options);
1317 }
1318
1319 extern "C" my_bool
innochecksum_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument MY_ATTRIBUTE ((unused)))1320 innochecksum_get_one_option(
1321 int optid,
1322 const struct my_option *opt MY_ATTRIBUTE((unused)),
1323 char *argument MY_ATTRIBUTE((unused)))
1324 {
1325 switch (optid) {
1326 #ifndef DBUG_OFF
1327 case '#':
1328 dbug_setting = argument
1329 ? argument
1330 : IF_WIN("d:O,innochecksum.trace",
1331 "d:o,/tmp/innochecksum.trace");
1332 DBUG_PUSH(dbug_setting);
1333 break;
1334 #endif /* !DBUG_OFF */
1335 case 'e':
1336 use_end_page = true;
1337 break;
1338 case 'p':
1339 end_page = start_page = do_page;
1340 use_end_page = true;
1341 do_one_page = true;
1342 break;
1343 case 'V':
1344 print_version();
1345 my_end(0);
1346 exit(EXIT_SUCCESS);
1347 break;
1348 case 'C':
1349 strict_verify = true;
1350 switch ((srv_checksum_algorithm_t) strict_check) {
1351
1352 case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
1353 case SRV_CHECKSUM_ALGORITHM_CRC32:
1354 srv_checksum_algorithm =
1355 SRV_CHECKSUM_ALGORITHM_STRICT_CRC32;
1356 break;
1357
1358 case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
1359 case SRV_CHECKSUM_ALGORITHM_INNODB:
1360 srv_checksum_algorithm =
1361 SRV_CHECKSUM_ALGORITHM_STRICT_INNODB;
1362 break;
1363
1364 case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
1365 case SRV_CHECKSUM_ALGORITHM_NONE:
1366 srv_checksum_algorithm =
1367 SRV_CHECKSUM_ALGORITHM_STRICT_NONE;
1368 break;
1369 default:
1370 return(true);
1371 }
1372 break;
1373 case 'n':
1374 no_check = true;
1375 break;
1376 case 'a':
1377 case 'S':
1378 break;
1379 case 'w':
1380 do_write = true;
1381 break;
1382 case 'D':
1383 page_type_dump = true;
1384 break;
1385 case 'l':
1386 is_log_enabled = true;
1387 break;
1388 case 'I':
1389 case '?':
1390 usage();
1391 my_end(0);
1392 exit(EXIT_SUCCESS);
1393 break;
1394 }
1395
1396 return(false);
1397 }
1398
1399 static
1400 bool
get_options(int * argc,char *** argv)1401 get_options(
1402 int *argc,
1403 char ***argv)
1404 {
1405 if (handle_options(argc, argv, innochecksum_options,
1406 innochecksum_get_one_option)) {
1407 my_end(0);
1408 exit(true);
1409 }
1410
1411 /* The next arg must be the filename */
1412 if (!*argc) {
1413 usage();
1414 my_end(0);
1415 return (true);
1416 }
1417
1418 return (false);
1419 }
1420
1421 /** Check from page 0 if table is encrypted.
1422 @param[in] filename Filename
1423 @param[in] page_size page size
1424 @param[in] page Page 0
1425 @retval true if tablespace is encrypted, false if not
1426 */
1427 static
check_encryption(const char * filename,const page_size_t & page_size,byte * page)1428 bool check_encryption(
1429 const char* filename,
1430 const page_size_t& page_size,
1431 byte * page)
1432 {
1433 ulint offset = (FSP_HEADER_OFFSET + (XDES_ARR_OFFSET + XDES_SIZE *
1434 (page_size.physical()) / FSP_EXTENT_SIZE));
1435
1436 if (memcmp(page + offset, CRYPT_MAGIC, MAGIC_SZ) != 0) {
1437 return false;
1438 }
1439
1440 ulint type = mach_read_from_1(page + offset + MAGIC_SZ + 0);
1441
1442 if (! (type == CRYPT_SCHEME_UNENCRYPTED ||
1443 type == CRYPT_SCHEME_1)) {
1444 return false;
1445 }
1446
1447 ulint iv_length = mach_read_from_1(page + offset + MAGIC_SZ + 1);
1448
1449 if (iv_length != CRYPT_SCHEME_1_IV_LEN) {
1450 return false;
1451 }
1452
1453 uint32_t min_key_version = mach_read_from_4
1454 (page + offset + MAGIC_SZ + 2 + iv_length);
1455
1456 uint32_t key_id = mach_read_from_4
1457 (page + offset + MAGIC_SZ + 2 + iv_length + 4);
1458
1459 if (type == CRYPT_SCHEME_1 && is_log_enabled) {
1460 fprintf(log_file,"Tablespace %s encrypted key_version " UINT32PF " key_id " UINT32PF "\n",
1461 filename, min_key_version, key_id);
1462 }
1463
1464 return (type == CRYPT_SCHEME_1);
1465 }
1466
1467 /**
1468 Verify page checksum.
1469 @param[in] buf page to verify
1470 @param[in] page_size page size
1471 @param[in] is_encrypted true if tablespace is encrypted
1472 @param[in] is_compressed true if tablespace is page compressed
1473 @param[in,out] mismatch_count Number of pages failed in checksum verify
1474 @retval 0 if page checksum matches or 1 if it does not match
1475 */
1476 static
verify_checksum(byte * buf,const page_size_t & page_size,bool is_encrypted,bool is_compressed,unsigned long long * mismatch_count)1477 int verify_checksum(
1478 byte* buf,
1479 const page_size_t& page_size,
1480 bool is_encrypted,
1481 bool is_compressed,
1482 unsigned long long* mismatch_count)
1483 {
1484 int exit_status = 0;
1485 bool is_corrupted = false;
1486
1487 is_corrupted = is_page_corrupted(
1488 buf, page_size, is_encrypted, is_compressed);
1489
1490 if (is_corrupted) {
1491 fprintf(stderr, "Fail: page::" UINT32PF " invalid\n",
1492 cur_page_num);
1493
1494 (*mismatch_count)++;
1495
1496 if (*mismatch_count > allow_mismatches) {
1497 fprintf(stderr,
1498 "Exceeded the "
1499 "maximum allowed "
1500 "checksum mismatch "
1501 "count::%llu current::%llu\n",
1502 *mismatch_count,
1503 allow_mismatches);
1504
1505 exit_status = 1;
1506 }
1507 }
1508
1509 return (exit_status);
1510 }
1511
1512 /** Rewrite page checksum if needed.
1513 @param[in] filename File name
1514 @param[in] fil_in File pointer
1515 @param[in] buf page
1516 @param[in] page_size page size
1517 @param[in] pos File position
1518 @param[in] is_encrypted true if tablespace is encrypted
1519 @param[in] is_compressed true if tablespace is page compressed
1520 @retval 0 if checksum rewrite was successful, 1 if error was detected */
1521 static
1522 int
rewrite_checksum(const char * filename,FILE * fil_in,byte * buf,const page_size_t & page_size,fpos_t * pos,bool is_encrypted,bool is_compressed)1523 rewrite_checksum(
1524 const char* filename,
1525 FILE* fil_in,
1526 byte* buf,
1527 const page_size_t& page_size,
1528 fpos_t* pos,
1529 bool is_encrypted,
1530 bool is_compressed)
1531 {
1532 int exit_status = 0;
1533 /* Rewrite checksum. Note that for encrypted and
1534 page compressed tables this is not currently supported. */
1535 if (do_write &&
1536 !is_encrypted &&
1537 !is_compressed
1538 && !write_file(filename, fil_in, buf,
1539 page_size.is_compressed(), pos,
1540 static_cast<ulong>(page_size.physical()))) {
1541
1542 exit_status = 1;
1543 }
1544
1545 return (exit_status);
1546 }
1547
main(int argc,char ** argv)1548 int main(
1549 int argc,
1550 char **argv)
1551 {
1552 /* our input file. */
1553 FILE* fil_in = NULL;
1554 /* our input filename. */
1555 char* filename;
1556 /* Buffer to store pages read. */
1557 byte* buf_ptr = NULL;
1558 byte* xdes_ptr = NULL;
1559 byte* buf = NULL;
1560 byte* xdes = NULL;
1561 /* bytes read count */
1562 ulint bytes;
1563 /* last time */
1564 time_t lastt = 0;
1565 /* stat, to get file size. */
1566 #ifdef _WIN32
1567 struct _stat64 st;
1568 #else
1569 struct stat st;
1570 #endif /* _WIN32 */
1571
1572 int exit_status = 0;
1573
1574 /* size of file (has to be 64 bits) */
1575 unsigned long long int size = 0;
1576 /* number of pages in file */
1577 uint32_t pages;
1578
1579 off_t offset = 0;
1580 /* count the no. of page corrupted. */
1581 unsigned long long mismatch_count = 0;
1582
1583 bool partial_page_read = false;
1584 /* Enabled when read from stdin is done. */
1585 bool read_from_stdin = false;
1586 FILE* fil_page_type = NULL;
1587 fpos_t pos;
1588
1589 /* enable when space_id of given file is zero. */
1590 bool is_system_tablespace = false;
1591
1592 ut_crc32_init();
1593 MY_INIT(argv[0]);
1594 DBUG_ENTER("main");
1595 DBUG_PROCESS(argv[0]);
1596
1597 if (get_options(&argc,&argv)) {
1598 exit_status = 1;
1599 goto my_exit;
1600 }
1601
1602 if (strict_verify && no_check) {
1603 fprintf(stderr, "Error: --strict-check option cannot be used "
1604 "together with --no-check option.\n");
1605 exit_status = 1;
1606 goto my_exit;
1607 }
1608
1609 if (no_check && !do_write) {
1610 fprintf(stderr, "Error: --no-check must be associated with "
1611 "--write option.\n");
1612 exit_status = 1;
1613 goto my_exit;
1614 }
1615
1616 if (page_type_dump) {
1617 fil_page_type = create_file(page_dump_filename);
1618 if (!fil_page_type) {
1619 exit_status = 1;
1620 goto my_exit;
1621 }
1622 }
1623
1624 if (is_log_enabled) {
1625 log_file = create_file(log_filename);
1626 if (!log_file) {
1627 exit_status = 1;
1628 goto my_exit;
1629 }
1630 fprintf(log_file, "InnoDB File Checksum Utility.\n");
1631 }
1632
1633 if (verbose) {
1634 my_print_variables(innochecksum_options);
1635 }
1636
1637
1638 buf_ptr = (byte*) malloc(UNIV_PAGE_SIZE_MAX * 2);
1639 xdes_ptr = (byte*)malloc(UNIV_PAGE_SIZE_MAX * 2);
1640 buf = (byte *) ut_align(buf_ptr, UNIV_PAGE_SIZE_MAX);
1641 xdes = (byte *) ut_align(xdes_ptr, UNIV_PAGE_SIZE_MAX);
1642
1643 /* The file name is not optional. */
1644 for (int i = 0; i < argc; ++i) {
1645
1646 /* Reset parameters for each file. */
1647 filename = argv[i];
1648 memset(&page_type, 0, sizeof(innodb_page_type));
1649 partial_page_read = false;
1650 skip_page = false;
1651
1652 if (is_log_enabled) {
1653 fprintf(log_file, "Filename = %s\n", filename);
1654 }
1655
1656 if (*filename == '-') {
1657 /* read from stdin. */
1658 fil_in = stdin;
1659 read_from_stdin = true;
1660
1661 }
1662
1663 /* stat the file to get size and page count. */
1664 if (!read_from_stdin &&
1665 #ifdef _WIN32
1666 _stat64(filename, &st)) {
1667 #else
1668 stat(filename, &st)) {
1669 #endif /* _WIN32 */
1670 fprintf(stderr, "Error: %s cannot be found\n",
1671 filename);
1672
1673 exit_status = 1;
1674 goto my_exit;
1675 }
1676
1677 if (!read_from_stdin) {
1678 size = st.st_size;
1679 fil_in = open_file(filename);
1680 /*If fil_in is NULL, terminate as some error encountered */
1681 if(fil_in == NULL) {
1682 exit_status = 1;
1683 goto my_exit;
1684 }
1685 /* Save the current file pointer in pos variable.*/
1686 if (0 != fgetpos(fil_in, &pos)) {
1687 perror("fgetpos");
1688 exit_status = 1;
1689 goto my_exit;
1690 }
1691 }
1692
1693 /* Read the minimum page size. */
1694 bytes = fread(buf, 1, UNIV_ZIP_SIZE_MIN, fil_in);
1695 partial_page_read = true;
1696
1697 if (bytes != UNIV_ZIP_SIZE_MIN) {
1698 fprintf(stderr, "Error: Was not able to read the "
1699 "minimum page size ");
1700 fprintf(stderr, "of %d bytes. Bytes read was " ULINTPF "\n",
1701 UNIV_ZIP_SIZE_MIN, bytes);
1702
1703 exit_status = 1;
1704 goto my_exit;
1705 }
1706
1707 /* enable variable is_system_tablespace when space_id of given
1708 file is zero. Use to skip the checksum verification and rewrite
1709 for doublewrite pages. */
1710 cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID);
1711 cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET);
1712
1713 /* Determine page size, zip_size and page compression
1714 from fsp_flags and encryption metadata from page 0 */
1715 const page_size_t& page_size = get_page_size(buf);
1716
1717 ulint flags = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + buf);
1718 ulint zip_size = page_size.is_compressed() ? page_size.logical() : 0;
1719 logical_page_size = page_size.is_compressed() ? zip_size : 0;
1720 physical_page_size = page_size.physical();
1721 bool is_compressed = FSP_FLAGS_HAS_PAGE_COMPRESSION(flags);
1722
1723 if (physical_page_size == UNIV_ZIP_SIZE_MIN) {
1724 partial_page_read = false;
1725 } else {
1726 /* Read rest of the page 0 to determine crypt_data */
1727 bytes = read_file(buf, partial_page_read, page_size.physical(), fil_in);
1728 if (bytes != page_size.physical()) {
1729 fprintf(stderr, "Error: Was not able to read the "
1730 "rest of the page ");
1731 fprintf(stderr, "of " ULINTPF " bytes. Bytes read was " ULINTPF "\n",
1732 page_size.physical() - UNIV_ZIP_SIZE_MIN, bytes);
1733
1734 exit_status = 1;
1735 goto my_exit;
1736 }
1737 partial_page_read = false;
1738 }
1739
1740
1741 /* Now that we have full page 0 in buffer, check encryption */
1742 bool is_encrypted = check_encryption(filename, page_size, buf);
1743
1744 /* Verify page 0 contents. Note that we can't allow
1745 checksum mismatch on page 0, because that would mean we
1746 could not trust it content. */
1747 if (!no_check) {
1748 unsigned long long tmp_allow_mismatches = allow_mismatches;
1749 allow_mismatches = 0;
1750
1751 exit_status = verify_checksum(
1752 buf, page_size, is_encrypted,
1753 is_compressed, &mismatch_count);
1754
1755 if (exit_status) {
1756 fprintf(stderr, "Error: Page 0 checksum mismatch, can't continue. \n");
1757 goto my_exit;
1758 }
1759 allow_mismatches = tmp_allow_mismatches;
1760 }
1761
1762 if ((exit_status = rewrite_checksum(filename, fil_in, buf,
1763 page_size, &pos, is_encrypted, is_compressed))) {
1764 goto my_exit;
1765 }
1766
1767 if (page_type_dump) {
1768 fprintf(fil_page_type,
1769 "\n\nFilename::%s\n", filename);
1770 fprintf(fil_page_type,
1771 "========================================"
1772 "======================================\n");
1773 fprintf(fil_page_type,
1774 "\tPAGE_NO\t\t|\t\tPAGE_TYPE\t\t"
1775 "\t|\tEXTRA INFO\n");
1776 fprintf(fil_page_type,
1777 "========================================"
1778 "======================================\n");
1779 }
1780
1781 if (per_page_details) {
1782 printf("page " UINT32PF " ", cur_page_num);
1783 }
1784
1785 memcpy(xdes, buf, physical_page_size);
1786
1787 if (page_type_summary || page_type_dump) {
1788 parse_page(buf, xdes, fil_page_type, page_size, is_encrypted);
1789 }
1790
1791 pages = uint32_t(size / page_size.physical());
1792
1793 if (just_count) {
1794 fprintf(read_from_stdin ? stderr : stdout,
1795 "Number of pages:" UINT32PF "\n", pages);
1796 continue;
1797 } else if (verbose && !read_from_stdin) {
1798 if (is_log_enabled) {
1799 fprintf(log_file, "file %s = %llu bytes "
1800 "(" UINT32PF " pages)\n",
1801 filename, size, pages);
1802 if (do_one_page) {
1803 fprintf(log_file, "Innochecksum: "
1804 "checking page::"
1805 UINT32PF ";\n",
1806 do_page);
1807 }
1808 }
1809 } else {
1810 if (is_log_enabled) {
1811 fprintf(log_file, "Innochecksum: checking "
1812 "pages in range::" UINT32PF
1813 " to " UINT32PF "\n",
1814 start_page, use_end_page ?
1815 end_page : (pages - 1));
1816 }
1817 }
1818
1819 off_t cur_offset = 0;
1820 /* Find the first non all-zero page and fetch the
1821 space id from there. */
1822 while (is_page_all_zeroes(buf, physical_page_size)) {
1823 bytes = ulong(read_file(
1824 buf, false, physical_page_size,
1825 fil_in));
1826
1827 if (feof(fil_in)) {
1828 fprintf(stderr, "All are "
1829 "zero-filled pages.");
1830 goto my_exit;
1831 }
1832
1833 cur_offset++;
1834 }
1835
1836 cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID);
1837 is_system_tablespace = (cur_space == 0);
1838
1839 if (cur_offset > 0) {
1840 /* Re-read the non-zero page to check the
1841 checksum. So move the file pointer to
1842 previous position and reset the page number too. */
1843 cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET);
1844 if (!start_page) {
1845 goto first_non_zero;
1846 }
1847 }
1848
1849 /* seek to the necessary position */
1850 if (start_page) {
1851 if (!read_from_stdin) {
1852 /* If read is not from stdin, we can use
1853 fseeko() to position the file pointer to
1854 the desired page. */
1855 partial_page_read = false;
1856
1857 offset = off_t(ulonglong(start_page)
1858 * page_size.physical());
1859 #ifdef _WIN32
1860 if (_fseeki64(fil_in, offset, SEEK_SET)) {
1861 #else
1862 if (fseeko(fil_in, offset, SEEK_SET)) {
1863 #endif /* _WIN32 */
1864 perror("Error: Unable to seek to "
1865 "necessary offset");
1866
1867 exit_status = 1;
1868 goto my_exit;
1869 }
1870 /* Save the current file pointer in
1871 pos variable. */
1872 if (0 != fgetpos(fil_in, &pos)) {
1873 perror("fgetpos");
1874
1875 exit_status = 1;
1876 goto my_exit;
1877 }
1878 } else {
1879
1880 ulong count = 0;
1881
1882 while (!feof(fil_in)) {
1883 if (start_page == count) {
1884 break;
1885 }
1886 /* We read a part of page to find the
1887 minimum page size. We cannot reset
1888 the file pointer to the beginning of
1889 the page if we are reading from stdin
1890 (fseeko() on stdin doesn't work). So
1891 read only the remaining part of page,
1892 if partial_page_read is enable. */
1893 bytes = read_file(buf,
1894 partial_page_read,
1895 static_cast<ulong>(
1896 page_size.physical()),
1897 fil_in);
1898
1899 partial_page_read = false;
1900 count++;
1901
1902 if (!bytes || feof(fil_in)) {
1903 goto unexpected_eof;
1904 }
1905 }
1906 }
1907 }
1908
1909 /* main checksumming loop */
1910 cur_page_num = start_page ? start_page : cur_page_num + 1;
1911
1912 while (!feof(fil_in)) {
1913
1914 bytes = read_file(buf, partial_page_read,
1915 static_cast<ulong>(
1916 page_size.physical()), fil_in);
1917 partial_page_read = false;
1918
1919 if (!bytes && feof(fil_in)) {
1920 if (cur_page_num == start_page) {
1921 unexpected_eof:
1922 fputs("Error: Unable "
1923 "to seek to necessary offset\n",
1924 stderr);
1925
1926 exit_status = 1;
1927 goto my_exit;
1928 }
1929 break;
1930 }
1931
1932 if (ferror(fil_in)) {
1933 fprintf(stderr, "Error reading " ULINTPF " bytes",
1934 page_size.physical());
1935 perror(" ");
1936
1937 exit_status = 1;
1938 goto my_exit;
1939 }
1940
1941 if (bytes != page_size.physical()) {
1942 fprintf(stderr, "Error: bytes read (" ULINTPF ") "
1943 "doesn't match page size (" ULINTPF ")\n",
1944 bytes, page_size.physical());
1945 exit_status = 1;
1946 goto my_exit;
1947 }
1948
1949 first_non_zero:
1950 if (is_system_tablespace) {
1951 /* enable when page is double write buffer.*/
1952 skip_page = is_page_doublewritebuffer(buf);
1953 } else {
1954 skip_page = false;
1955 }
1956
1957 ulint cur_page_type = mach_read_from_2(buf+FIL_PAGE_TYPE);
1958
1959 /* FIXME: Page compressed or Page compressed and encrypted
1960 pages do not contain checksum. */
1961 if (cur_page_type == FIL_PAGE_PAGE_COMPRESSED ||
1962 cur_page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
1963 skip_page = true;
1964 }
1965
1966 /* If no-check is enabled, skip the
1967 checksum verification.*/
1968 if (!no_check &&
1969 !is_page_free(xdes, page_size, cur_page_num) &&
1970 !skip_page &&
1971 (exit_status = verify_checksum(
1972 buf, page_size,
1973 is_encrypted, is_compressed,
1974 &mismatch_count))) {
1975 goto my_exit;
1976 }
1977
1978 if ((exit_status = rewrite_checksum(filename, fil_in, buf,
1979 page_size, &pos, is_encrypted, is_compressed))) {
1980 goto my_exit;
1981 }
1982
1983 /* end if this was the last page we were supposed to check */
1984 if (use_end_page && (cur_page_num >= end_page)) {
1985 break;
1986 }
1987
1988 if (per_page_details) {
1989 printf("page " UINT32PF " ", cur_page_num);
1990 }
1991
1992 if (page_get_page_no(buf) % physical_page_size == 0) {
1993 memcpy(xdes, buf, physical_page_size);
1994 }
1995
1996 if (page_type_summary || page_type_dump) {
1997 parse_page(buf, xdes, fil_page_type, page_size, is_encrypted);
1998 }
1999
2000 /* do counter increase and progress printing */
2001 cur_page_num++;
2002
2003 if (verbose && !read_from_stdin) {
2004 if ((cur_page_num % 64) == 0) {
2005 time_t now = time(0);
2006 if (!lastt) {
2007 lastt= now;
2008 } else if (now - lastt >= 1 && is_log_enabled) {
2009 fprintf(log_file, "page::" UINT32PF " "
2010 "okay: %.3f%% done\n",
2011 (cur_page_num - 1),
2012 (double) cur_page_num / pages * 100);
2013 lastt = now;
2014 }
2015 }
2016 }
2017 }
2018
2019 if (!read_from_stdin) {
2020 /* flcose() will flush the data and release the lock if
2021 any acquired. */
2022 fclose(fil_in);
2023 }
2024
2025 /* Enabled for page type summary. */
2026 if (page_type_summary) {
2027 if (!read_from_stdin) {
2028 fprintf(stdout, "\nFile::%s",filename);
2029 print_summary(stdout);
2030 } else {
2031 print_summary(stderr);
2032 }
2033 }
2034 }
2035
2036 if (is_log_enabled) {
2037 fclose(log_file);
2038 }
2039
2040 free(buf_ptr);
2041 free(xdes_ptr);
2042
2043 my_end(exit_status);
2044 DBUG_RETURN(exit_status);
2045
2046 my_exit:
2047 if (buf_ptr) {
2048 free(buf_ptr);
2049 }
2050
2051 if (xdes_ptr) {
2052 free(xdes_ptr);
2053 }
2054
2055 if (!read_from_stdin && fil_in) {
2056 fclose(fil_in);
2057 }
2058
2059 if (log_file) {
2060 fclose(log_file);
2061 }
2062
2063 my_end(exit_status);
2064 DBUG_RETURN(exit_status);
2065 }
2066