xref: /redis-3.2.3/src/redis-check-aof.c (revision 4365e5b2)
1 /*
2  * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3  * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   * Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *   * Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *   * Neither the name of Redis nor the names of its contributors may be used
15  *     to endorse or promote products derived from this software without
16  *     specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "fmacros.h"
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include "config.h"
38 
39 #define ERROR(...) { \
40     char __buf[1024]; \
41     sprintf(__buf, __VA_ARGS__); \
42     sprintf(error, "0x%16llx: %s", (long long)epos, __buf); \
43 }
44 
45 static char error[1024];
46 static off_t epos;
47 
consumeNewline(char * buf)48 int consumeNewline(char *buf) {
49     if (strncmp(buf,"\r\n",2) != 0) {
50         ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);
51         return 0;
52     }
53     return 1;
54 }
55 
readLong(FILE * fp,char prefix,long * target)56 int readLong(FILE *fp, char prefix, long *target) {
57     char buf[128], *eptr;
58     epos = ftello(fp);
59     if (fgets(buf,sizeof(buf),fp) == NULL) {
60         return 0;
61     }
62     if (buf[0] != prefix) {
63         ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);
64         return 0;
65     }
66     *target = strtol(buf+1,&eptr,10);
67     return consumeNewline(eptr);
68 }
69 
readBytes(FILE * fp,char * target,long length)70 int readBytes(FILE *fp, char *target, long length) {
71     long real;
72     epos = ftello(fp);
73     real = fread(target,1,length,fp);
74     if (real != length) {
75         ERROR("Expected to read %ld bytes, got %ld bytes",length,real);
76         return 0;
77     }
78     return 1;
79 }
80 
readString(FILE * fp,char ** target)81 int readString(FILE *fp, char** target) {
82     long len;
83     *target = NULL;
84     if (!readLong(fp,'$',&len)) {
85         return 0;
86     }
87 
88     /* Increase length to also consume \r\n */
89     len += 2;
90     *target = (char*)malloc(len);
91     if (!readBytes(fp,*target,len)) {
92         return 0;
93     }
94     if (!consumeNewline(*target+len-2)) {
95         return 0;
96     }
97     (*target)[len-2] = '\0';
98     return 1;
99 }
100 
readArgc(FILE * fp,long * target)101 int readArgc(FILE *fp, long *target) {
102     return readLong(fp,'*',target);
103 }
104 
process(FILE * fp)105 off_t process(FILE *fp) {
106     long argc;
107     off_t pos = 0;
108     int i, multi = 0;
109     char *str;
110 
111     while(1) {
112         if (!multi) pos = ftello(fp);
113         if (!readArgc(fp, &argc)) break;
114 
115         for (i = 0; i < argc; i++) {
116             if (!readString(fp,&str)) break;
117             if (i == 0) {
118                 if (strcasecmp(str, "multi") == 0) {
119                     if (multi++) {
120                         ERROR("Unexpected MULTI");
121                         break;
122                     }
123                 } else if (strcasecmp(str, "exec") == 0) {
124                     if (--multi) {
125                         ERROR("Unexpected EXEC");
126                         break;
127                     }
128                 }
129             }
130             free(str);
131         }
132 
133         /* Stop if the loop did not finish */
134         if (i < argc) {
135             if (str) free(str);
136             break;
137         }
138     }
139 
140     if (feof(fp) && multi && strlen(error) == 0) {
141         ERROR("Reached EOF before reading EXEC for MULTI");
142     }
143     if (strlen(error) > 0) {
144         printf("%s\n", error);
145     }
146     return pos;
147 }
148 
main(int argc,char ** argv)149 int main(int argc, char **argv) {
150     char *filename;
151     int fix = 0;
152 
153     if (argc < 2) {
154         printf("Usage: %s [--fix] <file.aof>\n", argv[0]);
155         exit(1);
156     } else if (argc == 2) {
157         filename = argv[1];
158     } else if (argc == 3) {
159         if (strcmp(argv[1],"--fix") != 0) {
160             printf("Invalid argument: %s\n", argv[1]);
161             exit(1);
162         }
163         filename = argv[2];
164         fix = 1;
165     } else {
166         printf("Invalid arguments\n");
167         exit(1);
168     }
169 
170     FILE *fp = fopen(filename,"r+");
171     if (fp == NULL) {
172         printf("Cannot open file: %s\n", filename);
173         exit(1);
174     }
175 
176     struct redis_stat sb;
177     if (redis_fstat(fileno(fp),&sb) == -1) {
178         printf("Cannot stat file: %s\n", filename);
179         exit(1);
180     }
181 
182     off_t size = sb.st_size;
183     if (size == 0) {
184         printf("Empty file: %s\n", filename);
185         exit(1);
186     }
187 
188     off_t pos = process(fp);
189     off_t diff = size-pos;
190     printf("AOF analyzed: size=%lld, ok_up_to=%lld, diff=%lld\n",
191         (long long) size, (long long) pos, (long long) diff);
192     if (diff > 0) {
193         if (fix) {
194             char buf[2];
195             printf("This will shrink the AOF from %lld bytes, with %lld bytes, to %lld bytes\n",(long long)size,(long long)diff,(long long)pos);
196             printf("Continue? [y/N]: ");
197             if (fgets(buf,sizeof(buf),stdin) == NULL ||
198                 strncasecmp(buf,"y",1) != 0) {
199                     printf("Aborting...\n");
200                     exit(1);
201             }
202             if (ftruncate(fileno(fp), pos) == -1) {
203                 printf("Failed to truncate AOF\n");
204                 exit(1);
205             } else {
206                 printf("Successfully truncated AOF\n");
207             }
208         } else {
209             printf("AOF is not valid\n");
210             exit(1);
211         }
212     } else {
213         printf("AOF is valid\n");
214     }
215 
216     fclose(fp);
217     return 0;
218 }
219