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