1
/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
InnoDB offline file checksum utility. 85% of the code in this file
18
was taken wholesale fron the InnoDB codebase.
20
The final 15% was originally written by Mark Smith of Danga
21
Interactive, Inc. <junior@danga.com>
23
Published with a permission.
26
/* needed to have access to 64 bit file functions */
27
#define _LARGEFILE_SOURCE
28
#define _LARGEFILE64_SOURCE
31
#define _XOPEN_SOURCE 500 /* needed to include getopt.h on some platforms. */
37
#include <sys/types.h>
42
/* all of these ripped from InnoDB code from MySQL 4.0.22 */
43
#define UT_HASH_RANDOM_MASK 1463735687
44
#define UT_HASH_RANDOM_MASK2 1653893711
45
#define FIL_PAGE_LSN 16
46
#define FIL_PAGE_FILE_FLUSH_LSN 26
47
#define FIL_PAGE_OFFSET 4
48
#define FIL_PAGE_DATA 38
49
#define FIL_PAGE_END_LSN_OLD_CHKSUM 8
50
#define FIL_PAGE_SPACE_OR_CHKSUM 0
51
#define UNIV_PAGE_SIZE (2 * 8192)
53
/* command line argument to do page checks (that's it) */
54
/* another argument to specify page ranges... seek to right spot and go from there */
56
typedef uint32_t ulint;
57
typedef unsigned char uchar;
59
/* innodb function in name; modified slightly to not have the ASM version (lots of #ifs that didn't apply) */
60
static ulint mach_read_from_4(uchar *b)
62
return( ((ulint)(b[0]) << 24)
63
+ ((ulint)(b[1]) << 16)
64
+ ((ulint)(b[2]) << 8)
72
/* out: folded value */
73
ulint n1, /* in: ulint */
74
ulint n2) /* in: ulint */
76
return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
77
^ UT_HASH_RANDOM_MASK) + n2);
83
/* out: folded value */
84
uchar* str, /* in: string of bytes */
85
ulint len) /* in: length */
90
for (i= 0; i < len; i++)
92
fold= ut_fold_ulint_pair(fold, (ulint)(*str));
101
buf_calc_page_new_checksum(
102
/*=======================*/
104
uchar* page) /* in: buffer page */
108
/* Since the fields FIL_PAGE_FILE_FLUSH_LSN and ..._ARCH_LOG_NO
109
are written outside the buffer pool to the first pages of data
110
files, we have to skip them in the page checksum calculation.
111
We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
112
checksum is stored, and also the last 8 bytes of page because
113
there we store the old formula checksum. */
115
checksum= ut_fold_binary(page + FIL_PAGE_OFFSET,
116
FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET)
117
+ ut_fold_binary(page + FIL_PAGE_DATA,
118
UNIV_PAGE_SIZE - FIL_PAGE_DATA
119
- FIL_PAGE_END_LSN_OLD_CHKSUM);
120
checksum= checksum & 0xFFFFFFFF;
126
buf_calc_page_old_checksum(
127
/*=======================*/
129
uchar* page) /* in: buffer page */
133
checksum= ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN);
135
checksum= checksum & 0xFFFFFFFF;
141
int main(int argc, char **argv)
143
FILE *f; /* our input file */
144
uchar *p; /* storage of pages read */
145
int bytes; /* bytes read count */
146
ulint ct; /* current page number (0 based) */
147
int now; /* current time */
148
int lastt; /* last time */
149
ulint oldcsum, oldcsumfield, csum, csumfield, logseq, logseqfield; /* ulints for checksum storage */
150
struct stat st; /* for stat, if you couldn't guess */
151
unsigned long long int size; /* size of file (has to be 64 bits) */
152
ulint pages; /* number of pages in file */
153
ulint start_page= 0, end_page= 0, use_end_page= 0; /* for starting and ending at certain pages */
155
int just_count= 0; /* if true, just print page count */
161
/* remove arguments */
162
while ((c= getopt(argc, argv, "cvds:e:p:")) != -1)
173
start_page= atoi(optarg);
176
end_page= atoi(optarg);
180
start_page= atoi(optarg);
181
end_page= atoi(optarg);
188
fprintf(stderr, "option -%c requires an argument\n", optopt);
192
fprintf(stderr, "unrecognized option: -%c\n", optopt);
198
/* debug implies verbose... */
199
if (debug) verbose= 1;
201
/* make sure we have the right arguments */
204
printf("InnoDB offline file checksum utility.\n");
205
printf("usage: %s [-c] [-s <start page>] [-e <end page>] [-p <page>] [-v] [-d] <filename>\n", argv[0]);
206
printf("\t-c\tprint the count of pages in the file\n");
207
printf("\t-s n\tstart on this page number (0 based)\n");
208
printf("\t-e n\tend at this page number (0 based)\n");
209
printf("\t-p n\tcheck only this page (0 based)\n");
210
printf("\t-v\tverbose (prints progress every 5 seconds)\n");
211
printf("\t-d\tdebug mode (prints checksums for each page)\n");
215
/* stat the file to get size and page count */
216
if (stat(argv[optind], &st))
218
perror("error statting file");
222
pages= size / UNIV_PAGE_SIZE;
225
printf("%u\n", pages);
230
printf("file %s= %llu bytes (%u pages)...\n", argv[1], size, pages);
231
printf("checking pages in range %u to %u\n", start_page, use_end_page ? end_page : (pages - 1));
234
/* open the file for reading */
235
f= fopen(argv[optind], "r");
238
perror("error opening file");
242
/* seek to the necessary position */
248
perror("unable to obtain file descriptor number");
252
offset= (off_t)start_page * (off_t)UNIV_PAGE_SIZE;
254
if (lseek(fd, offset, SEEK_SET) != offset)
256
perror("unable to seek to necessary offset");
261
/* allocate buffer for reading (so we don't realloc every time) */
262
p= (uchar *)malloc(UNIV_PAGE_SIZE);
264
/* main checksumming loop */
269
bytes= fread(p, 1, UNIV_PAGE_SIZE, f);
270
if (!bytes && feof(f)) return 0;
271
if (bytes != UNIV_PAGE_SIZE)
273
fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE);
277
/* check the "stored log sequence numbers" */
278
logseq= mach_read_from_4(p + FIL_PAGE_LSN + 4);
279
logseqfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
281
printf("page %u: log sequence number: first = %u; second = %u\n", ct, logseq, logseqfield);
282
if (logseq != logseqfield)
284
fprintf(stderr, "page %u invalid (fails log sequence number check)\n", ct);
288
/* check old method of checksumming */
289
oldcsum= buf_calc_page_old_checksum(p);
290
oldcsumfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM);
292
printf("page %u: old style: calculated = %u; recorded = %u\n", ct, oldcsum, oldcsumfield);
293
if (oldcsumfield != mach_read_from_4(p + FIL_PAGE_LSN) && oldcsumfield != oldcsum)
295
fprintf(stderr, "page %u invalid (fails old style checksum)\n", ct);
299
/* now check the new method */
300
csum= buf_calc_page_new_checksum(p);
301
csumfield= mach_read_from_4(p + FIL_PAGE_SPACE_OR_CHKSUM);
303
printf("page %u: new style: calculated = %u; recorded = %u\n", ct, csum, csumfield);
304
if (csumfield != 0 && csum != csumfield)
306
fprintf(stderr, "page %u invalid (fails new style checksum)\n", ct);
310
/* end if this was the last page we were supposed to check */
311
if (use_end_page && (ct >= end_page))
314
/* do counter increase and progress printing */
321
if (!lastt) lastt= now;
322
if (now - lastt >= 1)
324
printf("page %u okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100);