]> git.saurik.com Git - redis.git/commitdiff
tool to check if AOF is valid
authorPieter Noordhuis <pcnoordhuis@gmail.com>
Wed, 5 May 2010 11:36:29 +0000 (13:36 +0200)
committerPieter Noordhuis <pcnoordhuis@gmail.com>
Wed, 5 May 2010 11:36:29 +0000 (13:36 +0200)
Makefile
redis-check-aof.c [new file with mode: 0644]

index 13b6bf4508ddc9fb79ecaf89564be8fbb231895c..0d7cd28fc82c4d6febc3dc36c40a5c779db387f2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -20,13 +20,15 @@ OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort
 BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
 CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o linenoise.o
 CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
+CHECKAOFOBJ = redis-check-aof.o
 
 PRGNAME = redis-server
 BENCHPRGNAME = redis-benchmark
 CLIPRGNAME = redis-cli
 CHECKDUMPPRGNAME = redis-check-dump
+CHECKAOFPRGNAME = redis-check-aof
 
-all: redis-server redis-benchmark redis-cli redis-check-dump
+all: redis-server redis-benchmark redis-cli redis-check-dump redis-check-aof
 
 # Deps (use make dep to generate this)
 adlist.o: adlist.c adlist.h zmalloc.h
@@ -68,11 +70,14 @@ redis-cli: $(CLIOBJ)
 redis-check-dump: $(CHECKDUMPOBJ)
        $(CC) -o $(CHECKDUMPPRGNAME) $(CCOPT) $(DEBUG) $(CHECKDUMPOBJ)
 
+redis-check-aof: $(CHECKAOFOBJ)
+       $(CC) -o $(CHECKAOFPRGNAME) $(CCOPT) $(DEBUG) $(CHECKAOFOBJ)
+
 .c.o:
        $(CC) -c $(CFLAGS) $(DEBUG) $(COMPILE_TIME) $<
 
 clean:
-       rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) *.o *.gcda *.gcno *.gcov
+       rm -rf $(PRGNAME) $(BENCHPRGNAME) $(CLIPRGNAME) $(CHECKDUMPPRGNAME) $(CHECKAOFPRGNAME) *.o *.gcda *.gcno *.gcov
 
 dep:
        $(CC) -MM *.c
diff --git a/redis-check-aof.c b/redis-check-aof.c
new file mode 100644 (file)
index 0000000..3be6078
--- /dev/null
@@ -0,0 +1,161 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "config.h"
+
+#define ERROR(...) { \
+    char __buf[1024]; \
+    sprintf(__buf, __VA_ARGS__); \
+    sprintf(error, "0x%08lx: %s", epos, __buf); \
+}
+
+static char error[1024];
+static long epos;
+
+int consumeNewline(char *buf) {
+    if (strncmp(buf,"\r\n",2) != 0) {
+        ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);
+        return 0;
+    }
+    return 1;
+}
+
+int readLong(FILE *fp, char prefix, long *target) {
+    char buf[128], *eptr;
+    epos = ftell(fp);
+    if (fgets(buf,sizeof(buf),fp) == NULL) {
+        return 0;
+    }
+    if (buf[0] != prefix) {
+        ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);
+        return 0;
+    }
+    *target = strtol(buf+1,&eptr,10);
+    return consumeNewline(eptr);
+}
+
+int readBytes(FILE *fp, char *target, long length) {
+    long real;
+    epos = ftell(fp);
+    real = fread(target,1,length,fp);
+    if (real != length) {
+        ERROR("Expected to read %ld bytes, got %ld bytes",length,real);
+        return 0;
+    }
+    return 1;
+}
+
+int readString(FILE *fp, char** target) {
+    long len;
+    *target = NULL;
+    if (!readLong(fp,'$',&len)) {
+        return 0;
+    }
+
+    /* Increase length to also consume \r\n */
+    len += 2;
+    *target = (char*)malloc(len);
+    if (!readBytes(fp,*target,len)) {
+        free(*target);
+        *target = NULL;
+        return 0;
+    }
+    if (!consumeNewline(*target+len-2)) {
+        free(*target);
+        *target = NULL;
+        return 0;
+    }
+    (*target)[len-2] = '\0';
+    return 1;
+}
+
+int readArgc(FILE *fp, long *target) {
+    return readLong(fp,'*',target);
+}
+
+long process(FILE *fp) {
+    long argc, pos = 0;
+    int i, multi = 0;
+    char *str;
+
+    while(1) {
+        if (!multi) pos = ftell(fp);
+        if (!readArgc(fp, &argc)) {
+            break;
+        }
+
+        for (i = 0; i < argc; i++) {
+            if (!readString(fp,&str)) {
+                break;
+            }
+            if (i == 0) {
+                if (strcasecmp(str, "multi") == 0) {
+                    if (multi++) {
+                        ERROR("Unexpected MULTI");
+                        break;
+                    }
+                } else if (strcasecmp(str, "exec") == 0) {
+                    if (--multi) {
+                        ERROR("Unexpected EXEC");
+                        break;
+                    }
+                }
+            }
+            free(str);
+        }
+
+        /* Check if loop was finished */
+        if (i < argc) {
+            if (str) free(str);
+            break;
+        }
+    }
+
+    if (feof(fp) && multi && strlen(error) == 0) {
+        ERROR("Reached EOF before reading EXEC for MULTI");
+    }
+
+    if (strlen(error) > 0) {
+        printf("%s\n", error);
+    }
+
+    return pos;
+}
+
+int main(int argc, char **argv) {
+    /* expect the first argument to be the dump file */
+    if (argc <= 1) {
+        printf("Usage: %s <file.aof>\n", argv[0]);
+        exit(0);
+    }
+
+    FILE *fp = fopen(argv[1],"r");
+    if (fp == NULL) {
+        printf("Cannot open file: %s\n", argv[1]);
+        exit(1);
+    }
+
+    struct redis_stat sb;
+    if (redis_fstat(fileno(fp),&sb) == -1) {
+        printf("Cannot stat file: %s\n", argv[1]);
+        exit(1);
+    }
+
+    long size = sb.st_size;
+    if (size == 0) {
+        printf("Empty file: %s\n", argv[1]);
+        exit(1);
+    }
+
+    long pos = process(fp);
+    if (pos < size) {
+        printf("First invalid operation at offset %ld.\n", pos);
+        exit(1);
+    } else {
+        printf("AOF is valid.\n");
+    }
+
+    fclose(fp);
+    return 0;
+}