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