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.
29
#include <sys/types.h>
35
/* all of these ripped from InnoDB code from MySQL 4.0.22 */
36
#define UT_HASH_RANDOM_MASK 1463735687
37
#define UT_HASH_RANDOM_MASK2 1653893711
38
#define FIL_PAGE_LSN 16
39
#define FIL_PAGE_FILE_FLUSH_LSN 26
40
#define FIL_PAGE_OFFSET 4
41
#define FIL_PAGE_DATA 38
42
#define FIL_PAGE_END_LSN_OLD_CHKSUM 8
43
#define FIL_PAGE_SPACE_OR_CHKSUM 0
44
#define UNIV_PAGE_SIZE (2 * 8192)
46
/* command line argument to do page checks (that's it) */
47
/* another argument to specify page ranges... seek to right spot and go from there */
49
/* innodb function in name; modified slightly to not have the ASM version (lots of #ifs that didn't apply) */
50
static uint32_t mach_read_from_4(unsigned char *b)
52
return( ((uint32_t)(b[0]) << 24)
53
+ ((uint32_t)(b[1]) << 16)
54
+ ((uint32_t)(b[2]) << 8)
60
ut_fold_uint32_t_pair(
62
/* out: folded value */
63
uint32_t n1, /* in: uint32_t */
64
uint32_t n2) /* in: uint32_t */
66
return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
67
^ UT_HASH_RANDOM_MASK) + n2);
73
/* out: folded value */
74
unsigned char* str, /* in: string of bytes */
75
uint32_t len) /* in: length */
80
for (i= 0; i < len; i++)
82
fold= ut_fold_uint32_t_pair(fold, (uint32_t)(*str));
91
buf_calc_page_new_checksum(
92
/*=======================*/
94
unsigned char* page) /* in: buffer page */
98
/* Since the fields FIL_PAGE_FILE_FLUSH_LSN and ..._ARCH_LOG_NO
99
are written outside the buffer pool to the first pages of data
100
files, we have to skip them in the page checksum calculation.
101
We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
102
checksum is stored, and also the last 8 bytes of page because
103
there we store the old formula checksum. */
105
checksum= ut_fold_binary(page + FIL_PAGE_OFFSET,
106
FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET)
107
+ ut_fold_binary(page + FIL_PAGE_DATA,
108
UNIV_PAGE_SIZE - FIL_PAGE_DATA
109
- FIL_PAGE_END_LSN_OLD_CHKSUM);
110
checksum= checksum & 0xFFFFFFFF;
116
buf_calc_page_old_checksum(
117
/*=======================*/
119
unsigned char* page) /* in: buffer page */
123
checksum= ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN);
125
checksum= checksum & 0xFFFFFFFF;
131
int main(int argc, char **argv)
133
FILE *f; /* our input file */
134
unsigned char *p; /* storage of pages read */
135
int bytes; /* bytes read count */
136
uint32_t ct; /* current page number (0 based) */
137
int now; /* current time */
138
int lastt; /* last time */
139
uint32_t oldcsum, oldcsumfield, csum, csumfield, logseq, logseqfield; /* uint32_ts for checksum storage */
140
struct stat st; /* for stat, if you couldn't guess */
141
uint64_t size; /* size of file (has to be 64 bits) */
142
uint32_t pages; /* number of pages in file */
143
uint32_t start_page= 0, end_page= 0, use_end_page= 0; /* for starting and ending at certain pages */
145
int just_count= 0; /* if true, just print page count */
151
/* remove arguments */
152
while ((c= getopt(argc, argv, "cvds:e:p:")) != -1)
163
start_page= atoi(optarg);
166
end_page= atoi(optarg);
170
start_page= atoi(optarg);
171
end_page= atoi(optarg);
178
fprintf(stderr, "option -%c requires an argument\n", optopt);
182
fprintf(stderr, "unrecognized option: -%c\n", optopt);
188
/* debug implies verbose... */
189
if (debug) verbose= 1;
191
/* make sure we have the right arguments */
194
printf("InnoDB offline file checksum utility.\n");
195
printf("usage: %s [-c] [-s <start page>] [-e <end page>] [-p <page>] [-v] [-d] <filename>\n", argv[0]);
196
printf("\t-c\tprint the count of pages in the file\n");
197
printf("\t-s n\tstart on this page number (0 based)\n");
198
printf("\t-e n\tend at this page number (0 based)\n");
199
printf("\t-p n\tcheck only this page (0 based)\n");
200
printf("\t-v\tverbose (prints progress every 5 seconds)\n");
201
printf("\t-d\tdebug mode (prints checksums for each page)\n");
205
/* stat the file to get size and page count */
206
if (stat(argv[optind], &st))
208
perror("error statting file");
212
pages= size / UNIV_PAGE_SIZE;
215
printf("%u\n", pages);
220
printf("file %s= %"PRIu64" bytes (%u pages)...\n", argv[1], size, pages);
221
printf("checking pages in range %u to %u\n", start_page, use_end_page ? end_page : (pages - 1));
224
/* open the file for reading */
225
f= fopen(argv[optind], "r");
228
perror("error opening file");
232
/* seek to the necessary position */
238
perror("unable to obtain file descriptor number");
242
offset= (off_t)start_page * (off_t)UNIV_PAGE_SIZE;
244
if (lseek(fd, offset, SEEK_SET) != offset)
246
perror("unable to seek to necessary offset");
251
/* allocate buffer for reading (so we don't realloc every time) */
252
p= (unsigned char *)malloc(UNIV_PAGE_SIZE);
254
/* main checksumming loop */
259
bytes= fread(p, 1, UNIV_PAGE_SIZE, f);
260
if (!bytes && feof(f)) return 0;
261
if (bytes != UNIV_PAGE_SIZE)
263
fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE);
267
/* check the "stored log sequence numbers" */
268
logseq= mach_read_from_4(p + FIL_PAGE_LSN + 4);
269
logseqfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
271
printf("page %u: log sequence number: first = %u; second = %u\n", ct, logseq, logseqfield);
272
if (logseq != logseqfield)
274
fprintf(stderr, "page %u invalid (fails log sequence number check)\n", ct);
278
/* check old method of checksumming */
279
oldcsum= buf_calc_page_old_checksum(p);
280
oldcsumfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM);
282
printf("page %u: old style: calculated = %u; recorded = %u\n", ct, oldcsum, oldcsumfield);
283
if (oldcsumfield != mach_read_from_4(p + FIL_PAGE_LSN) && oldcsumfield != oldcsum)
285
fprintf(stderr, "page %u invalid (fails old style checksum)\n", ct);
289
/* now check the new method */
290
csum= buf_calc_page_new_checksum(p);
291
csumfield= mach_read_from_4(p + FIL_PAGE_SPACE_OR_CHKSUM);
293
printf("page %u: new style: calculated = %u; recorded = %u\n", ct, csum, csumfield);
294
if (csumfield != 0 && csum != csumfield)
296
fprintf(stderr, "page %u invalid (fails new style checksum)\n", ct);
300
/* end if this was the last page we were supposed to check */
301
if (use_end_page && (ct >= end_page))
304
/* do counter increase and progress printing */
311
if (!lastt) lastt= now;
312
if (now - lastt >= 1)
314
printf("page %u okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100);