log-writes: Add support to output human readable flags
[xfstests-dev.git] / src / log-writes / log-writes.c
index a215fefa833f92bd4cb2fa4f6709eb093028a3a0..a872429d3f41016f2429109acd48d24feb4e5999 100644 (file)
@@ -65,6 +65,9 @@ static int zero_range(struct log *log, u64 start, u64 len)
 
        memset(buf, 0, bufsize);
        while (len) {
+               if (len < bufsize)
+                       bufsize = len;
+
                ret = pwrite(log->replayfd, buf, bufsize, start);
                if (ret != bufsize) {
                        fprintf(stderr, "Error zeroing file: %d\n", errno);
@@ -117,6 +120,99 @@ int log_discard(struct log *log, struct log_write_entry *entry)
        return 0;
 }
 
+#define DEFINE_LOG_FLAGS_STR_ENTRY(x)  \
+       {LOG_##x##_FLAG, #x}
+
+struct flags_to_str_entry {
+       u64 flags;
+       const char *str;
+} log_flags_table[] = {
+       DEFINE_LOG_FLAGS_STR_ENTRY(FLUSH),
+       DEFINE_LOG_FLAGS_STR_ENTRY(FUA),
+       DEFINE_LOG_FLAGS_STR_ENTRY(DISCARD),
+       DEFINE_LOG_FLAGS_STR_ENTRY(MARK)
+};
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define LOG_FLAGS_BUF_SIZE     128
+/*
+ * Convert numeric flags to human readable flags.
+ * @flags:     numeric flags
+ * @buf:       output buffer for human readable string.
+ *             must have enough space (LOG_FLAGS_BUF_SIZE) to contain all
+ *             the string
+ */
+static void entry_flags_to_str(u64 flags, char *buf)
+{
+       int empty = 1;
+       int left_len;
+       int i;
+
+       buf[0] = '\0';
+       for (i = 0; i < ARRAY_SIZE(log_flags_table); i++) {
+               if (flags & log_flags_table[i].flags) {
+                       if (!empty)
+                               strncat(buf, "|", LOG_FLAGS_BUF_SIZE);
+                       empty = 0;
+                       strncat(buf, log_flags_table[i].str, LOG_FLAGS_BUF_SIZE);
+                       flags &= ~log_flags_table[i].flags;
+               }
+       }
+       if (flags) {
+               if (!empty)
+                       strncat(buf, "|", LOG_FLAGS_BUF_SIZE);
+               empty = 0;
+               left_len = LOG_FLAGS_BUF_SIZE - strnlen(buf,
+                                                       LOG_FLAGS_BUF_SIZE);
+               if (left_len > 0)
+                       snprintf(buf + strnlen(buf, LOG_FLAGS_BUF_SIZE),
+                                left_len, "UNKNOWN.0x%llx", flags);
+       }
+       if (empty)
+               strncpy(buf, "NONE", LOG_FLAGS_BUF_SIZE);
+}
+
+/*
+ * @log: the log we are replaying.
+ * @entry: entry to be replayed.
+ *
+ * @return: 0 if we should replay the entry, > 0 if we should skip it.
+ *
+ * Should we skip the entry in our log or replay onto the replay device.
+ */
+int log_should_skip(struct log *log, struct log_write_entry *entry)
+{
+       u64 sector = le64_to_cpu(entry->sector);
+       u64 nr_sectors = le64_to_cpu(entry->nr_sectors);
+
+       if (!nr_sectors)
+               return 0;
+       if (sector + nr_sectors <= log->start_sector ||
+           sector > log->end_sector)
+               return 1;
+       return 0;
+}
+
+/*
+ * @entry: entry to be replayed.
+ *
+ * @return: 1 if the entry is sane, 0 if it is invalid.
+ *
+ * Check if this is a sane log entry.
+ */
+int log_entry_valid(struct log_write_entry *entry)
+{
+       u64 flags = le64_to_cpu(entry->flags);
+
+       /* Suspect all zeroes entry */
+       if (!flags && !entry->nr_sectors)
+               return 0;
+       /* Suspect non zero padded entry */
+       if (flags != LOG_MARK_FLAG && entry->data[0] != 0)
+               return 0;
+       return 1;
+}
+
 /*
  * @log: the log we are replaying.
  * @entry: where we put the entry.
@@ -135,8 +231,10 @@ int log_replay_next_entry(struct log *log, struct log_write_entry *entry,
        size_t read_size = read_data ? log->sectorsize :
                sizeof(struct log_write_entry);
        char *buf;
+       char flags_buf[LOG_FLAGS_BUF_SIZE];
        ssize_t ret;
        off_t offset;
+       int skip = 0;
 
        if (log->cur_entry >= log->nr_entries)
                return 1;
@@ -146,31 +244,51 @@ int log_replay_next_entry(struct log *log, struct log_write_entry *entry,
                fprintf(stderr, "Error reading entry: %d\n", errno);
                return -1;
        }
+       if (!log_entry_valid(entry)) {
+               fprintf(stderr, "Malformed entry @%llu\n",
+                               log->cur_pos / log->sectorsize);
+               return -1;
+       }
        log->cur_entry++;
 
        size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
        if (read_size < log->sectorsize) {
-               if (lseek(log->logfd,
-                         log->sectorsize - sizeof(struct log_write_entry),
-                         SEEK_CUR) == (off_t)-1) {
+               log->cur_pos = lseek(log->logfd,
+                       log->sectorsize - sizeof(struct log_write_entry), SEEK_CUR);
+               if (log->cur_pos == (off_t)-1) {
                        fprintf(stderr, "Error seeking in log: %d\n", errno);
                        return -1;
                }
+       } else {
+               log->cur_pos += read_size;
        }
 
-       if (log_writes_verbose)
-               printf("replaying %d: sector %llu, size %llu, flags %llu\n",
-                      (int)log->cur_entry - 1,
+       flags = le64_to_cpu(entry->flags);
+       entry_flags_to_str(flags, flags_buf);
+       skip = log_should_skip(log, entry);
+       if (log_writes_verbose > 1 || (log_writes_verbose && !skip)) {
+               printf("%s %d@%llu: sector %llu, size %llu, flags 0x%llx(%s)\n",
+                      skip ? "skipping" : "replaying",
+                      (int)log->cur_entry - 1, log->cur_pos / log->sectorsize,
                       (unsigned long long)le64_to_cpu(entry->sector),
                       (unsigned long long)size,
-                      (unsigned long long)le64_to_cpu(entry->flags));
+                      (unsigned long long)flags, flags_buf);
+       }
        if (!size)
                return 0;
 
-       flags = le64_to_cpu(entry->flags);
        if (flags & LOG_DISCARD_FLAG)
                return log_discard(log, entry);
 
+       if (skip) {
+               log->cur_pos = lseek(log->logfd, size, SEEK_CUR);
+               if (log->cur_pos == (off_t)-1) {
+                       fprintf(stderr, "Error seeking in log: %d\n", errno);
+                       return -1;
+               }
+               return 0;
+       }
+
        buf = malloc(size);
        if (!buf) {
                fprintf(stderr, "Error allocating buffer %llu entry %llu\n", (unsigned long long)size, (unsigned long long)log->cur_entry - 1);
@@ -183,6 +301,7 @@ int log_replay_next_entry(struct log *log, struct log_write_entry *entry,
                free(buf);
                return -1;
        }
+       log->cur_pos += size;
 
        offset = le64_to_cpu(entry->sector) * log->sectorsize;
        ret = pwrite(log->replayfd, buf, size, offset);
@@ -212,7 +331,8 @@ int log_seek_entry(struct log *log, u64 entry_num)
        }
 
        /* Skip the first sector containing the log super block */
-       if (lseek(log->logfd, log->sectorsize, SEEK_SET) == (off_t)-1) {
+       log->cur_pos = lseek(log->logfd, log->sectorsize, SEEK_SET);
+       if (log->cur_pos == (off_t)-1) {
                fprintf(stderr, "Error seeking in file: %d\n", errno);
                return -1;
        }
@@ -229,9 +349,14 @@ int log_seek_entry(struct log *log, u64 entry_num)
                        fprintf(stderr, "Error reading entry: %d\n", errno);
                        return -1;
                }
+               if (!log_entry_valid(&entry)) {
+                       fprintf(stderr, "Malformed entry @%llu\n",
+                                       log->cur_pos / log->sectorsize);
+                       return -1;
+               }
                if (log_writes_verbose > 1)
-                       printf("seek entry %d: %llu, size %llu, flags %llu\n",
-                              (int)i,
+                       printf("seek entry %d@%llu: %llu, size %llu, flags 0x%llx\n",
+                              (int)i, log->cur_pos / log->sectorsize,
                               (unsigned long long)le64_to_cpu(entry.sector),
                               (unsigned long long)le64_to_cpu(entry.nr_sectors),
                               (unsigned long long)le64_to_cpu(entry.flags));
@@ -240,7 +365,8 @@ int log_seek_entry(struct log *log, u64 entry_num)
                if (!(flags & LOG_DISCARD_FLAG))
                        seek_size += le64_to_cpu(entry.nr_sectors) *
                                log->sectorsize;
-               if (lseek(log->logfd, seek_size, SEEK_CUR) == (off_t)-1) {
+               log->cur_pos = lseek(log->logfd, seek_size, SEEK_CUR);
+               if (log->cur_pos == (off_t)-1) {
                        fprintf(stderr, "Error seeking in file: %d\n", errno);
                        return -1;
                }
@@ -267,6 +393,7 @@ int log_seek_next_entry(struct log *log, struct log_write_entry *entry,
        size_t read_size = read_data ? log->sectorsize :
                sizeof(struct log_write_entry);
        u64 flags;
+       char flags_buf[LOG_FLAGS_BUF_SIZE];
        ssize_t ret;
 
        if (log->cur_entry >= log->nr_entries)
@@ -277,29 +404,38 @@ int log_seek_next_entry(struct log *log, struct log_write_entry *entry,
                fprintf(stderr, "Error reading entry: %d\n", errno);
                return -1;
        }
+       if (!log_entry_valid(entry)) {
+               fprintf(stderr, "Malformed entry @%llu\n",
+                               log->cur_pos / log->sectorsize);
+               return -1;
+       }
        log->cur_entry++;
 
        if (read_size < log->sectorsize) {
-               if (lseek(log->logfd,
-                         log->sectorsize - sizeof(struct log_write_entry),
-                         SEEK_CUR) == (off_t)-1) {
+               log->cur_pos = lseek(log->logfd,
+                       log->sectorsize - sizeof(struct log_write_entry), SEEK_CUR);
+               if (log->cur_pos == (off_t)-1) {
                        fprintf(stderr, "Error seeking in log: %d\n", errno);
                        return -1;
                }
+       } else {
+               log->cur_pos += read_size;
        }
+       flags = le64_to_cpu(entry->flags);
+       entry_flags_to_str(flags, flags_buf);
        if (log_writes_verbose > 1)
-               printf("seek entry %d: %llu, size %llu, flags %llu\n",
-                      (int)log->cur_entry - 1,
+               printf("seek entry %d@%llu: %llu, size %llu, flags 0x%llx(%s)\n",
+                      (int)log->cur_entry - 1, log->cur_pos / log->sectorsize,
                       (unsigned long long)le64_to_cpu(entry->sector),
                       (unsigned long long)le64_to_cpu(entry->nr_sectors),
-                      (unsigned long long)le64_to_cpu(entry->flags));
+                      (unsigned long long)flags, flags_buf);
 
-       flags = le32_to_cpu(entry->flags);
-       read_size = le32_to_cpu(entry->nr_sectors) * log->sectorsize;
+       read_size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
        if (!read_size || (flags & LOG_DISCARD_FLAG))
                return 0;
 
-       if (lseek(log->logfd, read_size, SEEK_CUR) == (off_t)-1) {
+       log->cur_pos = lseek(log->logfd, read_size, SEEK_CUR);
+       if (log->cur_pos == (off_t)-1) {
                fprintf(stderr, "Error seeking in log: %d\n", errno);
                return -1;
        }
@@ -369,8 +505,8 @@ struct log *log_open(char *logfile, char *replayfile)
        log->nr_entries = le64_to_cpu(super.nr_entries);
        log->max_zero_size = 128 * 1024 * 1024;
 
-       if (lseek(log->logfd, log->sectorsize - sizeof(super), SEEK_CUR) ==
-           (off_t) -1) {
+       log->cur_pos = lseek(log->logfd, log->sectorsize - sizeof(super), SEEK_CUR);
+       if (log->cur_pos == (off_t) -1) {
                fprintf(stderr, "Error seeking to first entry: %d\n", errno);
                log_free(log);
                return NULL;