mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
Added InnoDB offline file checksum utility.
This commit is contained in:
parent
98dc8e3242
commit
6aeca28a96
3 changed files with 304 additions and 1 deletions
|
@ -1117,3 +1117,4 @@ vio/test-sslclient
|
|||
vio/test-sslserver
|
||||
vio/viotest-ssl
|
||||
ndb/src/dummy.cpp
|
||||
extra/innochecksum
|
||||
|
|
|
@ -38,7 +38,7 @@ $(top_builddir)/include/mysqld_ername.h: $(top_builddir)/include/mysqld_error.h
|
|||
$(top_builddir)/include/sql_state.h: $(top_builddir)/include/mysqld_error.h
|
||||
|
||||
bin_PROGRAMS = replace comp_err perror resolveip my_print_defaults \
|
||||
resolve_stack_dump mysql_waitpid
|
||||
resolve_stack_dump mysql_waitpid innochecksum
|
||||
noinst_PROGRAMS = charset2html
|
||||
|
||||
# Don't update the files from bitkeeper
|
||||
|
|
302
extra/innochecksum.c
Normal file
302
extra/innochecksum.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
InnoDB offline file checksum utility. 85% of the code in this file
|
||||
was taken wholesale fron the InnoDB codebase.
|
||||
|
||||
The final 15% was originally written by Mark Smith of Danga
|
||||
Interactive, Inc. <junior@danga.com>
|
||||
|
||||
Published with a permission.
|
||||
*/
|
||||
|
||||
// needed to have access to 64 bit file functions
|
||||
#define _LARGEFILE_SOURCE
|
||||
#define _LARGEFILE64_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// all of these ripped from InnoDB code from MySQL 4.0.22
|
||||
#define UT_HASH_RANDOM_MASK 1463735687
|
||||
#define UT_HASH_RANDOM_MASK2 1653893711
|
||||
#define FIL_PAGE_LSN 16
|
||||
#define FIL_PAGE_FILE_FLUSH_LSN 26
|
||||
#define FIL_PAGE_OFFSET 4
|
||||
#define FIL_PAGE_DATA 38
|
||||
#define FIL_PAGE_END_LSN_OLD_CHKSUM 8
|
||||
#define FIL_PAGE_SPACE_OR_CHKSUM 0
|
||||
#define UNIV_PAGE_SIZE (2 * 8192)
|
||||
|
||||
// command line argument to do page checks (that's it)
|
||||
// another argument to specify page ranges... seek to right spot and go from there
|
||||
|
||||
typedef unsigned long int ulint;
|
||||
typedef unsigned char byte;
|
||||
|
||||
/* innodb function in name; modified slightly to not have the ASM version (lots of #ifs that didn't apply) */
|
||||
ulint mach_read_from_4(byte *b) {
|
||||
return( ((ulint)(b[0]) << 24)
|
||||
+ ((ulint)(b[1]) << 16)
|
||||
+ ((ulint)(b[2]) << 8)
|
||||
+ (ulint)(b[3])
|
||||
);
|
||||
}
|
||||
|
||||
ulint
|
||||
ut_fold_ulint_pair(
|
||||
/*===============*/
|
||||
/* out: folded value */
|
||||
ulint n1, /* in: ulint */
|
||||
ulint n2) /* in: ulint */
|
||||
{
|
||||
return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
|
||||
^ UT_HASH_RANDOM_MASK) + n2);
|
||||
}
|
||||
|
||||
ulint
|
||||
ut_fold_binary(
|
||||
/*===========*/
|
||||
/* out: folded value */
|
||||
byte* str, /* in: string of bytes */
|
||||
ulint len) /* in: length */
|
||||
{
|
||||
ulint i;
|
||||
ulint fold = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
fold = ut_fold_ulint_pair(fold, (ulint)(*str));
|
||||
|
||||
str++;
|
||||
}
|
||||
|
||||
return(fold);
|
||||
}
|
||||
|
||||
ulint
|
||||
buf_calc_page_new_checksum(
|
||||
/*=======================*/
|
||||
/* out: checksum */
|
||||
byte* page) /* in: buffer page */
|
||||
{
|
||||
ulint checksum;
|
||||
|
||||
/* Since the fields FIL_PAGE_FILE_FLUSH_LSN and ..._ARCH_LOG_NO
|
||||
are written outside the buffer pool to the first pages of data
|
||||
files, we have to skip them in the page checksum calculation.
|
||||
We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
|
||||
checksum is stored, and also the last 8 bytes of page because
|
||||
there we store the old formula checksum. */
|
||||
|
||||
checksum = ut_fold_binary(page + FIL_PAGE_OFFSET,
|
||||
FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET)
|
||||
+ ut_fold_binary(page + FIL_PAGE_DATA,
|
||||
UNIV_PAGE_SIZE - FIL_PAGE_DATA
|
||||
- FIL_PAGE_END_LSN_OLD_CHKSUM);
|
||||
checksum = checksum & 0xFFFFFFFF;
|
||||
|
||||
return(checksum);
|
||||
}
|
||||
|
||||
ulint
|
||||
buf_calc_page_old_checksum(
|
||||
/*=======================*/
|
||||
/* out: checksum */
|
||||
byte* page) /* in: buffer page */
|
||||
{
|
||||
ulint checksum;
|
||||
|
||||
checksum = ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN);
|
||||
|
||||
checksum = checksum & 0xFFFFFFFF;
|
||||
|
||||
return(checksum);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *f; // our input file
|
||||
byte *p; // storage of pages read
|
||||
int bytes; // bytes read count
|
||||
ulint ct; // current page number (0 based)
|
||||
int now; // current time
|
||||
int lastt; // last time
|
||||
ulint oldcsum, oldcsumfield, csum, csumfield, logseq, logseqfield; // ulints for checksum storage
|
||||
struct stat64 st; // for stat, if you couldn't guess
|
||||
unsigned long long int size; // size of file (has to be 64 bits)
|
||||
ulint pages; // number of pages in file
|
||||
ulint start_page = 0, end_page = 0, use_end_page = 0; // for starting and ending at certain pages
|
||||
int just_count = 0; // if true, just print page count
|
||||
int verbose = 0;
|
||||
int debug = 0;
|
||||
int c;
|
||||
int fd;
|
||||
|
||||
// remove arguments
|
||||
while ((c = getopt(argc, argv, "cvds:e:p:")) != -1) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'c':
|
||||
just_count = 1;
|
||||
break;
|
||||
case 's':
|
||||
start_page = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
end_page = atoi(optarg);
|
||||
use_end_page = 1;
|
||||
break;
|
||||
case 'p':
|
||||
start_page = atoi(optarg);
|
||||
end_page = atoi(optarg);
|
||||
use_end_page = 1;
|
||||
break;
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case ':':
|
||||
fprintf(stderr, "option -%c requires an argument\n", optopt);
|
||||
return 1;
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "unrecognized option: -%c\n", optopt);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// debug implies verbose...
|
||||
if (debug) verbose = 1;
|
||||
|
||||
// make sure we have the right arguments
|
||||
if (optind >= argc) {
|
||||
printf("InnoDB offline file checksum utility.\n");
|
||||
printf("usage: %s [-c] [-s <start page>] [-e <end page>] [-p <page>] [-v] [-d] <filename>\n", argv[0]);
|
||||
printf("\t-c\tprint the count of pages in the file\n");
|
||||
printf("\t-s n\tstart on this page number (0 based)\n");
|
||||
printf("\t-e n\tend at this page number (0 based)\n");
|
||||
printf("\t-p n\tcheck only this page (0 based)\n");
|
||||
printf("\t-v\tverbose (prints progress every 5 seconds)\n");
|
||||
printf("\t-d\tdebug mode (prints checksums for each page)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// stat the file to get size and page count
|
||||
if (stat64(argv[optind], &st)) {
|
||||
perror("error statting file");
|
||||
return 1;
|
||||
}
|
||||
size = st.st_size;
|
||||
pages = size / UNIV_PAGE_SIZE;
|
||||
if (just_count) {
|
||||
printf("%lu\n", pages);
|
||||
return 0;
|
||||
} else if (verbose) {
|
||||
printf("file %s = %llu bytes (%lu pages)...\n", argv[1], size, pages);
|
||||
printf("checking pages in range %lu to %lu\n", start_page, use_end_page ? end_page : (pages - 1));
|
||||
}
|
||||
|
||||
// open the file for reading
|
||||
f = fopen64(argv[optind], "r");
|
||||
if (!f) {
|
||||
perror("error opening file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// seek to the necessary position
|
||||
if (start_page) {
|
||||
fd = fileno(f);
|
||||
if (!fd) {
|
||||
perror("unable to obtain file descriptor number");
|
||||
return 1;
|
||||
}
|
||||
if (lseek64(fd, start_page * UNIV_PAGE_SIZE, SEEK_SET) != (start_page * UNIV_PAGE_SIZE)) {
|
||||
perror("unable to seek to necessary offset");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// allocate buffer for reading (so we don't realloc every time)
|
||||
p = (byte *)malloc(UNIV_PAGE_SIZE);
|
||||
|
||||
// main checksumming loop
|
||||
ct = start_page;
|
||||
lastt = 0;
|
||||
while (!feof(f)) {
|
||||
bytes = fread(p, 1, UNIV_PAGE_SIZE, f);
|
||||
if (!bytes && feof(f)) return 0;
|
||||
if (bytes != UNIV_PAGE_SIZE) {
|
||||
fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check the "stored log sequence numbers"
|
||||
logseq = mach_read_from_4(p + FIL_PAGE_LSN + 4);
|
||||
logseqfield = mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
|
||||
if (debug)
|
||||
printf("page %lu: log sequence number: first = %lu; second = %lu\n", ct, logseq, logseqfield);
|
||||
if (logseq != logseqfield) {
|
||||
fprintf(stderr, "page %lu invalid (fails log sequence number check)\n", ct);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check old method of checksumming
|
||||
oldcsum = buf_calc_page_old_checksum(p);
|
||||
oldcsumfield = mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM);
|
||||
if (debug)
|
||||
printf("page %lu: old style: calculated = %lu; recorded = %lu\n", ct, oldcsum, oldcsumfield);
|
||||
if (oldcsumfield != mach_read_from_4(p + FIL_PAGE_LSN) && oldcsumfield != oldcsum) {
|
||||
fprintf(stderr, "page %lu invalid (fails old style checksum)\n", ct);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// now check the new method
|
||||
csum = buf_calc_page_new_checksum(p);
|
||||
csumfield = mach_read_from_4(p + FIL_PAGE_SPACE_OR_CHKSUM);
|
||||
if (debug)
|
||||
printf("page %lu: new style: calculated = %lu; recorded = %lu\n", ct, csum, csumfield);
|
||||
if (csumfield != 0 && csum != csumfield) {
|
||||
fprintf(stderr, "page %lu invalid (fails new style checksum)\n", ct);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// end if this was the last page we were supposed to check
|
||||
if (use_end_page && (ct >= end_page))
|
||||
return 0;
|
||||
|
||||
// do counter increase and progress printing
|
||||
ct++;
|
||||
if (verbose) {
|
||||
if (ct % 64 == 0) {
|
||||
now = time(0);
|
||||
if (!lastt) lastt = now;
|
||||
if (now - lastt >= 1) {
|
||||
printf("page %lu okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100);
|
||||
lastt = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in a new issue