~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to extra/innochecksum.c

Merged vcol stuff.

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