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