mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1932 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1932 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
   Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
 | 
						|
   Copyright (c) 2014, 2022, MariaDB Corporation.
 | 
						|
 | 
						|
   This program is free software; you can redistribute it and/or modify
 | 
						|
   it under the terms of the GNU General Public License as published by
 | 
						|
   the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful,
 | 
						|
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
   GNU General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program; if not, write to the Free Software
 | 
						|
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA
 | 
						|
*/
 | 
						|
 | 
						|
/*
 | 
						|
  InnoDB offline file checksum utility.  85% of the code in this utility
 | 
						|
  is included from the InnoDB codebase.
 | 
						|
 | 
						|
  The final 15% was originally written by Mark Smith of Danga
 | 
						|
  Interactive, Inc. <junior@danga.com>
 | 
						|
 | 
						|
  Published with a permission.
 | 
						|
*/
 | 
						|
 | 
						|
#include <my_global.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <time.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#ifndef _WIN32
 | 
						|
# include <unistd.h>
 | 
						|
#endif
 | 
						|
#include <my_getopt.h>
 | 
						|
#include <m_string.h>
 | 
						|
#include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
 | 
						|
 | 
						|
/* Only parts of these files are included from the InnoDB codebase.
 | 
						|
The parts not included are excluded by #ifndef UNIV_INNOCHECKSUM. */
 | 
						|
 | 
						|
#include "mach0data.h"
 | 
						|
#include "page0page.h"
 | 
						|
#include "buf0checksum.h"        /* buf_calc_page_*() */
 | 
						|
#include "buf0buf.h"             /* buf_page_is_corrupted */
 | 
						|
#include "page0zip.h"            /* page_zip_*() */
 | 
						|
#include "trx0undo.h"            /* TRX_* */
 | 
						|
#include "fil0crypt.h"           /* fil_space_verify_crypt_checksum */
 | 
						|
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#ifdef UNIV_NONINL
 | 
						|
# include "fsp0fsp.inl"
 | 
						|
# include "mach0data.inl"
 | 
						|
# include "ut0rnd.inl"
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef PRIuMAX
 | 
						|
#define PRIuMAX   "llu"
 | 
						|
#endif
 | 
						|
 | 
						|
/* Global variables */
 | 
						|
static bool			verbose;
 | 
						|
static bool			just_count;
 | 
						|
static uint32_t			start_page;
 | 
						|
static uint32_t			end_page;
 | 
						|
static uint32_t			do_page;
 | 
						|
static bool			use_end_page;
 | 
						|
static bool			do_one_page;
 | 
						|
static my_bool do_leaf;
 | 
						|
static my_bool per_page_details;
 | 
						|
static ulint n_merge;
 | 
						|
static ulint physical_page_size;  /* Page size in bytes on disk. */
 | 
						|
static ulint extent_size;
 | 
						|
static ulint xdes_size;
 | 
						|
ulong srv_page_size;
 | 
						|
uint32_t srv_page_size_shift;
 | 
						|
/* Current page number (0 based). */
 | 
						|
uint32_t		cur_page_num;
 | 
						|
/* Current space. */
 | 
						|
uint32_t		cur_space;
 | 
						|
/* Skip the checksum verification. */
 | 
						|
static bool			no_check;
 | 
						|
/* Enabled for rewrite checksum. */
 | 
						|
static bool			do_write;
 | 
						|
/* Mismatches count allowed (0 by default). */
 | 
						|
static unsigned long long     	allow_mismatches=0;
 | 
						|
static bool			page_type_summary;
 | 
						|
static bool			page_type_dump;
 | 
						|
/* Store filename for page-type-dump option. */
 | 
						|
char*				page_dump_filename = 0;
 | 
						|
/* skip the checksum verification & rewrite if page is doublewrite buffer. */
 | 
						|
static bool			skip_page = 0;
 | 
						|
const char			*dbug_setting = "FALSE";
 | 
						|
char*				log_filename = NULL;
 | 
						|
/* User defined filename for logging. */
 | 
						|
FILE*				log_file = NULL;
 | 
						|
/* Enabled for log write option. */
 | 
						|
static bool			is_log_enabled = false;
 | 
						|
static bool			skip_freed_pages;
 | 
						|
static byte field_ref_zero_buf[UNIV_PAGE_SIZE_MAX];
 | 
						|
const byte *field_ref_zero = field_ref_zero_buf;
 | 
						|
 | 
						|
#ifndef _WIN32
 | 
						|
/* advisory lock for non-window system. */
 | 
						|
struct flock			lk;
 | 
						|
#endif /* _WIN32 */
 | 
						|
 | 
						|
/* Innodb page type. */
 | 
						|
struct innodb_page_type {
 | 
						|
	int n_undo_state_active;
 | 
						|
	int n_undo_state_cached;
 | 
						|
	int n_undo_state_to_purge;
 | 
						|
	int n_undo_state_prepared;
 | 
						|
	int n_undo_state_other;
 | 
						|
	int n_undo;
 | 
						|
	int n_fil_page_index;
 | 
						|
	int n_fil_page_undo_log;
 | 
						|
	int n_fil_page_inode;
 | 
						|
	int n_fil_page_ibuf_free_list;
 | 
						|
	int n_fil_page_ibuf_bitmap;
 | 
						|
	int n_fil_page_type_sys;
 | 
						|
	int n_fil_page_type_trx_sys;
 | 
						|
	int n_fil_page_type_fsp_hdr;
 | 
						|
	int n_fil_page_type_allocated;
 | 
						|
	int n_fil_page_type_xdes;
 | 
						|
	int n_fil_page_type_blob;
 | 
						|
	int n_fil_page_type_zblob;
 | 
						|
	int n_fil_page_type_other;
 | 
						|
	int n_fil_page_type_zblob2;
 | 
						|
	int n_fil_page_type_page_compressed;
 | 
						|
	int n_fil_page_type_page_compressed_encrypted;
 | 
						|
} page_type;
 | 
						|
 | 
						|
#define SIZE_RANGES_FOR_PAGE 10
 | 
						|
#define NUM_RETRIES 3
 | 
						|
#define DEFAULT_RETRY_DELAY 1000000
 | 
						|
 | 
						|
struct per_page_stats {
 | 
						|
  ulint n_recs;
 | 
						|
  ulint data_size;
 | 
						|
  ulint left_page_no;
 | 
						|
  ulint right_page_no;
 | 
						|
  per_page_stats(ulint n, ulint data, ulint left, ulint right) :
 | 
						|
      n_recs(n), data_size(data), left_page_no(left), right_page_no(right) {}
 | 
						|
  per_page_stats() : n_recs(0), data_size(0), left_page_no(0), right_page_no(0) {}
 | 
						|
};
 | 
						|
 | 
						|
