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