MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
innochecksum.cc
1 /*
2  Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 /*
19  InnoDB offline file checksum utility. 85% of the code in this utility
20  is included from the InnoDB codebase.
21 
22  The final 15% was originally written by Mark Smith of Danga
23  Interactive, Inc. <junior@danga.com>
24 
25  Published with a permission.
26 */
27 
28 #include <my_global.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <time.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #ifndef __WIN__
35 # include <unistd.h>
36 #endif
37 #include <my_getopt.h>
38 #include <m_string.h>
39 #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
40 
41 /* Only parts of these files are included from the InnoDB codebase.
42 The parts not included are excluded by #ifndef UNIV_INNOCHECKSUM. */
43 
44 #include "univ.i" /* include all of this */
45 
46 #include "buf0checksum.h" /* buf_calc_page_*() */
47 #include "fil0fil.h" /* FIL_* */
48 #include "fsp0fsp.h" /* fsp_flags_get_page_size() &
49  fsp_flags_get_zip_size() */
50 #include "mach0data.h" /* mach_read_from_4() */
51 #include "ut0crc32.h" /* ut_crc32_init() */
52 
53 #ifdef UNIV_NONINL
54 # include "fsp0fsp.ic"
55 # include "mach0data.ic"
56 # include "ut0rnd.ic"
57 #endif
58 
59 /* Global variables */
60 static my_bool verbose;
61 static my_bool debug;
62 static my_bool just_count;
63 static ulong start_page;
64 static ulong end_page;
65 static ulong do_page;
66 static my_bool use_end_page;
67 static my_bool do_one_page;
68 ulong srv_page_size; /* replaces declaration in srv0srv.c */
69 static ulong physical_page_size; /* Page size in bytes on disk. */
70 static ulong logical_page_size; /* Page size when uncompressed. */
71 
72 /* Get the page size of the filespace from the filespace header. */
73 static
74 my_bool
75 get_page_size(
76 /*==========*/
77  FILE* f,
79  byte* buf,
80  ulong* logical_page_size,
81  ulong* physical_page_size)
82 {
83  ulong flags;
84 
85  int bytes= fread(buf, 1, UNIV_PAGE_SIZE_MIN, f);
86 
87  if (ferror(f))
88  {
89  perror("Error reading file header");
90  return FALSE;
91  }
92 
93  if (bytes != UNIV_PAGE_SIZE_MIN)
94  {
95  fprintf(stderr, "Error; Was not able to read the minimum page size ");
96  fprintf(stderr, "of %d bytes. Bytes read was %d\n", UNIV_PAGE_SIZE_MIN, bytes);
97  return FALSE;
98  }
99 
100  rewind(f);
101 
102  flags = mach_read_from_4(buf + FIL_PAGE_DATA + FSP_SPACE_FLAGS);
103 
104  /* srv_page_size is used by InnoDB code as UNIV_PAGE_SIZE */
105  srv_page_size = *logical_page_size = fsp_flags_get_page_size(flags);
106 
107  /* fsp_flags_get_zip_size() will return zero if not compressed. */
108  *physical_page_size = fsp_flags_get_zip_size(flags);
109  if (*physical_page_size == 0)
110  *physical_page_size= *logical_page_size;
111 
112  return TRUE;
113 }
114 
115 
116 /* command line argument to do page checks (that's it) */
117 /* another argument to specify page ranges... seek to right spot and go from there */
118 
119 static struct my_option innochecksum_options[] =
120 {
121  {"help", '?', "Displays this help and exits.",
122  0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
123  {"info", 'I', "Synonym for --help.",
124  0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
125  {"version", 'V', "Displays version information and exits.",
126  0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
127  {"verbose", 'v', "Verbose (prints progress every 5 seconds).",
128  &verbose, &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
129  {"debug", 'd', "Debug mode (prints checksums for each page, implies verbose).",
130  &debug, &debug, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
131  {"count", 'c', "Print the count of pages in the file.",
132  &just_count, &just_count, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
133  {"start_page", 's', "Start on this page number (0 based).",
134  &start_page, &start_page, 0, GET_ULONG, REQUIRED_ARG,
135  0, 0, (longlong) 2L*1024L*1024L*1024L, 0, 1, 0},
136  {"end_page", 'e', "End at this page number (0 based).",
137  &end_page, &end_page, 0, GET_ULONG, REQUIRED_ARG,
138  0, 0, (longlong) 2L*1024L*1024L*1024L, 0, 1, 0},
139  {"page", 'p', "Check only this page (0 based).",
140  &do_page, &do_page, 0, GET_ULONG, REQUIRED_ARG,
141  0, 0, (longlong) 2L*1024L*1024L*1024L, 0, 1, 0},
142 
143  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
144 };
145 
146 static void print_version(void)
147 {
148  printf("%s Ver %s, for %s (%s)\n",
149  my_progname, INNODB_VERSION_STR,
150  SYSTEM_TYPE, MACHINE_TYPE);
151 }
152 
153 static void usage(void)
154 {
155  print_version();
156  puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
157  printf("InnoDB offline file checksum utility.\n");
158  printf("Usage: %s [-c] [-s <start page>] [-e <end page>] [-p <page>] [-v] [-d] <filename>\n", my_progname);
159  my_print_help(innochecksum_options);
160  my_print_variables(innochecksum_options);
161 }
162 
163 extern "C" my_bool
164 innochecksum_get_one_option(
165 /*========================*/
166  int optid,
167  const struct my_option *opt __attribute__((unused)),
168  char *argument __attribute__((unused)))
169 {
170  switch (optid) {
171  case 'd':
172  verbose=1; /* debug implies verbose... */
173  break;
174  case 'e':
175  use_end_page= 1;
176  break;
177  case 'p':
178  end_page= start_page= do_page;
179  use_end_page= 1;
180  do_one_page= 1;
181  break;
182  case 'V':
183  print_version();
184  exit(0);
185  break;
186  case 'I':
187  case '?':
188  usage();
189  exit(0);
190  break;
191  }
192  return 0;
193 }
194 
195 static int get_options(
196 /*===================*/
197  int *argc,
198  char ***argv)
199 {
200  int ho_error;
201 
202  if ((ho_error=handle_options(argc, argv, innochecksum_options, innochecksum_get_one_option)))
203  exit(ho_error);
204 
205  /* The next arg must be the filename */
206  if (!*argc)
207  {
208  usage();
209  return 1;
210  }
211  return 0;
212 } /* get_options */
213 
214 
215 int main(int argc, char **argv)
216 {
217  FILE* f; /* our input file */
218  char* filename; /* our input filename. */
219  unsigned char buf[UNIV_PAGE_SIZE_MAX]; /* Buffer to store pages read */
220  ulong bytes; /* bytes read count */
221  ulint ct; /* current page number (0 based) */
222  time_t now; /* current time */
223  time_t lastt; /* last time */
224  ulint oldcsum, oldcsumfield, csum, csumfield, crc32, logseq, logseqfield;
225  /* ulints for checksum storage */
226  struct stat st; /* for stat, if you couldn't guess */
227  unsigned long long int size; /* size of file (has to be 64 bits) */
228  ulint pages; /* number of pages in file */
229  off_t offset= 0;
230  int fd;
231 
232  printf("InnoDB offline file checksum utility.\n");
233 
234  ut_crc32_init();
235 
236  MY_INIT(argv[0]);
237 
238  if (get_options(&argc,&argv))
239  exit(1);
240 
241  if (verbose)
242  my_print_variables(innochecksum_options);
243 
244  /* The file name is not optional */
245  filename = *argv;
246  if (*filename == '\0')
247  {
248  fprintf(stderr, "Error; File name missing\n");
249  return 1;
250  }
251 
252  /* stat the file to get size and page count */
253  if (stat(filename, &st))
254  {
255  fprintf(stderr, "Error; %s cannot be found\n", filename);
256  return 1;
257  }
258  size= st.st_size;
259 
260  /* Open the file for reading */
261  f= fopen(filename, "rb");
262  if (f == NULL)
263  {
264  fprintf(stderr, "Error; %s cannot be opened", filename);
265  perror(" ");
266  return 1;
267  }
268 
269  if (!get_page_size(f, buf, &logical_page_size, &physical_page_size))
270  {
271  return 1;
272  }
273 
274  /* This tool currently does not support Compressed tables */
275  if (logical_page_size != physical_page_size)
276  {
277  fprintf(stderr, "Error; This file contains compressed pages\n");
278  return 1;
279  }
280 
281  pages= (ulint) (size / physical_page_size);
282 
283  if (just_count)
284  {
285  if (verbose)
286  printf("Number of pages: ");
287  printf("%lu\n", pages);
288  return 0;
289  }
290  else if (verbose)
291  {
292  printf("file %s = %llu bytes (%lu pages)...\n", filename, size, pages);
293  if (do_one_page)
294  printf("InnoChecksum; checking page %lu\n", do_page);
295  else
296  printf("InnoChecksum; checking pages in range %lu to %lu\n", start_page, use_end_page ? end_page : (pages - 1));
297  }
298 
299  /* seek to the necessary position */
300  if (start_page)
301  {
302  fd= fileno(f);
303  if (!fd)
304  {
305  perror("Error; Unable to obtain file descriptor number");
306  return 1;
307  }
308 
309  offset= (off_t)start_page * (off_t)physical_page_size;
310 
311  if (lseek(fd, offset, SEEK_SET) != offset)
312  {
313  perror("Error; Unable to seek to necessary offset");
314  return 1;
315  }
316  }
317 
318  /* main checksumming loop */
319  ct= start_page;
320  lastt= 0;
321  while (!feof(f))
322  {
323  bytes= fread(buf, 1, physical_page_size, f);
324  if (!bytes && feof(f))
325  return 0;
326 
327  if (ferror(f))
328  {
329  fprintf(stderr, "Error reading %lu bytes", physical_page_size);
330  perror(" ");
331  return 1;
332  }
333  if (bytes != physical_page_size)
334  {
335  fprintf(stderr, "Error; bytes read (%lu) doesn't match page size (%lu)\n", bytes, physical_page_size);
336  return 1;
337  }
338 
339  /* check the "stored log sequence numbers" */
340  logseq= mach_read_from_4(buf + FIL_PAGE_LSN + 4);
341  logseqfield= mach_read_from_4(buf + logical_page_size - FIL_PAGE_END_LSN_OLD_CHKSUM + 4);
342  if (debug)
343  printf("page %lu: log sequence number: first = %lu; second = %lu\n", ct, logseq, logseqfield);
344  if (logseq != logseqfield)
345  {
346  fprintf(stderr, "Fail; page %lu invalid (fails log sequence number check)\n", ct);
347  return 1;
348  }
349 
350  /* check old method of checksumming */
351  oldcsum= buf_calc_page_old_checksum(buf);
352  oldcsumfield= mach_read_from_4(buf + logical_page_size - FIL_PAGE_END_LSN_OLD_CHKSUM);
353  if (debug)
354  printf("page %lu: old style: calculated = %lu; recorded = %lu\n", ct, oldcsum, oldcsumfield);
355  if (oldcsumfield != mach_read_from_4(buf + FIL_PAGE_LSN) && oldcsumfield != oldcsum)
356  {
357  fprintf(stderr, "Fail; page %lu invalid (fails old style checksum)\n", ct);
358  return 1;
359  }
360 
361  /* now check the new method */
362  csum= buf_calc_page_new_checksum(buf);
363  crc32= buf_calc_page_crc32(buf);
364  csumfield= mach_read_from_4(buf + FIL_PAGE_SPACE_OR_CHKSUM);
365  if (debug)
366  printf("page %lu: new style: calculated = %lu; crc32 = %lu; recorded = %lu\n",
367  ct, csum, crc32, csumfield);
368  if (csumfield != 0 && crc32 != csumfield && csum != csumfield)
369  {
370  fprintf(stderr, "Fail; page %lu invalid (fails innodb and crc32 checksum)\n", ct);
371  return 1;
372  }
373 
374  /* end if this was the last page we were supposed to check */
375  if (use_end_page && (ct >= end_page))
376  return 0;
377 
378  /* do counter increase and progress printing */
379  ct++;
380  if (verbose)
381  {
382  if (ct % 64 == 0)
383  {
384  now= time(0);
385  if (!lastt) lastt= now;
386  if (now - lastt >= 1)
387  {
388  printf("page %lu okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100);
389  lastt= now;
390  }
391  }
392  }
393  }
394  return 0;
395 }
396