struct per_index_stats {
 | 
						|
  unsigned long long pages;
 | 
						|
  unsigned long long leaf_pages;
 | 
						|
  ulint first_leaf_page;
 | 
						|
  ulint count;
 | 
						|
  ulint free_pages;
 | 
						|
  ulint max_data_size;
 | 
						|
  unsigned long long total_n_recs;
 | 
						|
  unsigned long long total_data_bytes;
 | 
						|
 | 
						|
  /*!< first element for empty pages,
 | 
						|
  last element for pages with more than logical_page_size */
 | 
						|
  unsigned long long pages_in_size_range[SIZE_RANGES_FOR_PAGE+2];
 | 
						|
 | 
						|
  std::map<unsigned long long, per_page_stats> leaves;
 | 
						|
 | 
						|
  per_index_stats():pages(0), leaf_pages(0), first_leaf_page(0),
 | 
						|
                    count(0), free_pages(0), max_data_size(0), total_n_recs(0),
 | 
						|
                    total_data_bytes(0)
 | 
						|
  {
 | 
						|
    memset(pages_in_size_range, 0, sizeof(pages_in_size_range));
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
std::map<unsigned long long, per_index_stats> index_ids;
 | 
						|
 | 
						|
void print_index_leaf_stats(
 | 
						|
	unsigned long long id,
 | 
						|
	const per_index_stats& index,
 | 
						|
	FILE*	fil_out)
 | 
						|
 | 
						|
{
 | 
						|
	ulint page_no = index.first_leaf_page;
 | 
						|
	std::map<unsigned long long, per_page_stats>::const_iterator it_page = index.leaves.find(page_no);
 | 
						|
	fprintf(fil_out, "\nindex: %llu leaf page stats: n_pages = %llu\n",
 | 
						|
		id, index.leaf_pages);
 | 
						|
	fprintf(fil_out, "page_no\tdata_size\tn_recs\n");
 | 
						|
	while (it_page != index.leaves.end()) {
 | 
						|
		const per_page_stats& stat = it_page->second;
 | 
						|
		fprintf(fil_out, "%llu\t" ULINTPF "\t" ULINTPF "\n", it_page->first, stat.data_size, stat.n_recs);
 | 
						|
		page_no = stat.right_page_no;
 | 
						|
		it_page = index.leaves.find(page_no);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void defrag_analysis(
 | 
						|
	unsigned long long id,
 | 
						|
	const per_index_stats& index,
 | 
						|
	FILE*	fil_out)
 | 
						|
{
 | 
						|
	// TODO: make it work for compressed pages too
 | 
						|
	std::map<unsigned long long, per_page_stats>::const_iterator it = index.leaves.find(index.first_leaf_page);
 | 
						|
	ulint n_pages = 0;
 | 
						|
	ulint n_leaf_pages = 0;
 | 
						|
	while (it != index.leaves.end()) {
 | 
						|
		ulint data_size_total = 0;
 | 
						|
		for (ulong i = 0; i < n_merge; i++) {
 | 
						|
			const per_page_stats& stat = it->second;
 | 
						|
			n_leaf_pages ++;
 | 
						|
			data_size_total += stat.data_size;
 | 
						|
			it = index.leaves.find(stat.right_page_no);
 | 
						|
			if (it == index.leaves.end()) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (index.max_data_size) {
 | 
						|
			n_pages += data_size_total / index.max_data_size;
 | 
						|
			if (data_size_total % index.max_data_size != 0) {
 | 
						|
				n_pages += 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (index.leaf_pages) {
 | 
						|
		fprintf(fil_out, "count = " ULINTPF " free = " ULINTPF "\n", index.count, index.free_pages);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!n_leaf_pages) {
 | 
						|
		n_leaf_pages = 1;
 | 
						|
	}
 | 
						|
 | 
						|
	fprintf(fil_out, "%llu\t\t%llu\t\t" ULINTPF "\t\t" ULINTPF "\t\t" ULINTPF "\t\t%.2f\t" ULINTPF "\n",
 | 
						|
		id, index.leaf_pages, n_leaf_pages, n_merge, n_pages,
 | 
						|
		1.0 - (double)n_pages / (double)n_leaf_pages, index.max_data_size);
 | 
						|
}
 | 
						|
 | 
						|
void print_leaf_stats(
 | 
						|
	FILE*	fil_out)
 | 
						|
{
 | 
						|
	fprintf(fil_out, "\n**************************************************\n");
 | 
						|
	fprintf(fil_out, "index_id\t#leaf_pages\t#actual_leaf_pages\tn_merge\t"
 | 
						|
		"#leaf_after_merge\tdefrag\n");
 | 
						|
	for (std::map<unsigned long long, per_index_stats>::const_iterator it = index_ids.begin();
 | 
						|
	     it != index_ids.end(); it++) {
 | 
						|
		const per_index_stats& index = it->second;
 | 
						|
 | 
						|
		if (verbose) {
 | 
						|
			print_index_leaf_stats(it->first, index, fil_out);
 | 
						|
		}
 | 
						|
 | 
						|
		if (n_merge) {
 | 
						|
			defrag_analysis(it->first, index, fil_out);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/** Init the page size for the tablespace.
 | 
						|
@param[in]	buf	buffer used to read the page */
 | 
						|
static void init_page_size(const byte* buf)
 | 
						|
{
 | 
						|
	const unsigned	flags = mach_read_from_4(buf + FIL_PAGE_DATA
 | 
						|
						 + FSP_SPACE_FLAGS);
 | 
						|
 | 
						|
	if (fil_space_t::full_crc32(flags)) {
 | 
						|
		const uint32_t ssize = FSP_FLAGS_FCRC32_GET_PAGE_SSIZE(flags);
 | 
						|
		srv_page_size_shift = UNIV_ZIP_SIZE_SHIFT_MIN - 1 + ssize;
 | 
						|
		srv_page_size = 512U << ssize;
 | 
						|
		physical_page_size = srv_page_size;
 | 
						|
		extent_size = FSP_EXTENT_SIZE;
 | 
						|
		xdes_size = XDES_SIZE;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	const uint32_t ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags);
 | 
						|
 | 
						|
	srv_page_size_shift = ssize
 | 
						|
		? UNIV_ZIP_SIZE_SHIFT_MIN - 1 + ssize
 | 
						|
		: UNIV_PAGE_SIZE_SHIFT_ORIG;
 | 
						|
 | 
						|
	srv_page_size = fil_space_t::logical_size(flags);
 | 
						|
	physical_page_size = fil_space_t::physical_size(flags);
 | 
						|
	extent_size = FSP_EXTENT_SIZE;
 | 
						|
	xdes_size = XDES_SIZE;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef _WIN32
 | 
						|
/***********************************************//*
 | 
						|
 @param		[in] error	error no. from the getLastError().
 | 
						|
 | 
						|
 @retval error message corresponding to error no.
 | 
						|
*/
 | 
						|
static
 | 
						|
char*
 | 
						|
error_message(
 | 
						|
	int	error)
 | 
						|
{
 | 
						|
	static char err_msg[1024] = {'\0'};
 | 
						|
	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
 | 
						|
		NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | 
						|
		(LPTSTR)err_msg, sizeof(err_msg), NULL );
 | 
						|
 | 
						|
	return (err_msg);
 | 
						|
}
 | 
						|
#endif /* _WIN32 */
 | 
						|
 | 
						|
/***********************************************//*
 | 
						|
 @param>>_______[in] name>_____name of file.
 | 
						|
 @retval file pointer; file pointer is NULL when error occurred.
 | 
						|
*/
 | 
						|
 | 
						|
FILE*
 | 
						|
open_file(
 | 
						|
	const char*	name)
 | 
						|
{
 | 
						|
	int	fd;		/* file descriptor. */
 | 
						|
	FILE*	fil_in;
 | 
						|
#ifdef _WIN32
 | 
						|
	HANDLE		hFile;		/* handle to open file. */
 | 
						|
	DWORD		access;		/* define access control */
 | 
						|
	int		flags = 0;	/* define the mode for file
 | 
						|
					descriptor */
 | 
						|
 | 
						|
	if (do_write) {
 | 
						|
		access =  GENERIC_READ | GENERIC_WRITE;
 | 
						|
		flags =  _O_RDWR | _O_BINARY;
 | 
						|
	} else {
 | 
						|
		access = GENERIC_READ;
 | 
						|
		flags = _O_RDONLY | _O_BINARY;
 | 
						|
	}
 | 
						|
 | 
						|
	/* CreateFile() also provide advisory lock with the usage of
 | 
						|
	access and share mode of the file.*/
 | 
						|
	hFile = CreateFile(
 | 
						|
			(LPCTSTR) name, access, 0L, NULL,
 | 
						|
			OPEN_EXISTING, NULL, NULL);
 | 
						|
 | 
						|
	if (hFile == INVALID_HANDLE_VALUE) {
 | 
						|
		/* print the error message. */
 | 
						|
		fprintf(stderr, "Filename::%s %s\n", name,
 | 
						|
			error_message(GetLastError()));
 | 
						|
 | 
						|
			return (NULL);
 | 
						|
		}
 | 
						|
 | 
						|
	/* get the file descriptor. */
 | 
						|
	fd= _open_osfhandle((intptr_t)hFile, flags);
 | 
						|
#else /* _WIN32 */
 | 
						|
 | 
						|
	int	create_flag;
 | 
						|
	/* define the advisory lock and open file mode. */
 | 
						|
	if (do_write) {
 | 
						|
		create_flag = O_RDWR;
 | 
						|
		lk.l_type = F_WRLCK;
 | 
						|
	} else {
 | 
						|
		create_flag = O_RDONLY;
 | 
						|
		lk.l_type = F_RDLCK;
 | 
						|
	}
 | 
						|
 | 
						|
	fd = open(name, create_flag);
 | 
						|
 | 
						|
	lk.l_whence = SEEK_SET;
 | 
						|
	lk.l_start = lk.l_len = 0;
 | 
						|
 | 
						|
	if (fcntl(fd, F_SETLK, &lk) == -1) {
 | 
						|
		fprintf(stderr, "Error: Unable to lock file::"
 | 
						|
			" %s\n", name);
 | 
						|
		perror("fcntl");
 | 
						|
		return (NULL);
 | 
						|
	}
 | 
						|
#endif /* _WIN32 */
 | 
						|
 | 
						|
	if (do_write) {
 | 
						|
		fil_in = fdopen(fd, "rb+");
 | 
						|
	} else {
 | 
						|
		fil_in = fdopen(fd, "rb");
 | 
						|
	}
 | 
						|
 | 
						|
	return (fil_in);
 | 
						|
}
 | 
						|
 | 
						|
/************************************************************//*
 | 
						|
 Read the content of file
 | 
						|
 | 
						|
 @param  [in,out]	buf			read the file in buffer
 | 
						|
 @param  [in]		partial_page_read	enable when to read the
 | 
						|
						remaining buffer for first page.
 | 
						|
 @param  [in]		physical_page_size	Physical/Commpressed page size.
 | 
						|
 @param  [in,out]	fil_in			file pointer created for the
 | 
						|
						tablespace.
 | 
						|
 @retval no. of bytes read.
 | 
						|
*/
 | 
						|
ulint read_file(
 | 
						|
	byte*	buf,
 | 
						|
	bool	partial_page_read,
 | 
						|
	ulint	physical_page_size,
 | 
						|
	FILE*	fil_in)
 | 
						|
{
 | 
						|
	ulint bytes = 0;
 | 
						|
 | 
						|
	DBUG_ASSERT(physical_page_size >= UNIV_ZIP_SIZE_MIN);
 | 
						|
 | 
						|
	if (partial_page_read) {
 | 
						|
		buf += UNIV_ZIP_SIZE_MIN;
 | 
						|
		physical_page_size -= UNIV_ZIP_SIZE_MIN;
 | 
						|
		bytes = UNIV_ZIP_SIZE_MIN;
 | 
						|
	}
 | 
						|
 | 
						|
	bytes += ulint(fread(buf, 1, physical_page_size, fil_in));
 | 
						|
 | 
						|
	return bytes;
 | 
						|
}
 | 
						|
 | 
						|
/** Check whether the page contains all zeroes.
 | 
						|
@param[in]	buf	page
 | 
						|
@param[in]	size	physical size of the page
 | 
						|
@return true if the page is all zeroes; else false */
 | 
						|
static bool is_page_all_zeroes(
 | 
						|
	byte*	buf,
 | 
						|
	ulint	size)
 | 
						|
{
 | 
						|
	/* On pages that are not all zero, the page number
 | 
						|
	must match. */
 | 
						|
	const ulint* p = reinterpret_cast<const ulint*>(buf);
 | 
						|
	const ulint* const end = reinterpret_cast<const ulint*>(buf + size);
 | 
						|
	do {
 | 
						|
		if (*p++) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	} while (p != end);
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/** Check if page is corrupted or not.
 | 
						|
@param[in]	buf		page frame
 | 
						|
@param[in]	is_encrypted	true if page0 contained cryp_data
 | 
						|
				with crypt_scheme encrypted
 | 
						|
@param[in]	flags		tablespace flags
 | 
						|
@retval true if page is corrupted otherwise false. */
 | 
						|
static bool is_page_corrupted(byte *buf, bool is_encrypted, uint32_t flags)
 | 
						|
{
 | 
						|
 | 
						|
	/* enable if page is corrupted. */
 | 
						|
	bool is_corrupted;
 | 
						|
	/* use to store LSN values. */
 | 
						|
	uint32_t logseq;
 | 
						|
	uint32_t logseqfield;
 | 
						|
	const uint16_t page_type = mach_read_from_2(buf+FIL_PAGE_TYPE);
 | 
						|
	uint32_t key_version = buf_page_get_key_version(buf, flags);
 | 
						|
	uint32_t space_id = mach_read_from_4(
 | 
						|
		buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
 | 
						|
	ulint zip_size = fil_space_t::zip_size(flags);
 | 
						|
	ulint is_compressed = fil_space_t::is_compressed(flags);
 | 
						|
	const bool use_full_crc32 = fil_space_t::full_crc32(flags);
 | 
						|
 | 
						|
	if (mach_read_from_4(buf + FIL_PAGE_OFFSET) != cur_page_num
 | 
						|
	    || (space_id != cur_space
 | 
						|
		&& (!use_full_crc32 || (!is_encrypted && !is_compressed)))) {
 | 
						|
		/* On pages that are not all zero, the page number
 | 
						|
		must match. */
 | 
						|
		if (is_page_all_zeroes(buf,
 | 
						|
				       fil_space_t::physical_size(flags))) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		if (is_log_enabled) {
 | 
						|
			fprintf(log_file,
 | 
						|
				"page id mismatch space::" UINT32PF
 | 
						|
				" page::" UINT32PF " \n",
 | 
						|
				space_id, cur_page_num);
 | 
						|
		}
 | 
						|
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	/* We can't trust only a page type, thus we take account
 | 
						|
	also fsp_flags or crypt_data on page 0 */
 | 
						|
	if ((page_type == FIL_PAGE_PAGE_COMPRESSED && is_compressed) ||
 | 
						|
	    (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED &&
 | 
						|
	     is_compressed && is_encrypted)) {
 | 
						|
		/* Page compressed tables do not contain post compression
 | 
						|
		checksum. */
 | 
						|
		return (false);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!zip_size && (!is_compressed || !use_full_crc32)) {
 | 
						|
		/* check the stored log sequence numbers
 | 
						|
		for uncompressed tablespace. */
 | 
						|
		logseq = mach_read_from_4(buf + FIL_PAGE_LSN + 4);
 | 
						|
		logseqfield = use_full_crc32
 | 
						|
			? mach_read_from_4(buf + srv_page_size
 | 
						|
					   - FIL_PAGE_FCRC32_END_LSN)
 | 
						|
			: mach_read_from_4(buf + srv_page_size
 | 
						|
					   - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
 | 
						|
 | 
						|
		if (is_log_enabled) {
 | 
						|
			fprintf(log_file,
 | 
						|
				"space::" UINT32PF " page::" UINT32PF
 | 
						|
				"; log sequence number:first = " UINT32PF
 | 
						|
				"; second = " UINT32PF "\n",
 | 
						|
				space_id, cur_page_num, logseq, logseqfield);
 | 
						|
			if (logseq != logseqfield) {
 | 
						|
				fprintf(log_file,
 | 
						|
					"Fail; space::" UINT32PF
 | 
						|
					" page::" UINT32PF
 | 
						|
					" invalid (fails log "
 | 
						|
					"sequence number check)\n",
 | 
						|
					space_id, cur_page_num);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Again we can't trust only FIL_PAGE_FILE_FLUSH_LSN field
 | 
						|
	now repurposed as FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION,
 | 
						|
	we need to check also crypt_data contents.
 | 
						|
 | 
						|
	If page is encrypted, use different checksum calculation
 | 
						|
	as innochecksum can't decrypt pages. Note that some old InnoDB
 | 
						|
	versions did not initialize FIL_PAGE_FILE_FLUSH_LSN field
 | 
						|
	so if crypt checksum does not match we verify checksum using
 | 
						|
	normal method. */
 | 
						|
	if (is_encrypted && key_version != 0) {
 | 
						|
		is_corrupted = use_full_crc32
 | 
						|
			? !!buf_page_is_corrupted(false, buf, flags)
 | 
						|
			: !fil_space_verify_crypt_checksum(buf, zip_size);
 | 
						|
 | 
						|
		if (is_corrupted && log_file) {
 | 
						|
			fprintf(log_file,
 | 
						|
				"[page id: space=" UINT32PF
 | 
						|
				", page_number=" UINT32PF "] may be corrupted;"
 | 
						|
				" key_version=" UINT32PF "\n",
 | 
						|
				space_id, cur_page_num, key_version);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		is_corrupted = true;
 | 
						|
	}
 | 
						|
 | 
						|
	if (is_corrupted) {
 | 
						|
		is_corrupted = buf_page_is_corrupted(true, buf, flags);
 | 
						|
	}
 | 
						|
 | 
						|
	return(is_corrupted);
 | 
						|
}
 | 
						|
 | 
						|
/********************************************//*
 | 
						|
 Check if page is doublewrite buffer or not.
 | 
						|
 @param [in] page	buffer page
 | 
						|
 | 
						|
 @retval true  if page is doublewrite buffer otherwise false.
 | 
						|
*/
 | 
						|
static
 | 
						|
bool
 | 
						|
is_page_doublewritebuffer(
 | 
						|
	const byte*	page)
 | 
						|
{
 | 
						|
	if ((cur_page_num >= extent_size)
 | 
						|
		&& (cur_page_num < extent_size * 3)) {
 | 
						|
		/* page is doublewrite buffer. */
 | 
						|
		return (true);
 | 
						|
	}
 | 
						|
 | 
						|
	return (false);
 | 
						|
}
 | 
						|
 | 
						|
/*******************************************************//*
 | 
						|
Check if page is empty or not.
 | 
						|
 @param		[in] page		page to checked for empty.
 | 
						|
 @param		[in] len	size of page.
 | 
						|
 | 
						|
 @retval true if page is empty.
 | 
						|
 @retval false if page is not empty.
 | 
						|
*/
 | 
						|
static
 | 
						|
bool
 | 
						|
is_page_empty(
 | 
						|
	const byte*	page,
 | 
						|
	size_t		len)
 | 
						|
{
 | 
						|
	while (len--) {
 | 
						|
		if (*page++) {
 | 
						|
			return (false);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return (true);
 | 
						|
}
 | 
						|
 | 
						|
/********************************************************************//**
 | 
						|
Rewrite the checksum for the page.
 | 
						|
@param	[in/out] page			page buffer
 | 
						|
@param	[in] flags			tablespace flags
 | 
						|
 | 
						|
@retval true  : do rewrite
 | 
						|
@retval false : skip the rewrite as checksum stored match with
 | 
						|
		calculated or page is doublwrite buffer.
 | 
						|
*/
 | 
						|
static bool update_checksum(byte* page, uint32_t flags)
 | 
						|
{
 | 
						|
	ib_uint32_t	checksum = 0;
 | 
						|
	byte		stored1[4];	/* get FIL_PAGE_SPACE_OR_CHKSUM field checksum */
 | 
						|
	byte		stored2[4];	/* get FIL_PAGE_END_LSN_OLD_CHKSUM field checksum */
 | 
						|
 | 
						|
	ut_ad(page);
 | 
						|
	/* If page is doublewrite buffer, skip the rewrite of checksum. */
 | 
						|
	if (skip_page) {
 | 
						|
		return (false);
 | 
						|
	}
 | 
						|
 | 
						|
	const bool use_full_crc32 = fil_space_t::full_crc32(flags);
 | 
						|
	const bool iscompressed = fil_space_t::zip_size(flags);
 | 
						|
 | 
						|
	memcpy(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4);
 | 
						|
	memcpy(stored2, page + physical_page_size -
 | 
						|
	       FIL_PAGE_END_LSN_OLD_CHKSUM, 4);
 | 
						|
 | 
						|
	/* Check if page is empty, exclude the checksum field */
 | 
						|
	if (is_page_empty(page + 4, physical_page_size - 12)
 | 
						|
	    && is_page_empty(page + physical_page_size - 4, 4)) {
 | 
						|
 | 
						|
		memset(page + FIL_PAGE_SPACE_OR_CHKSUM, 0, 4);
 | 
						|
		memset(page + physical_page_size -
 | 
						|
		       FIL_PAGE_END_LSN_OLD_CHKSUM, 0, 4);
 | 
						|
 | 
						|
		goto func_exit;
 | 
						|
	}
 | 
						|
 | 
						|
	if (iscompressed) {
 | 
						|
		/* ROW_FORMAT=COMPRESSED */
 | 
						|
		checksum = page_zip_calc_checksum(page, physical_page_size,
 | 
						|
						  false);
 | 
						|
 | 
						|
		mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, checksum);
 | 
						|
		if (is_log_enabled) {
 | 
						|
			fprintf(log_file, "page::" UINT32PF "; Updated checksum ="
 | 
						|
				" " UINT32PF "\n", cur_page_num, checksum);
 | 
						|
		}
 | 
						|
 | 
						|
	} else if (use_full_crc32) {
 | 
						|
		ulint payload = buf_page_full_crc32_size(page, NULL, NULL)
 | 
						|
			- FIL_PAGE_FCRC32_CHECKSUM;
 | 
						|
		checksum = my_crc32c(0, page, payload);
 | 
						|
		byte* c = page + payload;
 | 
						|
		if (mach_read_from_4(c) == checksum) return false;
 | 
						|
		mach_write_to_4(c, checksum);
 | 
						|
		if (is_log_enabled) {
 | 
						|
			fprintf(log_file, "page::" UINT32PF "; Updated checksum"
 | 
						|
				" = %u\n", cur_page_num, checksum);
 | 
						|
		}
 | 
						|
		return true;
 | 
						|
	} else {
 | 
						|
		/* page is uncompressed. */
 | 
						|
 | 
						|
		/* Store the new formula checksum */
 | 
						|
		checksum = buf_calc_page_crc32(page);
 | 
						|
 | 
						|
		mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, checksum);
 | 
						|
		if (is_log_enabled) {
 | 
						|
			fprintf(log_file, "page::" UINT32PF
 | 
						|
				"; Updated checksum = " UINT32PF "\n",
 | 
						|
				cur_page_num, checksum);
 | 
						|
		}
 | 
						|
 | 
						|
		mach_write_to_4(page + physical_page_size -
 | 
						|
				FIL_PAGE_END_LSN_OLD_CHKSUM,checksum);
 | 
						|
	}
 | 
						|
 | 
						|
func_exit:
 | 
						|
	/* The following code is to check the stored checksum with the
 | 
						|
	calculated checksum. If it matches, then return FALSE to skip
 | 
						|
	the rewrite of checksum, otherwise return TRUE. */
 | 
						|
	if (iscompressed) {
 | 
						|
		if (!memcmp(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4)) {
 | 
						|
			return (false);
 | 
						|
		}
 | 
						|
		return (true);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!memcmp(stored1, page + FIL_PAGE_SPACE_OR_CHKSUM, 4)
 | 
						|
	    && !memcmp(stored2, page + physical_page_size -
 | 
						|
		       FIL_PAGE_END_LSN_OLD_CHKSUM, 4)) {
 | 
						|
		return (false);
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	return (true);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 Write the content to the file
 | 
						|
@param[in]		filename	name of the file.
 | 
						|
@param[in,out]		file		file pointer where content
 | 
						|
					have to be written
 | 
						|
@param[in]		buf		file buffer read
 | 
						|
@param[in]		flags		tablespace flags
 | 
						|
@param[in,out]		pos		current file position.
 | 
						|
 | 
						|
@retval true	if successfully written
 | 
						|
@retval false	if a non-recoverable error occurred
 | 
						|
*/
 | 
						|
static
 | 
						|
bool
 | 
						|
write_file(
 | 
						|
	const char*	filename,
 | 
						|
	FILE*		file,
 | 
						|
	byte*		buf,
 | 
						|
	uint32_t	flags,
 | 
						|
	fpos_t*		pos)
 | 
						|
{
 | 
						|
	bool	do_update;
 | 
						|
 | 
						|
	do_update = update_checksum(buf, flags);
 | 
						|
 | 
						|
	if (file != stdin) {
 | 
						|
		if (do_update) {
 | 
						|
			/* Set the previous file pointer position
 | 
						|
			saved in pos to current file position. */
 | 
						|
			if (0 != fsetpos(file, pos)) {
 | 
						|
				perror("fsetpos");
 | 
						|
				return(false);
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			/* Store the current file position in pos */
 | 
						|
			if (0 != fgetpos(file, pos)) {
 | 
						|
				perror("fgetpos");
 | 
						|
				return(false);
 | 
						|
			}
 | 
						|
			return(true);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (physical_page_size
 | 
						|
	    != fwrite(buf, 1, physical_page_size,
 | 
						|
		      file == stdin ? stdout : file)) {
 | 
						|
		fprintf(stderr,
 | 
						|
			"Failed to write page::" UINT32PF " to %s: %s\n",
 | 
						|
			cur_page_num, filename, strerror(errno));
 | 
						|
 | 
						|
		return(false);
 | 
						|
	}
 | 
						|
	if (file != stdin) {
 | 
						|
		fflush(file);
 | 
						|
		/* Store the current file position in pos */
 | 
						|
		if (0 != fgetpos(file, pos)) {
 | 
						|
			perror("fgetpos");
 | 
						|
			return(false);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return(true);
 | 
						|
}
 | 
						|
 | 
						|
// checks using current xdes page whether the page is free
 | 
						|
static inline bool is_page_free(const byte *xdes, ulint physical_page_size,
 | 
						|
                                uint32_t page_no)
 | 
						|
{
 | 
						|
  const byte *des=
 | 
						|
      xdes + XDES_ARR_OFFSET +
 | 
						|
      xdes_size * ((page_no & (physical_page_size - 1)) / extent_size);
 | 
						|
  return xdes_is_free(des, page_no % extent_size);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
Parse the page and collect/dump the information about page type
 | 
						|
@param [in] page	buffer page
 | 
						|
@param [out] xdes	extend descriptor page
 | 
						|
@param [in] file	file for diagnosis.
 | 
						|
@param [in] is_encrypted  tablespace is encrypted
 | 
						|
*/
 | 
						|
void
 | 
						|
parse_page(
 | 
						|
	const byte*	page,
 | 
						|
	byte*		xdes,
 | 
						|
	FILE*		file,
 | 
						|
	bool is_encrypted)
 | 
						|
{
 | 
						|
	unsigned long long id;
 | 
						|
	uint16_t undo_page_type;
 | 
						|
	const char *str;
 | 
						|
	ulint n_recs;
 | 
						|
	uint32_t page_no, left_page_no, right_page_no;
 | 
						|
	ulint data_bytes;
 | 
						|
	bool is_leaf;
 | 
						|
	ulint size_range_id;
 | 
						|
 | 
						|
	/* Check whether page is doublewrite buffer. */
 | 
						|
	str = skip_page ? "Double_write_buffer" : "-";
 | 
						|
	page_no = mach_read_from_4(page + FIL_PAGE_OFFSET);
 | 
						|
	if (skip_freed_pages) {
 | 
						|
		const byte *des= xdes + XDES_ARR_OFFSET +
 | 
						|
			xdes_size * ((page_no & (physical_page_size - 1))
 | 
						|
				     / extent_size);
 | 
						|
		if (mach_read_from_4(des) != XDES_FSEG &&
 | 
						|
		    xdes_is_free(des, page_no % extent_size)) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	switch (fil_page_get_type(page)) {
 | 
						|
 | 
						|
	case FIL_PAGE_INDEX: {
 | 
						|
		uint32_t key_version = mach_read_from_4(page + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
 | 
						|
		page_type.n_fil_page_index++;
 | 
						|
 | 
						|
		/* If page is encrypted we can't read index header */
 | 
						|
		if (!is_encrypted) {
 | 
						|
			id = mach_read_from_8(page + PAGE_HEADER + PAGE_INDEX_ID);
 | 
						|
			n_recs = mach_read_from_2(page + PAGE_HEADER + PAGE_N_RECS);
 | 
						|
			page_no = mach_read_from_4(page + FIL_PAGE_OFFSET);
 | 
						|
			left_page_no = mach_read_from_4(page + FIL_PAGE_PREV);
 | 
						|
			right_page_no = mach_read_from_4(page + FIL_PAGE_NEXT);
 | 
						|
			ulint is_comp = mach_read_from_2(page + PAGE_HEADER + PAGE_N_HEAP) & 0x8000;
 | 
						|
			ulint level = mach_read_from_2(page + PAGE_HEADER + PAGE_LEVEL);
 | 
						|
			ulint garbage = mach_read_from_2(page + PAGE_HEADER + PAGE_GARBAGE);
 | 
						|
 | 
						|
 | 
						|
			data_bytes = (ulint)(mach_read_from_2(page + PAGE_HEADER + PAGE_HEAP_TOP)
 | 
						|
				- (is_comp
 | 
						|
					? PAGE_NEW_SUPREMUM_END
 | 
						|
					: PAGE_OLD_SUPREMUM_END)
 | 
						|
				- garbage);
 | 
						|
 | 
						|
			is_leaf = (!*(const uint16*) (page + (PAGE_HEADER + PAGE_LEVEL)));
 | 
						|
 | 
						|
			if (file) {
 | 
						|
				fprintf(file, "#::" UINT32PF "\t\t|\t\tIndex page\t\t\t|"
 | 
						|
					"\tindex id=%llu,", cur_page_num, id);
 | 
						|
 | 
						|
				fprintf(file,
 | 
						|
					" page level=" ULINTPF
 | 
						|
					", No. of records=" ULINTPF
 | 
						|
					", garbage=" ULINTPF ", %s\n",
 | 
						|
					level, n_recs, garbage, str);
 | 
						|
			}
 | 
						|
 | 
						|
			size_range_id = (data_bytes * SIZE_RANGES_FOR_PAGE
 | 
						|
				+ srv_page_size - 1) / srv_page_size;
 | 
						|
 | 
						|
			if (size_range_id > SIZE_RANGES_FOR_PAGE + 1) {
 | 
						|
				/* data_bytes is bigger than logical_page_size */
 | 
						|
				size_range_id = SIZE_RANGES_FOR_PAGE + 1;
 | 
						|
			}
 | 
						|
			if (per_page_details) {
 | 
						|
				printf("index id=%llu page " UINT32PF " leaf %d n_recs " ULINTPF " data_bytes " ULINTPF
 | 
						|
					"\n", id, page_no, is_leaf, n_recs, data_bytes);
 | 
						|
			}
 | 
						|
			/* update per-index statistics */
 | 
						|
			{
 | 
						|
				per_index_stats &index = index_ids[id];
 | 
						|
				if (is_page_free(xdes, physical_page_size, page_no)) {
 | 
						|
					index.free_pages++;
 | 
						|
					return;
 | 
						|
				}
 | 
						|
 | 
						|
				index.pages++;
 | 
						|
 | 
						|
				if (is_leaf) {
 | 
						|
					index.leaf_pages++;
 | 
						|
					if (data_bytes > index.max_data_size) {
 | 
						|
						index.max_data_size = data_bytes;
 | 
						|
					}
 | 
						|
					struct per_page_stats pp(n_recs, data_bytes,
 | 
						|
						left_page_no, right_page_no);
 | 
						|
 | 
						|
					index.leaves[page_no] = pp;
 | 
						|
 | 
						|
					if (left_page_no == ULINT32_UNDEFINED) {
 | 
						|
						index.first_leaf_page = page_no;
 | 
						|
						index.count++;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				index.total_n_recs += n_recs;
 | 
						|
				index.total_data_bytes += data_bytes;
 | 
						|
				index.pages_in_size_range[size_range_id] ++;
 | 
						|
			}
 | 
						|
		} else if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tEncrypted Index page\t\t\t|"
 | 
						|
				"\tkey_version " UINT32PF ",%s\n", cur_page_num, key_version, str);
 | 
						|
		}
 | 
						|
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	case FIL_PAGE_UNDO_LOG:
 | 
						|
		page_type.n_fil_page_undo_log++;
 | 
						|
		undo_page_type = mach_read_from_2(page +
 | 
						|
				     TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE);
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tUndo log page\t\t\t|",
 | 
						|
				cur_page_num);
 | 
						|
		}
 | 
						|
		page_type.n_undo++;
 | 
						|
		undo_page_type = mach_read_from_2(page + TRX_UNDO_SEG_HDR +
 | 
						|
						  TRX_UNDO_STATE);
 | 
						|
		switch (undo_page_type) {
 | 
						|
			case TRX_UNDO_ACTIVE:
 | 
						|
				page_type.n_undo_state_active++;
 | 
						|
				if (file) {
 | 
						|
					fprintf(file, ", %s", "Undo log of "
 | 
						|
						"an active transaction");
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			case TRX_UNDO_CACHED:
 | 
						|
				page_type.n_undo_state_cached++;
 | 
						|
				if (file) {
 | 
						|
					fprintf(file, ", %s", "Page is "
 | 
						|
						"cached for quick reuse");
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			case TRX_UNDO_TO_PURGE:
 | 
						|
				page_type.n_undo_state_to_purge++;
 | 
						|
				if (file) {
 | 
						|
					fprintf(file, ", %s", "Will be "
 | 
						|
						"freed in purge when all undo"
 | 
						|
					"data in it is removed");
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			case TRX_UNDO_PREPARED:
 | 
						|
				page_type.n_undo_state_prepared++;
 | 
						|
				if (file) {
 | 
						|
					fprintf(file, ", %s", "Undo log of "
 | 
						|
						"an prepared transaction");
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			default:
 | 
						|
				page_type.n_undo_state_other++;
 | 
						|
				break;
 | 
						|
		}
 | 
						|
		if(file) {
 | 
						|
			fprintf(file, ", %s\n", str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_INODE:
 | 
						|
		page_type.n_fil_page_inode++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tInode page\t\t\t|"
 | 
						|
				"\t%s\n",cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_IBUF_FREE_LIST:
 | 
						|
		page_type.n_fil_page_ibuf_free_list++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tInsert buffer free list"
 | 
						|
				" page\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_TYPE_ALLOCATED:
 | 
						|
		page_type.n_fil_page_type_allocated++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tFreshly allocated "
 | 
						|
				"page\t\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_IBUF_BITMAP:
 | 
						|
		page_type.n_fil_page_ibuf_bitmap++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tInsert Buffer "
 | 
						|
				"Bitmap\t\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_TYPE_SYS:
 | 
						|
		page_type.n_fil_page_type_sys++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tSystem page\t\t\t|"
 | 
						|
				"\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_TYPE_TRX_SYS:
 | 
						|
		page_type.n_fil_page_type_trx_sys++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tTransaction system "
 | 
						|
				"page\t\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_TYPE_FSP_HDR:
 | 
						|
		page_type.n_fil_page_type_fsp_hdr++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tFile Space "
 | 
						|
				"Header\t\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_TYPE_XDES:
 | 
						|
		page_type.n_fil_page_type_xdes++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tExtent descriptor "
 | 
						|
				"page\t\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_TYPE_BLOB:
 | 
						|
		page_type.n_fil_page_type_blob++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tBLOB page\t\t\t|\t%s\n",
 | 
						|
				cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_TYPE_ZBLOB:
 | 
						|
		page_type.n_fil_page_type_zblob++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tCompressed BLOB "
 | 
						|
				"page\t\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_TYPE_ZBLOB2:
 | 
						|
		page_type.n_fil_page_type_zblob2++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tSubsequent Compressed "
 | 
						|
				"BLOB page\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
			break;
 | 
						|
 | 
						|
	case FIL_PAGE_PAGE_COMPRESSED:
 | 
						|
		page_type.n_fil_page_type_page_compressed++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tPage compressed "
 | 
						|
				"page\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
 | 
						|
	case FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED:
 | 
						|
		page_type.n_fil_page_type_page_compressed_encrypted++;
 | 
						|
		if (file) {
 | 
						|
			fprintf(file, "#::" UINT32PF "\t\t|\t\tPage compressed encrypted "
 | 
						|
				"page\t|\t%s\n", cur_page_num, str);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		page_type.n_fil_page_type_other++;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
/**
 | 
						|
@param [in/out] file_name	name of the filename
 | 
						|
 | 
						|
@retval FILE pointer if successfully created else NULL when error occurred.
 | 
						|
*/
 | 
						|
FILE*
 | 
						|
create_file(
 | 
						|
	char*	file_name)
 | 
						|
{
 | 
						|
	FILE*	file = NULL;
 | 
						|
 | 
						|
#ifndef _WIN32
 | 
						|
	file = fopen(file_name, "wb");
 | 
						|
	if (file == NULL) {
 | 
						|
		fprintf(stderr, "Failed to create file: %s: %s\n",
 | 
						|
			file_name, strerror(errno));
 | 
						|
		return(NULL);
 | 
						|
	}
 | 
						|
#else
 | 
						|
	HANDLE		hFile;		/* handle to open file. */
 | 
						|
	int fd = 0;
 | 
						|
	hFile = CreateFile((LPCTSTR) file_name,
 | 
						|
			  GENERIC_READ | GENERIC_WRITE,
 | 
						|
			  FILE_SHARE_READ | FILE_SHARE_DELETE,
 | 
						|
			  NULL, CREATE_NEW, NULL, NULL);
 | 
						|
 | 
						|
	if (hFile == INVALID_HANDLE_VALUE) {
 | 
						|
		/* print the error message. */
 | 
						|
		fprintf(stderr, "Filename::%s %s\n",
 | 
						|
			file_name,
 | 
						|
			error_message(GetLastError()));
 | 
						|
 | 
						|
			return(NULL);
 | 
						|
		}
 | 
						|
 | 
						|
	/* get the file descriptor. */
 | 
						|
	fd= _open_osfhandle((intptr_t)hFile, _O_RDWR | _O_BINARY);
 | 
						|
	file = fdopen(fd, "wb");
 | 
						|
#endif /* _WIN32 */
 | 
						|
 | 
						|
	return(file);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 Print the page type count of a tablespace.
 | 
						|
 @param [in] fil_out	stream where the output goes.
 | 
						|
*/
 | 
						|
void
 | 
						|
print_summary(
 | 
						|
	FILE*	fil_out)
 | 
						|
{
 | 
						|
	fprintf(fil_out, "\n================PAGE TYPE SUMMARY==============\n");
 | 
						|
	fprintf(fil_out, "#PAGE_COUNT\tPAGE_TYPE");
 | 
						|
	fprintf(fil_out, "\n===============================================\n");
 | 
						|
	fprintf(fil_out, "%8d\tIndex page\n",
 | 
						|
		page_type.n_fil_page_index);
 | 
						|
	fprintf(fil_out, "%8d\tUndo log page\n",
 | 
						|
		page_type.n_fil_page_undo_log);
 | 
						|
	fprintf(fil_out, "%8d\tInode page\n",
 | 
						|
		page_type.n_fil_page_inode);
 | 
						|
	fprintf(fil_out, "%8d\tInsert buffer free list page\n",
 | 
						|
		page_type.n_fil_page_ibuf_free_list);
 | 
						|
	fprintf(fil_out, "%8d\tFreshly allocated page\n",
 | 
						|
		page_type.n_fil_page_type_allocated);
 | 
						|
	fprintf(fil_out, "%8d\tInsert buffer bitmap\n",
 | 
						|
		page_type.n_fil_page_ibuf_bitmap);
 | 
						|
	fprintf(fil_out, "%8d\tSystem page\n",
 | 
						|
		page_type.n_fil_page_type_sys);
 | 
						|
	fprintf(fil_out, "%8d\tTransaction system page\n",
 | 
						|
		page_type.n_fil_page_type_trx_sys);
 | 
						|
	fprintf(fil_out, "%8d\tFile Space Header\n",
 | 
						|
		page_type.n_fil_page_type_fsp_hdr);
 | 
						|
	fprintf(fil_out, "%8d\tExtent descriptor page\n",
 | 
						|
		page_type.n_fil_page_type_xdes);
 | 
						|
	fprintf(fil_out, "%8d\tBLOB page\n",
 | 
						|
		page_type.n_fil_page_type_blob);
 | 
						|
	fprintf(fil_out, "%8d\tCompressed BLOB page\n",
 | 
						|
		page_type.n_fil_page_type_zblob);
 | 
						|
	fprintf(fil_out, "%8d\tPage compressed page\n",
 | 
						|
		page_type.n_fil_page_type_page_compressed);
 | 
						|
	fprintf(fil_out, "%8d\tPage compressed encrypted page\n",
 | 
						|
		page_type.n_fil_page_type_page_compressed_encrypted);
 | 
						|
	fprintf(fil_out, "%8d\tOther type of page\n",
 | 
						|
		page_type.n_fil_page_type_other);
 | 
						|
 | 
						|
	fprintf(fil_out, "\n===============================================\n");
 | 
						|
	fprintf(fil_out, "Additional information:\n");
 | 
						|
	fprintf(fil_out, "Undo page type: %d\n", page_type.n_undo);
 | 
						|
	fprintf(fil_out, "Undo page state: %d active, %d cached, %d"
 | 
						|
		" to_purge, %d prepared, %d other\n",
 | 
						|
		page_type.n_undo_state_active,
 | 
						|
		page_type.n_undo_state_cached,
 | 
						|
		page_type.n_undo_state_to_purge,
 | 
						|
		page_type.n_undo_state_prepared,
 | 
						|
		page_type.n_undo_state_other);
 | 
						|
 | 
						|
	fprintf(fil_out, "index_id\t#pages\t\t#leaf_pages\t#recs_per_page"
 | 
						|
		"\t#bytes_per_page\n");
 | 
						|
 | 
						|
	for (const auto &ids : index_ids) {
 | 
						|
		const per_index_stats& index = ids.second;
 | 
						|
		if (!index.pages) {
 | 
						|
			DBUG_ASSERT(index.free_pages);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		fprintf(fil_out, "%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",
 | 
						|
			ids.first, index.pages, index.leaf_pages,
 | 
						|
			index.total_n_recs / index.pages,
 | 
						|
			index.total_data_bytes / index.pages);
 | 
						|
	}
 | 
						|
 | 
						|
	fprintf(fil_out, "\n");
 | 
						|
	fprintf(fil_out, "index_id\tpage_data_bytes_histgram(empty,...,oversized)\n");
 | 
						|
 | 
						|
	for (std::map<unsigned long long, per_index_stats>::const_iterator it = index_ids.begin();
 | 
						|
	     it != index_ids.end(); it++) {
 | 
						|
		fprintf(fil_out, "%lld\t", it->first);
 | 
						|
		const per_index_stats& index = it->second;
 | 
						|
		for (ulint i = 0; i < SIZE_RANGES_FOR_PAGE+2; i++) {
 | 
						|
			fprintf(fil_out, "\t%lld", index.pages_in_size_range[i]);
 | 
						|
		}
 | 
						|
		fprintf(fil_out, "\n");
 | 
						|
	}
 | 
						|
 | 
						|
	if (do_leaf) {
 | 
						|
		print_leaf_stats(fil_out);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* command line argument for innochecksum tool. */
 | 
						|
static struct my_option innochecksum_options[] = {
 | 
						|
  {"help", '?', "Displays this help and exits.",
 | 
						|
    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"info", 'I', "Synonym for --help.",
 | 
						|
    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"version", 'V', "Displays version information and exits.",
 | 
						|
    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"verbose", 'v', "Verbose (prints progress every 5 seconds).",
 | 
						|
    &verbose, &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
#ifndef DBUG_OFF
 | 
						|
  {"debug", '#', "Output debug log. See https://mariadb.com/kb/en/library/creating-a-trace-file/",
 | 
						|
    &dbug_setting, &dbug_setting, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
#endif /* !DBUG_OFF */
 | 
						|
  {"count", 'c', "Print the count of pages in the file and exits.",
 | 
						|
    &just_count, &just_count, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"start_page", 's', "Start on this page number (0 based).",
 | 
						|
    &start_page, &start_page, 0, GET_UINT, REQUIRED_ARG,
 | 
						|
    0, 0, FIL_NULL, 0, 1, 0},
 | 
						|
  {"end_page", 'e', "End at this page number (0 based).",
 | 
						|
    &end_page, &end_page, 0, GET_UINT, REQUIRED_ARG,
 | 
						|
    0, 0, FIL_NULL, 0, 1, 0},
 | 
						|
  {"page", 'p', "Check only this page (0 based).",
 | 
						|
    &do_page, &do_page, 0, GET_UINT, REQUIRED_ARG,
 | 
						|
    0, 0, FIL_NULL, 0, 1, 0},
 | 
						|
  {"no-check", 'n', "Ignore the checksum verification.",
 | 
						|
    &no_check, &no_check, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"allow-mismatches", 'a', "Maximum checksum mismatch allowed.",
 | 
						|
    &allow_mismatches, &allow_mismatches, 0,
 | 
						|
    GET_ULL, REQUIRED_ARG, 0, 0, ULLONG_MAX, 0, 1, 0},
 | 
						|
  {"write", 'w', "Rewrite the checksum.",
 | 
						|
    &do_write, &do_write, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"page-type-summary", 'S', "Display a count of each page type "
 | 
						|
   "in a tablespace.", &page_type_summary, &page_type_summary, 0,
 | 
						|
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"page-type-dump", 'D', "Dump the page type info for each page in a "
 | 
						|
   "tablespace.", &page_dump_filename, &page_dump_filename, 0,
 | 
						|
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"per-page-details", 'i', "Print out per-page detail information.",
 | 
						|
   &per_page_details, &per_page_details, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
   {"log", 'l', "log output.",
 | 
						|
     &log_filename, &log_filename, 0,
 | 
						|
      GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"leaf", 'f', "Examine leaf index pages",
 | 
						|
    &do_leaf, &do_leaf, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 | 
						|
  {"merge", 'm', "leaf page count if merge given number of consecutive pages",
 | 
						|
   &n_merge, &n_merge, 0, GET_ULONG, REQUIRED_ARG, 0, 0, (longlong)10L, 0, 1, 0},
 | 
						|
  {"skip-freed-pages", 'r', "skip freed pages for the tablespace",
 | 
						|
   &skip_freed_pages, &skip_freed_pages, 0, GET_BOOL, NO_ARG,
 | 
						|
   0, 0, 0, 0, 0, 0},
 | 
						|
 | 
						|
  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 | 
						|
};
 | 
						|
 | 
						|
/** Print out the version and build information. */
 | 
						|
static void print_version()
 | 
						|
{
 | 
						|
#ifdef DBUG_OFF
 | 
						|
	printf("%s Ver %s, for %s (%s)\n",
 | 
						|
		my_progname, PACKAGE_VERSION,
 | 
						|
		SYSTEM_TYPE, MACHINE_TYPE);
 | 
						|
#else
 | 
						|
	printf("%s-debug Ver %s, for %s (%s)\n",
 | 
						|
		my_progname, PACKAGE_VERSION,
 | 
						|
		SYSTEM_TYPE, MACHINE_TYPE);
 | 
						|
#endif /* DBUG_OFF */
 | 
						|
}
 | 
						|
 | 
						|
static void usage(void)
 | 
						|
{
 | 
						|
	print_version();
 | 
						|
	puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
 | 
						|
	printf("InnoDB offline file checksum utility.\n");
 | 
						|
	printf("Usage: %s [-c] [-r] [-s <start page>] [-e <end page>] "
 | 
						|
		"[-p <page>] [-i] [-v]  [-a <allow mismatches>] [-n] "
 | 
						|
		"[-S] [-D <page type dump>] "
 | 
						|
		"[-l <log>] [-l] [-m <merge pages>] <filename or [-]>\n", my_progname);
 | 
						|
	printf("See https://mariadb.com/kb/en/library/innochecksum/"
 | 
						|
	       " for usage hints.\n");
 | 
						|
	my_print_help(innochecksum_options);
 | 
						|
	my_print_variables(innochecksum_options);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" my_bool
 | 
						|
innochecksum_get_one_option(
 | 
						|
	const struct my_option	*opt,
 | 
						|
	const char		*IF_DBUG(argument,),
 | 
						|
	const char *)
 | 
						|
{
 | 
						|
	switch (opt->id) {
 | 
						|
#ifndef DBUG_OFF
 | 
						|
	case '#':
 | 
						|
		dbug_setting = argument
 | 
						|
			? argument
 | 
						|
			: IF_WIN("d:O,innochecksum.trace",
 | 
						|
				 "d:o,/tmp/innochecksum.trace");
 | 
						|
		DBUG_PUSH(dbug_setting);
 | 
						|
		break;
 | 
						|
#endif /* !DBUG_OFF */
 | 
						|
	case 'e':
 | 
						|
		use_end_page = true;
 | 
						|
		break;
 | 
						|
	case 'p':
 | 
						|
		end_page = start_page = do_page;
 | 
						|
		use_end_page = true;
 | 
						|
		do_one_page = true;
 | 
						|
		break;
 | 
						|
	case 'V':
 | 
						|
		print_version();
 | 
						|
		my_end(0);
 | 
						|
		exit(EXIT_SUCCESS);
 | 
						|
		break;
 | 
						|
	case 'D':
 | 
						|
		page_type_dump = true;
 | 
						|
		break;
 | 
						|
	case 'l':
 | 
						|
		is_log_enabled = true;
 | 
						|
		break;
 | 
						|
	case 'I':
 | 
						|
	case '?':
 | 
						|
		usage();
 | 
						|
		my_end(0);
 | 
						|
		exit(EXIT_SUCCESS);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return(false);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
bool
 | 
						|
get_options(
 | 
						|
	int	*argc,
 | 
						|
	char	***argv)
 | 
						|
{
 | 
						|
	if (handle_options(argc, argv, innochecksum_options,
 | 
						|
			innochecksum_get_one_option)) {
 | 
						|
		my_end(0);
 | 
						|
		exit(true);
 | 
						|
	}
 | 
						|
 | 
						|
	/* The next arg must be the filename */
 | 
						|
	if (!*argc) {
 | 
						|
		usage();
 | 
						|
		my_end(0);
 | 
						|
		return (true);
 | 
						|
	}
 | 
						|
 | 
						|
	return (false);
 | 
						|
}
 | 
						|
 | 
						|
/** Check from page 0 if table is encrypted.
 | 
						|
@param[in]	filename	Filename
 | 
						|
@param[in]	page		Page 0
 | 
						|
@retval true if tablespace is encrypted, false if not
 | 
						|
*/
 | 
						|
static bool check_encryption(const char* filename, const byte* page)
 | 
						|
{
 | 
						|
	ulint offset = FSP_HEADER_OFFSET + XDES_ARR_OFFSET + xdes_size *
 | 
						|
		physical_page_size / extent_size;
 | 
						|
 | 
						|
	if (memcmp(page + offset, CRYPT_MAGIC, MAGIC_SZ) != 0) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	ulint type = mach_read_from_1(page + offset + MAGIC_SZ + 0);
 | 
						|
 | 
						|
	if (! (type == CRYPT_SCHEME_UNENCRYPTED ||
 | 
						|
	       type == CRYPT_SCHEME_1)) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	ulint iv_length = mach_read_from_1(page + offset + MAGIC_SZ + 1);
 | 
						|
 | 
						|
	if (iv_length != CRYPT_SCHEME_1_IV_LEN) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	uint32_t min_key_version = mach_read_from_4
 | 
						|
		(page + offset + MAGIC_SZ + 2 + iv_length);
 | 
						|
 | 
						|
	uint32_t key_id = mach_read_from_4
 | 
						|
		(page + offset + MAGIC_SZ + 2 + iv_length + 4);
 | 
						|
 | 
						|
	if (type == CRYPT_SCHEME_1 && is_log_enabled) {
 | 
						|
		fprintf(log_file,"Tablespace %s encrypted key_version " UINT32PF " key_id " UINT32PF "\n",
 | 
						|
			filename, min_key_version, key_id);
 | 
						|
	}
 | 
						|
 | 
						|
	return (type == CRYPT_SCHEME_1);
 | 
						|
}
 | 
						|
 | 
						|
/** Verify page checksum.
 | 
						|
@param[in] buf			page to verify
 | 
						|
@param[in] zip_size		ROW_FORMAT=COMPRESSED page size, or 0
 | 
						|
@param[in] is_encrypted		true if tablespace is encrypted
 | 
						|
@param[in,out] mismatch_count	Number of pages failed in checksum verify
 | 
						|
@param[in]	flags		tablespace flags
 | 
						|
@retval 0 if page checksum matches or 1 if it does not match */
 | 
						|
static int verify_checksum(
 | 
						|
	byte*			buf,
 | 
						|
	bool			is_encrypted,
 | 
						|
	unsigned long long*	mismatch_count,
 | 
						|
	uint32_t		flags)
 | 
						|
{
 | 
						|
	int exit_status = 0;
 | 
						|
	if (is_page_corrupted(buf, is_encrypted, flags)) {
 | 
						|
		fprintf(stderr, "Fail: page::" UINT32PF " invalid\n",
 | 
						|
			cur_page_num);
 | 
						|
 | 
						|
		(*mismatch_count)++;
 | 
						|
 | 
						|
		if (*mismatch_count > allow_mismatches) {
 | 
						|
			fprintf(stderr,
 | 
						|
				"Exceeded the "
 | 
						|
				"maximum allowed "
 | 
						|
				"checksum mismatch "
 | 
						|
				"count::%llu current::%llu\n",
 | 
						|
				*mismatch_count,
 | 
						|
				allow_mismatches);
 | 
						|
 | 
						|
			exit_status = 1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return (exit_status);
 | 
						|
}
 | 
						|
 | 
						|
/** Rewrite page checksum if needed.
 | 
						|
@param[in]	filename	File name
 | 
						|
@param[in]	fil_in		File pointer
 | 
						|
@param[in]	buf		page
 | 
						|
@param[in]	pos		File position
 | 
						|
@param[in]	is_encrypted	true if tablespace is encrypted
 | 
						|
@param[in]	flags		tablespace flags
 | 
						|
@retval 0 if checksum rewrite was successful, 1 if error was detected */
 | 
						|
static
 | 
						|
int
 | 
						|
rewrite_checksum(
 | 
						|
	const char*	filename,
 | 
						|
	FILE*		fil_in,
 | 
						|
	byte*		buf,
 | 
						|
	fpos_t*		pos,
 | 
						|
	bool		is_encrypted,
 | 
						|
	uint32_t	flags)
 | 
						|
{
 | 
						|
	bool is_compressed = fil_space_t::is_compressed(flags);
 | 
						|
 | 
						|
	/* Rewrite checksum. Note that for encrypted and
 | 
						|
	page compressed tables this is not currently supported. */
 | 
						|
	return do_write && !is_encrypted && !is_compressed
 | 
						|
		&& !write_file(filename, fil_in, buf, flags, pos);
 | 
						|
}
 | 
						|
 | 
						|
int main(
 | 
						|
	int	argc,
 | 
						|
	char	**argv)
 | 
						|
{
 | 
						|
	/* our input file. */
 | 
						|
	FILE*		fil_in = NULL;
 | 
						|
	/* our input filename. */
 | 
						|
	char*		filename;
 | 
						|
	/* Buffer to store pages read. */
 | 
						|
	byte*		buf = NULL;
 | 
						|
	byte*		xdes = NULL;
 | 
						|
	/* bytes read count */
 | 
						|
	ulint		bytes;
 | 
						|
	/* last time */
 | 
						|
	time_t		lastt = 0;
 | 
						|
	/* stat, to get file size. */
 | 
						|
#ifdef _WIN32
 | 
						|
	struct _stat64	st;
 | 
						|
#else
 | 
						|
	struct stat	st;
 | 
						|
#endif /* _WIN32 */
 | 
						|
 | 
						|
	int exit_status = 0;
 | 
						|
 | 
						|
	/* size of file (has to be 64 bits) */
 | 
						|
	unsigned long long int	size		= 0;
 | 
						|
	/* number of pages in file */
 | 
						|
	uint32_t	pages;
 | 
						|
 | 
						|
	off_t		offset			= 0;
 | 
						|
	/* count the no. of page corrupted. */
 | 
						|
	unsigned long long   mismatch_count		= 0;
 | 
						|
 | 
						|
	bool		partial_page_read	= false;
 | 
						|
	/* Enabled when read from stdin is done. */
 | 
						|
	bool		read_from_stdin		= false;
 | 
						|
	FILE*		fil_page_type		= NULL;
 | 
						|
	fpos_t		pos;
 | 
						|
 | 
						|
	/* enable when space_id of given file is zero. */
 | 
						|
	bool		is_system_tablespace = false;
 | 
						|
 | 
						|
	MY_INIT(argv[0]);
 | 
						|
	DBUG_ENTER("main");
 | 
						|
	DBUG_PROCESS(argv[0]);
 | 
						|
 | 
						|
	if (get_options(&argc,&argv)) {
 | 
						|
		exit_status = 1;
 | 
						|
		goto my_exit;
 | 
						|
	}
 | 
						|
 | 
						|
	if (no_check && !do_write) {
 | 
						|
		fprintf(stderr, "Error: --no-check must be associated with "
 | 
						|
			"--write option.\n");
 | 
						|
		exit_status = 1;
 | 
						|
		goto my_exit;
 | 
						|
	}
 | 
						|
 | 
						|
	if (page_type_dump) {
 | 
						|
		fil_page_type = create_file(page_dump_filename);
 | 
						|
		if (!fil_page_type) {
 | 
						|
			exit_status = 1;
 | 
						|
			goto my_exit;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (is_log_enabled) {
 | 
						|
		log_file = create_file(log_filename);
 | 
						|
		if (!log_file) {
 | 
						|
			exit_status = 1;
 | 
						|
			goto my_exit;
 | 
						|
		}
 | 
						|
		fprintf(log_file, "InnoDB File Checksum Utility.\n");
 | 
						|
	}
 | 
						|
 | 
						|
	if (verbose) {
 | 
						|
		my_print_variables(innochecksum_options);
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	buf = static_cast<byte*>(aligned_malloc(UNIV_PAGE_SIZE_MAX,
 | 
						|
						UNIV_PAGE_SIZE_MAX));
 | 
						|
	xdes = static_cast<byte*>(aligned_malloc(UNIV_PAGE_SIZE_MAX,
 | 
						|
						 UNIV_PAGE_SIZE_MAX));
 | 
						|
 | 
						|
	/* The file name is not optional. */
 | 
						|
	for (int i = 0; i < argc; ++i) {
 | 
						|
 | 
						|
		/* Reset parameters for each file. */
 | 
						|
		filename = argv[i];
 | 
						|
		memset(&page_type, 0, sizeof(innodb_page_type));
 | 
						|
		partial_page_read = false;
 | 
						|
		skip_page = false;
 | 
						|
 | 
						|
		if (is_log_enabled) {
 | 
						|
			fprintf(log_file, "Filename = %s\n", filename);
 | 
						|
		}
 | 
						|
 | 
						|
		if (*filename == '-') {
 | 
						|
			/* read from stdin. */
 | 
						|
			fil_in = stdin;
 | 
						|
			read_from_stdin = true;
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		/* stat the file to get size and page count. */
 | 
						|
		if (!read_from_stdin &&
 | 
						|
#ifdef _WIN32
 | 
						|
			_stat64(filename, &st)) {
 | 
						|
#else
 | 
						|
			stat(filename, &st)) {
 | 
						|
#endif /* _WIN32 */
 | 
						|
			fprintf(stderr, "Error: %s cannot be found\n",
 | 
						|
				filename);
 | 
						|
 | 
						|
			exit_status = 1;
 | 
						|
			goto my_exit;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!read_from_stdin) {
 | 
						|
			size = st.st_size;
 | 
						|
			fil_in = open_file(filename);
 | 
						|
			/*If fil_in is NULL, terminate as some error encountered */
 | 
						|
			if(fil_in == NULL) {
 | 
						|
				exit_status = 1;
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
			/* Save the current file pointer in pos variable.*/
 | 
						|
			if (0 != fgetpos(fil_in, &pos)) {
 | 
						|
				perror("fgetpos");
 | 
						|
				exit_status = 1;
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* Read the minimum page size. */
 | 
						|
		bytes = fread(buf, 1, UNIV_ZIP_SIZE_MIN, fil_in);
 | 
						|
		partial_page_read = true;
 | 
						|
 | 
						|
		if (bytes != UNIV_ZIP_SIZE_MIN) {
 | 
						|
			fprintf(stderr, "Error: Was not able to read the "
 | 
						|
				"minimum page size ");
 | 
						|
			fprintf(stderr, "of %d bytes.  Bytes read was " ULINTPF "\n",
 | 
						|
				UNIV_ZIP_SIZE_MIN, bytes);
 | 
						|
 | 
						|
			exit_status = 1;
 | 
						|
			goto my_exit;
 | 
						|
		}
 | 
						|
 | 
						|
		/* enable variable is_system_tablespace when space_id of given
 | 
						|
		file is zero. Use to skip the checksum verification and rewrite
 | 
						|
		for doublewrite pages. */
 | 
						|
		cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID);
 | 
						|
		cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET);
 | 
						|
 | 
						|
		/* Determine page size, zip_size and page compression
 | 
						|
		from fsp_flags and encryption metadata from page 0 */
 | 
						|
		init_page_size(buf);
 | 
						|
 | 
						|
		uint32_t flags = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + buf);
 | 
						|
 | 
						|
		if (physical_page_size == UNIV_ZIP_SIZE_MIN) {
 | 
						|
			partial_page_read = false;
 | 
						|
		} else {
 | 
						|
			/* Read rest of the page 0 to determine crypt_data */
 | 
						|
			bytes = read_file(buf, partial_page_read, physical_page_size, fil_in);
 | 
						|
			if (bytes != physical_page_size) {
 | 
						|
				fprintf(stderr, "Error: Was not able to read the "
 | 
						|
					"rest of the page ");
 | 
						|
				fprintf(stderr, "of " ULINTPF " bytes.  Bytes read was " ULINTPF "\n",
 | 
						|
					physical_page_size - UNIV_ZIP_SIZE_MIN, bytes);
 | 
						|
 | 
						|
				exit_status = 1;
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
			partial_page_read = false;
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		/* Now that we have full page 0 in buffer, check encryption */
 | 
						|
		bool is_encrypted = check_encryption(filename, buf);
 | 
						|
 | 
						|
		/* Verify page 0 contents. Note that we can't allow
 | 
						|
		checksum mismatch on page 0, because that would mean we
 | 
						|
		could not trust it content. */
 | 
						|
		if (!no_check) {
 | 
						|
			unsigned long long tmp_allow_mismatches = allow_mismatches;
 | 
						|
			allow_mismatches = 0;
 | 
						|
 | 
						|
			exit_status = verify_checksum(buf, is_encrypted,
 | 
						|
						      &mismatch_count, flags);
 | 
						|
 | 
						|
			if (exit_status) {
 | 
						|
				fprintf(stderr, "Error: Page 0 checksum mismatch, can't continue. \n");
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
			allow_mismatches = tmp_allow_mismatches;
 | 
						|
		}
 | 
						|
 | 
						|
		if ((exit_status = rewrite_checksum(
 | 
						|
					filename, fil_in, buf,
 | 
						|
					&pos, is_encrypted, flags))) {
 | 
						|
			goto my_exit;
 | 
						|
		}
 | 
						|
 | 
						|
		if (page_type_dump) {
 | 
						|
			fprintf(fil_page_type,
 | 
						|
				"\n\nFilename::%s\n", filename);
 | 
						|
			fprintf(fil_page_type,
 | 
						|
				"========================================"
 | 
						|
				"======================================\n");
 | 
						|
			fprintf(fil_page_type,
 | 
						|
				"\tPAGE_NO\t\t|\t\tPAGE_TYPE\t\t"
 | 
						|
				"\t|\tEXTRA INFO\n");
 | 
						|
			fprintf(fil_page_type,
 | 
						|
				"========================================"
 | 
						|
				"======================================\n");
 | 
						|
		}
 | 
						|
 | 
						|
		if (per_page_details) {
 | 
						|
			printf("page " UINT32PF " ", cur_page_num);
 | 
						|
		}
 | 
						|
 | 
						|
		memcpy(xdes, buf, physical_page_size);
 | 
						|
 | 
						|
		if (page_type_summary || page_type_dump) {
 | 
						|
			parse_page(buf, xdes, fil_page_type, is_encrypted);
 | 
						|
		}
 | 
						|
 | 
						|
		pages = uint32_t(size / physical_page_size);
 | 
						|
 | 
						|
		if (just_count) {
 | 
						|
			fprintf(read_from_stdin ? stderr : stdout,
 | 
						|
				"Number of pages:" UINT32PF "\n", pages);
 | 
						|
			continue;
 | 
						|
		} else if (verbose && !read_from_stdin) {
 | 
						|
			if (is_log_enabled) {
 | 
						|
				fprintf(log_file, "file %s = %llu bytes "
 | 
						|
					"(" UINT32PF " pages)\n",
 | 
						|
					filename, size, pages);
 | 
						|
				if (do_one_page) {
 | 
						|
					fprintf(log_file, "Innochecksum: "
 | 
						|
						"checking page::"
 | 
						|
						UINT32PF ";\n",
 | 
						|
						do_page);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			if (is_log_enabled) {
 | 
						|
				fprintf(log_file, "Innochecksum: checking "
 | 
						|
					"pages in range::" UINT32PF
 | 
						|
					" to " UINT32PF "\n",
 | 
						|
					start_page, use_end_page ?
 | 
						|
					end_page : (pages - 1));
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		off_t cur_offset = 0;
 | 
						|
		/* Find the first non all-zero page and fetch the
 | 
						|
		space id from there. */
 | 
						|
		while (is_page_all_zeroes(buf, physical_page_size)) {
 | 
						|
			bytes = ulong(read_file(
 | 
						|
					buf, false, physical_page_size,
 | 
						|
					fil_in));
 | 
						|
 | 
						|
			if (feof(fil_in)) {
 | 
						|
				fprintf(stderr, "All are "
 | 
						|
					"zero-filled pages.");
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
 | 
						|
			cur_offset++;
 | 
						|
		}
 | 
						|
 | 
						|
		cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID);
 | 
						|
		is_system_tablespace = (cur_space == 0);
 | 
						|
 | 
						|
		if (cur_offset > 0) {
 | 
						|
			/* Re-read the non-zero page to check the
 | 
						|
			checksum. So move the file pointer to
 | 
						|
			previous position and reset the page number too. */
 | 
						|
			cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET);
 | 
						|
			if (!start_page) {
 | 
						|
				goto first_non_zero;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* seek to the necessary position */
 | 
						|
		if (start_page) {
 | 
						|
			if (!read_from_stdin) {
 | 
						|
				/* If read is not from stdin, we can use
 | 
						|
				fseeko() to position the file pointer to
 | 
						|
				the desired page. */
 | 
						|
				partial_page_read = false;
 | 
						|
 | 
						|
				offset = off_t(ulonglong(start_page)
 | 
						|
					       * physical_page_size);
 | 
						|
				if (IF_WIN(_fseeki64,fseeko)(fil_in, offset,
 | 
						|
							     SEEK_SET)) {
 | 
						|
					perror("Error: Unable to seek to "
 | 
						|
						"necessary offset");
 | 
						|
 | 
						|
					exit_status = 1;
 | 
						|
					goto my_exit;
 | 
						|
				}
 | 
						|
				/* Save the current file pointer in
 | 
						|
				pos variable. */
 | 
						|
				if (0 != fgetpos(fil_in, &pos)) {
 | 
						|
					perror("fgetpos");
 | 
						|
 | 
						|
					exit_status = 1;
 | 
						|
					goto my_exit;
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
 | 
						|
				ulong count = 0;
 | 
						|
 | 
						|
				while (!feof(fil_in)) {
 | 
						|
					if (start_page == count) {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
					/* We read a part of page to find the
 | 
						|
					minimum page size. We cannot reset
 | 
						|
					the file pointer to the beginning of
 | 
						|
					the page if we are reading from stdin
 | 
						|
					(fseeko() on stdin doesn't work). So
 | 
						|
					read only the remaining part of page,
 | 
						|
					if partial_page_read is enable. */
 | 
						|
					bytes = read_file(buf,
 | 
						|
							  partial_page_read,
 | 
						|
							  physical_page_size,
 | 
						|
							  fil_in);
 | 
						|
 | 
						|
					partial_page_read = false;
 | 
						|
					count++;
 | 
						|
 | 
						|
					if (!bytes || feof(fil_in)) {
 | 
						|
						goto unexpected_eof;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* main checksumming loop */
 | 
						|
		cur_page_num = start_page ? start_page : cur_page_num + 1;
 | 
						|
 | 
						|
		while (!feof(fil_in)) {
 | 
						|
 | 
						|
			bytes = read_file(buf, partial_page_read,
 | 
						|
					  physical_page_size, fil_in);
 | 
						|
			partial_page_read = false;
 | 
						|
 | 
						|
			if (!bytes && feof(fil_in)) {
 | 
						|
				if (cur_page_num == start_page) {
 | 
						|
unexpected_eof:
 | 
						|
					fputs("Error: Unable "
 | 
						|
					      "to seek to necessary offset\n",
 | 
						|
					      stderr);
 | 
						|
 | 
						|
					exit_status = 1;
 | 
						|
					goto my_exit;
 | 
						|
				}
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			if (ferror(fil_in)) {
 | 
						|
#ifdef _AIX
 | 
						|
				/*
 | 
						|
				  AIX fseeko can go past eof without error.
 | 
						|
				  the error occurs on read, hence output the
 | 
						|
				  same error here as would show up on other
 | 
						|
				  platforms. This shows up in the mtr test
 | 
						|
				  innodb_zip.innochecksum_3-4k,crc32,innodb
 | 
						|
				*/
 | 
						|
				if (errno == EFBIG) {
 | 
						|
					goto unexpected_eof;
 | 
						|
				}
 | 
						|
#endif
 | 
						|
				fprintf(stderr, "Error reading " ULINTPF " bytes",
 | 
						|
					physical_page_size);
 | 
						|
				perror(" ");
 | 
						|
 | 
						|
				exit_status = 1;
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
 | 
						|
			if (bytes != physical_page_size) {
 | 
						|
				fprintf(stderr, "Error: bytes read (" ULINTPF ") "
 | 
						|
					"doesn't match page size (" ULINTPF ")\n",
 | 
						|
					bytes, physical_page_size);
 | 
						|
				exit_status = 1;
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
 | 
						|
first_non_zero:
 | 
						|
			if (is_system_tablespace) {
 | 
						|
				/* enable when page is double write buffer.*/
 | 
						|
				skip_page = is_page_doublewritebuffer(buf);
 | 
						|
			} else {
 | 
						|
				skip_page = false;
 | 
						|
			}
 | 
						|
 | 
						|
			const uint16_t cur_page_type = fil_page_get_type(buf);
 | 
						|
 | 
						|
			/* FIXME: Page compressed or Page compressed and encrypted
 | 
						|
			pages do not contain checksum. */
 | 
						|
			if (cur_page_type == FIL_PAGE_PAGE_COMPRESSED ||
 | 
						|
			    cur_page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
 | 
						|
				skip_page = true;
 | 
						|
			}
 | 
						|
 | 
						|
			/* If no-check is enabled, skip the
 | 
						|
			checksum verification.*/
 | 
						|
			if (!no_check
 | 
						|
			    && !skip_page
 | 
						|
			    && !is_page_free(xdes, physical_page_size, cur_page_num)
 | 
						|
			    && (exit_status = verify_checksum(
 | 
						|
						buf, is_encrypted,
 | 
						|
						&mismatch_count, flags))) {
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
 | 
						|
			if ((exit_status = rewrite_checksum(
 | 
						|
						filename, fil_in, buf,
 | 
						|
						&pos, is_encrypted, flags))) {
 | 
						|
				goto my_exit;
 | 
						|
			}
 | 
						|
 | 
						|
			/* end if this was the last page we were supposed to check */
 | 
						|
			if (use_end_page && (cur_page_num >= end_page)) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			if (per_page_details) {
 | 
						|
				printf("page " UINT32PF " ", cur_page_num);
 | 
						|
			}
 | 
						|
 | 
						|
			if (cur_page_num % physical_page_size == 0) {
 | 
						|
				memcpy(xdes, buf, physical_page_size);
 | 
						|
			}
 | 
						|
 | 
						|
			if (page_type_summary || page_type_dump) {
 | 
						|
				parse_page(buf, xdes, fil_page_type, is_encrypted);
 | 
						|
			}
 | 
						|
 | 
						|
			/* do counter increase and progress printing */
 | 
						|
			cur_page_num++;
 | 
						|
 | 
						|
			if (verbose && !read_from_stdin) {
 | 
						|
				if ((cur_page_num % 64) == 0) {
 | 
						|
					time_t now = time(0);
 | 
						|
					if (!lastt) {
 | 
						|
						lastt= now;
 | 
						|
					} else if (now - lastt >= 1 && is_log_enabled) {
 | 
						|
						fprintf(log_file, "page::" UINT32PF " "
 | 
						|
							"okay: %.3f%% done\n",
 | 
						|
							(cur_page_num - 1),
 | 
						|
							(double) cur_page_num / pages * 100);
 | 
						|
						lastt = now;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (!read_from_stdin) {
 | 
						|
			/* flcose() will flush the data and release the lock if
 | 
						|
			any acquired. */
 | 
						|
			fclose(fil_in);
 | 
						|
		}
 | 
						|
 | 
						|
		/* Enabled for page type summary. */
 | 
						|
		if (page_type_summary) {
 | 
						|
			if (!read_from_stdin) {
 | 
						|
				fprintf(stdout, "\nFile::%s",filename);
 | 
						|
				print_summary(stdout);
 | 
						|
			} else {
 | 
						|
				print_summary(stderr);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (is_log_enabled) {
 | 
						|
		fclose(log_file);
 | 
						|
	}
 | 
						|
 | 
						|
	goto common_exit;
 | 
						|
 | 
						|
my_exit:
 | 
						|
	if (!read_from_stdin && fil_in) {
 | 
						|
		fclose(fil_in);
 | 
						|
	}
 | 
						|
 | 
						|
	if (log_file) {
 | 
						|
		fclose(log_file);
 | 
						|
	}
 | 
						|
 | 
						|
common_exit:
 | 
						|
	aligned_free(buf);
 | 
						|
	aligned_free(xdes);
 | 
						|
	my_end(exit_status);
 | 
						|
	DBUG_RETURN(exit_status);
 | 
						|
}
 |