c3de9c4bd0742c3d4cde00380c484cd9f3c775e4
[xfstests-dev.git] / src / log-writes / replay-log.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <getopt.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include "log-writes.h"
7
8 enum option_indexes {
9         NEXT_FLUSH,
10         NEXT_FUA,
11         START_ENTRY,
12         END_MARK,
13         LOG,
14         REPLAY,
15         LIMIT,
16         VERBOSE,
17         FIND,
18         NUM_ENTRIES,
19         NO_DISCARD,
20         FSCK,
21         CHECK,
22         START_MARK,
23 };
24
25 static struct option long_options[] = {
26         {"next-flush", no_argument, NULL, 0},
27         {"next-fua", no_argument, NULL, 0},
28         {"start-entry", required_argument, NULL, 0},
29         {"end-mark", required_argument, NULL, 0},
30         {"log", required_argument, NULL, 0},
31         {"replay", required_argument, NULL, 0},
32         {"limit", required_argument, NULL, 0},
33         {"verbose", no_argument, NULL, 'v'},
34         {"find", no_argument, NULL, 0},
35         {"num-entries", no_argument, NULL, 0},
36         {"no-discard", no_argument, NULL, 0},
37         {"fsck", required_argument, NULL, 0},
38         {"check", required_argument, NULL, 0},
39         {"start-mark", required_argument, NULL, 0},
40         { NULL, 0, NULL, 0 },
41 };
42
43 static void usage(void)
44 {
45         fprintf(stderr, "Usage: replay-log --log <logfile> [options]\n");
46         fprintf(stderr, "\t--replay <device> - replay onto a specific "
47                 "device\n");
48         fprintf(stderr, "\t--limit <number> - number of entries to replay\n");
49         fprintf(stderr, "\t--next-flush - replay to/find the next flush\n");
50         fprintf(stderr, "\t--next-fua - replay to/find the next fua\n");
51         fprintf(stderr, "\t--start-entry <entry> - start at the given "
52                 "entry #\n");
53         fprintf(stderr, "\t--start-mark <mark> - mark to start from\n");
54         fprintf(stderr, "\t--end-mark <mark> - replay to/find the given mark\n");
55         fprintf(stderr, "\t--find - put replay-log in find mode, will search "
56                 "based on the other options\n");
57         fprintf(stderr, "\t--number-entries - print the number of entries in "
58                 "the log\n");
59         fprintf(stderr, "\t--no-discard - don't process discard entries\n");
60         fprintf(stderr, "\t--fsck - the fsck command to run, must specify "
61                 "--check\n");
62         fprintf(stderr, "\t--check [<number>|flush|fua] when to check the "
63                 "file system, mush specify --fsck\n");
64         exit(1);
65 }
66
67 /*
68  * Check if the log entry flag matches one of the stop_flags.
69  * If stop_flag has LOG_MARK, then looking also for match of
70  * the mark label.
71  */
72 static int should_stop(struct log_write_entry *entry, u64 stop_flags,
73                        char *mark)
74 {
75         u64 flags = le64_to_cpu(entry->flags);
76         int check_mark = (stop_flags & LOG_MARK_FLAG);
77         /* mark data begins after entry header */
78         char *buf = (char *)(entry + 1);
79         /* entry buffer is padded with at least 1 zero after data_len */
80         u64 buflen = le64_to_cpu(entry->data_len) + 1;
81
82         if (flags & stop_flags) {
83                 if (!check_mark)
84                         return 1;
85                 if ((flags & LOG_MARK_FLAG) &&
86                     !strncmp(mark, buf, buflen))
87                         return 1;
88         }
89         return 0;
90 }
91
92 static int run_fsck(struct log *log, char *fsck_command)
93 {
94         int ret = fsync(log->replayfd);
95         if (ret)
96                 return ret;
97         ret = system(fsck_command);
98         if (ret >= 0)
99                 ret = WEXITSTATUS(ret);
100         return ret ? -1 : 0;
101 }
102
103 enum log_replay_check_mode {
104         CHECK_NUMBER = 1,
105         CHECK_FUA = 2,
106         CHECK_FLUSH = 3,
107 };
108
109 static int seek_to_mark(struct log *log, struct log_write_entry *entry,
110                         char *mark)
111 {
112         int ret;
113
114         while ((ret = log_seek_next_entry(log, entry, 1)) == 0) {
115                 if (should_stop(entry, LOG_MARK_FLAG, mark))
116                         break;
117         }
118         if (ret == 1) {
119                 fprintf(stderr, "Couldn't find starting mark\n");
120                 ret = -1;
121         }
122
123         return ret;
124 }
125
126 int main(int argc, char **argv)
127 {
128         char *logfile = NULL, *replayfile = NULL, *fsck_command = NULL;
129         struct log_write_entry *entry;
130         u64 stop_flags = 0;
131         u64 start_entry = 0;
132         u64 run_limit = 0;
133         u64 num_entries = 0;
134         u64 check_number = 0;
135         char *end_mark = NULL, *start_mark = NULL;
136         char *tmp = NULL;
137         struct log *log;
138         int find_mode = 0;
139         int c;
140         int opt_index;
141         int ret;
142         int print_num_entries = 0;
143         int discard = 1;
144         enum log_replay_check_mode check_mode = 0;
145
146         while ((c = getopt_long(argc, argv, "v", long_options,
147                                 &opt_index)) >= 0) {
148                 switch(c) {
149                 case 'v':
150                         log_writes_verbose++;
151                         continue;
152                 default:
153                         break;
154                 }
155
156                 switch(opt_index) {
157                 case NEXT_FLUSH:
158                         stop_flags |= LOG_FLUSH_FLAG;
159                         break;
160                 case NEXT_FUA:
161                         stop_flags |= LOG_FUA_FLAG;
162                         break;
163                 case START_ENTRY:
164                         start_entry = strtoull(optarg, &tmp, 0);
165                         if (tmp && *tmp != '\0') {
166                                 fprintf(stderr, "Invalid entry number\n");
167                                 exit(1);
168                         }
169                         tmp = NULL;
170                         break;
171                 case START_MARK:
172                         /*
173                          * Biggest sectorsize is 4k atm, so limit the mark to 4k
174                          * minus the size of the entry.  Say 4097 since we want
175                          * an extra slot for \0.
176                          */
177                         start_mark = strndup(optarg, 4097 -
178                                              sizeof(struct log_write_entry));
179                         if (!start_mark) {
180                                 fprintf(stderr, "Couldn't allocate memory\n");
181                                 exit(1);
182                         }
183                         break;
184                 case END_MARK:
185                         /*
186                          * Biggest sectorsize is 4k atm, so limit the mark to 4k
187                          * minus the size of the entry.  Say 4097 since we want
188                          * an extra slot for \0.
189                          */
190                         end_mark = strndup(optarg, 4097 -
191                                            sizeof(struct log_write_entry));
192                         if (!end_mark) {
193                                 fprintf(stderr, "Couldn't allocate memory\n");
194                                 exit(1);
195                         }
196                         stop_flags |= LOG_MARK_FLAG;
197                         break;
198                 case LOG:
199                         logfile = strdup(optarg);
200                         if (!logfile) {
201                                 fprintf(stderr, "Couldn't allocate memory\n");
202                                 exit(1);
203                         }
204                         break;
205                 case REPLAY:
206                         replayfile = strdup(optarg);
207                         if (!replayfile) {
208                                 fprintf(stderr, "Couldn't allocate memory\n");
209                                 exit(1);
210                         }
211                         break;
212                 case LIMIT:
213                         run_limit = strtoull(optarg, &tmp, 0);
214                         if (tmp && *tmp != '\0') {
215                                 fprintf(stderr, "Invalid entry number\n");
216                                 exit(1);
217                         }
218                         tmp = NULL;
219                         break;
220                 case FIND:
221                         find_mode = 1;
222                         break;
223                 case NUM_ENTRIES:
224                         print_num_entries = 1;
225                         break;
226                 case NO_DISCARD:
227                         discard = 0;
228                         break;
229                 case FSCK:
230                         fsck_command = strdup(optarg);
231                         if (!fsck_command) {
232                                 fprintf(stderr, "Couldn't allocate memory\n");
233                                 exit(1);
234                         }
235                         break;
236                 case CHECK:
237                         if (!strcmp(optarg, "flush")) {
238                                 check_mode = CHECK_FLUSH;
239                         } else if (!strcmp(optarg, "fua")) {
240                                 check_mode = CHECK_FUA;
241                         } else {
242                                 check_mode = CHECK_NUMBER;
243                                 check_number = strtoull(optarg, &tmp, 0);
244                                 if (!check_number || (tmp && *tmp != '\0')) {
245                                         fprintf(stderr,
246                                                 "Invalid entry number\n");
247                                         exit(1);
248                                 }
249                                 tmp = NULL;
250                         }
251                         break;
252                 default:
253                         usage();
254                 }
255         }
256
257         if (!logfile)
258                 usage();
259
260         log = log_open(logfile, replayfile);
261         if (!log)
262                 exit(1);
263         free(logfile);
264         free(replayfile);
265
266         if (!discard)
267                 log->flags |= LOG_IGNORE_DISCARD;
268
269         entry = malloc(log->sectorsize);
270         if (!entry) {
271                 fprintf(stderr, "Couldn't allocate buffer\n");
272                 log_free(log);
273                 exit(1);
274         }
275
276         if (start_mark) {
277                 ret = seek_to_mark(log, entry, start_mark);
278                 if (ret)
279                         exit(1);
280                 free(start_mark);
281         } else {
282                 ret = log_seek_entry(log, start_entry);
283                 if (ret)
284                         exit(1);
285         }
286
287         if ((fsck_command && !check_mode) || (!fsck_command && check_mode))
288                 usage();
289
290         /* We just want to find a given entry */
291         if (find_mode) {
292                 while ((ret = log_seek_next_entry(log, entry, 1)) == 0) {
293                         num_entries++;
294                         if ((run_limit && num_entries == run_limit) ||
295                             should_stop(entry, stop_flags, end_mark)) {
296                                 printf("%llu\n",
297                                        (unsigned long long)log->cur_entry - 1);
298                                 log_free(log);
299                                 return 0;
300                         }
301                 }
302                 log_free(log);
303                 if (ret < 0)
304                         return ret;
305                 fprintf(stderr, "Couldn't find entry\n");
306                 return 1;
307         }
308
309         /* Used for scripts, just print the number of entries in the log */
310         if (print_num_entries) {
311                 printf("%llu\n", (unsigned long long)log->nr_entries);
312                 log_free(log);
313                 return 0;
314         }
315
316         /* No replay, just spit out the log info. */
317         if (!replayfile) {
318                 printf("Log version=%d, sectorsize=%lu, entries=%llu\n",
319                        WRITE_LOG_VERSION, (unsigned long)log->sectorsize,
320                        (unsigned long long)log->nr_entries);
321                 log_free(log);
322                 return 0;
323         }
324
325         while ((ret = log_replay_next_entry(log, entry, 1)) == 0) {
326                 num_entries++;
327                 if (fsck_command) {
328                         if ((check_mode == CHECK_NUMBER) &&
329                             !(num_entries % check_number))
330                                 ret = run_fsck(log, fsck_command);
331                         else if ((check_mode == CHECK_FUA) &&
332                                  should_stop(entry, LOG_FUA_FLAG, NULL))
333                                 ret = run_fsck(log, fsck_command);
334                         else if ((check_mode == CHECK_FLUSH) &&
335                                  should_stop(entry, LOG_FLUSH_FLAG, NULL))
336                                 ret = run_fsck(log, fsck_command);
337                         else
338                                 ret = 0;
339                         if (ret) {
340                                 fprintf(stderr, "Fsck errored out on entry "
341                                         "%llu\n",
342                                         (unsigned long long)log->cur_entry - 1);
343                                 break;
344                         }
345                 }
346
347                 if ((run_limit && num_entries == run_limit) ||
348                     should_stop(entry, stop_flags, end_mark))
349                         break;
350         }
351         fsync(log->replayfd);
352         log_free(log);
353         free(end_mark);
354         if (ret < 0)
355                 exit(1);
356         return 0;
357 }