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
30
#define _XOPEN_SOURCE 500 /* needed to include getopt.h on some platforms. */
35
#include <sys/types.h>
39
/* all of these ripped from InnoDB code from MySQL 4.0.22 */
40
#define UT_HASH_RANDOM_MASK 1463735687
41
#define UT_HASH_RANDOM_MASK2 1653893711
42
#define FIL_PAGE_LSN 16
43
#define FIL_PAGE_FILE_FLUSH_LSN 26
44
#define FIL_PAGE_OFFSET 4
45
#define FIL_PAGE_DATA 38
46
#define FIL_PAGE_END_LSN_OLD_CHKSUM 8
47
#define FIL_PAGE_SPACE_OR_CHKSUM 0
48
#define UNIV_PAGE_SIZE (2 * 8192)
50
/* command line argument to do page checks (that's it) */
51
/* another argument to specify page ranges... seek to right spot and go from there */
53
typedef unsigned long int ulint;
54
typedef unsigned char uchar;
56
/* innodb function in name; modified slightly to not have the ASM version (lots of #ifs that didn't apply) */
57
ulint mach_read_from_4(uchar *b)
59
return( ((ulint)(b[0]) << 24)
60
+ ((ulint)(b[1]) << 16)
61
+ ((ulint)(b[2]) << 8)
69
/* out: folded value */
70
ulint n1, /* in: ulint */
71
ulint n2) /* in: ulint */
73
return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
74
^ UT_HASH_RANDOM_MASK) + n2);
80
/* out: folded value */
81
uchar* str, /* in: string of bytes */
82
ulint len) /* in: length */
87
for (i= 0; i < len; i++)
89
fold= ut_fold_ulint_pair(fold, (ulint)(*str));
98
buf_calc_page_new_checksum(
99
/*=======================*/
101
uchar* page) /* in: buffer page */
105
/* Since the fields FIL_PAGE_FILE_FLUSH_LSN and ..._ARCH_LOG_NO
106
are written outside the buffer pool to the first pages of data
107
files, we have to skip them in the page checksum calculation.
108
We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
109
checksum is stored, and also the last 8 bytes of page because
110
there we store the old formula checksum. */
112
checksum= ut_fold_binary(page + FIL_PAGE_OFFSET,
113
FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET)
114
+ ut_fold_binary(page + FIL_PAGE_DATA,
115
UNIV_PAGE_SIZE - FIL_PAGE_DATA
116
- FIL_PAGE_END_LSN_OLD_CHKSUM);
117
checksum= checksum & 0xFFFFFFFF;
123
buf_calc_page_old_checksum(
124
/*=======================*/
126
uchar* page) /* in: buffer page */
130
checksum= ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN);
132
checksum= checksum & 0xFFFFFFFF;
138
int main(int argc, char **argv)
140
FILE *f; /* our input file */
141
uchar *p; /* storage of pages read */
142
int bytes; /* bytes read count */
143
ulint ct; /* current page number (0 based) */
144
int now; /* current time */
145
int lastt; /* last time */
146
ulint oldcsum, oldcsumfield, csum, csumfield, logseq, logseqfield; /* ulints for checksum storage */
147
struct stat st; /* for stat, if you couldn't guess */
148
unsigned long long int size; /* size of file (has to be 64 bits) */
149
ulint pages; /* number of pages in file */
150
ulint start_page= 0, end_page= 0, use_end_page= 0; /* for starting and ending at certain pages */
152
int just_count= 0; /* if true, just print page count */
158
/* remove arguments */
159
while ((c= getopt(argc, argv, "cvds:e:p:")) != -1)
170
start_page= atoi(optarg);
173
end_page= atoi(optarg);
177
start_page= atoi(optarg);
178
end_page= atoi(optarg);
185
fprintf(stderr, "option -%c requires an argument\n", optopt);
189
fprintf(stderr, "unrecognized option: -%c\n", optopt);
195
/* debug implies verbose... */
196
if (debug) verbose= 1;
198
/* make sure we have the right arguments */
201
printf("InnoDB offline file checksum utility.\n");
202
printf("usage: %s [-c] [-s <start page>] [-e <end page>] [-p <page>] [-v] [-d] <filename>\n", argv[0]);
203
printf("\t-c\tprint the count of pages in the file\n");
204
printf("\t-s n\tstart on this page number (0 based)\n");
205
printf("\t-e n\tend at this page number (0 based)\n");
206
printf("\t-p n\tcheck only this page (0 based)\n");
207
printf("\t-v\tverbose (prints progress every 5 seconds)\n");
208
printf("\t-d\tdebug mode (prints checksums for each page)\n");
212
/* stat the file to get size and page count */
213
if (stat(argv[optind], &st))
215
perror("error statting file");
219
pages= size / UNIV_PAGE_SIZE;
222
printf("%lu\n", pages);
227
printf("file %s= %llu bytes (%lu pages)...\n", argv[1], size, pages);
228
printf("checking pages in range %lu to %lu\n", start_page, use_end_page ? end_page : (pages - 1));
231
/* open the file for reading */
232
f= fopen(argv[optind], "r");
235
perror("error opening file");
239
/* seek to the necessary position */
245
perror("unable to obtain file descriptor number");
249
offset= (off_t)start_page * (off_t)UNIV_PAGE_SIZE;
251
if (lseek(fd, offset, SEEK_SET) != offset)
253
perror("unable to seek to necessary offset");
258
/* allocate buffer for reading (so we don't realloc every time) */
259
p= (uchar *)malloc(UNIV_PAGE_SIZE);
261
/* main checksumming loop */
266
bytes= fread(p, 1, UNIV_PAGE_SIZE, f);
267
if (!bytes && feof(f)) return 0;
268
if (bytes != UNIV_PAGE_SIZE)
270
fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE);
274
/* check the "stored log sequence numbers" */
275
logseq= mach_read_from_4(p + FIL_PAGE_LSN + 4);
276
logseqfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
278
printf("page %lu: log sequence number: first = %lu; second = %lu\n", ct, logseq, logseqfield);
279
if (logseq != logseqfield)
281
fprintf(stderr, "page %lu invalid (fails log sequence number check)\n", ct);
285
/* check old method of checksumming */
286
oldcsum= buf_calc_page_old_checksum(p);
287
oldcsumfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM);
289
printf("page %lu: old style: calculated = %lu; recorded = %lu\n", ct, oldcsum, oldcsumfield);
290
if (oldcsumfield != mach_read_from_4(p + FIL_PAGE_LSN) && oldcsumfield != oldcsum)
292
fprintf(stderr, "page %lu invalid (fails old style checksum)\n", ct);
296
/* now check the new method */
297
csum= buf_calc_page_new_checksum(p);
298
csumfield= mach_read_from_4(p + FIL_PAGE_SPACE_OR_CHKSUM);
300
printf("page %lu: new style: calculated = %lu; recorded = %lu\n", ct, csum, csumfield);
301
if (csumfield != 0 && csum != csumfield)
303
fprintf(stderr, "page %lu invalid (fails new style checksum)\n", ct);
307
/* end if this was the last page we were supposed to check */
308
if (use_end_page && (ct >= end_page))
311
/* do counter increase and progress printing */
318
if (!lastt) lastt= now;
319
if (now - lastt >= 1)
321
printf("page %lu okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100);