]> git.saurik.com Git - redis.git/blame - src/redis-check-dump.c
BSD license added to every C source and header file.
[redis.git] / src / redis-check-dump.c
CommitLineData
d288ee65 1/*
2 * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
08af4d5c
PN
32#include <stdlib.h>
33#include <stdio.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <sys/stat.h>
37#include <sys/mman.h>
38#include <string.h>
39#include <arpa/inet.h>
40#include <stdint.h>
41#include <limits.h>
42#include "lzf.h"
43
44/* Object types */
45#define REDIS_STRING 0
46#define REDIS_LIST 1
47#define REDIS_SET 2
48#define REDIS_ZSET 3
49#define REDIS_HASH 4
2e63cfe2 50#define REDIS_HASH_ZIPMAP 9
51#define REDIS_LIST_ZIPLIST 10
52#define REDIS_SET_INTSET 11
53#define REDIS_ZSET_ZIPLIST 12
b014c1f2 54#define REDIS_HASH_ZIPLIST 13
08af4d5c
PN
55
56/* Objects encoding. Some kind of objects like Strings and Hashes can be
57 * internally represented in multiple ways. The 'encoding' field of the object
58 * is set to one of this fields for this object. */
59#define REDIS_ENCODING_RAW 0 /* Raw representation */
60#define REDIS_ENCODING_INT 1 /* Encoded as integer */
61#define REDIS_ENCODING_ZIPMAP 2 /* Encoded as zipmap */
62#define REDIS_ENCODING_HT 3 /* Encoded as an hash table */
63
64/* Object types only used for dumping to disk */
16144589 65#define REDIS_EXPIRETIME_MS 252
08af4d5c
PN
66#define REDIS_EXPIRETIME 253
67#define REDIS_SELECTDB 254
68#define REDIS_EOF 255
69
70/* Defines related to the dump file format. To store 32 bits lengths for short
71 * keys requires a lot of space, so we check the most significant 2 bits of
72 * the first byte to interpreter the length:
73 *
74 * 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte
75 * 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte
76 * 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow
77 * 11|000000 this means: specially encoded object will follow. The six bits
78 * number specify the kind of object that follows.
79 * See the REDIS_RDB_ENC_* defines.
80 *
81 * Lenghts up to 63 are stored using a single byte, most DB keys, and may
82 * values, will fit inside. */
83#define REDIS_RDB_6BITLEN 0
84#define REDIS_RDB_14BITLEN 1
85#define REDIS_RDB_32BITLEN 2
86#define REDIS_RDB_ENCVAL 3
87#define REDIS_RDB_LENERR UINT_MAX
88
89/* When a length of a string object stored on disk has the first two bits
90 * set, the remaining two bits specify a special encoding for the object
91 * accordingly to the following defines: */
92#define REDIS_RDB_ENC_INT8 0 /* 8 bit signed integer */
93#define REDIS_RDB_ENC_INT16 1 /* 16 bit signed integer */
94#define REDIS_RDB_ENC_INT32 2 /* 32 bit signed integer */
95#define REDIS_RDB_ENC_LZF 3 /* string compressed with FASTLZ */
96
97#define ERROR(...) { \
98 printf(__VA_ARGS__); \
99 exit(1); \
100}
101
102/* data type to hold offset in file and size */
103typedef struct {
104 void *data;
f85202c3
PN
105 size_t size;
106 size_t offset;
08af4d5c
PN
107} pos;
108
109static unsigned char level = 0;
110static pos positions[16];
111
112#define CURR_OFFSET (positions[level].offset)
113
114/* Hold a stack of errors */
115typedef struct {
116 char error[16][1024];
f85202c3
PN
117 size_t offset[16];
118 size_t level;
08af4d5c
PN
119} errors_t;
120static errors_t errors;
121
122#define SHIFT_ERROR(provided_offset, ...) { \
123 sprintf(errors.error[errors.level], __VA_ARGS__); \
124 errors.offset[errors.level] = provided_offset; \
125 errors.level++; \
126}
127
128/* Data type to hold opcode with optional key name an success status */
129typedef struct {
130 char* key;
131 int type;
132 char success;
133} entry;
134
135/* Global vars that are actally used as constants. The following double
136 * values are used for double on-disk serialization, and are initialized
137 * at runtime to avoid strange compiler optimizations. */
138static double R_Zero, R_PosInf, R_NegInf, R_Nan;
139
140/* store string types for output */
141static char types[256][16];
142
12a042f8 143/* Prototypes */
144uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
145
16144589
S
146/* Return true if 't' is a valid object type. */
147int checkType(unsigned char t) {
148 /* In case a new object type is added, update the following
149 * condition as necessary. */
150 return
151 (t >= REDIS_HASH_ZIPMAP && t <= REDIS_HASH_ZIPLIST) ||
152 t <= REDIS_HASH ||
153 t >= REDIS_EXPIRETIME_MS;
154}
155
08af4d5c
PN
156/* when number of bytes to read is negative, do a peek */
157int readBytes(void *target, long num) {
158 char peek = (num < 0) ? 1 : 0;
159 num = (num < 0) ? -num : num;
160
161 pos p = positions[level];
162 if (p.offset + num > p.size) {
163 return 0;
164 } else {
f85202c3 165 memcpy(target, (void*)((size_t)p.data + p.offset), num);
08af4d5c
PN
166 if (!peek) positions[level].offset += num;
167 }
168 return 1;
169}
170
171int processHeader() {
172 char buf[10] = "_________";
173 int dump_version;
174
175 if (!readBytes(buf, 9)) {
176 ERROR("Cannot read header\n");
177 }
178
179 /* expect the first 5 bytes to equal REDIS */
180 if (memcmp(buf,"REDIS",5) != 0) {
181 ERROR("Wrong signature in header\n");
182 }
183
184 dump_version = (int)strtol(buf + 5, NULL, 10);
12a042f8 185 if (dump_version < 1 || dump_version > 6) {
08af4d5c
PN
186 ERROR("Unknown RDB format version: %d\n", dump_version);
187 }
12a042f8 188 return dump_version;
08af4d5c
PN
189}
190
191int loadType(entry *e) {
192 uint32_t offset = CURR_OFFSET;
193
194 /* this byte needs to qualify as type */
195 unsigned char t;
196 if (readBytes(&t, 1)) {
16144589 197 if (checkType(t)) {
08af4d5c
PN
198 e->type = t;
199 return 1;
200 } else {
201 SHIFT_ERROR(offset, "Unknown type (0x%02x)", t);
202 }
203 } else {
204 SHIFT_ERROR(offset, "Could not read type");
205 }
206
207 /* failure */
208 return 0;
209}
210
211int peekType() {
212 unsigned char t;
16144589 213 if (readBytes(&t, -1) && (checkType(t)))
2e63cfe2 214 return t;
08af4d5c
PN
215 return -1;
216}
217
218/* discard time, just consume the bytes */
16144589 219int processTime(int type) {
08af4d5c 220 uint32_t offset = CURR_OFFSET;
16144589
S
221 unsigned char t[8];
222 int timelen = (type == REDIS_EXPIRETIME_MS) ? 8 : 4;
223
224 if (readBytes(t,timelen)) {
08af4d5c
PN
225 return 1;
226 } else {
227 SHIFT_ERROR(offset, "Could not read time");
228 }
229
230 /* failure */
231 return 0;
232}
233
234uint32_t loadLength(int *isencoded) {
235 unsigned char buf[2];
236 uint32_t len;
237 int type;
238
239 if (isencoded) *isencoded = 0;
240 if (!readBytes(buf, 1)) return REDIS_RDB_LENERR;
241 type = (buf[0] & 0xC0) >> 6;
242 if (type == REDIS_RDB_6BITLEN) {
243 /* Read a 6 bit len */
244 return buf[0] & 0x3F;
245 } else if (type == REDIS_RDB_ENCVAL) {
246 /* Read a 6 bit len encoding type */
247 if (isencoded) *isencoded = 1;
248 return buf[0] & 0x3F;
249 } else if (type == REDIS_RDB_14BITLEN) {
250 /* Read a 14 bit len */
251 if (!readBytes(buf+1,1)) return REDIS_RDB_LENERR;
252 return ((buf[0] & 0x3F) << 8) | buf[1];
253 } else {
254 /* Read a 32 bit len */
255 if (!readBytes(&len, 4)) return REDIS_RDB_LENERR;
256 return (unsigned int)ntohl(len);
257 }
258}
259
260char *loadIntegerObject(int enctype) {
261 uint32_t offset = CURR_OFFSET;
262 unsigned char enc[4];
263 long long val;
264
265 if (enctype == REDIS_RDB_ENC_INT8) {
266 uint8_t v;
267 if (!readBytes(enc, 1)) return NULL;
268 v = enc[0];
269 val = (int8_t)v;
270 } else if (enctype == REDIS_RDB_ENC_INT16) {
271 uint16_t v;
272 if (!readBytes(enc, 2)) return NULL;
273 v = enc[0]|(enc[1]<<8);
274 val = (int16_t)v;
275 } else if (enctype == REDIS_RDB_ENC_INT32) {
276 uint32_t v;
277 if (!readBytes(enc, 4)) return NULL;
278 v = enc[0]|(enc[1]<<8)|(enc[2]<<16)|(enc[3]<<24);
279 val = (int32_t)v;
280 } else {
281 SHIFT_ERROR(offset, "Unknown integer encoding (0x%02x)", enctype);
282 return NULL;
283 }
284
285 /* convert val into string */
286 char *buf;
287 buf = malloc(sizeof(char) * 128);
288 sprintf(buf, "%lld", val);
289 return buf;
290}
291
292char* loadLzfStringObject() {
293 unsigned int slen, clen;
294 char *c, *s;
295
296 if ((clen = loadLength(NULL)) == REDIS_RDB_LENERR) return NULL;
297 if ((slen = loadLength(NULL)) == REDIS_RDB_LENERR) return NULL;
298
299 c = malloc(clen);
300 if (!readBytes(c, clen)) {
301 free(c);
302 return NULL;
303 }
304
305 s = malloc(slen+1);
306 if (lzf_decompress(c,clen,s,slen) == 0) {
307 free(c); free(s);
308 return NULL;
309 }
310
311 free(c);
312 return s;
313}
314
315/* returns NULL when not processable, char* when valid */
316char* loadStringObject() {
317 uint32_t offset = CURR_OFFSET;
318 int isencoded;
319 uint32_t len;
320
321 len = loadLength(&isencoded);
322 if (isencoded) {
323 switch(len) {
324 case REDIS_RDB_ENC_INT8:
325 case REDIS_RDB_ENC_INT16:
326 case REDIS_RDB_ENC_INT32:
327 return loadIntegerObject(len);
328 case REDIS_RDB_ENC_LZF:
329 return loadLzfStringObject();
330 default:
331 /* unknown encoding */
332 SHIFT_ERROR(offset, "Unknown string encoding (0x%02x)", len);
333 return NULL;
334 }
335 }
336
337 if (len == REDIS_RDB_LENERR) return NULL;
338
339 char *buf = malloc(sizeof(char) * (len+1));
340 buf[len] = '\0';
341 if (!readBytes(buf, len)) {
342 free(buf);
343 return NULL;
344 }
345 return buf;
346}
347
348int processStringObject(char** store) {
349 unsigned long offset = CURR_OFFSET;
350 char *key = loadStringObject();
351 if (key == NULL) {
352 SHIFT_ERROR(offset, "Error reading string object");
353 free(key);
354 return 0;
355 }
356
357 if (store != NULL) {
358 *store = key;
359 } else {
360 free(key);
361 }
362 return 1;
363}
364
365double* loadDoubleValue() {
366 char buf[256];
367 unsigned char len;
368 double* val;
369
370 if (!readBytes(&len,1)) return NULL;
371
372 val = malloc(sizeof(double));
373 switch(len) {
374 case 255: *val = R_NegInf; return val;
375 case 254: *val = R_PosInf; return val;
376 case 253: *val = R_Nan; return val;
377 default:
378 if (!readBytes(buf, len)) {
379 free(val);
380 return NULL;
381 }
382 buf[len] = '\0';
383 sscanf(buf, "%lg", val);
384 return val;
385 }
386}
387
388int processDoubleValue(double** store) {
389 unsigned long offset = CURR_OFFSET;
390 double *val = loadDoubleValue();
391 if (val == NULL) {
392 SHIFT_ERROR(offset, "Error reading double value");
393 free(val);
394 return 0;
395 }
396
397 if (store != NULL) {
398 *store = val;
399 } else {
400 free(val);
401 }
402 return 1;
403}
404
405int loadPair(entry *e) {
406 uint32_t offset = CURR_OFFSET;
407 uint32_t i;
408
409 /* read key first */
410 char *key;
411 if (processStringObject(&key)) {
412 e->key = key;
413 } else {
414 SHIFT_ERROR(offset, "Error reading entry key");
415 return 0;
416 }
417
418 uint32_t length = 0;
419 if (e->type == REDIS_LIST ||
420 e->type == REDIS_SET ||
421 e->type == REDIS_ZSET ||
422 e->type == REDIS_HASH) {
423 if ((length = loadLength(NULL)) == REDIS_RDB_LENERR) {
424 SHIFT_ERROR(offset, "Error reading %s length", types[e->type]);
425 return 0;
426 }
427 }
428
429 switch(e->type) {
430 case REDIS_STRING:
2e63cfe2 431 case REDIS_HASH_ZIPMAP:
432 case REDIS_LIST_ZIPLIST:
433 case REDIS_SET_INTSET:
434 case REDIS_ZSET_ZIPLIST:
b014c1f2 435 case REDIS_HASH_ZIPLIST:
08af4d5c
PN
436 if (!processStringObject(NULL)) {
437 SHIFT_ERROR(offset, "Error reading entry value");
438 return 0;
439 }
440 break;
441 case REDIS_LIST:
442 case REDIS_SET:
443 for (i = 0; i < length; i++) {
444 offset = CURR_OFFSET;
445 if (!processStringObject(NULL)) {
446 SHIFT_ERROR(offset, "Error reading element at index %d (length: %d)", i, length);
447 return 0;
448 }
449 }
450 break;
451 case REDIS_ZSET:
452 for (i = 0; i < length; i++) {
453 offset = CURR_OFFSET;
454 if (!processStringObject(NULL)) {
455 SHIFT_ERROR(offset, "Error reading element key at index %d (length: %d)", i, length);
456 return 0;
457 }
458 offset = CURR_OFFSET;
459 if (!processDoubleValue(NULL)) {
460 SHIFT_ERROR(offset, "Error reading element value at index %d (length: %d)", i, length);
461 return 0;
462 }
463 }
464 break;
465 case REDIS_HASH:
466 for (i = 0; i < length; i++) {
467 offset = CURR_OFFSET;
468 if (!processStringObject(NULL)) {
469 SHIFT_ERROR(offset, "Error reading element key at index %d (length: %d)", i, length);
470 return 0;
471 }
472 offset = CURR_OFFSET;
473 if (!processStringObject(NULL)) {
474 SHIFT_ERROR(offset, "Error reading element value at index %d (length: %d)", i, length);
475 return 0;
476 }
477 }
478 break;
479 default:
480 SHIFT_ERROR(offset, "Type not implemented");
481 return 0;
482 }
483 /* because we're done, we assume success */
484 e->success = 1;
485 return 1;
486}
487
488entry loadEntry() {
489 entry e = { NULL, -1, 0 };
490 uint32_t length, offset[4];
491
492 /* reset error container */
493 errors.level = 0;
494
495 offset[0] = CURR_OFFSET;
496 if (!loadType(&e)) {
497 return e;
498 }
499
500 offset[1] = CURR_OFFSET;
501 if (e.type == REDIS_SELECTDB) {
502 if ((length = loadLength(NULL)) == REDIS_RDB_LENERR) {
503 SHIFT_ERROR(offset[1], "Error reading database number");
504 return e;
505 }
506 if (length > 63) {
507 SHIFT_ERROR(offset[1], "Database number out of range (%d)", length);
508 return e;
509 }
510 } else if (e.type == REDIS_EOF) {
511 if (positions[level].offset < positions[level].size) {
512 SHIFT_ERROR(offset[0], "Unexpected EOF");
513 } else {
514 e.success = 1;
515 }
516 return e;
517 } else {
518 /* optionally consume expire */
16144589
S
519 if (e.type == REDIS_EXPIRETIME ||
520 e.type == REDIS_EXPIRETIME_MS) {
521 if (!processTime(e.type)) return e;
08af4d5c
PN
522 if (!loadType(&e)) return e;
523 }
524
525 offset[1] = CURR_OFFSET;
526 if (!loadPair(&e)) {
527 SHIFT_ERROR(offset[1], "Error for type %s", types[e.type]);
528 return e;
529 }
530 }
531
532 /* all entries are followed by a valid type:
533 * e.g. a new entry, SELECTDB, EXPIRE, EOF */
534 offset[2] = CURR_OFFSET;
535 if (peekType() == -1) {
536 SHIFT_ERROR(offset[2], "Followed by invalid type");
537 SHIFT_ERROR(offset[0], "Error for type %s", types[e.type]);
538 e.success = 0;
539 } else {
540 e.success = 1;
541 }
542
543 return e;
544}
545
546void printCentered(int indent, int width, char* body) {
547 char head[256], tail[256];
548 memset(head, '\0', 256);
549 memset(tail, '\0', 256);
550
551 memset(head, '=', indent);
552 memset(tail, '=', width - 2 - indent - strlen(body));
553 printf("%s %s %s\n", head, body, tail);
554}
555
7b30cc3a 556void printValid(uint64_t ops, uint64_t bytes) {
08af4d5c 557 char body[80];
a047bf52 558 sprintf(body, "Processed %llu valid opcodes (in %llu bytes)",
559 (unsigned long long) ops, (unsigned long long) bytes);
08af4d5c
PN
560 printCentered(4, 80, body);
561}
562
7b30cc3a 563void printSkipped(uint64_t bytes, uint64_t offset) {
08af4d5c 564 char body[80];
a047bf52 565 sprintf(body, "Skipped %llu bytes (resuming at 0x%08llx)",
566 (unsigned long long) bytes, (unsigned long long) offset);
08af4d5c
PN
567 printCentered(4, 80, body);
568}
569
570void printErrorStack(entry *e) {
571 unsigned int i;
572 char body[64];
573
574 if (e->type == -1) {
575 sprintf(body, "Error trace");
576 } else if (e->type >= 253) {
577 sprintf(body, "Error trace (%s)", types[e->type]);
578 } else if (!e->key) {
579 sprintf(body, "Error trace (%s: (unknown))", types[e->type]);
580 } else {
581 char tmp[41];
582 strncpy(tmp, e->key, 40);
583
584 /* display truncation at the last 3 chars */
585 if (strlen(e->key) > 40) {
586 memset(&tmp[37], '.', 3);
587 }
588
589 /* display unprintable characters as ? */
590 for (i = 0; i < strlen(tmp); i++) {
591 if (tmp[i] <= 32) tmp[i] = '?';
592 }
593 sprintf(body, "Error trace (%s: %s)", types[e->type], tmp);
594 }
595
596 printCentered(4, 80, body);
597
598 /* display error stack */
599 for (i = 0; i < errors.level; i++) {
10c12171 600 printf("0x%08lx - %s\n",
601 (unsigned long) errors.offset[i], errors.error[i]);
08af4d5c
PN
602 }
603}
604
605void process() {
7b30cc3a 606 uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0;
08af4d5c 607 entry entry;
12a042f8 608 int dump_version = processHeader();
609
610 /* Exclude the final checksum for RDB >= 5. Will be checked at the end. */
611 if (dump_version >= 5) {
612 if (positions[0].size < 8) {
613 printf("RDB version >= 5 but no room for checksum.\n");
614 exit(1);
615 }
616 positions[0].size -= 8;;
617 }
08af4d5c
PN
618
619 level = 1;
620 while(positions[0].offset < positions[0].size) {
621 positions[1] = positions[0];
622
623 entry = loadEntry();
624 if (!entry.success) {
625 printValid(num_valid_ops, num_valid_bytes);
626 printErrorStack(&entry);
627 num_errors++;
628 num_valid_ops = 0;
629 num_valid_bytes = 0;
630
631 /* search for next valid entry */
7b30cc3a
PN
632 uint64_t offset = positions[0].offset + 1;
633 int i = 0;
634
08af4d5c
PN
635 while (!entry.success && offset < positions[0].size) {
636 positions[1].offset = offset;
637
638 /* find 3 consecutive valid entries */
639 for (i = 0; i < 3; i++) {
640 entry = loadEntry();
641 if (!entry.success) break;
642 }
643 /* check if we found 3 consecutive valid entries */
644 if (i < 3) {
645 offset++;
646 }
647 }
648
649 /* print how many bytes we have skipped to find a new valid opcode */
650 if (offset < positions[0].size) {
651 printSkipped(offset - positions[0].offset, offset);
652 }
653
654 positions[0].offset = offset;
655 } else {
656 num_valid_ops++;
657 num_valid_bytes += positions[1].offset - positions[0].offset;
658
659 /* advance position */
660 positions[0] = positions[1];
661 }
046f70f7 662 free(entry.key);
08af4d5c
PN
663 }
664
665 /* because there is another potential error,
666 * print how many valid ops we have processed */
667 printValid(num_valid_ops, num_valid_bytes);
668
669 /* expect an eof */
670 if (entry.type != REDIS_EOF) {
671 /* last byte should be EOF, add error */
672 errors.level = 0;
673 SHIFT_ERROR(positions[0].offset, "Expected EOF, got %s", types[entry.type]);
674
675 /* this is an EOF error so reset type */
676 entry.type = -1;
677 printErrorStack(&entry);
678
679 num_errors++;
680 }
681
12a042f8 682 /* Verify checksum */
683 if (dump_version >= 5) {
684 uint64_t crc = crc64(0,positions[0].data,positions[0].size);
685 uint64_t crc2;
686 unsigned char *p = (unsigned char*)positions[0].data+positions[0].size;
687 crc2 = ((uint64_t)p[0] << 0) |
688 ((uint64_t)p[1] << 8) |
689 ((uint64_t)p[2] << 16) |
690 ((uint64_t)p[3] << 24) |
691 ((uint64_t)p[4] << 32) |
692 ((uint64_t)p[5] << 40) |
693 ((uint64_t)p[6] << 48) |
694 ((uint64_t)p[7] << 56);
695 if (crc != crc2) {
696 SHIFT_ERROR(positions[0].offset, "RDB CRC64 does not match.");
697 } else {
698 printf("CRC64 checksum is OK\n");
699 }
700 }
701
08af4d5c 702 /* print summary on errors */
7b30cc3a 703 if (num_errors) {
08af4d5c 704 printf("\n");
a047bf52 705 printf("Total unprocessable opcodes: %llu\n",
706 (unsigned long long) num_errors);
08af4d5c
PN
707 }
708}
709
710int main(int argc, char **argv) {
711 /* expect the first argument to be the dump file */
712 if (argc <= 1) {
713 printf("Usage: %s <dump.rdb>\n", argv[0]);
714 exit(0);
715 }
716
717 int fd;
f85202c3 718 off_t size;
08af4d5c
PN
719 struct stat stat;
720 void *data;
721
722 fd = open(argv[1], O_RDONLY);
723 if (fd < 1) {
724 ERROR("Cannot open file: %s\n", argv[1]);
725 }
726 if (fstat(fd, &stat) == -1) {
727 ERROR("Cannot stat: %s\n", argv[1]);
728 } else {
729 size = stat.st_size;
730 }
731
f85202c3
PN
732 if (sizeof(size_t) == sizeof(int32_t) && size >= INT_MAX) {
733 ERROR("Cannot check dump files >2GB on a 32-bit platform\n");
734 }
735
08af4d5c
PN
736 data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
737 if (data == MAP_FAILED) {
738 ERROR("Cannot mmap: %s\n", argv[1]);
739 }
740
741 /* Initialize static vars */
742 positions[0].data = data;
743 positions[0].size = size;
744 positions[0].offset = 0;
745 errors.level = 0;
746
747 /* Object types */
748 sprintf(types[REDIS_STRING], "STRING");
749 sprintf(types[REDIS_LIST], "LIST");
750 sprintf(types[REDIS_SET], "SET");
751 sprintf(types[REDIS_ZSET], "ZSET");
752 sprintf(types[REDIS_HASH], "HASH");
753
754 /* Object types only used for dumping to disk */
755 sprintf(types[REDIS_EXPIRETIME], "EXPIRETIME");
756 sprintf(types[REDIS_SELECTDB], "SELECTDB");
757 sprintf(types[REDIS_EOF], "EOF");
758
759 /* Double constants initialization */
760 R_Zero = 0.0;
761 R_PosInf = 1.0/R_Zero;
762 R_NegInf = -1.0/R_Zero;
763 R_Nan = R_Zero/R_Zero;
764
765 process();
766
767 munmap(data, size);
768 close(fd);
769 return 0;
770}