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