]> git.saurik.com Git - redis.git/blobdiff - src/redis-check-dump.c
Test: more MIGRATE tests.
[redis.git] / src / redis-check-dump.c
index 77be686c2454c6b6c1b7de9b528d249d0f534118..7efecb1a3991897ef129d61621ae39b05592b05a 100644 (file)
@@ -1,3 +1,34 @@
+/*
+ * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * 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 <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -20,6 +51,7 @@
 #define REDIS_LIST_ZIPLIST 10
 #define REDIS_SET_INTSET 11
 #define REDIS_ZSET_ZIPLIST 12
 #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
 
 /* Objects encoding. Some kind of objects like Strings and Hashes can be
  * internally represented in multiple ways. The 'encoding' field of the object
@@ -30,6 +62,7 @@
 #define REDIS_ENCODING_HT 3     /* Encoded as an hash table */
 
 /* Object types only used for dumping to disk */
 #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
 #define REDIS_EXPIRETIME 253
 #define REDIS_SELECTDB 254
 #define REDIS_EOF 255
@@ -107,6 +140,19 @@ static double R_Zero, R_PosInf, R_NegInf, R_Nan;
 /* store string types for output */
 static char types[256][16];
 
 /* 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;
 /* when number of bytes to read is negative, do a peek */
 int readBytes(void *target, long num) {
     char peek = (num < 0) ? 1 : 0;
@@ -136,10 +182,10 @@ int processHeader() {
     }
 
     dump_version = (int)strtol(buf + 5, NULL, 10);
     }
 
     dump_version = (int)strtol(buf + 5, NULL, 10);
-    if (dump_version < 1 || dump_version > 2) {
+    if (dump_version < 1 || dump_version > 6) {
         ERROR("Unknown RDB format version: %d\n", dump_version);
     }
         ERROR("Unknown RDB format version: %d\n", dump_version);
     }
-    return 1;
+    return dump_version;
 }
 
 int loadType(entry *e) {
 }
 
 int loadType(entry *e) {
@@ -148,7 +194,7 @@ int loadType(entry *e) {
     /* this byte needs to qualify as type */
     unsigned char t;
     if (readBytes(&t, 1)) {
     /* this byte needs to qualify as type */
     unsigned char t;
     if (readBytes(&t, 1)) {
-        if (t <= 4 || (t >=9 && t <= 12) || t >= 253) {
+        if (checkType(t)) {
             e->type = t;
             return 1;
         } else {
             e->type = t;
             return 1;
         } else {
@@ -164,16 +210,18 @@ int loadType(entry *e) {
 
 int peekType() {
     unsigned char t;
 
 int peekType() {
     unsigned char t;
-    if (readBytes(&t, -1) && (t <= 4 || (t >=9 && t <= 12) || t >= 253))
+    if (readBytes(&t, -1) && (checkType(t)))
         return t;
     return -1;
 }
 
 /* discard time, just consume the bytes */
         return t;
     return -1;
 }
 
 /* discard time, just consume the bytes */
-int processTime() {
+int processTime(int type) {
     uint32_t offset = CURR_OFFSET;
     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");
         return 1;
     } else {
         SHIFT_ERROR(offset, "Could not read time");
@@ -384,6 +432,7 @@ int loadPair(entry *e) {
     case REDIS_LIST_ZIPLIST:
     case REDIS_SET_INTSET:
     case REDIS_ZSET_ZIPLIST:
     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;
         if (!processStringObject(NULL)) {
             SHIFT_ERROR(offset, "Error reading entry value");
             return 0;
@@ -467,8 +516,9 @@ entry loadEntry() {
         return e;
     } else {
         /* optionally consume expire */
         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;
         }
 
             if (!loadType(&e)) return e;
         }
 
@@ -555,7 +605,16 @@ void printErrorStack(entry *e) {
 void process() {
     uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0;
     entry entry;
 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) {
 
     level = 1;
     while(positions[0].offset < positions[0].size) {
@@ -620,6 +679,26 @@ void process() {
         num_errors++;
     }
 
         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");
     /* print summary on errors */
     if (num_errors) {
         printf("\n");