logdump.c revision e1018eeaa3285cd0ca26986d929194c1b577d211
1/* 2 * logdump.c --- dump the contents of the journal out to a file 3 * 4 * Authro: Stephen C. Tweedie, 2001 <sct@redhat.com> 5 * Copyright (C) 2001 Red Hat, Inc. 6 * Based on portions Copyright (C) 1994 Theodore Ts'o. 7 * 8 * This file may be redistributed under the terms of the GNU Public 9 * License. 10 */ 11 12#include <stdio.h> 13#include <unistd.h> 14#include <stdlib.h> 15#include <ctype.h> 16#include <string.h> 17#include <time.h> 18#ifdef HAVE_ERRNO_H 19#include <errno.h> 20#endif 21#include <sys/types.h> 22#include <sys/stat.h> 23#include <fcntl.h> 24#include <utime.h> 25#ifdef HAVE_GETOPT_H 26#include <getopt.h> 27#else 28extern int optind; 29extern char *optarg; 30#endif 31#ifdef HAVE_OPTRESET 32extern int optreset; /* defined by BSD, but not others */ 33#endif 34 35#include "debugfs.h" 36#include "jfs_user.h" 37#include <uuid/uuid.h> 38 39enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL}; 40 41int dump_all, dump_contents, dump_descriptors; 42unsigned int block_to_dump, group_to_dump, bitmap_to_dump; 43unsigned int inode_block_to_dump, inode_offset_to_dump, bitmap_to_dump; 44ext2_ino_t inode_to_dump; 45 46struct journal_source 47{ 48 enum journal_location where; 49 int fd; 50 ext2_file_t file; 51}; 52 53static void dump_journal(char *, FILE *, struct journal_source *); 54 55static void dump_descriptor_block(FILE *, struct journal_source *, 56 char *, journal_superblock_t *, 57 unsigned int *, int, tid_t); 58 59static void dump_revoke_block(FILE *, char *, journal_superblock_t *, 60 unsigned int, int, tid_t); 61 62static void dump_metadata_block(FILE *, struct journal_source *, 63 journal_superblock_t*, 64 unsigned int, unsigned int, int, tid_t); 65 66static void do_hexdump (FILE *, char *, int); 67 68#define WRAP(jsb, blocknr) \ 69 if (blocknr >= be32_to_cpu((jsb)->s_maxlen)) \ 70 blocknr -= (be32_to_cpu((jsb)->s_maxlen) - \ 71 be32_to_cpu((jsb)->s_first)); 72 73 74void do_logdump(int argc, char **argv) 75{ 76 int c; 77 int retval; 78 char *out_fn; 79 FILE *out_file; 80 81 char *inode_spec = NULL; 82 char *journal_fn = NULL; 83 int journal_fd = 0; 84 ext2_ino_t journal_inum; 85 struct ext2_inode journal_inode; 86 ext2_file_t journal_file; 87 88 char *tmp; 89 90 const char *logdump_usage = ("Usage: logdump " 91 "[-ac] [-b<block>] [-i<inode>] " 92 "[-f<journal_file>] [output_file]"); 93 94 struct journal_source journal_source; 95 96 optind = 0; 97#ifdef HAVE_OPTRESET 98 optreset = 1; /* Makes BSD getopt happy */ 99#endif 100 journal_source.where = 0; 101 journal_source.fd = 0; 102 journal_source.file = 0; 103 dump_all = 0; 104 dump_contents = 0; 105 dump_descriptors = 1; 106 block_to_dump = -1; 107 bitmap_to_dump = -1; 108 inode_block_to_dump = -1; 109 inode_to_dump = -1; 110 111 while ((c = getopt (argc, argv, "ab:ci:f:")) != EOF) { 112 switch (c) { 113 case 'a': 114 dump_all++; 115 break; 116 case 'b': 117 block_to_dump = strtoul(optarg, &tmp, 0); 118 if (*tmp) { 119 com_err(argv[0], 0, 120 "Bad block number - %s", optarg); 121 return; 122 } 123 dump_descriptors = 0; 124 break; 125 case 'c': 126 dump_contents++; 127 break; 128 case 'f': 129 journal_fn = optarg; 130 break; 131 case 'i': 132 inode_spec = optarg; 133 dump_descriptors = 0; 134 break; 135 default: 136 com_err(argv[0], 0, logdump_usage); 137 return; 138 } 139 } 140 if (optind != argc && optind != argc-1) { 141 com_err(argv[0], 0, logdump_usage); 142 return; 143 } 144 145 if (inode_spec) { 146 int inode_group, group_offset, inodes_per_block; 147 148 if (check_fs_open(argv[0])) 149 return; 150 151 inode_to_dump = string_to_inode(inode_spec); 152 if (!inode_to_dump) 153 return; 154 155 inode_group = ((inode_to_dump - 1) 156 / current_fs->super->s_inodes_per_group); 157 group_offset = ((inode_to_dump - 1) 158 % current_fs->super->s_inodes_per_group); 159 inodes_per_block = (current_fs->blocksize 160 / sizeof(struct ext2_inode)); 161 162 inode_block_to_dump = 163 current_fs->group_desc[inode_group].bg_inode_table + 164 (group_offset / inodes_per_block); 165 inode_offset_to_dump = ((group_offset % inodes_per_block) 166 * sizeof(struct ext2_inode)); 167 printf("Inode %u is at group %u, block %u, offset %u\n", 168 inode_to_dump, inode_group, 169 inode_block_to_dump, inode_offset_to_dump); 170 } 171 172 if (optind == argc) { 173 out_file = stdout; 174 } else { 175 out_fn = argv[optind]; 176 out_file = fopen(out_fn, "w"); 177 if (!out_file < 0) { 178 com_err(argv[0], errno, "while opening %s for logdump", 179 out_fn); 180 return; 181 } 182 } 183 184 if (block_to_dump != -1 && current_fs != NULL) { 185 group_to_dump = ((block_to_dump - 186 current_fs->super->s_first_data_block) 187 / current_fs->super->s_blocks_per_group); 188 bitmap_to_dump = current_fs->group_desc[group_to_dump].bg_block_bitmap; 189 } 190 191 if (!journal_fn && check_fs_open(argv[0])) 192 return; 193 194 if (journal_fn) { 195 /* Set up to read journal from a regular file somewhere */ 196 journal_fd = open(journal_fn, O_RDONLY, 0); 197 if (journal_fd < 0) { 198 com_err(argv[0], errno, "while opening %s for logdump", 199 journal_fn); 200 return; 201 } 202 203 journal_source.where = JOURNAL_IS_EXTERNAL; 204 journal_source.fd = journal_fd; 205 } else if ((journal_inum = current_fs->super->s_journal_inum)) { 206 if (debugfs_read_inode(journal_inum, &journal_inode, argv[0])) 207 return; 208 209 retval = ext2fs_file_open(current_fs, journal_inum, 210 0, &journal_file); 211 if (retval) { 212 com_err(argv[0], retval, "while opening ext2 file"); 213 return; 214 } 215 journal_source.where = JOURNAL_IS_INTERNAL; 216 journal_source.file = journal_file; 217 } else if ((journal_fn = 218 ext2fs_find_block_device(current_fs->super->s_journal_dev))) { 219 journal_fd = open(journal_fn, O_RDONLY, 0); 220 if (journal_fd < 0) { 221 com_err(argv[0], errno, "while opening %s for logdump", 222 journal_fn); 223 free(journal_fn); 224 return; 225 } 226 fprintf(out_file, "Using external journal found at %s\n", 227 journal_fn); 228 free(journal_fn); 229 journal_source.where = JOURNAL_IS_EXTERNAL; 230 journal_source.fd = journal_fd; 231 } else { 232 com_err(argv[0], 0, "filesystem has no journal"); 233 return; 234 } 235 236 dump_journal(argv[0], out_file, &journal_source); 237 238 if (journal_source.where == JOURNAL_IS_INTERNAL) 239 ext2fs_file_close(journal_file); 240 else 241 close(journal_fd); 242 243 if (out_file != stdout) 244 fclose(out_file); 245 246 return; 247} 248 249 250static int read_journal_block(const char *cmd, struct journal_source *source, 251 off_t offset, char *buf, int size, 252 unsigned int *got) 253{ 254 int retval; 255 256 if (source->where == JOURNAL_IS_EXTERNAL) { 257 if (lseek(source->fd, offset, SEEK_SET) < 0) { 258 retval = errno; 259 com_err(cmd, retval, "while seeking in reading journal"); 260 return retval; 261 } 262 retval = read(source->fd, buf, size); 263 if (retval >= 0) { 264 *got = retval; 265 retval = 0; 266 } else 267 retval = errno; 268 } else { 269 retval = ext2fs_file_lseek(source->file, offset, 270 EXT2_SEEK_SET, NULL); 271 if (retval) { 272 com_err(cmd, retval, "while seeking in reading journal"); 273 return retval; 274 } 275 276 retval = ext2fs_file_read(source->file, buf, size, got); 277 } 278 279 if (retval) 280 com_err(cmd, retval, "while while reading journal"); 281 else if (*got != size) { 282 com_err(cmd, 0, "short read (read %d, expected %d) while while reading journal", *got, size); 283 retval = -1; 284 } 285 286 return retval; 287} 288 289static const char *type_to_name(int btype) 290{ 291 switch (btype) { 292 case JFS_DESCRIPTOR_BLOCK: 293 return "descriptor block"; 294 case JFS_COMMIT_BLOCK: 295 return "commit block"; 296 case JFS_SUPERBLOCK_V1: 297 return "V1 superblock"; 298 case JFS_SUPERBLOCK_V2: 299 return "V2 superblock"; 300 case JFS_REVOKE_BLOCK: 301 return "revoke table"; 302 } 303 return "unrecognised type"; 304} 305 306 307static void dump_journal(char *cmdname, FILE *out_file, 308 struct journal_source *source) 309{ 310 struct ext2_super_block *sb; 311 char jsb_buffer[1024]; 312 char buf[8192]; 313 journal_superblock_t *jsb; 314 int blocksize = 1024; 315 unsigned int got; 316 int retval; 317 __u32 magic, sequence, blocktype; 318 journal_header_t *header; 319 320 tid_t transaction; 321 unsigned int blocknr = 0; 322 323 /* First, check to see if there's an ext2 superblock header */ 324 retval = read_journal_block(cmdname, source, 0, 325 buf, 2048, &got); 326 if (retval) 327 return; 328 329 jsb = (journal_superblock_t *) buf; 330 sb = (struct ext2_super_block *) (buf+1024); 331#ifdef ENABLE_SWAPFS 332 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) 333 ext2fs_swap_super(sb); 334#endif 335 336 if ((be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) && 337 (sb->s_magic == EXT2_SUPER_MAGIC) && 338 (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { 339 blocksize = EXT2_BLOCK_SIZE(sb); 340 blocknr = (blocksize == 1024) ? 2 : 1; 341 uuid_unparse(sb->s_uuid, jsb_buffer); 342 fprintf(out_file, "Ext2 superblock header found.\n"); 343 if (dump_all) { 344 fprintf(out_file, "\tuuid=%s\n", jsb_buffer); 345 fprintf(out_file, "\tblocksize=%d\n", blocksize); 346 fprintf(out_file, "\tjournal data size %ld\n", 347 (long) sb->s_blocks_count); 348 } 349 } 350 351 /* Next, read the journal superblock */ 352 353 retval = read_journal_block(cmdname, source, blocknr*blocksize, 354 jsb_buffer, 1024, &got); 355 if (retval) 356 return; 357 358 jsb = (journal_superblock_t *) jsb_buffer; 359 if (be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) { 360 fprintf(out_file, 361 "Journal superblock magic number invalid!\n"); 362 return; 363 } 364 blocksize = be32_to_cpu(jsb->s_blocksize); 365 transaction = be32_to_cpu(jsb->s_sequence); 366 blocknr = be32_to_cpu(jsb->s_start); 367 368 fprintf(out_file, "Journal starts at block %u, transaction %u\n", 369 blocknr, transaction); 370 371 if (!blocknr) 372 /* Empty journal, nothing to do. */ 373 return; 374 375 while (1) { 376 retval = read_journal_block(cmdname, source, 377 blocknr*blocksize, buf, 378 blocksize, &got); 379 if (retval || got != blocksize) 380 return; 381 382 header = (journal_header_t *) buf; 383 384 magic = be32_to_cpu(header->h_magic); 385 sequence = be32_to_cpu(header->h_sequence); 386 blocktype = be32_to_cpu(header->h_blocktype); 387 388 if (magic != JFS_MAGIC_NUMBER) { 389 fprintf (out_file, "No magic number at block %u: " 390 "end of journal.\n", blocknr); 391 return; 392 } 393 394 if (sequence != transaction) { 395 fprintf (out_file, "Found sequence %u (not %u) at " 396 "block %u: end of journal.\n", 397 sequence, transaction, blocknr); 398 return; 399 } 400 401 if (dump_descriptors) { 402 fprintf (out_file, "Found expected sequence %u, " 403 "type %u (%s) at block %u\n", 404 sequence, blocktype, 405 type_to_name(blocktype), blocknr); 406 } 407 408 switch (blocktype) { 409 case JFS_DESCRIPTOR_BLOCK: 410 dump_descriptor_block(out_file, source, buf, jsb, 411 &blocknr, blocksize, 412 transaction); 413 continue; 414 415 case JFS_COMMIT_BLOCK: 416 transaction++; 417 blocknr++; 418 WRAP(jsb, blocknr); 419 continue; 420 421 case JFS_REVOKE_BLOCK: 422 dump_revoke_block(out_file, buf, jsb, 423 blocknr, blocksize, 424 transaction); 425 blocknr++; 426 WRAP(jsb, blocknr); 427 continue; 428 429 default: 430 fprintf (out_file, "Unexpected block type %u at " 431 "block %u.\n", blocktype, blocknr); 432 return; 433 } 434 } 435} 436 437 438static void dump_descriptor_block(FILE *out_file, 439 struct journal_source *source, 440 char *buf, 441 journal_superblock_t *jsb, 442 unsigned int *blockp, int blocksize, 443 tid_t transaction) 444{ 445 int offset; 446 char *tagp; 447 journal_block_tag_t *tag; 448 unsigned int blocknr; 449 __u32 tag_block; 450 __u32 tag_flags; 451 452 453 offset = sizeof(journal_header_t); 454 blocknr = *blockp; 455 456 if (dump_all) 457 fprintf(out_file, "Dumping descriptor block, sequence %u, at " 458 "block %u:\n", transaction, blocknr); 459 460 ++blocknr; 461 WRAP(jsb, blocknr); 462 463 do { 464 /* Work out the location of the current tag, and skip to 465 * the next one... */ 466 tagp = &buf[offset]; 467 tag = (journal_block_tag_t *) tagp; 468 offset += sizeof(journal_block_tag_t); 469 470 /* ... and if we have gone too far, then we've reached the 471 end of this block. */ 472 if (offset > blocksize) 473 break; 474 475 tag_block = be32_to_cpu(tag->t_blocknr); 476 tag_flags = be32_to_cpu(tag->t_flags); 477 478 if (!(tag_flags & JFS_FLAG_SAME_UUID)) 479 offset += 16; 480 481 dump_metadata_block(out_file, source, jsb, 482 blocknr, tag_block, blocksize, 483 transaction); 484 485 ++blocknr; 486 WRAP(jsb, blocknr); 487 488 } while (!(tag_flags & JFS_FLAG_LAST_TAG)); 489 490 *blockp = blocknr; 491} 492 493 494static void dump_revoke_block(FILE *out_file, char *buf, 495 journal_superblock_t *jsb, 496 unsigned int blocknr, int blocksize, 497 tid_t transaction) 498{ 499 int offset, max; 500 journal_revoke_header_t *header; 501 unsigned int *entry, rblock; 502 503 if (dump_all) 504 fprintf(out_file, "Dumping revoke block, sequence %u, at " 505 "block %u:\n", transaction, blocknr); 506 507 header = (journal_revoke_header_t *) buf; 508 offset = sizeof(journal_revoke_header_t); 509 max = be32_to_cpu(header->r_count); 510 511 while (offset < max) { 512 entry = (unsigned int *) (buf + offset); 513 rblock = be32_to_cpu(*entry); 514 if (dump_all || rblock == block_to_dump) { 515 fprintf(out_file, " Revoke FS block %u", rblock); 516 if (dump_all) 517 fprintf(out_file, "\n"); 518 else 519 fprintf(out_file," at block %u, sequence %u\n", 520 blocknr, transaction); 521 } 522 offset += 4; 523 } 524} 525 526 527static void show_extent(FILE *out_file, int start_extent, int end_extent, 528 __u32 first_block) 529{ 530 if (start_extent >= 0 && first_block != 0) 531 fprintf(out_file, "(%d+%u): %u ", 532 start_extent, end_extent-start_extent, first_block); 533} 534 535static void show_indirect(FILE *out_file, const char *name, __u32 where) 536{ 537 if (where) 538 fprintf(out_file, "(%s): %u ", name, where); 539} 540 541 542static void dump_metadata_block(FILE *out_file, struct journal_source *source, 543 journal_superblock_t *jsb, 544 unsigned int log_blocknr, 545 unsigned int fs_blocknr, 546 int blocksize, 547 tid_t transaction) 548{ 549 unsigned int got; 550 int retval; 551 char buf[8192]; 552 553 if (!(dump_all 554 || (fs_blocknr == block_to_dump) 555 || (fs_blocknr == inode_block_to_dump) 556 || (fs_blocknr == bitmap_to_dump))) 557 return; 558 559 fprintf(out_file, " FS block %u logged at ", fs_blocknr); 560 if (!dump_all) 561 fprintf(out_file, "sequence %u, ", transaction); 562 fprintf(out_file, "journal block %u\n", log_blocknr); 563 564 /* There are two major special cases to parse: 565 * 566 * If this block is a block 567 * bitmap block, we need to give it special treatment so that we 568 * can log any allocates and deallocates which affect the 569 * block_to_dump query block. 570 * 571 * If the block is an inode block for the inode being searched 572 * for, then we need to dump the contents of that inode 573 * structure symbolically. 574 */ 575 576 if (!(dump_contents && dump_all) 577 && fs_blocknr != block_to_dump 578 && fs_blocknr != bitmap_to_dump 579 && fs_blocknr != inode_block_to_dump) 580 return; 581 582 retval = read_journal_block("logdump", source, 583 blocksize * log_blocknr, 584 buf, blocksize, &got); 585 if (retval) 586 return; 587 588 if (fs_blocknr == bitmap_to_dump) { 589 struct ext2_super_block *super; 590 int offset; 591 592 super = current_fs->super; 593 offset = ((fs_blocknr - super->s_first_data_block) % 594 super->s_blocks_per_group); 595 596 fprintf(out_file, " (block bitmap for block %u: " 597 "block is %s)\n", 598 block_to_dump, 599 ext2fs_test_bit(offset, buf) ? "SET" : "CLEAR"); 600 } 601 602 if (fs_blocknr == inode_block_to_dump) { 603 struct ext2_inode *inode; 604 int first, prev, this, start_extent, i; 605 606 fprintf(out_file, " (inode block for inode %u):\n", 607 inode_to_dump); 608 609 inode = (struct ext2_inode *) (buf + inode_offset_to_dump); 610 internal_dump_inode(out_file, " ", inode_to_dump, inode, 0); 611 612 /* Dump out the direct/indirect blocks here: 613 * internal_dump_inode can only dump them from the main 614 * on-disk inode, not from the journaled copy of the 615 * inode. */ 616 617 fprintf (out_file, " Blocks: "); 618 first = prev = start_extent = -1; 619 620 for (i=0; i<EXT2_NDIR_BLOCKS; i++) { 621 this = inode->i_block[i]; 622 if (start_extent >= 0 && this == prev+1) { 623 prev = this; 624 continue; 625 } else { 626 show_extent(out_file, start_extent, i, first); 627 start_extent = i; 628 first = prev = this; 629 } 630 } 631 show_extent(out_file, start_extent, i, first); 632 show_indirect(out_file, "IND", inode->i_block[i++]); 633 show_indirect(out_file, "DIND", inode->i_block[i++]); 634 show_indirect(out_file, "TIND", inode->i_block[i++]); 635 636 fprintf(out_file, "\n"); 637 } 638 639 if (dump_contents) 640 do_hexdump(out_file, buf, blocksize); 641 642} 643 644static void do_hexdump (FILE *out_file, char *buf, int blocksize) 645{ 646 int i,j; 647 int *intp; 648 char *charp; 649 unsigned char c; 650 651 intp = (int *) buf; 652 charp = (char *) buf; 653 654 for (i=0; i<blocksize; i+=16) { 655 fprintf(out_file, " %04x: ", i); 656 for (j=0; j<16; j+=4) 657 fprintf(out_file, "%08x ", *intp++); 658 for (j=0; j<16; j++) { 659 c = *charp++; 660 if (c < ' ' || c >= 127) 661 c = '.'; 662 fprintf(out_file, "%c", c); 663 } 664 fprintf(out_file, "\n"); 665 } 666} 667 668