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