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