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