// Copyright (c) Vitaliy Filippov, 2019+ // License: VNPL-1.1 (see README.md for details) #include "disk_tool.h" int disk_tool_t::dump_journal() { if (dsk.journal_block_size < DIRECT_IO_ALIGNMENT || (dsk.journal_block_size % DIRECT_IO_ALIGNMENT) || dsk.journal_block_size > 128*1024) { fprintf(stderr, "Invalid journal block size\n"); return 1; } first = true; if (json) printf("[\n"); if (all) { dsk.journal_fd = open(dsk.journal_device.c_str(), O_DIRECT|O_RDONLY); if (dsk.journal_fd < 0) { fprintf(stderr, "Failed to open journal device %s: %s\n", dsk.journal_device.c_str(), strerror(errno)); return 1; } void *journal_buf = memalign_or_die(MEM_ALIGNMENT, dsk.journal_block_size); journal_pos = 0; while (journal_pos < dsk.journal_len) { int r = pread(dsk.journal_fd, journal_buf, dsk.journal_block_size, dsk.journal_offset+journal_pos); assert(r == dsk.journal_block_size); uint64_t s; for (s = 0; s < dsk.journal_block_size; s += 8) { if (*((uint64_t*)((uint8_t*)journal_buf+s)) != 0) break; } if (json) { printf("%s{\"offset\":\"0x%lx\"", first ? "" : ",\n", journal_pos); first = false; } if (s == dsk.journal_block_size) { if (json) printf(",\"type\":\"zero\"}"); else printf("offset %08lx: zeroes\n", journal_pos); journal_pos += dsk.journal_block_size; } else if (((journal_entry*)journal_buf)->magic == JOURNAL_MAGIC) { if (!json) printf("offset %08lx:\n", journal_pos); else printf(",\"entries\":[\n"); first2 = true; process_journal_block(journal_buf, [this](int num, journal_entry *je) { dump_journal_entry(num, je, json); }); if (json) printf(first2 ? "]}" : "\n]}"); } else { if (json) printf(",\"type\":\"data\",\"pattern\":\"%08lx\"}", *((uint64_t*)journal_buf)); else printf("offset %08lx: no magic in the beginning, looks like random data (pattern=%08lx)\n", journal_pos, *((uint64_t*)journal_buf)); journal_pos += dsk.journal_block_size; } } free(journal_buf); close(dsk.journal_fd); dsk.journal_fd = -1; } else { process_journal([this](void *data) { first2 = true; if (!json) printf("offset %08lx:\n", journal_pos); auto pos = journal_pos; int r = process_journal_block(data, [this, pos](int num, journal_entry *je) { if (json && first2) { printf("%s{\"offset\":\"0x%lx\",\"entries\":[\n", first ? "" : ",\n", pos); first = false; } dump_journal_entry(num, je, json); }); if (json) printf(first2 ? "" : "\n]}"); else if (r <= 0) printf("end of the journal\n"); return r; }); } if (json) printf(first ? "]\n" : "\n]\n"); return 0; } int disk_tool_t::process_journal(std::function block_fn) { dsk.journal_fd = open(dsk.journal_device.c_str(), O_DIRECT|O_RDONLY); if (dsk.journal_fd < 0) { fprintf(stderr, "Failed to open journal device %s: %s\n", dsk.journal_device.c_str(), strerror(errno)); return 1; } void *data = memalign_or_die(MEM_ALIGNMENT, dsk.journal_block_size); journal_pos = 0; int r = pread(dsk.journal_fd, data, dsk.journal_block_size, dsk.journal_offset+journal_pos); assert(r == dsk.journal_block_size); journal_entry *je = (journal_entry*)(data); if (je->magic != JOURNAL_MAGIC || je->type != JE_START || je_crc32(je) != je->crc32) { fprintf(stderr, "offset %08lx: journal superblock is invalid\n", journal_pos); r = 1; } else { started = false; crc32_last = 0; block_fn(data); started = false; crc32_last = 0; journal_pos = je->start.journal_start; while (1) { if (journal_pos >= dsk.journal_len) journal_pos = dsk.journal_block_size; r = pread(dsk.journal_fd, data, dsk.journal_block_size, dsk.journal_offset+journal_pos); assert(r == dsk.journal_block_size); r = block_fn(data); if (r <= 0) break; } } close(dsk.journal_fd); dsk.journal_fd = -1; free(data); return r; } int disk_tool_t::process_journal_block(void *buf, std::function iter_fn) { uint32_t pos = 0; journal_pos += dsk.journal_block_size; int entry = 0; bool wrapped = false; while (pos <= dsk.journal_block_size-JOURNAL_ENTRY_HEADER_SIZE) { journal_entry *je = (journal_entry*)((uint8_t*)buf + pos); if (je->magic != JOURNAL_MAGIC || je->type < JE_MIN || je->type > JE_MAX || !all && started && je->crc32_prev != crc32_last || pos > dsk.journal_block_size-je->size) { break; } bool crc32_valid = je_crc32(je) == je->crc32; if (!all && !crc32_valid) { break; } started = true; crc32_last = je->crc32; if (je->type == JE_SMALL_WRITE || je->type == JE_SMALL_WRITE_INSTANT) { journal_calc_data_pos = journal_pos; if (journal_pos + je->small_write.len > dsk.journal_len) { // data continues from the beginning of the journal journal_calc_data_pos = journal_pos = dsk.journal_block_size; wrapped = true; } journal_pos += je->small_write.len; if (journal_pos >= dsk.journal_len) { journal_pos = dsk.journal_block_size; wrapped = true; } small_write_data = memalign_or_die(MEM_ALIGNMENT, je->small_write.len); assert(pread(dsk.journal_fd, small_write_data, je->small_write.len, dsk.journal_offset+je->small_write.data_offset) == je->small_write.len); data_crc32 = crc32c(0, small_write_data, je->small_write.len); } iter_fn(entry, je); if (je->type == JE_SMALL_WRITE || je->type == JE_SMALL_WRITE_INSTANT) { free(small_write_data); small_write_data = NULL; } pos += je->size; entry++; } if (wrapped) { journal_pos = dsk.journal_len; } return entry; } void disk_tool_t::dump_journal_entry(int num, journal_entry *je, bool json) { if (json) { if (!first2) printf(",\n"); first2 = false; printf( "{\"crc32\":\"%08x\",\"valid\":%s,\"crc32_prev\":\"%08x\"", je->crc32, (je_crc32(je) == je->crc32 ? "true" : "false"), je->crc32_prev ); } else { printf( "entry % 3d: crc32=%08x %s prev=%08x ", num, je->crc32, (je_crc32(je) == je->crc32 ? "(valid)" : "(invalid)"), je->crc32_prev ); } if (je->type == JE_START) { printf( json ? ",\"type\":\"start\",\"start\":\"0x%lx\"}" : "je_start start=%08lx\n", je->start.journal_start ); } else if (je->type == JE_SMALL_WRITE || je->type == JE_SMALL_WRITE_INSTANT) { printf( json ? ",\"type\":\"small_write%s\",\"inode\":\"0x%lx\",\"stripe\":\"0x%lx\",\"ver\":\"%lu\",\"offset\":%u,\"len\":%u,\"loc\":\"0x%lx\"" : "je_small_write%s oid=%lx:%lx ver=%lu offset=%u len=%u loc=%08lx", je->type == JE_SMALL_WRITE_INSTANT ? "_instant" : "", je->small_write.oid.inode, je->small_write.oid.stripe, je->small_write.version, je->small_write.offset, je->small_write.len, je->small_write.data_offset ); if (journal_calc_data_pos != je->small_write.data_offset) { printf(json ? ",\"bad_loc\":true,\"calc_loc\":\"0x%lx\"" : " (mismatched, calculated = %lu)", journal_pos); } printf( json ? ",\"data_crc32\":\"%08x\",\"data_valid\":%s}" : " data_crc32=%08x%s\n", je->small_write.crc32_data, (data_crc32 != je->small_write.crc32_data ? (json ? "false" : " (invalid)") : (json ? "true" : " (valid)")) ); } else if (je->type == JE_BIG_WRITE || je->type == JE_BIG_WRITE_INSTANT) { printf( json ? ",\"type\":\"big_write%s\",\"inode\":\"0x%lx\",\"stripe\":\"0x%lx\",\"ver\":\"%lu\",\"loc\":\"0x%lx\"}" : "je_big_write%s oid=%lx:%lx ver=%lu loc=%08lx\n", je->type == JE_BIG_WRITE_INSTANT ? "_instant" : "", je->big_write.oid.inode, je->big_write.oid.stripe, je->big_write.version, je->big_write.location ); } else if (je->type == JE_STABLE) { printf( json ? ",\"type\":\"stable\",\"inode\":\"0x%lx\",\"stripe\":\"0x%lx\",\"ver\":\"%lu\"}" : "je_stable oid=%lx:%lx ver=%lu\n", je->stable.oid.inode, je->stable.oid.stripe, je->stable.version ); } else if (je->type == JE_ROLLBACK) { printf( json ? ",\"type\":\"rollback\",\"inode\":\"0x%lx\",\"stripe\":\"0x%lx\",\"ver\":\"%lu\"}" : "je_rollback oid=%lx:%lx ver=%lu\n", je->rollback.oid.inode, je->rollback.oid.stripe, je->rollback.version ); } else if (je->type == JE_DELETE) { printf( json ? ",\"type\":\"delete\",\"inode\":\"0x%lx\",\"stripe\":\"0x%lx\",\"ver\":\"%lu\"}" : "je_delete oid=%lx:%lx ver=%lu\n", je->del.oid.inode, je->del.oid.stripe, je->del.version ); } }