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);
181
fprintf(stderr, "unrecognized option: -%c\n", optopt);
186
/* debug implies verbose... */
187
if (debug) verbose= 1;
189
/* make sure we have the right arguments */
192
printf("InnoDB offline file checksum utility.\n");
193
printf("usage: %s [-c] [-s <start page>] [-e <end page>] [-p <page>] [-v] [-d] <filename>\n", argv[0]);
194
printf("\t-c\tprint the count of pages in the file\n");
195
printf("\t-s n\tstart on this page number (0 based)\n");
196
printf("\t-e n\tend at this page number (0 based)\n");
197
printf("\t-p n\tcheck only this page (0 based)\n");
198
printf("\t-v\tverbose (prints progress every 5 seconds)\n");
199
printf("\t-d\tdebug mode (prints checksums for each page)\n");
203
/* stat the file to get size and page count */
204
if (stat(argv[optind], &st))
206
perror("error statting file");
210
pages= size / UNIV_PAGE_SIZE;
213
printf("%u\n", pages);
218
printf("file %s= %"PRIu64" bytes (%u pages)...\n", argv[1], size, pages);
219
printf("checking pages in range %u to %u\n", start_page, use_end_page ? end_page : (pages - 1));
222
/* open the file for reading */
223
f= fopen(argv[optind], "r");
226
perror("error opening file");
230
/* seek to the necessary position */
236
perror("unable to obtain file descriptor number");
240
offset= (off_t)start_page * (off_t)UNIV_PAGE_SIZE;
242
if (lseek(fd, offset, SEEK_SET) != offset)
244
perror("unable to seek to necessary offset");
249
/* allocate buffer for reading (so we don't realloc every time) */
250
p= (unsigned char *)malloc(UNIV_PAGE_SIZE);
252
/* main checksumming loop */
257
bytes= fread(p, 1, UNIV_PAGE_SIZE, f);
258
if (!bytes && feof(f)) return 0;
259
if (bytes != UNIV_PAGE_SIZE)
261
fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE);
265
/* check the "stored log sequence numbers" */
266
logseq= mach_read_from_4(p + FIL_PAGE_LSN + 4);
267
logseqfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
269
printf("page %u: log sequence number: first = %u; second = %u\n", ct, logseq, logseqfield);
270
if (logseq != logseqfield)
272
fprintf(stderr, "page %u invalid (fails log sequence number check)\n", ct);
276
/* check old method of checksumming */
277
oldcsum= buf_calc_page_old_checksum(p);
278
oldcsumfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM);
280
printf("page %u: old style: calculated = %u; recorded = %u\n", ct, oldcsum, oldcsumfield);
281
if (oldcsumfield != mach_read_from_4(p + FIL_PAGE_LSN) && oldcsumfield != oldcsum)
283
fprintf(stderr, "page %u invalid (fails old style checksum)\n", ct);
287
/* now check the new method */
288
csum= buf_calc_page_new_checksum(p);
289
csumfield= mach_read_from_4(p + FIL_PAGE_SPACE_OR_CHKSUM);
291
printf("page %u: new style: calculated = %u; recorded = %u\n", ct, csum, csumfield);
292
if (csumfield != 0 && csum != csumfield)
294
fprintf(stderr, "page %u invalid (fails new style checksum)\n", ct);
298
/* end if this was the last page we were supposed to check */
299
if (use_end_page && (ct >= end_page))
302
/* do counter increase and progress printing */
309
if (!lastt) lastt= now;
310
if (now - lastt >= 1)
312
printf("page %u okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100);