~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to extra/innochecksum.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
 
2
 
 
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.
 
6
 
 
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.
 
11
 
 
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 */
 
15
 
 
16
/*
 
17
  InnoDB offline file checksum utility.  85% of the code in this file
 
18
  was taken wholesale fron the InnoDB codebase.
 
19
 
 
20
  The final 15% was originally written by Mark Smith of Danga
 
21
  Interactive, Inc. <junior@danga.com>
 
22
 
 
23
  Published with a permission.
 
24
*/
 
25
 
 
26
/* needed to have access to 64 bit file functions */
 
27
#define _LARGEFILE_SOURCE
 
28
#define _LARGEFILE64_SOURCE
 
29
 
 
30
#define _XOPEN_SOURCE 500 /* needed to include getopt.h on some platforms. */
 
31
 
 
32
#include <stdio.h>
 
33
#include <stdlib.h>
 
34
#include <time.h>
 
35
#include <sys/types.h>
 
36
#include <sys/stat.h>
 
37
#include <unistd.h>
 
38
 
 
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)
 
49
 
 
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 */
 
52
 
 
53
typedef unsigned long int ulint;
 
54
typedef unsigned char uchar;
 
55
 
 
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)
 
58
{
 
59
  return( ((ulint)(b[0]) << 24)
 
60
          + ((ulint)(b[1]) << 16)
 
61
          + ((ulint)(b[2]) << 8)
 
62
          + (ulint)(b[3])
 
63
          );
 
64
}
 
65
 
 
66
ulint
 
67
ut_fold_ulint_pair(
 
68
/*===============*/
 
69
            /* out: folded value */
 
70
    ulint   n1, /* in: ulint */
 
71
    ulint   n2) /* in: ulint */
 
72
{
 
73
    return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
 
74
                        ^ UT_HASH_RANDOM_MASK) + n2);
 
75
}
 
76
 
 
77
ulint
 
78
ut_fold_binary(
 
79
/*===========*/
 
80
            /* out: folded value */
 
81
    uchar*   str,    /* in: string of bytes */
 
82
    ulint   len)    /* in: length */
 
83
{
 
84
    ulint   i;
 
85
    ulint   fold= 0;
 
86
 
 
87
    for (i= 0; i < len; i++)
 
88
    {
 
89
      fold= ut_fold_ulint_pair(fold, (ulint)(*str));
 
90
 
 
91
      str++;
 
92
    }
 
93
 
 
94
    return(fold);
 
95
}
 
96
 
 
97
ulint
 
98
buf_calc_page_new_checksum(
 
99
/*=======================*/
 
100
               /* out: checksum */
 
101
    uchar*    page) /* in: buffer page */
 
102
{
 
103
    ulint checksum;
 
104
 
 
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. */
 
111
 
 
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;
 
118
 
 
119
    return(checksum);
 
120
}
 
121
 
 
122
ulint
 
123
buf_calc_page_old_checksum(
 
124
/*=======================*/
 
125
               /* out: checksum */
 
126
    uchar*    page) /* in: buffer page */
 
127
{
 
128
    ulint checksum;
 
129
 
 
130
    checksum= ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN);
 
131
 
 
132
    checksum= checksum & 0xFFFFFFFF;
 
133
 
 
134
    return(checksum);
 
135
}
 
136
 
 
137
 
 
138
int main(int argc, char **argv)
 
