1 /* 2 ** A utility for printing all or part of an SQLite database file. 3 */ 4 #include <stdio.h> 5 #include <ctype.h> 6 #include <sys/types.h> 7 #include <sys/stat.h> 8 #include <fcntl.h> 9 #include <unistd.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 14 static int pagesize = 1024; /* Size of a database page */ 15 static int db = -1; /* File descriptor for reading the DB */ 16 static int mxPage = 0; /* Last page number */ 17 static int perLine = 16; /* HEX elements to print per line */ 18 19 typedef long long int i64; /* Datatype for 64-bit integers */ 20 21 22 /* 23 ** Convert the var-int format into i64. Return the number of bytes 24 ** in the var-int. Write the var-int value into *pVal. 25 */ 26 static int decodeVarint(const unsigned char *z, i64 *pVal){ 27 i64 v = 0; 28 int i; 29 for(i=0; i<8; i++){ 30 v = (v<<7) + (z[i]&0x7f); 31 if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; } 32 } 33 v = (v<<8) + (z[i]&0xff); 34 *pVal = v; 35 return 9; 36 } 37 38 /* 39 ** Extract a big-endian 32-bit integer 40 */ 41 static unsigned int decodeInt32(const unsigned char *z){ 42 return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3]; 43 } 44 45 /* Report an out-of-memory error and die. 46 */ 47 static void out_of_memory(void){ 48 fprintf(stderr,"Out of memory...\n"); 49 exit(1); 50 } 51 52 /* 53 ** Read content from the file. 54 ** 55 ** Space to hold the content is obtained from malloc() and needs to be 56 ** freed by the caller. 57 */ 58 static unsigned char *getContent(int ofst, int nByte){ 59 unsigned char *aData; 60 aData = malloc(nByte); 61 if( aData==0 ) out_of_memory(); 62 lseek(db, ofst, SEEK_SET); 63 read(db, aData, nByte); 64 return aData; 65 } 66 67 /* 68 ** Print a range of bytes as hex and as ascii. 69 */ 70 static unsigned char *print_byte_range( 71 int ofst, /* First byte in the range of bytes to print */ 72 int nByte, /* Number of bytes to print */ 73 int printOfst /* Add this amount to the index on the left column */ 74 ){ 75 unsigned char *aData; 76 int i, j; 77 const char *zOfstFmt; 78 79 if( ((printOfst+nByte)&~0xfff)==0 ){ 80 zOfstFmt = " %03x: "; 81 }else if( ((printOfst+nByte)&~0xffff)==0 ){ 82 zOfstFmt = " %04x: "; 83 }else if( ((printOfst+nByte)&~0xfffff)==0 ){ 84 zOfstFmt = " %05x: "; 85 }else if( ((printOfst+nByte)&~0xffffff)==0 ){ 86 zOfstFmt = " %06x: "; 87 }else{ 88 zOfstFmt = " %08x: "; 89 } 90 91 aData = getContent(ofst, nByte); 92 for(i=0; i<nByte; i += perLine){ 93 fprintf(stdout, zOfstFmt, i+printOfst); 94 for(j=0; j<perLine; j++){ 95 if( i+j>nByte ){ 96 fprintf(stdout, " "); 97 }else{ 98 fprintf(stdout,"%02x ", aData[i+j]); 99 } 100 } 101 for(j=0; j<perLine; j++){ 102 if( i+j>nByte ){ 103 fprintf(stdout, " "); 104 }else{ 105 fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.'); 106 } 107 } 108 fprintf(stdout,"\n"); 109 } 110 return aData; 111 } 112 113 /* 114 ** Print an entire page of content as hex 115 */ 116 static print_page(int iPg){ 117 int iStart; 118 unsigned char *aData; 119 iStart = (iPg-1)*pagesize; 120 fprintf(stdout, "Page %d: (offsets 0x%x..0x%x)\n", 121 iPg, iStart, iStart+pagesize-1); 122 aData = print_byte_range(iStart, pagesize, 0); 123 free(aData); 124 } 125 126 /* Print a line of decode output showing a 4-byte integer. 127 */ 128 static print_decode_line( 129 unsigned char *aData, /* Content being decoded */ 130 int ofst, int nByte, /* Start and size of decode */ 131 const char *zMsg /* Message to append */ 132 ){ 133 int i, j; 134 int val = aData[ofst]; 135 char zBuf[100]; 136 sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]); 137 i = strlen(zBuf); 138 for(j=1; j<4; j++){ 139 if( j>=nByte ){ 140 sprintf(&zBuf[i], " "); 141 }else{ 142 sprintf(&zBuf[i], " %02x", aData[ofst+j]); 143 val = val*256 + aData[ofst+j]; 144 } 145 i += strlen(&zBuf[i]); 146 } 147 sprintf(&zBuf[i], " %9d", val); 148 printf("%s %s\n", zBuf, zMsg); 149 } 150 151 /* 152 ** Decode the database header. 153 */ 154 static void print_db_header(void){ 155 unsigned char *aData; 156 aData = print_byte_range(0, 100, 0); 157 printf("Decoded:\n"); 158 print_decode_line(aData, 16, 2, "Database page size"); 159 print_decode_line(aData, 18, 1, "File format write version"); 160 print_decode_line(aData, 19, 1, "File format read version"); 161 print_decode_line(aData, 20, 1, "Reserved space at end of page"); 162 print_decode_line(aData, 24, 4, "File change counter"); 163 print_decode_line(aData, 28, 4, "Size of database in pages"); 164 print_decode_line(aData, 32, 4, "Page number of first freelist page"); 165 print_decode_line(aData, 36, 4, "Number of freelist pages"); 166 print_decode_line(aData, 40, 4, "Schema cookie"); 167 print_decode_line(aData, 44, 4, "Schema format version"); 168 print_decode_line(aData, 48, 4, "Default page cache size"); 169 print_decode_line(aData, 52, 4, "Largest auto-vac root page"); 170 print_decode_line(aData, 56, 4, "Text encoding"); 171 print_decode_line(aData, 60, 4, "User version"); 172 print_decode_line(aData, 64, 4, "Incremental-vacuum mode"); 173 print_decode_line(aData, 68, 4, "meta[7]"); 174 print_decode_line(aData, 72, 4, "meta[8]"); 175 print_decode_line(aData, 76, 4, "meta[9]"); 176 print_decode_line(aData, 80, 4, "meta[10]"); 177 print_decode_line(aData, 84, 4, "meta[11]"); 178 print_decode_line(aData, 88, 4, "meta[12]"); 179 print_decode_line(aData, 92, 4, "Change counter for version number"); 180 print_decode_line(aData, 96, 4, "SQLite version number"); 181 } 182 183 /* 184 ** Create a description for a single cell. 185 */ 186 static int describeCell(unsigned char cType, unsigned char *a, char **pzDesc){ 187 int i; 188 int nDesc = 0; 189 int n = 0; 190 int leftChild; 191 i64 nPayload; 192 i64 rowid; 193 static char zDesc[100]; 194 i = 0; 195 if( cType<=5 ){ 196 leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3]; 197 a += 4; 198 n += 4; 199 sprintf(zDesc, "left-child: %d ", leftChild); 200 nDesc = strlen(zDesc); 201 } 202 if( cType!=5 ){ 203 i = decodeVarint(a, &nPayload); 204 a += i; 205 n += i; 206 sprintf(&zDesc[nDesc], "sz: %lld ", nPayload); 207 nDesc += strlen(&zDesc[nDesc]); 208 } 209 if( cType==5 || cType==13 ){ 210 i = decodeVarint(a, &rowid); 211 a += i; 212 n += i; 213 sprintf(&zDesc[nDesc], "rowid: %lld ", rowid); 214 nDesc += strlen(&zDesc[nDesc]); 215 } 216 *pzDesc = zDesc; 217 return n; 218 } 219 220 /* 221 ** Decode a btree page 222 */ 223 static void decode_btree_page(unsigned char *a, int pgno, int hdrSize){ 224 const char *zType = "unknown"; 225 int nCell; 226 int i; 227 int iCellPtr; 228 switch( a[0] ){ 229 case 2: zType = "index interior node"; break; 230 case 5: zType = "table interior node"; break; 231 case 10: zType = "index leaf"; break; 232 case 13: zType = "table leaf"; break; 233 } 234 printf("Decode of btree page %d:\n", pgno); 235 print_decode_line(a, 0, 1, zType); 236 print_decode_line(a, 1, 2, "Offset to first freeblock"); 237 print_decode_line(a, 3, 2, "Number of cells on this page"); 238 nCell = a[3]*256 + a[4]; 239 print_decode_line(a, 5, 2, "Offset to cell content area"); 240 print_decode_line(a, 7, 1, "Fragmented byte count"); 241 if( a[0]==2 || a[0]==5 ){ 242 print_decode_line(a, 8, 4, "Right child"); 243 iCellPtr = 12; 244 }else{ 245 iCellPtr = 8; 246 } 247 for(i=0; i<nCell; i++){ 248 int cofst = iCellPtr + i*2; 249 char *zDesc; 250 cofst = a[cofst]*256 + a[cofst+1]; 251 describeCell(a[0], &a[cofst-hdrSize], &zDesc); 252 printf(" %03x: cell[%d] %s\n", cofst, i, zDesc); 253 } 254 } 255 256 /* 257 ** Decode a freelist trunk page. 258 */ 259 static void decode_trunk_page( 260 int pgno, /* The page number */ 261 int pagesize, /* Size of each page */ 262 int detail, /* Show leaf pages if true */ 263 int recursive /* Follow the trunk change if true */ 264 ){ 265 int n, i, k; 266 unsigned char *a; 267 while( pgno>0 ){ 268 a = getContent((pgno-1)*pagesize, pagesize); 269 printf("Decode of freelist trunk page %d:\n", pgno); 270 print_decode_line(a, 0, 4, "Next freelist trunk page"); 271 print_decode_line(a, 4, 4, "Number of entries on this page"); 272 if( detail ){ 273 n = (int)decodeInt32(&a[4]); 274 for(i=0; i<n; i++){ 275 unsigned int x = decodeInt32(&a[8+4*i]); 276 char zIdx[10]; 277 sprintf(zIdx, "[%d]", i); 278 printf(" %5s %7u", zIdx, x); 279 if( i%5==4 ) printf("\n"); 280 } 281 if( i%5!=0 ) printf("\n"); 282 } 283 if( !recursive ){ 284 pgno = 0; 285 }else{ 286 pgno = (int)decodeInt32(&a[0]); 287 } 288 free(a); 289 } 290 } 291 292 /* 293 ** Print a usage comment 294 */ 295 static void usage(const char *argv0){ 296 fprintf(stderr, "Usage %s FILENAME ?args...?\n\n", argv0); 297 fprintf(stderr, 298 "args:\n" 299 " dbheader Show database header\n" 300 " NNN..MMM Show hex of pages NNN through MMM\n" 301 " NNN..end Show hex of pages NNN through end of file\n" 302 " NNNb Decode btree page NNN\n" 303 " NNNt Decode freelist trunk page NNN\n" 304 " NNNtd Show leave freelist pages on the decode\n" 305 " NNNtr Recurisvely decode freelist starting at NNN\n" 306 ); 307 } 308 309 int main(int argc, char **argv){ 310 struct stat sbuf; 311 unsigned char zPgSz[2]; 312 if( argc<2 ){ 313 usage(argv[0]); 314 exit(1); 315 } 316 db = open(argv[1], O_RDONLY); 317 if( db<0 ){ 318 fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]); 319 exit(1); 320 } 321 zPgSz[0] = 0; 322 zPgSz[1] = 0; 323 lseek(db, 16, SEEK_SET); 324 read(db, zPgSz, 2); 325 pagesize = zPgSz[0]*256 + zPgSz[1]*65536; 326 if( pagesize==0 ) pagesize = 1024; 327 printf("Pagesize: %d\n", pagesize); 328 fstat(db, &sbuf); 329 mxPage = sbuf.st_size/pagesize; 330 printf("Available pages: 1..%d\n", mxPage); 331 if( argc==2 ){ 332 int i; 333 for(i=1; i<=mxPage; i++) print_page(i); 334 }else{ 335 int i; 336 for(i=2; i<argc; i++){ 337 int iStart, iEnd; 338 char *zLeft; 339 if( strcmp(argv[i], "dbheader")==0 ){ 340 print_db_header(); 341 continue; 342 } 343 if( !isdigit(argv[i][0]) ){ 344 fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]); 345 continue; 346 } 347 iStart = strtol(argv[i], &zLeft, 0); 348 if( zLeft && strcmp(zLeft,"..end")==0 ){ 349 iEnd = mxPage; 350 }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){ 351 iEnd = strtol(&zLeft[2], 0, 0); 352 }else if( zLeft && zLeft[0]=='b' ){ 353 int ofst, nByte, hdrSize; 354 unsigned char *a; 355 if( iStart==1 ){ 356 ofst = hdrSize = 100; 357 nByte = pagesize-100; 358 }else{ 359 hdrSize = 0; 360 ofst = (iStart-1)*pagesize; 361 nByte = pagesize; 362 } 363 a = getContent(ofst, nByte); 364 decode_btree_page(a, iStart, hdrSize); 365 free(a); 366 continue; 367 }else if( zLeft && zLeft[0]=='t' ){ 368 unsigned char *a; 369 int detail = 0; 370 int recursive = 0; 371 int i; 372 for(i=1; zLeft[i]; i++){ 373 if( zLeft[i]=='r' ) recursive = 1; 374 if( zLeft[i]=='d' ) detail = 1; 375 } 376 decode_trunk_page(iStart, pagesize, detail, recursive); 377 continue; 378 }else{ 379 iEnd = iStart; 380 } 381 if( iStart<1 || iEnd<iStart || iEnd>mxPage ){ 382 fprintf(stderr, 383 "Page argument should be LOWER?..UPPER?. Range 1 to %d\n", 384 mxPage); 385 exit(1); 386 } 387 while( iStart<=iEnd ){ 388 print_page(iStart); 389 iStart++; 390 } 391 } 392 } 393 close(db); 394 } 395