X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/b882056c93f3e3afe5fde9d62fb569ce30a76a76..af0b220756571bc8faf57a0c7b7389dd86a60376:/src/redis-check-dump.c diff --git a/src/redis-check-dump.c b/src/redis-check-dump.c index 987e1db3..7efecb1a 100644 --- a/src/redis-check-dump.c +++ b/src/redis-check-dump.c @@ -1,3 +1,34 @@ +/* + * Copyright (c) 2009-2012, Pieter Noordhuis + * Copyright (c) 2009-2012, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + #include #include #include @@ -16,6 +47,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 @@ -26,6 +62,7 @@ #define REDIS_ENCODING_HT 3 /* Encoded as an hash table */ /* Object types only used for dumping to disk */ +#define REDIS_EXPIRETIME_MS 252 #define REDIS_EXPIRETIME 253 #define REDIS_SELECTDB 254 #define REDIS_EOF 255 @@ -103,6 +140,19 @@ 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); + +/* Return true if 't' is a valid object type. */ +int checkType(unsigned char t) { + /* In case a new object type is added, update the following + * condition as necessary. */ + return + (t >= REDIS_HASH_ZIPMAP && t <= REDIS_HASH_ZIPLIST) || + t <= REDIS_HASH || + t >= REDIS_EXPIRETIME_MS; +} + /* when number of bytes to read is negative, do a peek */ int readBytes(void *target, long num) { char peek = (num < 0) ? 1 : 0; @@ -132,10 +182,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 +194,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 (checkType(t)) { e->type = t; return 1; } else { @@ -160,15 +210,18 @@ int loadType(entry *e) { int peekType() { unsigned char t; - if (readBytes(&t, -1) && (t <= 4 || t >= 253)) return t; + if (readBytes(&t, -1) && (checkType(t))) + return t; return -1; } /* discard time, just consume the bytes */ -int processTime() { +int processTime(int type) { uint32_t offset = CURR_OFFSET; - unsigned char t[4]; - if (readBytes(t, 4)) { + unsigned char t[8]; + int timelen = (type == REDIS_EXPIRETIME_MS) ? 8 : 4; + + if (readBytes(t,timelen)) { return 1; } else { SHIFT_ERROR(offset, "Could not read time"); @@ -375,6 +428,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; @@ -458,8 +516,9 @@ entry loadEntry() { return e; } else { /* optionally consume expire */ - if (e.type == REDIS_EXPIRETIME) { - if (!processTime()) return e; + if (e.type == REDIS_EXPIRETIME || + e.type == REDIS_EXPIRETIME_MS) { + if (!processTime(e.type)) return e; if (!loadType(&e)) return e; } @@ -538,14 +597,24 @@ void printErrorStack(entry *e) { /* display error stack */ for (i = 0; i < errors.level; i++) { - printf("0x%08lx - %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) { @@ -590,6 +659,7 @@ void process() { /* advance position */ positions[0] = positions[1]; } + free(entry.key); } /* because there is another potential error, @@ -609,6 +679,26 @@ 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");