139
{
 
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 */
 
151
  off_t offset= 0;
 
152
  int just_count= 0;          /* if true, just print page count */
 
153
  int verbose= 0;
 
154
  int debug= 0;
 
155
  int c;
 
156
  int fd;
 
157
 
 
158
  /* remove arguments */
 
159
  while ((c= getopt(argc, argv, "cvds:e:p:")) != -1)
 
160
  {
 
161
    switch (c)
 
162
    {
 
163
    case 'v':
 
164
      verbose= 1;
 
165
      break;
 
166
    case 'c':
 
167
      just_count= 1;
 
168
      break;
 
169
    case 's':
 
170
      start_page= atoi(optarg);
 
171
      break;
 
172
    case 'e':
 
173
      end_page= atoi(optarg);
 
174
      use_end_page= 1;
 
175
      break;
 
176
    case 'p':
 
177
      start_page= atoi(optarg);
 
178
      end_page= atoi(optarg);
 
179
      use_end_page= 1;
 
180
      break;
 
181
    case 'd':
 
182
      debug= 1;
 
183
      break;
 
184
    case ':':
 
185
      fprintf(stderr, "option -%c requires an argument\n", optopt);
 
186
      return 1;
 
187
      break;
 
188
    case '?':
 
189
      fprintf(stderr, "unrecognized option: -%c\n", optopt);
 
190
      return 1;
 
191
      break;
 
192
    }
 
193
  }
 
194
 
 
195
  /* debug implies verbose... */
 
196
  if (debug) verbose= 1;
 
197
 
 
198
  /* make sure we have the right arguments */
 
199
  if (optind >= argc)
 
200
  {
 
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");
 
209
    return 1;
 
210
  }
 
211
 
 
212
  /* stat the file to get size and page count */
 
213
  if (stat(argv[optind], &st))
 
214
  {
 
215
    perror("error statting file");
 
216
    return 1;
 
217
  }
 
218
  size= st.st_size;
 
219
  pages= size / UNIV_PAGE_SIZE;
 
220
  if (just_count)
 
221
  {
 
222
    printf("%lu\n", pages);
 
223
    return 0;
 
224
  }
 
225
  else if (verbose)
 
226
  {
 
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));
 
229
  }
 
230
 
 
231
  /* open the file for reading */
 
232
  f= fopen(argv[optind], "r");
 
233
  if (!f)
 
234
  {
 
235
    perror("error opening file");
 
236
    return 1;
 
237
  }
 
238
 
 
239
  /* seek to the necessary position */
 
240
  if (start_page)
 
241
  {
 
242
    fd= fileno(f);
 
243
    if (!fd)
 
244
    {
 
245
      perror("unable to obtain file descriptor number");
 
246
      return 1;
 
247
    }
 
248
 
 
249
    offset= (off_t)start_page * (off_t)UNIV_PAGE_SIZE;
 
250
 
 
251
    if (lseek(fd, offset, SEEK_SET) != offset)
 
252
    {
 
253
      perror("unable to seek to necessary offset");
 
254
      return 1;
 
255
    }
 
256
  }
 
257
 
 
258
  /* allocate buffer for reading (so we don't realloc every time) */
 
259
  p= (uchar *)malloc(UNIV_PAGE_SIZE);
 
260
 
 
261
  /* main checksumming loop */
 
262
  ct= start_page;
 
263
  lastt= 0;
 
264
  while (!feof(f))
 
265
  {
 
266
    bytes= fread(p, 1, UNIV_PAGE_SIZE, f);
 
267
    if (!bytes && feof(f)) return 0;
 
268
    if (bytes != UNIV_PAGE_SIZE)
 
269
    {
 
270
      fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE);
 
271
      return 1;
 
272
    }
 
273
 
 
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);
 
277
    if (debug)
 
278
      printf("page %lu: log sequence number: first = %lu; second = %lu\n", ct, logseq, logseqfield);
 
279
    if (logseq != logseqfield)
 
280
    {
 
281
      fprintf(stderr, "page %lu invalid (fails log sequence number check)\n", ct);
 
282
      return 1;
 
283
    }
 
284
 
 
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);
 
288
    if (debug)
 
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)
 
291
    {
 
292
      fprintf(stderr, "page %lu invalid (fails old style checksum)\n", ct);
 
293
      return 1;
 
294
    }
 
295
 
 
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);
 
299
    if (debug)
 
300
      printf("page %lu: new style: calculated = %lu; recorded = %lu\n", ct, csum, csumfield);
 
301
    if (csumfield != 0 && csum != csumfield)
 
302
    {
 
303
      fprintf(stderr, "page %lu invalid (fails new style checksum)\n", ct);
 
304
      return 1;
 
305
    }
 
306
 
 
307
    /* end if this was the last page we were supposed to check */
 
308
    if (use_end_page && (ct >= end_page))
 
309
      return 0;
 
310
 
 
311
    /* do counter increase and progress printing */
 
312
    ct++;
 
313
    if (verbose)
 
314
    {
 
315
      if (ct % 64 == 0)
 
316
      {
 
317
        now= time(0);
 
318
        if (!lastt) lastt= now;
 
319
        if (now - lastt >= 1)
 
320
        {
 
321
          printf("page %lu okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100);
 
322
          lastt= now;
 
323
        }
 
324
      }
 
325
    }
 
326
  }
 
327
  return 0;
 
328
}
 
329