Whamcloud - gitweb
Many files:
authorTheodore Ts'o <tytso@mit.edu>
Thu, 29 Mar 2001 20:49:58 +0000 (20:49 +0000)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 29 Mar 2001 20:49:58 +0000 (20:49 +0000)
  debugfs.c (dump_blocks, dump_inode, internal_dump_inode): Add
   internal_dump_inode() interface for the logdump command.
  logdump.c: Imported code from Stephen Tweedie to dump the ext3 journal.

debugfs/ChangeLog
debugfs/Makefile.in
debugfs/debug_cmds.ct
debugfs/debugfs.c
debugfs/debugfs.h
debugfs/jfs_user.h [new file with mode: 0644]
debugfs/logdump.c [new file with mode: 0644]

index 6101d42..e0bb10c 100644 (file)
@@ -1,3 +1,11 @@
+2001-03-29  Theodore Tso  <tytso@valinux.com>
+
+       * debugfs.c (dump_blocks, dump_inode, internal_dump_inode): Add
+               internal_dump_inode() interface for the logdump command.
+
+       * logdump.c: Imported code from Stephen Tweedie to dump the ext3
+               journal.
+
 2001-03-18  Theodore Tso  <tytso@valinux.com>
 
        * debugfs.c (do_write, do_mknod): Remove extra (useless) call to
index 5634ddf..60c0076 100644 (file)
@@ -17,11 +17,11 @@ MANPAGES=   debugfs.8
 MK_CMDS=       _SS_DIR_OVERRIDE=../lib/ss ../lib/ss/mk_cmds
 
 DEBUG_OBJS= debug_cmds.o debugfs.o util.o ncheck.o icheck.o ls.o \
-       lsdel.o dump.o setsuper.o
+       lsdel.o dump.o setsuper.o logdump.o
 
 SRCS= debug_cmds.c $(srcdir)/debugfs.c $(srcdir)/util.c $(srcdir)/ls.c \
        $(srcdir)/ncheck.c $(srcdir)/icheck.c $(srcdir)/lsdel.c \
-       $(srcdir)/dump.c $(srcdir)/setsuper.c
+       $(srcdir)/dump.c $(srcdir)/setsuper.c ${srcdir}/logdump.c
 
 LIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSS) $(LIBCOM_ERR)  $(LIBUUID)
 DEPLIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSS) $(LIBCOM_ERR)  $(DEPLIBUUID)
@@ -111,3 +111,9 @@ setsuper.o: $(srcdir)/setsuper.c $(srcdir)/debugfs.h \
  $(top_srcdir)/include/linux/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
  $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
  $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h
+logdump.o: $(srcdir)/logdump.c $(srcdir)/debugfs.h \
+ $(top_srcdir)/include/linux/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \
+ $(srcdir)/jfs_user.h $(top_srcdir)/include/linux/jfs.h \
+ $(top_srcdir)/include/linux/jfs_compat.h $(top_srcdir)/include/linux/list.h
index 0d142ba..4cd034f 100644 (file)
@@ -121,5 +121,8 @@ request do_rdump, "Recursively dump a directory to the native filesystem",
 request do_set_super, "Set superblock value",
        set_super_value, ssv;
 
+request do_logdump, "Dump the contents of the journal",
+       logdump;
+
 end;
 
index 9ede1b4..21f2b09 100644 (file)
@@ -385,11 +385,11 @@ static int list_blocks_proc(ext2_filsys fs, blk_t *blocknr, int blockcnt,
 }
 
 
-static void dump_blocks(FILE *f, ext2_ino_t inode)
+static void dump_blocks(FILE *f, char *prefix, ext2_ino_t inode)
 {
        struct list_blocks_struct lb;
 
-       fprintf(f, "BLOCKS:\n");
+       fprintf(f, "%sBLOCKS:\n%s", prefix, prefix);
        lb.total = 0;
        lb.first_block = 0;
        lb.f = f;
@@ -398,82 +398,93 @@ static void dump_blocks(FILE *f, ext2_ino_t inode)
                             list_blocks_proc, (void *)&lb);
        finish_range(&lb);
        if (lb.total)
