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>
43
/* all of these ripped from InnoDB code from MySQL 4.0.22 */
44
#define UT_HASH_RANDOM_MASK 1463735687
45
#define UT_HASH_RANDOM_MASK2 1653893711
46
#define FIL_PAGE_LSN 16
47
#define FIL_PAGE_FILE_FLUSH_LSN 26
48
#define FIL_PAGE_OFFSET 4
49
#define FIL_PAGE_DATA 38
50
#define FIL_PAGE_END_LSN_OLD_CHKSUM 8
51
#define FIL_PAGE_SPACE_OR_CHKSUM 0
52
#define UNIV_PAGE_SIZE (2 * 8192)
54
/* command line argument to do page checks (that's it) */
55
/* another argument to specify page ranges... seek to right spot and go from there */
57
/* innodb function in name; modified slightly to not have the ASM version (lots of #ifs that didn't apply) */
58
static uint32_t mach_read_from_4(unsigned char *b)
60
return( ((uint32_t)(b[0]) << 24)
61
+ ((uint32_t)(b[1]) << 16)
62
+ ((uint32_t)(b[2]) << 8)
68
ut_fold_uint32_t_pair(
70
/* out: folded value */
71
uint32_t n1, /* in: uint32_t */
72
uint32_t n2) /* in: uint32_t */
74
return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
75
^ UT_HASH_RANDOM_MASK) + n2);
81
/* out: folded value */
82
unsigned char* str, /* in: string of bytes */
83
uint32_t len) /* in: length */
88
for (i= 0; i < len; i++)
90
fold= ut_fold_uint32_t_pair(fold, (uint32_t)(*str));
99
buf_calc_page_new_checksum(
100
/*=======================*/
102
unsigned char* page) /* in: buffer page */
106
/* Since the fields FIL_PAGE_FILE_FLUSH_LSN and ..._ARCH_LOG_NO
107
are written outside the buffer pool to the first pages of data
108
files, we have to skip them in the page checksum calculation.
109
We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
110
checksum is stored, and also the last 8 bytes of page because
111
there we store the old formula checksum. */
113
checksum= ut_fold_binary(page + FIL_PAGE_OFFSET,
114
FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET)
115
+ ut_fold_binary(page + FIL_PAGE_DATA,
116
UNIV_PAGE_SIZE - FIL_PAGE_DATA
117
- FIL_PAGE_END_LSN_OLD_CHKSUM);
118
checksum= checksum & 0xFFFFFFFF;
124
buf_calc_page_old_checksum(
125
/*=======================*/
127
unsigned char* page) /* in: buffer page */
131
checksum= ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN);
133
checksum= checksum & 0xFFFFFFFF;
139
int main(int argc, char **argv)
141
FILE *f; /* our input file */
142
unsigned char *p; /* storage of pages read */
143
int bytes; /* bytes read count */
144
uint32_t ct; /* current page number (0 based) */
145
int now; /* current time */
146
int lastt; /* last time */
147
uint32_t oldcsum, oldcsumfield, csum, csumfield, logseq, logseqfield; /* uint32_ts for checksum storage */
148
struct stat st; /* for stat, if you couldn't guess */
149
uint64_t size; /* size of file (has to be 64 bits) */
150
uint32_t pages; /* number of pages in file */
151
uint32_t start_page= 0, end_page= 0, use_end_page= 0; /* for starting and ending at certain pages */
153
int just_count= 0; /* if true, just print page count */
159
/* remove arguments */
160
while ((c= getopt(argc, argv, "cvds:e:p:")) != -1)
171
start_page= atoi(optarg);
174
end_page= atoi(optarg);
178
start_page= atoi(optarg);
179
end_page= atoi(optarg);
186
fprintf(stderr, "option -%c requires an argument\n", optopt);
190
fprintf(stderr, "unrecognized option: -%c\n", optopt);
196
/* debug implies verbose... */
197
if (debug) verbose= 1;
199
/* make sure we have the right arguments */
202
printf("InnoDB offline file checksum utility.\n");
203
printf("usage: %s [-c] [-s <start page>] [-e <end page>] [-p <page>] [-v] [-d] <filename>\n", argv[0]);
204
printf("\t-c\tprint the count of pages in the file\n");
205
printf("\t-s n\tstart on this page number (0 based)\n");
206
printf("\t-e n\tend at this page number (0 based)\n");
207
printf("\t-p n\tcheck only this page (0 based)\n");
208
printf("\t-v\tverbose (prints progress every 5 seconds)\n");
209
printf("\t-d\tdebug mode (prints checksums for each page)\n");
213
/* stat the file to get size and page count */
214
if (stat(argv[optind], &st))
216
perror("error statting file");
220
pages= size / UNIV_PAGE_SIZE;
223
printf("%u\n", pages);
228
printf("file %s= %"PRIu64" bytes (%u pages)...\n", argv[1], size, pages);
229
printf("checking pages in range %u to %u\n", start_page, use_end_page ? end_page : (pages - 1));
232
/* open the file for reading */
233
f= fopen(argv[optind], "r");
236
perror("error opening file");
240
/* seek to the necessary position */
246
perror("unable to obtain file descriptor number");
250
offset= (off_t)start_page * (off_t)UNIV_PAGE_SIZE;
252
if (lseek(fd, offset, SEEK_SET) != offset)
254
perror("unable to seek to necessary offset");
259
/* allocate buffer for reading (so we don't realloc every time) */
260
p= (unsigned char *)malloc(UNIV_PAGE_SIZE);
262
/* main checksumming loop */
267
bytes= fread(p, 1, UNIV_PAGE_SIZE, f);
268
if (!bytes && feof(f)) return 0;
269
if (bytes != UNIV_PAGE_SIZE)
271
fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE);
275
/* check the "stored log sequence numbers" */
276
logseq= mach_read_from_4(p + FIL_PAGE_LSN + 4);
277
logseqfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
279
printf("page %u: log sequence number: first = %u; second = %u\n", ct, logseq, logseqfield);
280
if (logseq != logseqfield)
282
fprintf(stderr, "page %u invalid (fails log sequence number check)\n", ct);
286
/* check old method of checksumming */
287
oldcsum= buf_calc_page_old_checksum(p);
288
oldcsumfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM);
290
printf("page %u: old style: calculated = %u; recorded = %u\n", ct, oldcsum, oldcsumfield);
291
if (oldcsumfield != mach_read_from_4(p + FIL_PAGE_LSN) && oldcsumfield != oldcsum)
293
fprintf(stderr, "page %u invalid (fails old style checksum)\n", ct);
297
/* now check the new method */
298
csum= buf_calc_page_new_checksum(p);
299
csumfield= mach_read_from_4(p + FIL_PAGE_SPACE_OR_CHKSUM);
301
printf("page %u: new style: calculated = %u; recorded = %u\n", ct, csum, csumfield);
302
if (csumfield != 0 && csum != csumfield)
304
fprintf(stderr, "page %u invalid (fails new style checksum)\n", ct);
308
/* end if this was the last page we were supposed to check */
309
if (use_end_page && (ct >= end_page))
312
/* do counter increase and progress printing */
319
if (!lastt) lastt= now;
320
if (now - lastt >= 1)
322
printf("page %u okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100);