X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/7b30cc3a7bed6ea1d5b4131f977d554d78791bf7..dbbbe49ef57c5c000469e206c81e5da58bf604ba:/src/redis-check-dump.c diff --git a/src/redis-check-dump.c b/src/redis-check-dump.c index a7e85973..5eac925a 100644 --- a/src/redis-check-dump.c +++ b/src/redis-check-dump.c @@ -16,6 +16,11 @@ #define REDIS_SET 2 #define REDIS_ZSET 3 #define REDIS_HASH 4 +#define REDIS_HASH_ZIPMAP 9 +#define REDIS_LIST_ZIPLIST 10 +#define REDIS_SET_INTSET 11 +#define REDIS_ZSET_ZIPLIST 12 +#define REDIS_HASH_ZIPLIST 13 /* Objects encoding. Some kind of objects like Strings and Hashes can be * internally represented in multiple ways. The 'encoding' field of the object @@ -65,8 +70,8 @@ /* data type to hold offset in file and size */ typedef struct { void *data; - uint64_t size; - uint64_t offset; + size_t size; + size_t offset; } pos; static unsigned char level = 0; @@ -77,8 +82,8 @@ static pos positions[16]; /* Hold a stack of errors */ typedef struct { char error[16][1024]; - uint64_t offset[16]; - uint32_t level; + size_t offset[16]; + size_t level; } errors_t; static errors_t errors; @@ -103,6 +108,9 @@ static double R_Zero, R_PosInf, R_NegInf, R_Nan; /* store string types for output */ static char types[256][16]; +/* Prototypes */ +uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l); + /* when number of bytes to read is negative, do a peek */ int readBytes(void *target, long num) { char peek = (num < 0) ? 1 : 0; @@ -112,7 +120,7 @@ int readBytes(void *target, long num) { if (p.offset + num > p.size) { return 0; } else { - memcpy(target, (void*)((unsigned long)p.data + p.offset), num); + memcpy(target, (void*)((size_t)p.data + p.offset), num); if (!peek) positions[level].offset += num; } return 1; @@ -132,10 +140,10 @@ int processHeader() { } dump_version = (int)strtol(buf + 5, NULL, 10); - if (dump_version != 1) { + if (dump_version < 1 || dump_version > 6) { ERROR("Unknown RDB format version: %d\n", dump_version); } - return 1; + return dump_version; } int loadType(entry *e) { @@ -144,7 +152,7 @@ int loadType(entry *e) { /* this byte needs to qualify as type */ unsigned char t; if (readBytes(&t, 1)) { - if (t <= 4 || t >= 253) { + if (t <= 4 || (t >=9 && t <= 12) || t >= 253) { e->type = t; return 1; } else { @@ -160,7 +168,8 @@ int loadType(entry *e) { int peekType() { unsigned char t; - if (readBytes(&t, -1) && (t <= 4 || t >= 253)) return t; + if (readBytes(&t, -1) && (t <= 4 || (t >=9 && t <= 12) || t >= 253)) + return t; return -1; } @@ -375,6 +384,11 @@ int loadPair(entry *e) { switch(e->type) { case REDIS_STRING: + case REDIS_HASH_ZIPMAP: + case REDIS_LIST_ZIPLIST: + case REDIS_SET_INTSET: + case REDIS_ZSET_ZIPLIST: + case REDIS_HASH_ZIPLIST: if (!processStringObject(NULL)) { SHIFT_ERROR(offset, "Error reading entry value"); return 0; @@ -496,13 +510,15 @@ void printCentered(int indent, int width, char* body) { void printValid(uint64_t ops, uint64_t bytes) { char body[80]; - sprintf(body, "Processed %llu valid opcodes (in %llu bytes)", ops, bytes); + sprintf(body, "Processed %llu valid opcodes (in %llu bytes)", + (unsigned long long) ops, (unsigned long long) bytes); printCentered(4, 80, body); } void printSkipped(uint64_t bytes, uint64_t offset) { char body[80]; - sprintf(body, "Skipped %llu bytes (resuming at 0x%08llx)", bytes, offset); + sprintf(body, "Skipped %llu bytes (resuming at 0x%08llx)", + (unsigned long long) bytes, (unsigned long long) offset); printCentered(4, 80, body); } @@ -536,14 +552,24 @@ void printErrorStack(entry *e) { /* display error stack */ for (i = 0; i < errors.level; i++) { - printf("0x%08llx - %s\n", errors.offset[i], errors.error[i]); + printf("0x%08lx - %s\n", + (unsigned long) errors.offset[i], errors.error[i]); } } void process() { uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0; entry entry; - processHeader(); + int dump_version = processHeader(); + + /* Exclude the final checksum for RDB >= 5. Will be checked at the end. */ + if (dump_version >= 5) { + if (positions[0].size < 8) { + printf("RDB version >= 5 but no room for checksum.\n"); + exit(1); + } + positions[0].size -= 8;; + } level = 1; while(positions[0].offset < positions[0].size) { @@ -588,6 +614,7 @@ void process() { /* advance position */ positions[0] = positions[1]; } + free(entry.key); } /* because there is another potential error, @@ -607,10 +634,31 @@ void process() { num_errors++; } + /* Verify checksum */ + if (dump_version >= 5) { + uint64_t crc = crc64(0,positions[0].data,positions[0].size); + uint64_t crc2; + unsigned char *p = (unsigned char*)positions[0].data+positions[0].size; + crc2 = ((uint64_t)p[0] << 0) | + ((uint64_t)p[1] << 8) | + ((uint64_t)p[2] << 16) | + ((uint64_t)p[3] << 24) | + ((uint64_t)p[4] << 32) | + ((uint64_t)p[5] << 40) | + ((uint64_t)p[6] << 48) | + ((uint64_t)p[7] << 56); + if (crc != crc2) { + SHIFT_ERROR(positions[0].offset, "RDB CRC64 does not match."); + } else { + printf("CRC64 checksum is OK\n"); + } + } + /* print summary on errors */ if (num_errors) { printf("\n"); - printf("Total unprocessable opcodes: %llu\n", num_errors); + printf("Total unprocessable opcodes: %llu\n", + (unsigned long long) num_errors); } } @@ -622,7 +670,7 @@ int main(int argc, char **argv) { } int fd; - size_t size; + off_t size; struct stat stat; void *data; @@ -636,6 +684,10 @@ int main(int argc, char **argv) { size = stat.st_size; } + if (sizeof(size_t) == sizeof(int32_t) && size >= INT_MAX) { + ERROR("Cannot check dump files >2GB on a 32-bit platform\n"); + } + data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { ERROR("Cannot mmap: %s\n", argv[1]);