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