]> git.saurik.com Git - redis.git/blob - redis-check-aof.c
31abe1be9d94a47b27e337eb15ba0a618077e8ec
[redis.git] / redis-check-aof.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include "config.h"
7
8 #define ERROR(...) { \
9 char __buf[1024]; \
10 sprintf(__buf, __VA_ARGS__); \
11 sprintf(error, "0x%08lx: %s", epos, __buf); \
12 }
13
14 static char error[1024];
15 static long epos;
16
17 int consumeNewline(char *buf) {
18 if (strncmp(buf,"\r\n",2) != 0) {
19 ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);
20 return 0;
21 }
22 return 1;
23 }
24
25 int readLong(FILE *fp, char prefix, long *target) {
26 char buf[128], *eptr;
27 epos = ftell(fp);
28 if (fgets(buf,sizeof(buf),fp) == NULL) {
29 return 0;
30 }
31 if (buf[0] != prefix) {
32 ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);
33 return 0;
34 }
35 *target = strtol(buf+1,&eptr,10);
36 return consumeNewline(eptr);
37 }
38
39 int readBytes(FILE *fp, char *target, long length) {
40 long real;
41 epos = ftell(fp);
42 real = fread(target,1,length,fp);
43 if (real != length) {
44 ERROR("Expected to read %ld bytes, got %ld bytes",length,real);
45 return 0;
46 }
47 return 1;
48 }
49
50 int readString(FILE *fp, char** target) {
51 long len;
52 *target = NULL;
53 if (!readLong(fp,'$',&len)) {
54 return 0;
55 }
56
57 /* Increase length to also consume \r\n */
58 len += 2;
59 *target = (char*)malloc(len);
60 if (!readBytes(fp,*target,len)) {
61 free(*target);
62 *target = NULL;
63 return 0;
64 }
65 if (!consumeNewline(*target+len-2)) {
66 free(*target);
67 *target = NULL;
68 return 0;
69 }
70 (*target)[len-2] = '\0';
71 return 1;
72 }
73
74 int readArgc(FILE *fp, long *target) {
75 return readLong(fp,'*',target);
76 }
77
78 long process(FILE *fp) {
79 long argc, pos = 0;
80 int i, multi = 0;
81 char *str;
82
83 while(1) {
84 if (!multi) pos = ftell(fp);
85 if (!readArgc(fp, &argc)) {
86 break;
87 }
88
89 for (i = 0; i < argc; i++) {
90 if (!readString(fp,&str)) {
91 break;
92 }
93 if (i == 0) {
94 if (strcasecmp(str, "multi") == 0) {
95 if (multi++) {
96 ERROR("Unexpected MULTI");
97 break;
98 }
99 } else if (strcasecmp(str, "exec") == 0) {
100 if (--multi) {
101 ERROR("Unexpected EXEC");
102 break;
103 }
104 }
105 }
106 free(str);
107 }
108
109 /* Check if loop was finished */
110 if (i < argc) {
111 if (str) free(str);
112 break;
113 }
114 }
115
116 if (feof(fp) && multi && strlen(error) == 0) {
117 ERROR("Reached EOF before reading EXEC for MULTI");
118 }
119
120 if (strlen(error) > 0) {
121 printf("%s\n", error);
122 }
123
124 return pos;
125 }
126
127 int main(int argc, char **argv) {
128 /* expect the first argument to be the dump file */
129 if (argc <= 1) {
130 printf("Usage: %s [--fix] <file.aof>\n", argv[0]);
131 exit(0);
132 }
133
134 char *filename;
135 int fix = 0;
136 if (argc == 3) {
137 if (strcmp(argv[1],"--fix") != 0) {
138 printf("Invalid argument: %s\n", argv[1]);
139 exit(1);
140 }
141 fix = 1;
142 filename = argv[2];
143 } else if (argc == 2) {
144 filename = argv[1];
145 } else {
146 printf("Invalid argument");
147 exit(1);
148 }
149
150 FILE *fp = fopen(filename,"r+");
151 if (fp == NULL) {
152 printf("Cannot open file: %s\n", filename);
153 exit(1);
154 }
155
156 struct redis_stat sb;
157 if (redis_fstat(fileno(fp),&sb) == -1) {
158 printf("Cannot stat file: %s\n", filename);
159 exit(1);
160 }
161
162 long size = sb.st_size;
163 if (size == 0) {
164 printf("Empty file: %s\n", filename);
165 exit(1);
166 }
167
168 long pos = process(fp);
169 if (pos < size) {
170 if (fix) {
171 if (ftruncate(fileno(fp), pos) == -1) {
172 printf("Could not truncate AOF to size %ld\n", pos);
173 exit(1);
174 } else {
175 printf("AOF succesfully truncated to %ld bytes\n", pos);
176 }
177 } else {
178 printf("First invalid operation at offset %ld\n", pos);
179 }
180 } else {
181 printf("AOF is valid\n");
182 }
183
184 fclose(fp);
185 return 0;
186 }