1 // SPDX-License-Identifier: GPL-2.0
7 #include "log-writes.h"
28 static struct option long_options[] = {
29 {"next-flush", no_argument, NULL, 0},
30 {"next-fua", no_argument, NULL, 0},
31 {"start-entry", required_argument, NULL, 0},
32 {"end-mark", required_argument, NULL, 0},
33 {"log", required_argument, NULL, 0},
34 {"replay", required_argument, NULL, 0},
35 {"limit", required_argument, NULL, 0},
36 {"verbose", no_argument, NULL, 'v'},
37 {"find", no_argument, NULL, 0},
38 {"num-entries", no_argument, NULL, 0},
39 {"no-discard", no_argument, NULL, 0},
40 {"fsck", required_argument, NULL, 0},
41 {"check", required_argument, NULL, 0},
42 {"start-mark", required_argument, NULL, 0},
43 {"start-sector", required_argument, NULL, 0},
44 {"end-sector", required_argument, NULL, 0},
48 static void usage(void)
50 fprintf(stderr, "Usage: replay-log --log <logfile> [options]\n");
51 fprintf(stderr, "\t--replay <device> - replay onto a specific "
53 fprintf(stderr, "\t--limit <number> - number of entries to replay\n");
54 fprintf(stderr, "\t--next-flush - replay to/find the next flush\n");
55 fprintf(stderr, "\t--next-fua - replay to/find the next fua\n");
56 fprintf(stderr, "\t--start-entry <entry> - start at the given "
58 fprintf(stderr, "\t--start-mark <mark> - mark to start from\n");
59 fprintf(stderr, "\t--end-mark <mark> - replay to/find the given mark\n");
60 fprintf(stderr, "\t--find - put replay-log in find mode, will search "
61 "based on the other options\n");
62 fprintf(stderr, "\t--number-entries - print the number of entries in "
64 fprintf(stderr, "\t--no-discard - don't process discard entries\n");
65 fprintf(stderr, "\t--fsck - the fsck command to run, must specify "
67 fprintf(stderr, "\t--check [<number>|flush|fua] when to check the "
68 "file system, mush specify --fsck\n");
69 fprintf(stderr, "\t--start-sector <sector> - replay ops on region "
70 "from <sector> onto <device>\n");
71 fprintf(stderr, "\t--end-sector <sector> - replay ops on region "
72 "to <sector> onto <device>\n");
73 fprintf(stderr, "\t-v or --verbose - print replayed ops\n");
74 fprintf(stderr, "\t-vv - print also skipped ops\n");
79 * Check if the log entry flag matches one of the stop_flags.
80 * If stop_flag has LOG_MARK, then looking also for match of
83 static int should_stop(struct log_write_entry *entry, u64 stop_flags,
86 u64 flags = le64_to_cpu(entry->flags);
87 int check_mark = (stop_flags & LOG_MARK_FLAG);
88 /* mark data begins after entry header */
89 char *buf = entry->data;
90 /* entry buffer is padded with at least 1 zero after data_len */
91 u64 buflen = le64_to_cpu(entry->data_len) + 1;
93 if (flags & stop_flags) {
96 if ((flags & LOG_MARK_FLAG) &&
97 !strncmp(mark, buf, buflen))
103 static int run_fsck(struct log *log, char *fsck_command)
105 int ret = fsync(log->replayfd);
108 ret = system(fsck_command);
110 ret = WEXITSTATUS(ret);
114 enum log_replay_check_mode {
120 static int seek_to_mark(struct log *log, struct log_write_entry *entry,
125 while ((ret = log_seek_next_entry(log, entry, 1)) == 0) {
126 if (should_stop(entry, LOG_MARK_FLAG, mark))
130 fprintf(stderr, "Couldn't find starting mark\n");
137 int main(int argc, char **argv)
139 char *logfile = NULL, *replayfile = NULL, *fsck_command = NULL;
140 struct log_write_entry *entry;
143 u64 start_sector = 0;
144 u64 end_sector = -1ULL;
147 u64 check_number = 0;
148 char *end_mark = NULL, *start_mark = NULL;
155 int print_num_entries = 0;
157 enum log_replay_check_mode check_mode = 0;
159 while ((c = getopt_long(argc, argv, "v", long_options,
163 log_writes_verbose++;
171 stop_flags |= LOG_FLUSH_FLAG;
174 stop_flags |= LOG_FUA_FLAG;
177 start_entry = strtoull(optarg, &tmp, 0);
178 if (tmp && *tmp != '\0') {
179 fprintf(stderr, "Invalid entry number\n");
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.
190 start_mark = strndup(optarg, 4097 -
191 sizeof(struct log_write_entry));
193 fprintf(stderr, "Couldn't allocate memory\n");
199 * Biggest sectorsize is 4k atm, so limit the mark to 4k
200 * minus the size of the entry. Say 4097 since we want
201 * an extra slot for \0.
203 end_mark = strndup(optarg, 4097 -
204 sizeof(struct log_write_entry));
206 fprintf(stderr, "Couldn't allocate memory\n");
209 stop_flags |= LOG_MARK_FLAG;
212 logfile = strdup(optarg);
214 fprintf(stderr, "Couldn't allocate memory\n");
219 replayfile = strdup(optarg);
221 fprintf(stderr, "Couldn't allocate memory\n");
226 run_limit = strtoull(optarg, &tmp, 0);
227 if (tmp && *tmp != '\0') {
228 fprintf(stderr, "Invalid entry number\n");
237 print_num_entries = 1;
243 fsck_command = strdup(optarg);
245 fprintf(stderr, "Couldn't allocate memory\n");
250 if (!strcmp(optarg, "flush")) {
251 check_mode = CHECK_FLUSH;
252 } else if (!strcmp(optarg, "fua")) {
253 check_mode = CHECK_FUA;
255 check_mode = CHECK_NUMBER;
256 check_number = strtoull(optarg, &tmp, 0);
257 if (!check_number || (tmp && *tmp != '\0')) {
259 "Invalid entry number\n");
266 start_sector = strtoull(optarg, &tmp, 0);
267 if (tmp && *tmp != '\0') {
268 fprintf(stderr, "Invalid sector number\n");
274 end_sector = strtoull(optarg, &tmp, 0);
275 if (tmp && *tmp != '\0') {
276 fprintf(stderr, "Invalid sector number\n");
289 log = log_open(logfile, replayfile);
296 log->flags |= LOG_IGNORE_DISCARD;
298 log->start_sector = start_sector;
299 log->end_sector = end_sector;
301 entry = malloc(log->sectorsize);
303 fprintf(stderr, "Couldn't allocate buffer\n");
309 ret = seek_to_mark(log, entry, start_mark);
314 ret = log_seek_entry(log, start_entry);
319 if ((fsck_command && !check_mode) || (!fsck_command && check_mode))
322 /* We just want to find a given entry */
324 while ((ret = log_seek_next_entry(log, entry, 1)) == 0) {
326 if ((run_limit && num_entries == run_limit) ||
327 should_stop(entry, stop_flags, end_mark)) {
328 printf("%llu@%llu\n",
329 (unsigned long long)log->cur_entry - 1,
330 log->cur_pos / log->sectorsize);
338 fprintf(stderr, "Couldn't find entry\n");
342 /* Used for scripts, just print the number of entries in the log */
343 if (print_num_entries) {
344 printf("%llu\n", (unsigned long long)log->nr_entries);
349 /* No replay, just spit out the log info. */
351 printf("Log version=%d, sectorsize=%lu, entries=%llu\n",
352 WRITE_LOG_VERSION, (unsigned long)log->sectorsize,
353 (unsigned long long)log->nr_entries);
358 while ((ret = log_replay_next_entry(log, entry, 1)) == 0) {
361 if ((check_mode == CHECK_NUMBER) &&
362 !(num_entries % check_number))
363 ret = run_fsck(log, fsck_command);
364 else if ((check_mode == CHECK_FUA) &&
365 should_stop(entry, LOG_FUA_FLAG, NULL))
366 ret = run_fsck(log, fsck_command);
367 else if ((check_mode == CHECK_FLUSH) &&
368 should_stop(entry, LOG_FLUSH_FLAG, NULL))
369 ret = run_fsck(log, fsck_command);
373 fprintf(stderr, "Fsck errored out on entry "
375 (unsigned long long)log->cur_entry - 1);
380 if ((run_limit && num_entries == run_limit) ||
381 should_stop(entry, stop_flags, end_mark))
384 fsync(log->replayfd);