-               fprintf(f, "\nTOTAL: %d\n", lb.total);
+               fprintf(f, "\n%sTOTAL: %d\n", prefix, lb.total);
        fprintf(f,"\n");
 }
 
 
-static void dump_inode(ext2_ino_t inode_num, struct ext2_inode inode)
+void internal_dump_inode(FILE *out, char *prefix,
+                        ext2_ino_t inode_num, struct ext2_inode *inode,
+                        int do_dump_blocks)
 {
        const char *i_type;
-       FILE    *out;
        char frag, fsize;
        int os = current_fs->super->s_creator_os;
        
-       out = open_pager();
-       if (LINUX_S_ISDIR(inode.i_mode)) i_type = "directory";
-       else if (LINUX_S_ISREG(inode.i_mode)) i_type = "regular";
-       else if (LINUX_S_ISLNK(inode.i_mode)) i_type = "symlink";
-       else if (LINUX_S_ISBLK(inode.i_mode)) i_type = "block special";
-       else if (LINUX_S_ISCHR(inode.i_mode)) i_type = "character special";
-       else if (LINUX_S_ISFIFO(inode.i_mode)) i_type = "FIFO";
-       else if (LINUX_S_ISSOCK(inode.i_mode)) i_type = "socket";
+       if (LINUX_S_ISDIR(inode->i_mode)) i_type = "directory";
+       else if (LINUX_S_ISREG(inode->i_mode)) i_type = "regular";
+       else if (LINUX_S_ISLNK(inode->i_mode)) i_type = "symlink";
+       else if (LINUX_S_ISBLK(inode->i_mode)) i_type = "block special";
+       else if (LINUX_S_ISCHR(inode->i_mode)) i_type = "character special";
+       else if (LINUX_S_ISFIFO(inode->i_mode)) i_type = "FIFO";
+       else if (LINUX_S_ISSOCK(inode->i_mode)) i_type = "socket";
        else i_type = "bad type";
-       fprintf(out, "Inode: %u   Type: %s    ", inode_num, i_type);
-       fprintf(out, "Mode:  %04o   Flags: 0x%x   Generation: %u\n",
-               inode.i_mode & 0777, inode.i_flags, inode.i_generation);
-       fprintf(out, "User: %5d   Group: %5d   Size: ",
-               inode.i_uid, inode.i_gid);
-       if (LINUX_S_ISDIR(inode.i_mode))
-               fprintf(out, "%d\n", inode.i_size);
+       fprintf(out, "%sInode: %u   Type: %s    ", prefix, inode_num, i_type);
+       fprintf(out, "%sMode:  %04o   Flags: 0x%x   Generation: %u\n",
+               prefix, 
+               inode->i_mode & 0777, inode->i_flags, inode->i_generation);
+       fprintf(out, "%sUser: %5d   Group: %5d   Size: ",
+               prefix, inode->i_uid, inode->i_gid);
+       if (LINUX_S_ISDIR(inode->i_mode))
+               fprintf(out, "%d\n", inode->i_size);
        else {
-               __u64 i_size = (inode.i_size |
-                               ((unsigned long long)inode.i_size_high << 32));
+               __u64 i_size = (inode->i_size |
+                               ((unsigned long long)inode->i_size_high << 32));
                
                fprintf(out, "%lld\n", i_size);
        }
        if (current_fs->super->s_creator_os == EXT2_OS_HURD)
                fprintf(out,
-                       "File ACL: %d    Directory ACL: %d Translator: %d\n",
-                       inode.i_file_acl, LINUX_S_ISDIR(inode.i_mode) ? inode.i_dir_acl : 0,
-                       inode.osd1.hurd1.h_i_translator);
+                       "%sFile ACL: %d    Directory ACL: %d Translator: %d\n",
+                       prefix,
+                       inode->i_file_acl, LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0,
+                       inode->osd1.hurd1.h_i_translator);
        else
