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