log-writes: Add support to output human readable flags
[xfstests-dev.git] / src / log-writes / log-writes.c
1 #include <linux/fs.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <sys/ioctl.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include "log-writes.h"
12
13 int log_writes_verbose = 0;
14
15 /*
16  * @log: the log to free.
17  *
18  * This will close any open fd's the log has and free up its memory.
19  */
20 void log_free(struct log *log)
21 {
22         if (log->replayfd >= 0)
23                 close(log->replayfd);
24         if (log->logfd >= 0)
25                 close(log->logfd);
26         free(log);
27 }
28
29 static int discard_range(struct log *log, u64 start, u64 len)
30 {
31         u64 range[2] = { start, len };
32
33         if (ioctl(log->replayfd, BLKDISCARD, &range) < 0) {
34                 if (log_writes_verbose)
35                         printf("replay device doesn't support discard, "
36                                "switching to writing zeros\n");
37                 log->flags |= LOG_DISCARD_NOT_SUPP;
38         }
39         return 0;
40 }
41
42 static int zero_range(struct log *log, u64 start, u64 len)
43 {
44         u64 bufsize = len;
45         ssize_t ret;
46         char *buf = NULL;
47
48         if (log->max_zero_size < len) {
49                 if (log_writes_verbose)
50                         printf("discard len %llu larger than max %llu\n",
51                                (unsigned long long)len,
52                                (unsigned long long)log->max_zero_size);
53                 return 0;
54         }
55
56         while (!buf) {
57                 buf = malloc(bufsize);
58                 if (!buf)
59                         bufsize >>= 1;
60                 if (!bufsize) {
61                         fprintf(stderr, "Couldn't allocate zero buffer");
62                         return -1;
63                 }
64         }
65
66         memset(buf, 0, bufsize);
67         while (len) {
68                 if (len < bufsize)
69                         bufsize = len;
70
71                 ret = pwrite(log->replayfd, buf, bufsize, start);
72                 if (ret != bufsize) {
73                         fprintf(stderr, "Error zeroing file: %d\n", errno);
74                         free(buf);
75                         return -1;
76                 }
77                 len -= ret;
78                 start += ret;
79         }
80         free(buf);
81         return 0;
82 }
83
84 /*
85  * @log: the log we are replaying.
86  * @entry: the discard entry.
87  *
88  * Discard the given length.  If the device supports discard we will call that
89  * ioctl, otherwise we will write 0's to emulate discard.  If the discard size
90  * is larger than log->max_zero_size then we will simply skip the zero'ing if
91  * the drive doesn't support discard.
92  */
93 int log_discard(struct log *log, struct log_write_entry *entry)
94 {
95         u64 start = le64_to_cpu(entry->sector) * log->sectorsize;
96         u64 size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
97         u64 max_chunk = 1 * 1024 * 1024 * 1024;
98
99         if (log->flags & LOG_IGNORE_DISCARD)
100                 return 0;
101
102         while (size) {
103                 u64 len = size > max_chunk ? max_chunk : size;
104                 int ret;
105
106                 /*
107                  * Do this check first in case it is our first discard, that way
108                  * if we return EOPNOTSUPP we will fall back to the 0 method
109                  * automatically.
110                  */
111                 if (!(log->flags & LOG_DISCARD_NOT_SUPP))
112                         ret = discard_range(log, start, len);
113                 if (log->flags & LOG_DISCARD_NOT_SUPP)
114                         ret = zero_range(log, start, len);
115                 if (ret)
116                         return -1;
117                 size -= len;
118                 start += len;
119         }
120         return 0;
121 }
122
123 #define DEFINE_LOG_FLAGS_STR_ENTRY(x)   \
124         {LOG_##x##_FLAG, #x}
125
126 struct flags_to_str_entry {
127         u64 flags;
128         const char *str;
129 } log_flags_table[] = {
130         DEFINE_LOG_FLAGS_STR_ENTRY(FLUSH),
131         DEFINE_LOG_FLAGS_STR_ENTRY(FUA),
132         DEFINE_LOG_FLAGS_STR_ENTRY(DISCARD),
133         DEFINE_LOG_FLAGS_STR_ENTRY(MARK)
134 };
135
136 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
137 #define LOG_FLAGS_BUF_SIZE      128
138 /*
139  * Convert numeric flags to human readable flags.
140  * @flags:      numeric flags
141  * @buf:        output buffer for human readable string.
142  *              must have enough space (LOG_FLAGS_BUF_SIZE) to contain all
143  *              the string
144  */
145 static void entry_flags_to_str(u64 flags, char *buf)
146 {
147         int empty = 1;
148         int left_len;
149         int i;
150
151         buf[0] = '\0';
152         for (i = 0; i < ARRAY_SIZE(log_flags_table); i++) {
153                 if (flags & log_flags_table[i].flags) {
154                         if (!empty)
155                                 strncat(buf, "|", LOG_FLAGS_BUF_SIZE);
156                         empty = 0;
157                         strncat(buf, log_flags_table[i].str, LOG_FLAGS_BUF_SIZE);
158                         flags &= ~log_flags_table[i].flags;
159                 }
160         }
161         if (flags) {
162                 if (!empty)
163                         strncat(buf, "|", LOG_FLAGS_BUF_SIZE);
164                 empty = 0;
165                 left_len = LOG_FLAGS_BUF_SIZE - strnlen(buf,
166                                                         LOG_FLAGS_BUF_SIZE);
167                 if (left_len > 0)
168                         snprintf(buf + strnlen(buf, LOG_FLAGS_BUF_SIZE),
169                                  left_len, "UNKNOWN.0x%llx", flags);
170         }
171         if (empty)
172                 strncpy(buf, "NONE", LOG_FLAGS_BUF_SIZE);
173 }
174
175 /*
176  * @log: the log we are replaying.
177  * @entry: entry to be replayed.
178  *
179  * @return: 0 if we should replay the entry, > 0 if we should skip it.
180  *
181  * Should we skip the entry in our log or replay onto the replay device.
182  */
183 int log_should_skip(struct log *log, struct log_write_entry *entry)
184 {
185         u64 sector = le64_to_cpu(entry->sector);
186         u64 nr_sectors = le64_to_cpu(entry->nr_sectors);
187
188         if (!nr_sectors)
189                 return 0;
190         if (sector + nr_sectors <= log->start_sector ||
191             sector > log->end_sector)
192                 return 1;
193         return 0;
194 }
195
196 /*
197  * @entry: entry to be replayed.
198  *
199  * @return: 1 if the entry is sane, 0 if it is invalid.
200  *
201  * Check if this is a sane log entry.
202  */
203 int log_entry_valid(struct log_write_entry *entry)
204 {
205         u64 flags = le64_to_cpu(entry->flags);
206
207         /* Suspect all zeroes entry */
208         if (!flags && !entry->nr_sectors)
209                 return 0;
210         /* Suspect non zero padded entry */
211         if (flags != LOG_MARK_FLAG && entry->data[0] != 0)
212                 return 0;
213         return 1;
214 }
215
216 /*
217  * @log: the log we are replaying.
218  * @entry: where we put the entry.
219  * @read_data: read the entry data as well, entry must be log->sectorsize sized
220  * if this is set.
221  *
222  * @return: 0 if we replayed, 1 if we are at the end, -1 if there was an error.
223  *
224  * Replay the next entry in our log onto the replay device.
225  */
226 int log_replay_next_entry(struct log *log, struct log_write_entry *entry,
227                           int read_data)
228 {
229         u64 size;
230         u64 flags;
231         size_t read_size = read_data ? log->sectorsize :
232                 sizeof(struct log_write_entry);
233         char *buf;
234         char flags_buf[LOG_FLAGS_BUF_SIZE];
235         ssize_t ret;
236         off_t offset;
237         int skip = 0;
238
239         if (log->cur_entry >= log->nr_entries)
240                 return 1;
241
242         ret = read(log->logfd, entry, read_size);
243         if (ret != read_size) {
244                 fprintf(stderr, "Error reading entry: %d\n", errno);
245                 return -1;
246         }
247         if (!log_entry_valid(entry)) {
248                 fprintf(stderr, "Malformed entry @%llu\n",
249                                 log->cur_pos / log->sectorsize);
250                 return -1;
251         }
252         log->cur_entry++;
253
254         size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
255         if (read_size < log->sectorsize) {
256                 log->cur_pos = lseek(log->logfd,
257                         log->sectorsize - sizeof(struct log_write_entry), SEEK_CUR);
258                 if (log->cur_pos == (off_t)-1) {
259                         fprintf(stderr, "Error seeking in log: %d\n", errno);
260                         return -1;
261                 }
262         } else {
263                 log->cur_pos += read_size;
264         }
265
266         flags = le64_to_cpu(entry->flags);
267         entry_flags_to_str(flags, flags_buf);
268         skip = log_should_skip(log, entry);
269         if (log_writes_verbose > 1 || (log_writes_verbose && !skip)) {
270                 printf("%s %d@%llu: sector %llu, size %llu, flags 0x%llx(%s)\n",
271                        skip ? "skipping" : "replaying",
272                        (int)log->cur_entry - 1, log->cur_pos / log->sectorsize,
273                        (unsigned long long)le64_to_cpu(entry->sector),
274                        (unsigned long long)size,
275                        (unsigned long long)flags, flags_buf);
276         }
277         if (!size)
278                 return 0;
279
280         if (flags & LOG_DISCARD_FLAG)
281                 return log_discard(log, entry);
282
283         if (skip) {
284                 log->cur_pos = lseek(log->logfd, size, SEEK_CUR);
285                 if (log->cur_pos == (off_t)-1) {
286                         fprintf(stderr, "Error seeking in log: %d\n", errno);
287                         return -1;
288                 }
289                 return 0;
290         }
291
292         buf = malloc(size);
293         if (!buf) {
294                 fprintf(stderr, "Error allocating buffer %llu entry %llu\n", (unsigned long long)size, (unsigned long long)log->cur_entry - 1);
295                 return -1;
296         }
297
298         ret = read(log->logfd, buf, size);
299         if (ret != size) {
300                 fprintf(stderr, "Error reading data: %d\n", errno);
301                 free(buf);
302                 return -1;
303         }
304         log->cur_pos += size;
305
306         offset = le64_to_cpu(entry->sector) * log->sectorsize;
307         ret = pwrite(log->replayfd, buf, size, offset);
308         free(buf);
309         if (ret != size) {
310                 fprintf(stderr, "Error writing data: %d\n", errno);
311                 return -1;
312         }
313
314         return 0;
315 }
316
317 /*
318  * @log: the log we are manipulating.
319  * @entry_num: the entry we want.
320  *
321  * Seek to the given entry in the log, starting at 0 and ending at
322  * log->nr_entries - 1.
323  */
324 int log_seek_entry(struct log *log, u64 entry_num)
325 {
326         u64 i = 0;
327
328         if (entry_num >= log->nr_entries) {
329                 fprintf(stderr, "Invalid entry number\n");
330                 return -1;
331         }
332
333         /* Skip the first sector containing the log super block */
334         log->cur_pos = lseek(log->logfd, log->sectorsize, SEEK_SET);
335         if (log->cur_pos == (off_t)-1) {
336                 fprintf(stderr, "Error seeking in file: %d\n", errno);
337                 return -1;
338         }
339
340         log->cur_entry = 0;
341         for (i = 0; i < entry_num; i++) {
342                 struct log_write_entry entry;
343                 ssize_t ret;
344                 off_t seek_size;
345                 u64 flags;
346
347                 ret = read(log->logfd, &entry, sizeof(entry));
348                 if (ret != sizeof(entry)) {
349                         fprintf(stderr, "Error reading entry: %d\n", errno);
350                         return -1;
351                 }
352                 if (!log_entry_valid(&entry)) {
353                         fprintf(stderr, "Malformed entry @%llu\n",
354                                         log->cur_pos / log->sectorsize);
355                         return -1;
356                 }
357                 if (log_writes_verbose > 1)
358                         printf("seek entry %d@%llu: %llu, size %llu, flags 0x%llx\n",
359                                (int)i, log->cur_pos / log->sectorsize,
360                                (unsigned long long)le64_to_cpu(entry.sector),
361                                (unsigned long long)le64_to_cpu(entry.nr_sectors),
362                                (unsigned long long)le64_to_cpu(entry.flags));
363                 flags = le64_to_cpu(entry.flags);
364                 seek_size = log->sectorsize - sizeof(entry);
365                 if (!(flags & LOG_DISCARD_FLAG))
366                         seek_size += le64_to_cpu(entry.nr_sectors) *
367                                 log->sectorsize;
368                 log->cur_pos = lseek(log->logfd, seek_size, SEEK_CUR);
369                 if (log->cur_pos == (off_t)-1) {
370                         fprintf(stderr, "Error seeking in file: %d\n", errno);
371                         return -1;
372                 }
373                 log->cur_entry++;
374         }
375
376         return 0;
377 }
378
379 /*
380  * @log: the log we are manipulating.
381  * @entry: the entry we read.
382  * @read_data: read the extra data for the entry, your entry must be
383  * log->sectorsize large.
384  *
385  * @return: 1 if we hit the end of the log, 0 we got the next entry, < 0 if
386  * there was an error.
387  *
388  * Seek to the next entry in the log.
389  */
390 int log_seek_next_entry(struct log *log, struct log_write_entry *entry,
391                         int read_data)
392 {
393         size_t read_size = read_data ? log->sectorsize :
394                 sizeof(struct log_write_entry);
395         u64 flags;
396         char flags_buf[LOG_FLAGS_BUF_SIZE];
397         ssize_t ret;
398
399         if (log->cur_entry >= log->nr_entries)
400                 return 1;
401
402         ret = read(log->logfd, entry, read_size);
403         if (ret != read_size) {
404                 fprintf(stderr, "Error reading entry: %d\n", errno);
405                 return -1;
406         }
407         if (!log_entry_valid(entry)) {
408                 fprintf(stderr, "Malformed entry @%llu\n",
409                                 log->cur_pos / log->sectorsize);
410                 return -1;
411         }
412         log->cur_entry++;
413
414         if (read_size < log->sectorsize) {
415                 log->cur_pos = lseek(log->logfd,
416                         log->sectorsize - sizeof(struct log_write_entry), SEEK_CUR);
417                 if (log->cur_pos == (off_t)-1) {
418                         fprintf(stderr, "Error seeking in log: %d\n", errno);
419                         return -1;
420                 }
421         } else {
422                 log->cur_pos += read_size;
423         }
424         flags = le64_to_cpu(entry->flags);
425         entry_flags_to_str(flags, flags_buf);
426         if (log_writes_verbose > 1)
427                 printf("seek entry %d@%llu: %llu, size %llu, flags 0x%llx(%s)\n",
428                        (int)log->cur_entry - 1, log->cur_pos / log->sectorsize,
429                        (unsigned long long)le64_to_cpu(entry->sector),
430                        (unsigned long long)le64_to_cpu(entry->nr_sectors),
431                        (unsigned long long)flags, flags_buf);
432
433         read_size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
434         if (!read_size || (flags & LOG_DISCARD_FLAG))
435                 return 0;
436
437         log->cur_pos = lseek(log->logfd, read_size, SEEK_CUR);
438         if (log->cur_pos == (off_t)-1) {
439                 fprintf(stderr, "Error seeking in log: %d\n", errno);
440                 return -1;
441         }
442
443         return 0;
444 }
445
446 /*
447  * @logfile: the file that contains the write log.
448  * @replayfile: the file/device to replay onto, can be NULL.
449  *
450  * Opens a logfile and makes sure it is valid and returns a struct log.
451  */
452 struct log *log_open(char *logfile, char *replayfile)
453 {
454         struct log *log;
455         struct log_write_super super;
456         ssize_t ret;
457
458         log = malloc(sizeof(struct log));
459         if (!log) {
460                 fprintf(stderr, "Couldn't alloc log\n");
461                 return NULL;
462         }
463
464         log->replayfd = -1;
465
466         log->logfd = open(logfile, O_RDONLY);
467         if (log->logfd < 0) {
468                 fprintf(stderr, "Couldn't open log %s: %d\n", logfile,
469                         errno);
470                 log_free(log);
471                 return NULL;
472         }
473
474         if (replayfile) {
475                 log->replayfd = open(replayfile, O_WRONLY);
476                 if (log->replayfd < 0) {
477                         fprintf(stderr, "Couldn't open replay file %s: %d\n",
478                                 replayfile, errno);
479                         log_free(log);
480                         return NULL;
481                 }
482         }
483
484         ret = read(log->logfd, &super, sizeof(struct log_write_super));
485         if (ret < sizeof(struct log_write_super)) {
486                 fprintf(stderr, "Error reading super: %d\n", errno);
487                 log_free(log);
488                 return NULL;
489         }
490
491         if (le64_to_cpu(super.magic) != WRITE_LOG_MAGIC) {
492                 fprintf(stderr, "Magic doesn't match\n");
493                 log_free(log);
494                 return NULL;
495         }
496
497         if (le64_to_cpu(super.version) != WRITE_LOG_VERSION) {
498                 fprintf(stderr, "Version mismatch, wanted %d, have %d\n",
499                         WRITE_LOG_VERSION, (int)le64_to_cpu(super.version));
500                 log_free(log);
501                 return NULL;
502         }
503
504         log->sectorsize = le32_to_cpu(super.sectorsize);
505         log->nr_entries = le64_to_cpu(super.nr_entries);
506         log->max_zero_size = 128 * 1024 * 1024;
507
508         log->cur_pos = lseek(log->logfd, log->sectorsize - sizeof(super), SEEK_CUR);
509         if (log->cur_pos == (off_t) -1) {
510                 fprintf(stderr, "Error seeking to first entry: %d\n", errno);
511                 log_free(log);
512                 return NULL;
513         }
514         log->cur_entry = 0;
515
516         return log;
517 }