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