-               fprintf(out, "File ACL: %d    Directory ACL: %d\n",
-                       inode.i_file_acl, LINUX_S_ISDIR(inode.i_mode) ? inode.i_dir_acl : 0);
-       fprintf(out, "Links: %d   Blockcount: %d\n", inode.i_links_count,
-               inode.i_blocks);
+               fprintf(out, "%sFile ACL: %d    Directory ACL: %d\n",
+                       prefix,
+                       inode->i_file_acl, LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0);
+       fprintf(out, "%sLinks: %d   Blockcount: %d\n", 
+               prefix, inode->i_links_count, inode->i_blocks);
        switch (os) {
            case EXT2_OS_LINUX:
-               frag = inode.osd2.linux2.l_i_frag;
-               fsize = inode.osd2.linux2.l_i_fsize;
+               frag = inode->osd2.linux2.l_i_frag;
+               fsize = inode->osd2.linux2.l_i_fsize;
                break;
            case EXT2_OS_HURD:
-               frag = inode.osd2.hurd2.h_i_frag;
-               fsize = inode.osd2.hurd2.h_i_fsize;
+               frag = inode->osd2.hurd2.h_i_frag;
+               fsize = inode->osd2.hurd2.h_i_fsize;
                break;
            case EXT2_OS_MASIX:
-               frag = inode.osd2.masix2.m_i_frag;
-               fsize = inode.osd2.masix2.m_i_fsize;
+               frag = inode->osd2.masix2.m_i_frag;
+               fsize = inode->osd2.masix2.m_i_fsize;
                break;
            default:
                frag = fsize = 0;
        }
-       fprintf(out, "Fragment:  Address: %d    Number: %d    Size: %d\n",
-               inode.i_faddr, frag, fsize);
-       fprintf(out, "ctime: 0x%08x -- %s", inode.i_ctime,
-               time_to_string(inode.i_ctime));
-       fprintf(out, "atime: 0x%08x -- %s", inode.i_atime,
-               time_to_string(inode.i_atime));
-       fprintf(out, "mtime: 0x%08x -- %s", inode.i_mtime,
-               time_to_string(inode.i_mtime));
-       if (inode.i_dtime) 
-         fprintf(out, "dtime: 0x%08x -- %s", inode.i_dtime,
-                 time_to_string(inode.i_dtime));
-       if (LINUX_S_ISLNK(inode.i_mode) && inode.i_blocks == 0)
-               fprintf(out, "Fast_link_dest: %.*s\n",
-                       (int) inode.i_size, (char *)inode.i_block);
-       else
-               dump_blocks(out, inode_num);
+       fprintf(out, "%sFragment:  Address: %d    Number: %d    Size: %d\n",
+               prefix, inode->i_faddr, frag, fsize);
+       fprintf(out, "%sctime: 0x%08x -- %s", prefix, inode->i_ctime,
+               time_to_string(inode->i_ctime));
+       fprintf(out, "%satime: 0x%08x -- %s", prefix, inode->i_atime,
+               time_to_string(inode->i_atime));
+       fprintf(out, "%smtime: 0x%08x -- %s", prefix, inode->i_mtime,
+               time_to_string(inode->i_mtime));
+       if (inode->i_dtime) 
+         fprintf(out, "%sdtime: 0x%08x -- %s", prefix, inode->i_dtime,
+                 time_to_string(inode->i_dtime));
+       if (LINUX_S_ISLNK(inode->i_mode) && inode->i_blocks == 0)
+               fprintf(out, "%sFast_link_dest: %.*s\n", prefix,
+                       (int) inode->i_size, (char *)inode->i_block);
+       else if (do_dump_blocks)
+               dump_blocks(out, prefix, inode_num);
+}
+
+static void dump_inode(ext2_ino_t inode_num, struct ext2_inode inode)
+{
+       FILE    *out;
+       
+       out = open_pager();
+       internal_dump_inode(out, "", inode_num, &inode, 1);
        close_pager(out);
 }
 
index 0fc504e..be0ad3a 100644 (file)
@@ -44,6 +44,9 @@ extern void do_ncheck(int argc, char **argv);
 extern void do_set_super(int argc, char **);
 
 /* debugfs.c */
+extern void internal_dump_inode(FILE *, char *, ext2_ino_t, 
+                               struct ext2_inode *, int);
+
 extern void do_dirty_filesys(int argc, char **argv);
 extern void do_open_filesys(int argc, char **argv);
 extern void do_close_filesys(int argc, char **argv);
