00001 /* Copyright (C) 2000-2005 MySQL AB & Innobase Oy 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* 00018 InnoDB offline file checksum utility. 85% of the code in this file 00019 was taken wholesale fron the InnoDB codebase. 00020 00021 The final 15% was originally written by Mark Smith of Danga 00022 Interactive, Inc. <junior@danga.com> 00023 00024 Published with a permission. 00025 */ 00026 00027 /* needed to have access to 64 bit file functions */ 00028 #define _LARGEFILE_SOURCE 00029 #define _LARGEFILE64_SOURCE 00030 00031 #define _XOPEN_SOURCE 500 /* needed to include getopt.h on some platforms. */ 00032 00033 #include <stdio.h> 00034 #include <stdlib.h> 00035 #include <time.h> 00036 #include <sys/types.h> 00037 #include <sys/stat.h> 00038 #include <unistd.h> 00039 00040 /* all of these ripped from InnoDB code from MySQL 4.0.22 */ 00041 #define UT_HASH_RANDOM_MASK 1463735687 00042 #define UT_HASH_RANDOM_MASK2 1653893711 00043 #define FIL_PAGE_LSN 16 00044 #define FIL_PAGE_FILE_FLUSH_LSN 26 00045 #define FIL_PAGE_OFFSET 4 00046 #define FIL_PAGE_DATA 38 00047 #define FIL_PAGE_END_LSN_OLD_CHKSUM 8 00048 #define FIL_PAGE_SPACE_OR_CHKSUM 0 00049 #define UNIV_PAGE_SIZE (2 * 8192) 00050 00051 /* command line argument to do page checks (that's it) */ 00052 /* another argument to specify page ranges... seek to right spot and go from there */ 00053 00054 typedef unsigned long int ulint; 00055 typedef unsigned char byte; 00056 00057 /* innodb function in name; modified slightly to not have the ASM version (lots of #ifs that didn't apply) */ 00058 ulint mach_read_from_4(byte *b) 00059 { 00060 return( ((ulint)(b[0]) << 24) 00061 + ((ulint)(b[1]) << 16) 00062 + ((ulint)(b[2]) << 8) 00063 + (ulint)(b[3]) 00064 ); 00065 } 00066 00067 ulint 00068 ut_fold_ulint_pair( 00069 /*===============*/ 00070 /* out: folded value */ 00071 ulint n1, /* in: ulint */ 00072 ulint n2) /* in: ulint */ 00073 { 00074 return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1) 00075 ^ UT_HASH_RANDOM_MASK) + n2); 00076 } 00077 00078 ulint 00079 ut_fold_binary( 00080 /*===========*/ 00081 /* out: folded value */ 00082 byte* str, /* in: string of bytes */ 00083 ulint len) /* in: length */ 00084 { 00085 ulint i; 00086 ulint fold= 0; 00087 00088 for (i= 0; i < len; i++) 00089 { 00090 fold= ut_fold_ulint_pair(fold, (ulint)(*str)); 00091 00092 str++; 00093 } 00094 00095 return(fold); 00096 } 00097 00098 ulint 00099 buf_calc_page_new_checksum( 00100 /*=======================*/ 00101 /* out: checksum */ 00102 byte* page) /* in: buffer page */ 00103 { 00104 ulint checksum; 00105 00106 /* Since the fields FIL_PAGE_FILE_FLUSH_LSN and ..._ARCH_LOG_NO 00107 are written outside the buffer pool to the first pages of data 00108 files, we have to skip them in the page checksum calculation. 00109 We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the 00110 checksum is stored, and also the last 8 bytes of page because 00111 there we store the old formula checksum. */ 00112 00113 checksum= ut_fold_binary(page + FIL_PAGE_OFFSET, 00114 FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET) 00115 + ut_fold_binary(page + FIL_PAGE_DATA, 00116 UNIV_PAGE_SIZE - FIL_PAGE_DATA 00117 - FIL_PAGE_END_LSN_OLD_CHKSUM); 00118 checksum= checksum & 0xFFFFFFFF; 00119 00120 return(checksum); 00121 } 00122 00123 ulint 00124 buf_calc_page_old_checksum( 00125 /*=======================*/ 00126 /* out: checksum */ 00127 byte* page) /* in: buffer page */ 00128 { 00129 ulint checksum; 00130 00131 checksum= ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN); 00132 00133 checksum= checksum & 0xFFFFFFFF; 00134 00135 return(checksum); 00136 } 00137 00138 00139 int main(int argc, char **argv) 00140 { 00141 FILE *f; /* our input file */ 00142 byte *p; /* storage of pages read */ 00143 int bytes; /* bytes read count */ 00144 ulint ct; /* current page number (0 based) */ 00145 int now; /* current time */ 00146 int lastt; /* last time */ 00147 ulint oldcsum, oldcsumfield, csum, csumfield, logseq, logseqfield; /* ulints for checksum storage */ 00148 struct stat st; /* for stat, if you couldn't guess */ 00149 unsigned long long int size; /* size of file (has to be 64 bits) */ 00150 ulint pages; /* number of pages in file */ 00151 ulint start_page= 0, end_page= 0, use_end_page= 0; /* for starting and ending at certain pages */ 00152 off_t offset= 0; 00153 int just_count= 0; /* if true, just print page count */ 00154 int verbose= 0; 00155 int debug= 0; 00156 int c; 00157 int fd; 00158 00159 /* remove arguments */ 00160 while ((c= getopt(argc, argv, "cvds:e:p:")) != -1) 00161 { 00162 switch (c) 00163 { 00164 case 'v': 00165 verbose= 1; 00166 break; 00167 case 'c': 00168 just_count= 1; 00169 break; 00170 case 's': 00171 start_page= atoi(optarg); 00172 break; 00173 case 'e': 00174 end_page= atoi(optarg); 00175 use_end_page= 1; 00176 break; 00177 case 'p': 00178 start_page= atoi(optarg); 00179 end_page= atoi(optarg); 00180 use_end_page= 1; 00181 break; 00182 case 'd': 00183 debug= 1; 00184 break; 00185 case ':': 00186 fprintf(stderr, "option -%c requires an argument\n", optopt); 00187 return 1; 00188 break; 00189 case '?': 00190 fprintf(stderr, "unrecognized option: -%c\n", optopt); 00191 return 1; 00192 break; 00193 } 00194 } 00195 00196 /* debug implies verbose... */ 00197 if (debug) verbose= 1; 00198 00199 /* make sure we have the right arguments */ 00200 if (optind >= argc) 00201 { 00202 printf("InnoDB offline file checksum utility.\n"); 00203 printf("usage: %s [-c] [-s <start page>] [-e <end page>] [-p <page>] [-v] [-d] <filename>\n", argv[0]); 00204 printf("\t-c\tprint the count of pages in the file\n"); 00205 printf("\t-s n\tstart on this page number (0 based)\n"); 00206 printf("\t-e n\tend at this page number (0 based)\n"); 00207 printf("\t-p n\tcheck only this page (0 based)\n"); 00208 printf("\t-v\tverbose (prints progress every 5 seconds)\n"); 00209 printf("\t-d\tdebug mode (prints checksums for each page)\n"); 00210 return 1; 00211 } 00212 00213 /* stat the file to get size and page count */ 00214 if (stat(argv[optind], &st)) 00215 { 00216 perror("error statting file"); 00217 return 1; 00218 } 00219 size= st.st_size; 00220 pages= size / UNIV_PAGE_SIZE; 00221 if (just_count) 00222 { 00223 printf("%lu\n", pages); 00224 return 0; 00225 } 00226 else if (verbose) 00227 { 00228 printf("file %s= %llu bytes (%lu pages)...\n", argv[1], size, pages); 00229 printf("checking pages in range %lu to %lu\n", start_page, use_end_page ? end_page : (pages - 1)); 00230 } 00231 00232 /* open the file for reading */ 00233 f= fopen(argv[optind], "r"); 00234 if (!f) 00235 { 00236 perror("error opening file"); 00237 return 1; 00238 } 00239 00240 /* seek to the necessary position */ 00241 if (start_page) 00242 { 00243 fd= fileno(f); 00244 if (!fd) 00245 { 00246 perror("unable to obtain file descriptor number"); 00247 return 1; 00248 } 00249 00250 offset= (off_t)start_page * (off_t)UNIV_PAGE_SIZE; 00251 00252 if (lseek(fd, offset, SEEK_SET) != offset) 00253 { 00254 perror("unable to seek to necessary offset"); 00255 return 1; 00256 } 00257 } 00258 00259 /* allocate buffer for reading (so we don't realloc every time) */ 00260 p= (byte *)malloc(UNIV_PAGE_SIZE); 00261 00262 /* main checksumming loop */ 00263 ct= start_page; 00264 lastt= 0; 00265 while (!feof(f)) 00266 { 00267 bytes= fread(p, 1, UNIV_PAGE_SIZE, f); 00268 if (!bytes && feof(f)) return 0; 00269 if (bytes != UNIV_PAGE_SIZE) 00270 { 00271 fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE); 00272 return 1; 00273 } 00274 00275 /* check the "stored log sequence numbers" */ 00276 logseq= mach_read_from_4(p + FIL_PAGE_LSN + 4); 00277 logseqfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4); 00278 if (debug) 00279 printf("page %lu: log sequence number: first = %lu; second = %lu\n", ct, logseq, logseqfield); 00280 if (logseq != logseqfield) 00281 { 00282 fprintf(stderr, "page %lu invalid (fails log sequence number check)\n", ct); 00283 return 1; 00284 } 00285 00286 /* check old method of checksumming */ 00287 oldcsum= buf_calc_page_old_checksum(p); 00288 oldcsumfield= mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM); 00289 if (debug) 00290 printf("page %lu: old style: calculated = %lu; recorded = %lu\n", ct, oldcsum, oldcsumfield); 00291 if (oldcsumfield != mach_read_from_4(p + FIL_PAGE_LSN) && oldcsumfield != oldcsum) 00292 { 00293 fprintf(stderr, "page %lu invalid (fails old style checksum)\n", ct); 00294 return 1; 00295 } 00296 00297 /* now check the new method */ 00298 csum= buf_calc_page_new_checksum(p); 00299 csumfield= mach_read_from_4(p + FIL_PAGE_SPACE_OR_CHKSUM); 00300 if (debug) 00301 printf("page %lu: new style: calculated = %lu; recorded = %lu\n", ct, csum, csumfield); 00302 if (csumfield != 0 && csum != csumfield) 00303 { 00304 fprintf(stderr, "page %lu invalid (fails new style checksum)\n", ct); 00305 return 1; 00306 } 00307 00308 /* end if this was the last page we were supposed to check */ 00309 if (use_end_page && (ct >= end_page)) 00310 return 0; 00311 00312 /* do counter increase and progress printing */ 00313 ct++; 00314 if (verbose) 00315 { 00316 if (ct % 64 == 0) 00317 { 00318 now= time(0); 00319 if (!lastt) lastt= now; 00320 if (now - lastt >= 1) 00321 { 00322 printf("page %lu okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100); 00323 lastt= now; 00324 } 00325 } 00326 } 00327 } 00328 return 0; 00329 } 00330
1.4.7

