2 * logdump.c --- dump the contents of the journal out to a file
4 * Author: Stephen C. Tweedie, 2001 <sct@redhat.com>
5 * Copyright (C) 2001 Red Hat, Inc.
6 * Based on portions Copyright (C) 1994 Theodore Ts'o.
8 * This file may be redistributed under the terms of the GNU Public
22 #include <sys/types.h>
34 #include "blkid/blkid.h"
36 #include <uuid/uuid.h>
38 enum journal_location
{JOURNAL_IS_INTERNAL
, JOURNAL_IS_EXTERNAL
};
40 #define ANY_BLOCK ((blk64_t) -1)
42 static int dump_all
, dump_super
, 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
;
49 enum journal_location where
;
54 static void dump_journal(char *, FILE *, struct journal_source
*);
56 static void dump_descriptor_block(FILE *, struct journal_source
*,
57 char *, journal_superblock_t
*,
58 unsigned int *, int, tid_t
);
60 static void dump_revoke_block(FILE *, char *, journal_superblock_t
*,
61 unsigned int, int, tid_t
);
63 static void dump_metadata_block(FILE *, struct journal_source
*,
64 journal_superblock_t
*,
65 unsigned int, unsigned int, unsigned int,
68 static void do_hexdump (FILE *, char *, int);
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));
75 void do_logdump(int argc
, char **argv
, int sci_idx
EXT2FS_ATTR((unused
)),
76 void *infop
EXT2FS_ATTR((unused
)))
83 char *inode_spec
= NULL
;
84 char *journal_fn
= NULL
;
87 ext2_ino_t journal_inum
;
88 struct ext2_inode journal_inode
;
89 ext2_file_t journal_file
;
91 struct journal_source journal_source
;
92 struct ext2_super_block
*es
= NULL
;
94 journal_source
.where
= JOURNAL_IS_INTERNAL
;
95 journal_source
.fd
= 0;
96 journal_source
.file
= 0;
101 dump_descriptors
= 1;
102 block_to_dump
= ANY_BLOCK
;
104 inode_block_to_dump
= ANY_BLOCK
;
108 while ((c
= getopt (argc
, argv
, "ab:ci:f:OsS")) != EOF
) {
114 block_to_dump
= strtoul(optarg
, &tmp
, 0);
117 "Bad block number - %s", optarg
);
120 dump_descriptors
= 0;
130 dump_descriptors
= 0;
145 if (optind
!= argc
&& optind
!= argc
-1) {
150 es
= current_fs
->super
;
153 int inode_group
, group_offset
, inodes_per_block
;
155 if (check_fs_open(argv
[0]))
158 inode_to_dump
= string_to_inode(inode_spec
);
162 inode_group
= ((inode_to_dump
- 1)
163 / es
->s_inodes_per_group
);
164 group_offset
= ((inode_to_dump
- 1)
165 % es
->s_inodes_per_group
);
166 inodes_per_block
= (current_fs
->blocksize
167 / sizeof(struct ext2_inode
));
169 inode_block_to_dump
=
170 ext2fs_inode_table_loc(current_fs
, inode_group
) +
171 (group_offset
/ inodes_per_block
);
172 inode_offset_to_dump
= ((group_offset
% inodes_per_block
)
173 * sizeof(struct ext2_inode
));
174 printf("Inode %u is at group %u, block %llu, offset %u\n",
175 inode_to_dump
, inode_group
,
176 inode_block_to_dump
, inode_offset_to_dump
);
179 if (optind
== argc
) {
182 out_fn
= argv
[optind
];
183 out_file
= fopen(out_fn
, "w");
185 com_err(argv
[0], errno
, "while opening %s for logdump",
191 if (block_to_dump
!= ANY_BLOCK
&& current_fs
!= NULL
) {
192 group_to_dump
= ((block_to_dump
-
193 es
->s_first_data_block
)
194 / es
->s_blocks_per_group
);
195 bitmap_to_dump
= ext2fs_block_bitmap_loc(current_fs
, group_to_dump
);
198 if (!journal_fn
&& check_fs_open(argv
[0]))
202 /* Set up to read journal from a regular file somewhere */
203 journal_fd
= open(journal_fn
, O_RDONLY
, 0);
204 if (journal_fd
< 0) {
205 com_err(argv
[0], errno
, "while opening %s for logdump",
210 journal_source
.where
= JOURNAL_IS_EXTERNAL
;
211 journal_source
.fd
= journal_fd
;
212 } else if ((journal_inum
= es
->s_journal_inum
)) {
214 if (es
->s_jnl_backup_type
!= EXT3_JNL_BACKUP_BLOCKS
) {
216 "no journal backup in super block\n");
219 memset(&journal_inode
, 0, sizeof(struct ext2_inode
));
220 memcpy(&journal_inode
.i_block
[0], es
->s_jnl_blocks
,
222 journal_inode
.i_size_high
= es
->s_jnl_blocks
[15];
223 journal_inode
.i_size
= es
->s_jnl_blocks
[16];
224 journal_inode
.i_links_count
= 1;
225 journal_inode
.i_mode
= LINUX_S_IFREG
| 0600;
227 if (debugfs_read_inode(journal_inum
, &journal_inode
,
232 retval
= ext2fs_file_open2(current_fs
, journal_inum
,
233 &journal_inode
, 0, &journal_file
);
235 com_err(argv
[0], retval
, "while opening ext2 file");
238 journal_source
.where
= JOURNAL_IS_INTERNAL
;
239 journal_source
.file
= journal_file
;
243 uuid_unparse(es
->s_journal_uuid
, uuid
);
244 journal_fn
= blkid_get_devname(NULL
, "UUID", uuid
);
246 journal_fn
= blkid_devno_to_devname(es
->s_journal_dev
);
248 com_err(argv
[0], 0, "filesystem has no journal");
251 journal_fd
= open(journal_fn
, O_RDONLY
, 0);
252 if (journal_fd
< 0) {
253 com_err(argv
[0], errno
, "while opening %s for logdump",
258 fprintf(out_file
, "Using external journal found at %s\n",
261 journal_source
.where
= JOURNAL_IS_EXTERNAL
;
262 journal_source
.fd
= journal_fd
;
265 dump_journal(argv
[0], out_file
, &journal_source
);
267 if (journal_source
.where
== JOURNAL_IS_INTERNAL
)
268 ext2fs_file_close(journal_file
);
273 if (out_file
&& (out_file
!= stdout
))
279 fprintf(stderr
, "%s: Usage: logdump [-acsOS] [-b<block>] [-i<filespec>]\n\t"
280 "[-f<journal_file>] [output_file]\n", argv
[0]);
284 static int read_journal_block(const char *cmd
, struct journal_source
*source
,
285 ext2_loff_t offset
, char *buf
, unsigned int size
)
290 if (source
->where
== JOURNAL_IS_EXTERNAL
) {
291 if (lseek(source
->fd
, offset
, SEEK_SET
) < 0) {
295 retval
= read(source
->fd
, buf
, size
);
303 retval
= ext2fs_file_llseek(source
->file
, offset
,
304 EXT2_SEEK_SET
, NULL
);
307 com_err(cmd
, retval
, "while seeking in reading journal");
310 retval
= ext2fs_file_read(source
->file
, buf
, size
, &got
);
313 com_err(cmd
, retval
, "while reading journal");
318 com_err(cmd
, 0, "short read (read %u, expected %u) "
319 "while reading journal", got
, size
);
325 static const char *type_to_name(int btype
)
328 case JFS_DESCRIPTOR_BLOCK
:
329 return "descriptor block";
330 case JFS_COMMIT_BLOCK
:
331 return "commit block";
332 case JFS_SUPERBLOCK_V1
:
333 return "V1 superblock";
334 case JFS_SUPERBLOCK_V2
:
335 return "V2 superblock";
336 case JFS_REVOKE_BLOCK
:
337 return "revoke table";
339 return "unrecognised type";
343 static void dump_journal(char *cmdname
, FILE *out_file
,
344 struct journal_source
*source
)
346 struct ext2_super_block
*sb
;
347 char jsb_buffer
[1024];
349 journal_superblock_t
*jsb
;
350 unsigned int blocksize
= 1024;
352 __u32 magic
, sequence
, blocktype
;
353 journal_header_t
*header
;
355 unsigned int blocknr
= 0;
357 /* First, check to see if there's an ext2 superblock header */
358 retval
= read_journal_block(cmdname
, source
, 0, buf
, 2048);
362 jsb
= (journal_superblock_t
*) buf
;
363 sb
= (struct ext2_super_block
*) (buf
+1024);
364 #ifdef WORDS_BIGENDIAN
365 if (sb
->s_magic
== ext2fs_swab16(EXT2_SUPER_MAGIC
))
366 ext2fs_swap_super(sb
);
369 if ((be32_to_cpu(jsb
->s_header
.h_magic
) != JFS_MAGIC_NUMBER
) &&
370 (sb
->s_magic
== EXT2_SUPER_MAGIC
) &&
371 ext2fs_has_feature_journal_dev(sb
)) {
372 blocksize
= EXT2_BLOCK_SIZE(sb
);
373 blocknr
= (blocksize
== 1024) ? 2 : 1;
374 uuid_unparse(sb
->s_uuid
, jsb_buffer
);
375 fprintf(out_file
, "Ext2 superblock header found.\n");
377 fprintf(out_file
, "\tuuid=%s\n", jsb_buffer
);
378 fprintf(out_file
, "\tblocksize=%d\n", blocksize
);
379 fprintf(out_file
, "\tjournal data size %lu\n",
380 (unsigned long) ext2fs_blocks_count(sb
));
384 /* Next, read the journal superblock */
385 retval
= read_journal_block(cmdname
, source
,
386 ((ext2_loff_t
) blocknr
) * blocksize
,
392 e2p_list_journal_super(out_file
, jsb_buffer
,
393 current_fs
->blocksize
, 0);
394 fputc('\n', out_file
);
397 jsb
= (journal_superblock_t
*) jsb_buffer
;
398 if (be32_to_cpu(jsb
->s_header
.h_magic
) != JFS_MAGIC_NUMBER
) {
400 "Journal superblock magic number invalid!\n");
403 blocksize
= be32_to_cpu(jsb
->s_blocksize
);
404 transaction
= be32_to_cpu(jsb
->s_sequence
);
405 blocknr
= be32_to_cpu(jsb
->s_start
);
407 fprintf(out_file
, "Journal starts at block %u, transaction %u\n",
408 blocknr
, transaction
);
411 /* Empty journal, nothing to do. */
419 retval
= read_journal_block(cmdname
, source
,
420 ((ext2_loff_t
) blocknr
) * blocksize
,
425 header
= (journal_header_t
*) buf
;
427 magic
= be32_to_cpu(header
->h_magic
);
428 sequence
= be32_to_cpu(header
->h_sequence
);
429 blocktype
= be32_to_cpu(header
->h_blocktype
);
431 if (magic
!= JFS_MAGIC_NUMBER
) {
432 fprintf (out_file
, "No magic number at block %u: "
433 "end of journal.\n", blocknr
);
437 if (sequence
!= transaction
) {
438 fprintf (out_file
, "Found sequence %u (not %u) at "
439 "block %u: end of journal.\n",
440 sequence
, transaction
, blocknr
);
445 if (dump_descriptors
) {
446 fprintf (out_file
, "Found expected sequence %u, "
447 "type %u (%s) at block %u\n",
449 type_to_name(blocktype
), blocknr
);
453 case JFS_DESCRIPTOR_BLOCK
:
454 dump_descriptor_block(out_file
, source
, buf
, jsb
,
459 case JFS_COMMIT_BLOCK
:
465 case JFS_REVOKE_BLOCK
:
466 dump_revoke_block(out_file
, buf
, jsb
,
474 fprintf (out_file
, "Unexpected block type %u at "
475 "block %u.\n", blocktype
, blocknr
);
481 static inline size_t journal_super_tag_bytes(journal_superblock_t
*jsb
)
485 if (JSB_HAS_INCOMPAT_FEATURE(jsb
, JFS_FEATURE_INCOMPAT_CSUM_V3
))
486 return sizeof(journal_block_tag3_t
);
488 sz
= sizeof(journal_block_tag_t
);
490 if (JSB_HAS_INCOMPAT_FEATURE(jsb
, JFS_FEATURE_INCOMPAT_CSUM_V2
))
493 if (JSB_HAS_INCOMPAT_FEATURE(jsb
, JFS_FEATURE_INCOMPAT_64BIT
))
496 return sz
- sizeof(__u32
);
499 static void dump_descriptor_block(FILE *out_file
,
500 struct journal_source
*source
,
502 journal_superblock_t
*jsb
,
503 unsigned int *blockp
, int blocksize
,
506 int offset
, tag_size
, csum_size
= 0;
508 journal_block_tag_t
*tag
;
509 unsigned int blocknr
;
513 tag_size
= journal_super_tag_bytes(jsb
);
514 offset
= sizeof(journal_header_t
);
517 if (JSB_HAS_INCOMPAT_FEATURE(jsb
, JFS_FEATURE_INCOMPAT_CSUM_V3
) ||
518 JSB_HAS_INCOMPAT_FEATURE(jsb
, JFS_FEATURE_INCOMPAT_CSUM_V2
))
519 csum_size
= sizeof(struct journal_block_tail
);
522 fprintf(out_file
, "Dumping descriptor block, sequence %u, at "
523 "block %u:\n", transaction
, blocknr
);
529 /* Work out the location of the current tag, and skip to
532 tag
= (journal_block_tag_t
*) tagp
;
535 /* ... and if we have gone too far, then we've reached the
536 end of this block. */
537 if (offset
> blocksize
- csum_size
)
540 tag_block
= be32_to_cpu(tag
->t_blocknr
);
541 tag_flags
= be16_to_cpu(tag
->t_flags
);
543 if (!(tag_flags
& JFS_FLAG_SAME_UUID
))
546 dump_metadata_block(out_file
, source
, jsb
,
547 blocknr
, tag_block
, tag_flags
, blocksize
,
553 } while (!(tag_flags
& JFS_FLAG_LAST_TAG
));
559 static void dump_revoke_block(FILE *out_file
, char *buf
,
560 journal_superblock_t
*jsb
EXT2FS_ATTR((unused
)),
561 unsigned int blocknr
,
562 int blocksize
EXT2FS_ATTR((unused
)),
566 journal_revoke_header_t
*header
;
567 unsigned long long rblock
;
568 int tag_size
= sizeof(__u32
);
571 fprintf(out_file
, "Dumping revoke block, sequence %u, at "
572 "block %u:\n", transaction
, blocknr
);
574 if (be32_to_cpu(jsb
->s_feature_incompat
) & JFS_FEATURE_INCOMPAT_64BIT
)
575 tag_size
= sizeof(__u64
);
577 header
= (journal_revoke_header_t
*) buf
;
578 offset
= sizeof(journal_revoke_header_t
);
579 max
= be32_to_cpu(header
->r_count
);
581 while (offset
< max
) {
582 if (tag_size
== sizeof(__u32
)) {
583 __u32
*entry
= (__u32
*) (buf
+ offset
);
584 rblock
= be32_to_cpu(*entry
);
586 __u64
*entry
= (__u64
*) (buf
+ offset
);
587 rblock
= ext2fs_be64_to_cpu(*entry
);
589 if (dump_all
|| rblock
== block_to_dump
) {
590 fprintf(out_file
, " Revoke FS block %llu", rblock
);
592 fprintf(out_file
, "\n");
594 fprintf(out_file
," at block %u, sequence %u\n",
595 blocknr
, transaction
);
602 static void show_extent(FILE *out_file
, int start_extent
, int end_extent
,
605 if (start_extent
>= 0 && first_block
!= 0)
606 fprintf(out_file
, "(%d+%u): %u ",
607 start_extent
, end_extent
-start_extent
, first_block
);
610 static void show_indirect(FILE *out_file
, const char *name
, __u32 where
)
613 fprintf(out_file
, "(%s): %u ", name
, where
);
617 static void dump_metadata_block(FILE *out_file
, struct journal_source
*source
,
618 journal_superblock_t
*jsb
EXT2FS_ATTR((unused
)),
619 unsigned int log_blocknr
,
620 unsigned int fs_blocknr
,
621 unsigned int log_tag_flags
,
629 || (fs_blocknr
== block_to_dump
)
630 || (fs_blocknr
== inode_block_to_dump
)
631 || (fs_blocknr
== bitmap_to_dump
)))
634 fprintf(out_file
, " FS block %u logged at ", fs_blocknr
);
636 fprintf(out_file
, "sequence %u, ", transaction
);
637 fprintf(out_file
, "journal block %u (flags 0x%x)\n", log_blocknr
,
640 /* There are two major special cases to parse:
642 * If this block is a block
643 * bitmap block, we need to give it special treatment so that we
644 * can log any allocates and deallocates which affect the
645 * block_to_dump query block.
647 * If the block is an inode block for the inode being searched
648 * for, then we need to dump the contents of that inode
649 * structure symbolically.
652 if (!(dump_contents
&& dump_all
)
653 && fs_blocknr
!= block_to_dump
654 && fs_blocknr
!= bitmap_to_dump
655 && fs_blocknr
!= inode_block_to_dump
)
658 retval
= read_journal_block("logdump", source
,
659 ((ext2_loff_t
) log_blocknr
) * blocksize
,
664 if (fs_blocknr
== bitmap_to_dump
) {
665 struct ext2_super_block
*super
;
668 super
= current_fs
->super
;
669 offset
= ((block_to_dump
- super
->s_first_data_block
) %
670 super
->s_blocks_per_group
);
672 fprintf(out_file
, " (block bitmap for block %llu: "
675 ext2fs_test_bit(offset
, buf
) ? "SET" : "CLEAR");
678 if (fs_blocknr
== inode_block_to_dump
) {
679 struct ext2_inode
*inode
;
680 int first
, prev
, this, start_extent
, i
;
682 fprintf(out_file
, " (inode block for inode %u):\n",
685 inode
= (struct ext2_inode
*) (buf
+ inode_offset_to_dump
);
686 internal_dump_inode(out_file
, " ", inode_to_dump
, inode
, 0);
688 /* Dump out the direct/indirect blocks here:
689 * internal_dump_inode can only dump them from the main
690 * on-disk inode, not from the journaled copy of the
693 fprintf (out_file
, " Blocks: ");
694 first
= prev
= start_extent
= -1;
696 for (i
=0; i
<EXT2_NDIR_BLOCKS
; i
++) {
697 this = inode
->i_block
[i
];
698 if (start_extent
>= 0 && this == prev
+1) {
702 show_extent(out_file
, start_extent
, i
, first
);
707 show_extent(out_file
, start_extent
, i
, first
);
708 show_indirect(out_file
, "IND", inode
->i_block
[i
++]);
709 show_indirect(out_file
, "DIND", inode
->i_block
[i
++]);
710 show_indirect(out_file
, "TIND", inode
->i_block
[i
++]);
712 fprintf(out_file
, "\n");
716 do_hexdump(out_file
, buf
, blocksize
);
720 static void do_hexdump (FILE *out_file
, char *buf
, int blocksize
)
728 charp
= (char *) buf
;
730 for (i
=0; i
<blocksize
; i
+=16) {
731 fprintf(out_file
, " %04x: ", i
);
732 for (j
=0; j
<16; j
+=4)
733 fprintf(out_file
, "%08x ", *intp
++);
734 for (j
=0; j
<16; j
++) {
736 if (c
< ' ' || c
>= 127)
738 fprintf(out_file
, "%c", c
);
740 fprintf(out_file
, "\n");