@@ -77,3 +80,4 @@ extern void do_rmdir(int argc, char **argv);
 extern void do_show_debugfs_params(int argc, char **argv);
 extern void do_expand_dir(int argc, char **argv);
 extern void do_features(int argc, char *argv[]);
+
diff --git a/debugfs/jfs_user.h b/debugfs/jfs_user.h
new file mode 100644 (file)
index 0000000..17a82c7
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _JFS_USER_H
+#define _JFS_USER_H
+
+typedef unsigned short kdev_t;
+
+#include <linux/jfs.h>
+
+
+#endif /* _JFS_USER_H */
diff --git a/debugfs/logdump.c b/debugfs/logdump.c
new file mode 100644 (file)
index 0000000..6c9fda4
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ * logdump.c --- dump the contents of the journal out to a file
+ * 
+ * Authro: Stephen C. Tweedie, 2001  <sct@redhat.com>
+ * Copyright (C) 2001 Red Hat, Inc.
+ * Based on portions  Copyright (C) 1994 Theodore Ts'o.  
+ *
+ * This file may be redistributed under the terms of the GNU Public 
+ * License.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utime.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else 
+extern int optind;
+extern char *optarg;
+#endif
+#ifdef HAVE_OPTRESET
+extern int optreset;           /* defined by BSD, but not others */
+#endif
+
+#include "debugfs.h"
+#include "jfs_user.h"
+
+enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL};
+
+int            dump_all, dump_contents, dump_descriptors;
+unsigned int   block_to_dump, group_to_dump, bitmap_to_dump;
+unsigned int   inode_block_to_dump, inode_offset_to_dump, bitmap_to_dump;
+ext2_ino_t     inode_to_dump;
+
+struct journal_source 
+{
+       enum journal_location where;
+       int fd;
+       ext2_file_t file;
+};
+
+static void dump_journal(char *, FILE *, struct journal_source *);
+
+static void dump_descriptor_block(FILE *, struct journal_source *,
+                                 char *, journal_superblock_t *,
+                                 unsigned int *, int, tid_t);
+
+static void dump_revoke_block(FILE *, char *, journal_superblock_t *,
+                                 unsigned int, int, tid_t);
+
+static void dump_metadata_block(FILE *, struct journal_source *,
+                               journal_superblock_t*, 
+                               unsigned int, unsigned int, int, tid_t);
+
+static void do_hexdump (FILE *, char *, int);
+
+#define WRAP(jsb, blocknr)                                     \
+       if (blocknr >= be32_to_cpu((jsb)->s_maxlen))            \
+               blocknr -= (be32_to_cpu((jsb)->s_maxlen) -      \
+                           be32_to_cpu((jsb)->s_first));
+
+
+void do_logdump(int argc, char **argv)
+{
+       ext2_ino_t      inode;
+       int             c;
+       int             fd;
+       int             retval;
+       char            *out_fn;
+       FILE            *out_file;
+       
+       char            *inode_spec = NULL;
+       char            *journal_fn = NULL;
+       int             journal_fd = 0;
+       ext2_ino_t      journal_inum;
+       struct ext2_inode journal_inode;
+       ext2_file_t     journal_file;
+       
+       char            *tmp;
+       
+       const char      *logdump_usage = ("Usage: logdump "
+                                         "[-ac] [-b<block>] [-i<inode>] "
+                                         "[-f<journal_file>] [output_file]");
+       
+       struct journal_source journal_source = {};
+
+       optind = 0;
+#ifdef HAVE_OPTRESET
+       optreset = 1;           /* Makes BSD getopt happy */
+#endif
+       dump_all = 0;
+       dump_contents = 0;
+       dump_descriptors = 1;
+       block_to_dump = -1;
+       bitmap_to_dump = -1;
+       inode_block_to_dump = -1;
+       inode_to_dump = -1;
+       
+       while ((c = getopt (argc, argv, "ab:ci:f:")) != EOF) {
+               switch (c) {
+               case 'a':
+                       dump_all++;
+                       break;
+               case 'b':
+                       block_to_dump = strtoul(optarg, &tmp, 0);
+                       if (*tmp) {
+                               com_err(argv[0], 0,
+                                       "Bad block number - %s", optarg);
+                               return;
+                       }
+                       dump_descriptors = 0;
+                       break;
+               case 'c':
+                       dump_contents++;
+                       break;
+               case 'f':
+                       journal_fn = optarg;
+                       break;
+               case 'i':
+                       inode_spec = optarg;
+                       dump_descriptors = 0;
+                       break;
+               default:
+                       com_err(argv[0], 0, logdump_usage);
+                       return;
+               }
+       }
+       if (optind != argc && optind != argc-1) {
+               com_err(argv[0], 0, logdump_usage);
+               return;
+       }
+
+       if (inode_spec) {
+               int inode_group, group_offset, inodes_per_block;
+               
+               if (check_fs_open(argv[0]))
+                       return;
+
+               inode_to_dump = string_to_inode(inode_spec);
+               if (!inode_to_dump)
+                       return;
+
+               inode_group = ((inode_to_dump - 1)
+                              / current_fs->super->s_inodes_per_group);
+               group_offset = ((inode_to_dump - 1)
+                               % current_fs->super->s_inodes_per_group);
+               inodes_per_block = (current_fs->blocksize 
+                                   / sizeof(struct ext2_inode));
+               
+               inode_block_to_dump = 
+                       current_fs->group_desc[inode_group].bg_inode_table + 
+                       (group_offset / inodes_per_block);
+               inode_offset_to_dump = ((group_offset % inodes_per_block)
+                                       * sizeof(struct ext2_inode));
+               printf("Inode %u is at group %u, block %u, offset %u\n",
+                      inode_to_dump, inode_group,
+                      inode_block_to_dump, inode_offset_to_dump);
+       }
+
+       if (optind == argc) {
+               out_file = stdout;
+       } else {
+               out_fn = argv[optind];
+               out_file = fopen(out_fn, "w");
+               if (!out_file < 0) {
+                       com_err(argv[0], errno, "while opening %s for logdump",
+                               out_fn);
+                       return;
+               }
+       }
+
+       if (block_to_dump != -1 && current_fs != NULL) {
+               group_to_dump = ((block_to_dump - 
+                                 current_fs->super->s_first_data_block)
+                                / current_fs->super->s_blocks_per_group);
+               bitmap_to_dump = current_fs->group_desc[group_to_dump].bg_block_bitmap;
+       }
+                                
+       if (journal_fn) {
+
+               /* Set up to read journal from a regular file somewhere */
+               journal_fd = open(journal_fn, O_RDONLY, 0);
+               if (journal_fd < 0) {
+                       com_err(argv[0], errno, "while opening %s for logdump",
+                               journal_fn);
+                       return;
+               }
+               
+               journal_source.where = JOURNAL_IS_EXTERNAL;
+               journal_source.fd = journal_fd;
+
+       } else {
+
+               /* Set up to read journal from the open filesystem */
+               if (check_fs_open(argv[0]))
+                       return;
+               journal_inum = current_fs->super->s_journal_inum;
+               if (!journal_inum) {
+                       com_err(argv[0], 0, "filesystem has no journal");
+                       return;
+               }
+
+               retval = ext2fs_read_inode(current_fs, journal_inum, 
+                                          &journal_inode);
+               if (retval) {
+                       com_err(argv[0], retval,
+                               "while reading inode %u", journal_inum);
+                       return;
+               }
+               
+               retval = ext2fs_file_open(current_fs, journal_inum,
+                                         0, &journal_file);
+               if (retval) {
+                       com_err(argv[0], retval, "while opening ext2 file");
+                       return;
+               }
+               
+               journal_source.where = JOURNAL_IS_INTERNAL;
+               journal_source.file = journal_file;
+       }
+
+       dump_journal(argv[0], out_file, &journal_source);
+
+       if (journal_source.where == JOURNAL_IS_INTERNAL)
+               ext2fs_file_close(journal_file);
+       else
+               close(journal_fd);
+
+       if (out_file != stdout)
+               fclose(out_file);
+
+       return;
+}
+
+
+int read_journal_block(char *cmd, struct journal_source *source, off_t offset,
+                      char *buf, int size, int *got)
+{
+       int retval;
+       
+       if (source->where == JOURNAL_IS_EXTERNAL) {
+               retval = pread(source->fd, buf, size, offset);
+               if (retval >= 0) {
+                       *got = retval;
+                       retval = 0;
+               }
+               retval = errno;
+       } else {
+               retval = ext2fs_file_lseek(source->file, offset, 
+                                          EXT2_SEEK_SET, NULL);
+               if (retval) {
+                       com_err(cmd, retval, "while seeking in reading journal");
+                       return retval;
+               }
+               
+               retval = ext2fs_file_read(source->file, buf, size, got);
+       }
+       
+       if (retval)
+               com_err(cmd, retval, "while while reading journal");
+       else if (*got != size) {
+               com_err(cmd, 0, "short read (read %d, expected %d) while while reading journal", *got, size);
+               retval = -1;
+       }
+       
+       return retval;
+}
+
+static char *type_to_name(int btype)
+{
+       switch (btype) {
+       case JFS_DESCRIPTOR_BLOCK:
+               return "descriptor block";
+       case JFS_COMMIT_BLOCK:
+               return "commit block";
+       case JFS_SUPERBLOCK_V1:
+               return "V1 superblock";
+       case JFS_SUPERBLOCK_V2:
+               return "V2 superblock";
+       case JFS_REVOKE_BLOCK:
+               return "revoke table";
+       default:
+       }
+       return "unrecognised type";
+}
+
+
+static void dump_journal(char *cmdname, FILE *out_file, 
+                        struct journal_source *source)
+{
+       char                    jsb_buffer[1024];
+       char                    buf[8192];
+       journal_superblock_t    *jsb;
+       int                     blocksize;
+       int                     got;
+       int                     retval;
+       __u32                   magic, sequence, blocktype;
+       journal_header_t        *header;
+       
+       tid_t                   transaction;
+       unsigned int            blocknr;
+       
+       /* First: locate the journal superblock */
+
+       retval = read_journal_block(cmdname, source, 0, 
+                                   jsb_buffer, 1024, &got);
+       if (retval)
+               return;
+       
+       jsb = (journal_superblock_t *) jsb_buffer;
+       blocksize = be32_to_cpu(jsb->s_blocksize);
+       transaction = be32_to_cpu(jsb->s_sequence);
+       blocknr = be32_to_cpu(jsb->s_start);
+
+       fprintf(out_file, "Journal starts at block %u, transaction %u\n",
+               blocknr, transaction);
+
+       if (!blocknr)
+               /* Empty journal, nothing to do. */
+               return;
+               
+       while (1) {
+               retval = read_journal_block(cmdname, source, 
+                                           blocknr*blocksize, buf,
+                                           blocksize, &got);
+               if (retval || got != blocksize)
+                       return;
+       
+               header = (journal_header_t *) buf;
+
+               magic = be32_to_cpu(header->h_magic);
+               sequence = be32_to_cpu(header->h_sequence);
+               blocktype = be32_to_cpu(header->h_blocktype);
+               
+               if (magic != JFS_MAGIC_NUMBER) {
+                       fprintf (out_file, "No magic number at block %u: "
+                                "end of journal.\n", blocknr);
+                       return;
+               }
+               
+               if (sequence != transaction) {
+                       fprintf (out_file, "Found sequence %u (not %u) at "
+                                "block %u: end of journal.\n", 
+                                sequence, transaction, blocknr);
+                       return;
+               }
+
+               if (dump_descriptors) {
+                       fprintf (out_file, "Found expected sequence %u, "
+                                "type %u (%s) at block %u\n",
+                                sequence, blocktype, 
+                                type_to_name(blocktype), blocknr);
+               }
+               
+               switch (blocktype) {
+               case JFS_DESCRIPTOR_BLOCK:
+                       dump_descriptor_block(out_file, source, buf, jsb, 
+                                             &blocknr, blocksize,
+                                             transaction);
+                       continue;
+
+               case JFS_COMMIT_BLOCK:
+                       transaction++;
+                       blocknr++;
+                       WRAP(jsb, blocknr);
+                       continue;
+                       
+               case JFS_REVOKE_BLOCK:
+                       dump_revoke_block(out_file, buf, jsb,
+                                         blocknr, blocksize, 
+                                         transaction);
+                       blocknr++;
+                       WRAP(jsb, blocknr);
+                       continue;
+
+               default:
+                       fprintf (out_file, "Unexpected block type %u at "
+                                "block %u.\n", blocktype, blocknr);
+                       return;
+               }
+       }
+}
+
+
+static void dump_descriptor_block(FILE *out_file, 
+                                 struct journal_source *source, 
+                                 char *buf,
+                                 journal_superblock_t *jsb, 
+                                 unsigned int *blockp, int blocksize,
+                                 tid_t transaction)
+{
+       int                     offset;
+       char                    *tagp;
+       journal_block_tag_t     *tag;
+       unsigned int            blocknr;
+       __u32                   tag_block;
+       __u32                   tag_flags;
+               
+
+       offset = sizeof(journal_header_t);
+       blocknr = *blockp;
+
+       if (dump_all) 
+               fprintf(out_file, "Dumping descriptor block, sequence %u, at "
+                       "block %u:\n", transaction, blocknr);
+       
+       ++blocknr;
+       WRAP(jsb, blocknr);
+       
+       do {
+               /* Work out the location of the current tag, and skip to 
+                * the next one... */
+               tagp = &buf[offset];
+               tag = (journal_block_tag_t *) tagp;
+               offset += sizeof(journal_block_tag_t);
+
+               /* ... and if we have gone too far, then we've reached the
+                  end of this block. */
+               if (offset > blocksize)
+                       break;
+       
+               tag_block = be32_to_cpu(tag->t_blocknr);
+               tag_flags = be32_to_cpu(tag->t_flags);
+
+               if (!(tag_flags & JFS_FLAG_SAME_UUID))
+                       offset += 16;
+
+               dump_metadata_block(out_file, source, jsb, 
+                                   blocknr, tag_block, blocksize, 
+                                   transaction);
+
+               ++blocknr;
+               WRAP(jsb, blocknr);
+               
+       } while (!(tag_flags & JFS_FLAG_LAST_TAG));
+       
+       *blockp = blocknr;
+}
+
+
+static void dump_revoke_block(FILE *out_file, char *buf,
+                                 journal_superblock_t *jsb, 
+                                 unsigned int blocknr, int blocksize,
+                                 tid_t transaction)
+{
+       int                     offset, max;
+       journal_revoke_header_t *header;
+       unsigned int            *entry, rblock;
+       
+       if (dump_all) 
+               fprintf(out_file, "Dumping revoke block, sequence %u, at "
+                       "block %u:\n", transaction, blocknr);
+       
+       header = (journal_revoke_header_t *) buf;
+       offset = sizeof(journal_revoke_header_t);
+       max = be32_to_cpu(header->r_count);
+
+       while (offset < max) {
+               entry = (unsigned int *) (buf + offset);
+               rblock = be32_to_cpu(*entry);
+               if (dump_all || rblock == block_to_dump) {
+                       fprintf(out_file, "  Revoke FS block %u", rblock);
+                       if (dump_all)
+                               fprintf(out_file, "\n");
+                       else
+                               fprintf(out_file," at block %u, sequence %u\n",
+                                       blocknr, transaction);
+               }
+               offset += 4;
+       }
+}
+
+
+static void show_extent(FILE *out_file, int start_extent, int end_extent,
+                       __u32 first_block)
+{
+       if (start_extent >= 0 && first_block != 0)
+               fprintf(out_file, "(%d+%u): %u ", 
+                       start_extent, end_extent-start_extent, first_block);
+}
+
+static void show_indirect(FILE *out_file, char *name, __u32 where)
+{
+       if (where)
+               fprintf(out_file, "(%s): %u ", name, where);
+}
+
+
+static void dump_metadata_block(FILE *out_file, struct journal_source *source,
+                               journal_superblock_t *jsb,
+                               unsigned int log_blocknr, 
+                               unsigned int fs_blocknr, 
+                               int blocksize,
+                               tid_t transaction)
+{
+       int got, retval;
+       char buf[8192];
+       
+       if (!(dump_all
+             || (fs_blocknr == block_to_dump)
+             || (fs_blocknr == inode_block_to_dump)
+             || (fs_blocknr == bitmap_to_dump)))
+               return;
+       
+       fprintf(out_file, "  FS block %u logged at ", fs_blocknr);
+       if (!dump_all) 
+               fprintf(out_file, "sequence %u, ", transaction);
+       fprintf(out_file, "journal block %u\n", log_blocknr);
+       
+       /* There are two major special cases to parse:
+        * 
+        * If this block is a block
+        * bitmap block, we need to give it special treatment so that we
+        * can log any allocates and deallocates which affect the
+        * block_to_dump query block. 
+        * 
+        * If the block is an inode block for the inode being searched
+        * for, then we need to dump the contents of that inode
+        * structure symbolically.  
+        */
+       
+       if (!(dump_contents && dump_all)
+           && fs_blocknr != block_to_dump
+           && fs_blocknr != bitmap_to_dump 
+           && fs_blocknr != inode_block_to_dump)
+               return;
+       
+       retval = read_journal_block("logdump", source, 
+                                   blocksize * log_blocknr,
+                                   buf, blocksize, &got);
+       if (retval)
+               return;
+       
+       if (fs_blocknr == bitmap_to_dump) {
+               struct ext2_super_block *super;
+               int offset;
+               
+               super = current_fs->super;
+               offset = ((fs_blocknr - super->s_first_data_block) %
+                         super->s_blocks_per_group);
+       
+               fprintf(out_file, "    (block bitmap for block %u: "
+                       "block is %s)\n", 
+                       block_to_dump,
+                       ext2fs_test_bit(offset, buf) ? "SET" : "CLEAR");
+       }
+       
+       if (fs_blocknr == inode_block_to_dump) {
+               struct ext2_inode *inode;
+               int first, prev, this, start_extent, i;
+               
+               fprintf(out_file, "    (inode block for inode %u):\n",
+                       inode_to_dump);
+               
+               inode = (struct ext2_inode *) (buf + inode_offset_to_dump);
+               internal_dump_inode(out_file, "    ", inode_to_dump, inode, 0);
+               
+               /* Dump out the direct/indirect blocks here:
+                * internal_dump_inode can only dump them from the main
+                * on-disk inode, not from the journaled copy of the
+                * inode. */
+               
+               fprintf (out_file, "    Blocks:  ");
+               start_extent = -1;
+
+               for (i=0; i<EXT2_NDIR_BLOCKS; i++) {
+                       this = inode->i_block[i];
+                       if (start_extent >= 0  && this == prev+1) {
+                               prev = this;
+                               continue;
+                       } else {
+                               show_extent(out_file, start_extent, i, first);
+                               start_extent = i;
+                               first = prev = this;
+                       }
+               }
+               show_extent(out_file, start_extent, i, first);
+               show_indirect(out_file, "IND", inode->i_block[i++]);
+               show_indirect(out_file, "DIND", inode->i_block[i++]);
+               show_indirect(out_file, "TIND", inode->i_block[i++]);
+               
+               fprintf(out_file, "\n");
+       }
+
+       if (dump_contents)
+               do_hexdump(out_file, buf, blocksize);
+       
+}
+
+static void do_hexdump (FILE *out_file, char *buf, int blocksize)
+{
+       int i,j;
+       int *intp;
+       char *charp;
+       unsigned char c;
+       
+       intp = (int *) buf;
+       charp = (char *) buf;
+       
+       for (i=0; i<blocksize; i+=16) {
+               fprintf(out_file, "    %04x:  ", i);
+               for (j=0; j<16; j+=4)
+                       fprintf(out_file, "%08x ", *intp++);
+               for (j=0; j<16; j++) {
+                       c = *charp++;
+                       if (c < ' ' || c >= 127)
+                               c = '.';
+                       fprintf(out_file, "%c", c);
+               }
+               fprintf(out_file, "\n");
+       }
+}
+