Whamcloud - gitweb
Many files:
authorTheodore Ts'o <tytso@mit.edu>
Mon, 14 Aug 2000 14:25:19 +0000 (14:25 +0000)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 14 Aug 2000 14:25:19 +0000 (14:25 +0000)
  journal.c: implement loading of ext3 journal for recovery code
  problem.c (fix_problem): return answer from PR_AFTER_CODE to caller.
   Add journal problems.
  recovery.c (journal_recover): user-space ext3 journal recovery code
  unix.c (main) : check journal and do recovery in separate steps
  jfs.h, recovery.c: Files ext3 kernel code.
  jfs_compat.h: Compatibility header file to allow kernel code to be
   linked to e2fsck.

12 files changed:
e2fsck/ChangeLog
e2fsck/Makefile.in
e2fsck/e2fsck.8.in
e2fsck/e2fsck.h
e2fsck/jfs.h [new file with mode: 0644]
e2fsck/jfs_compat.h [new file with mode: 0644]
e2fsck/journal.c
e2fsck/message.c
e2fsck/problem.c
e2fsck/problem.h
e2fsck/recovery.c [new file with mode: 0644]
e2fsck/unix.c

index 2c4efaa..86bdc2d 100644 (file)
@@ -1,3 +1,14 @@
+2000-07-12  Andreas Dilger <adilger@turbolinux.com>
+
+       * journal.c: implement loading of ext3 journal for recovery code
+
+       * problem.c (fix_problem): return answer from PR_AFTER_CODE to caller.
+               Add journal problems.
+
+       * recovery.c (journal_recover): user-space ext3 journal recovery code
+
+       * unix.c (main) : check journal and do recovery in separate steps
+
 2000-08-07    <tytso@snap.thunk.org>
 
        * unix.c (calc_percent): Make sure that we don't take a floating
index 9cbb3a0..f5abb2f 100644 (file)
@@ -56,14 +56,14 @@ PROFILED_DEPLIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) \
 
 OBJS= unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o pass3.o pass4.o \
        pass5.o journal.o swapfs.o badblocks.o util.o dirinfo.o ehandler.o \
-       problem.o message.o $(MTRACE_OBJ)
+       problem.o message.o recovery.o $(MTRACE_OBJ)
 
 PROFILED_OBJS= profiled/unix.o profiled/e2fsck.o profiled/super.o \
        profiled/pass1.o profiled/pass1b.o \
        profiled/pass2.o profiled/pass3.o profiled/pass4.o profiled/pass5.o \
        profiled/journal.o profiled/badblocks.o profiled/util.o \
        profiled/dirinfo.o profiled/ehandler.o profiled/message.o \
-       profiled/problem.o profiled/swapfs.o
+       profiled/problem.o profiled/swapfs.o profiled/recovery.o
 
 SRCS= $(srcdir)/e2fsck.c \
        $(srcdir)/super.c \
@@ -74,6 +74,7 @@ SRCS= $(srcdir)/e2fsck.c \
        $(srcdir)/pass4.c \
        $(srcdir)/pass5.c \
        $(srcdir)/journal.c \
+       $(srcdir)/recovery.c \
        $(srcdir)/badblocks.c \
        $(srcdir)/util.c \
        $(srcdir)/unix.c \
@@ -157,15 +158,15 @@ distclean: clean
 # Makefile dependencies follow.  This must be the last section in
 # the Makefile.in file
 #
-e2fsck.o: $(srcdir)/e2fsck.c $(srcdir)/e2fsck.h \
+e2fsck.o: $(srcdir)/e2fsck.c $(srcdir)/e2fsck.h $(srcdir)/jfs_compat.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)/problem.h
+ $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/problem.h $(srcdir)/jfs.h
 super.o: $(srcdir)/super.c $(top_srcdir)/lib/uuid/uuid.h $(srcdir)/e2fsck.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)/problem.h
-pass1.o: $(srcdir)/pass1.c $(srcdir)/e2fsck.h \
+pass1.o: $(srcdir)/pass1.c $(srcdir)/e2fsck.h $(srcdir)/jfs_compat.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)/problem.h
@@ -189,6 +190,12 @@ pass5.o: $(srcdir)/pass5.c $(srcdir)/e2fsck.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)/problem.h
+journal.o: $(srcdir)/journal.c $(srcdir)/jfs_compat.h $(srcdir)/e2fsck.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 \
+ $(srcdir)/jfs.h
+recovery.o: $(srcdir)/recovery.c $(srcdir)/jfs_compat.h $(srcdir)/e2fsck.h \
+ $(srcdir)/jfs.h
 badblocks.o: $(srcdir)/badblocks.c $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
  $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
index a72194a..c76ff03 100644 (file)
@@ -29,7 +29,9 @@ e2fsck \- check a Linux second extended file system
 .I device
 .SH DESCRIPTION
 .B e2fsck
-is used to check a Linux second extended file system.
+is used to check a Linux second extended file system (e2fs).  E2fsck also 
+supports ext2 filesystems countaining a journal, which are
+also sometimes known as ext3 filesystems.
 .TP
 .I device
 is the special file corresponding to the device (e.g
index 6b69ed7..31c0a03 100644 (file)
@@ -283,7 +283,8 @@ extern const char *ehandler_operation(const char *op);
 extern void ehandler_init(io_channel channel);
 
 /* journal.c */
-extern int e2fsck_run_ext3_journal(const char *device);
+extern int e2fsck_check_ext3_journal(e2fsck_t ctx);
+extern int e2fsck_run_ext3_journal(e2fsck_t ctx);
 
 /* pass1.c */
 extern void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
diff --git a/e2fsck/jfs.h b/e2fsck/jfs.h
new file mode 100644 (file)
index 0000000..2d08977
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * linux/include/linux/jfs.h
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1998
+ *
+ * Copyright 1998 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Definitions for transaction data structures for the buffer cache
+ * filesystem journaling support.
+ */
+
+#ifndef _LINUX_JFS_H
+#define _LINUX_JFS_H
+
+#ifndef __KERNEL__
+#include "jfs_compat.h"
+#endif
+
+/*
+ * Debug code
+ */
+
+/* #define JFS_DEBUG */
+
+#ifdef JFS_DEBUG
+extern int jfs_enable_debug;
+
+#define jfs_debug(n, f, a...)                                          \
+       do {                                                            \
+               if ((n) <= jfs_enable_debug) {                          \
+                       printk (KERN_DEBUG "JFS DEBUG: (%s, %d): %s: ", \
+                               __FILE__, __LINE__, __FUNCTION__);      \
+                       printk (f, ## a);                       \
+               }                                                       \
+       } while (0)
+#else
+#define jfs_debug(f, a...)     /**/
+#endif
+
+#define JFS_MIN_JOURNAL_BLOCKS 1024
+
+/*
+ * Internal structures used by the logging mechanism:
+ */
+
+#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+
+/*
+ * On-disk structures
+ */
+
+/* 
+ * Descriptor block types:
+ */
+
+#define JFS_DESCRIPTOR_BLOCK   1
+#define JFS_COMMIT_BLOCK       2
+#define JFS_SUPERBLOCK         3
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+typedef struct journal_header_s
+{
+       __u32           h_magic;
+       __u32           h_blocktype;
+       __u32           h_sequence;
+} journal_header_t;
+
+
+/* 
+ * The block tag: used to describe a single buffer in the journal 
+ */
+typedef struct journal_block_tag_s
+{
+       __u32           t_blocknr;      /* The on-disk block number */
+       __u32           t_flags;        /* See below */
+} journal_block_tag_t;
+
+/* Definitions for the journal tag flags word: */
+#define JFS_FLAG_ESCAPE                1       /* on-disk block is escaped */
+#define JFS_FLAG_SAME_UUID     2       /* block has same uuid as previous */
+#define JFS_FLAG_DELETED       4       /* block deleted by this transaction */
+#define JFS_FLAG_LAST_TAG      8       /* last tag in this descriptor block */
+
+
+/*
+ * The journal superblock
+ */
+typedef struct journal_superblock_s
+{
+       journal_header_t s_header;
+
+       /* Static information describing the journal */
+       __u32           s_blocksize;    /* journal device blocksize */
+       __u32           s_maxlen;       /* total blocks in journal file */
+       __u32           s_first;        /* first block of log information */
+       
+       /* Dynamic information describing the current state of the log */
+       __u32           s_sequence;     /* first commit ID expected in log */
+       __u32           s_start;        /* blocknr of start of log */
+       
+} journal_superblock_t;
+
+#ifdef __KERNEL__
+
+#include <asm/semaphone.h>
+#include <linux/fs.h>
+
+
+#define J_ASSERT(assert)                                               \
+       do { if (!(assert)) {                                           \
+               printk (KERN_CRIT                                       \
+                       "Assertion failure in %s() at %s line %d: "     \
+                       "\"%s\"\n",                                     \
+                       __FUNCTION__, __FILE__, __LINE__, # assert);    \
+               * ((char *) 0) = 0;                                     \
+       } } while (0)
+
+
+
+/* The handle_t type represents a single atomic update being performed
+ * by some process.  All filesystem modifications made by the process go
+ * through this handle.  Recursive operations (such as quota operations)
+ * are gathered into a single update.
+ *
+ * The buffer credits field is used to account for journaled buffers
+ * being modified by the running process.  To ensure that there is
+ * enough log space for all outstanding operations, we need to limit the
+ * number of outstanding buffers possible at any time.  When the
+ * operation completes, any buffer credits not used are credited back to
+ * the transaction, so that at all times we know how many buffers the
+ * outstanding updates on a transaction might possibly touch. */
+
+struct handle_s 
+{
+       /* Which compound transaction is this update a part of? */
+       transaction_t         * h_transaction;
+
+       /* Number of remaining buffers we are allowed to dirty: */
+       int                     h_buffer_credits;
+
+       /* Reference count on this handle */
+       int                     h_ref;
+
+       /* Flags */
+       unsigned int            h_sync  : 1;    /* sync-on-close */
+};
+
+
+/* The transaction_t type is the guts of the journaling mechanism.  It
+ * tracks a compound transaction through its various states:
+ *
+ * RUNNING:    accepting new updates
+ * LOCKED:     Updates still running but we don't accept new ones
+ * RUNDOWN:    Updates are tidying up but have finished requesting
+ *             new buffers to modify (state not used for now)
+ * FLUSH:       All updates complete, but we are still writing to disk
+ * COMMIT:      All data on disk, writing commit record
+ * FINISHED:   We still have to keep the transaction for checkpointing.
+ *
+ * The transaction keeps track of all of the buffers modified by a
+ * running transaction, and all of the buffers committed but not yet
+ * flushed to home for finished transactions.
+ */
+
+struct transaction_s 
+{
+       /* Pointer to the journal for this transaction. */
+       journal_t *             t_journal;
+       
+       /* Sequence number for this transaction */
+       tid_t                   t_tid;
+       
+       /* Transaction's current state */
+       enum {
+               T_RUNNING,
+               T_LOCKED,
+               T_RUNDOWN,
+               T_FLUSH,
+               T_COMMIT,
+               T_FINISHED 
+       }                       t_state;
+
+       /* Where in the log does this transaction's commit start? */
+       unsigned long           t_log_start;
+       
+       /* Doubly-linked circular list of all inodes owned by this
+           transaction */
+       struct inode *          t_ilist;
+       
+       /* Number of buffers on the t_buffers list */
+       int                     t_nr_buffers;
+       
+       /* Doubly-linked circular list of all buffers reserved but not
+           yet modified by this transaction */
+       struct buffer_head *    t_reserved_list;
+       
+       /* Doubly-linked circular list of all metadata buffers owned by this
+           transaction */
+       struct buffer_head *    t_buffers;
+       
+       /* Doubly-linked circular list of all data buffers still to be
+           flushed before this transaction can be committed */
+       struct buffer_head *    t_datalist;
+       
+       /* Doubly-linked circular list of all forget buffers (superceded
+           buffers which we can un-checkpoint once this transaction
+           commits) */
+       struct buffer_head *    t_forget;
+       
+       /* Doubly-linked circular list of all buffers still to be
+           flushed before this transaction can be checkpointed */
+       struct buffer_head *    t_checkpoint_list;
+       
+       /* Doubly-linked circular list of temporary buffers currently
+           undergoing IO in the log */
+       struct buffer_head *    t_iobuf_list;
+       
+       /* Doubly-linked circular list of metadata buffers being
+           shadowed by log IO.  The IO buffers on the iobuf list and the
+           shadow buffers on this list match each other one for one at
+           all times. */
+       struct buffer_head *    t_shadow_list;
+       
+       /* Doubly-linked circular list of control buffers being written
+           to the log. */
+       struct buffer_head *    t_log_list;
+       
+       /* Number of outstanding updates running on this transaction */
+       int                     t_updates;
+
+       /* Number of buffers reserved for use by all handles in this
+        * transaction handle but not yet modified. */
+       int                     t_outstanding_credits;
+       
+       /* Wait queue to wait for updates to complete */
+       struct wait_queue *     t_wait;
+
+       /* Forward and backward links for the circular list of all
+        * transactions awaiting checkpoint */
+       transaction_t           *t_cpnext, *t_cpprev;
+
+       /* When will the transaction expire (become due for commit), in
+        * jiffies ? */
+       unsigned long           t_expires;
+};
+#endif /* __KERNEL__ */
+
+
+/* The journal_t maintains all of the journaling state information for a
+ * single filesystem.  It is linked to from the fs superblock structure.
+ * 
+ * We use the journal_t to keep track of all outstanding transaction
+ * activity on the filesystem, and to manage the state of the log
+ * writing process. */
+
+struct journal_s
+{
+       /* General journaling state flags */
+       unsigned long           j_flags;
+       
+       /* The superblock buffer */
+       struct buffer_head *    j_sb_buffer;
+       journal_superblock_t *  j_superblock;
+       
+#ifdef __KERNEL__
+       /* Transactions: The current running transaction... */
+       transaction_t *         j_running_transaction;
+       
+       /* ... the transaction we are pushing to disk ... */
+       transaction_t *         j_committing_transaction;
+       
+       /* ... and a linked circular list of all transactions waiting
+        * for checkpointing. */
+       transaction_t *         j_checkpoint_transactions;
+
+       /* Wait queue for locking of the journal structure.  */
+       struct wait_queue *     j_wait_lock;
+
+       /* Wait queue for waiting for a locked transaction to start
+           committing */
+       struct wait_queue *     j_wait_transaction_locked;
+       
+       /* Wait queue for waiting for checkpointing to complete */
+       struct wait_queue *     j_wait_logspace;
+       
+       /* Wait queue for waiting for commit to complete */
+       struct wait_queue *     j_wait_done_commit;
+       
+       /* Wait queue to trigger checkpointing */
+       struct wait_queue *     j_wait_checkpoint;
+       
+       /* Wait queue to trigger commit */
+       struct wait_queue *     j_wait_commit;
+       
+       /* Semaphore for locking against concurrent checkpoints */
+       struct semaphore        j_checkpoint_sem;
+               
+       /* Journal running state: */
+       /* The lock flag is *NEVER* touched from interrupts. */
+       unsigned int            j_locked : 1;
+
+       /* Pointer to the current commit thread for this journal */
+       struct task_struct *    j_task;
+
+       /* The timer used to wakeup the commit thread: */
+       struct timer_list *     j_commit_timer;
+       int                     j_commit_timer_active;
+#endif
+
+       /* Journal head: identifies the first unused block in the journal. */
+       unsigned long           j_head;
+       
+       /* Journal tail: identifies the oldest still-used block in the
+        * journal. */
+       unsigned long           j_tail;
+
+       /* Journal free: how many free blocks are there in the journal? */
+       unsigned long           j_free;
+
+       /* Journal start and end: the block numbers of the first usable
+        * block and one beyond the last usable block in the journal. */
+       unsigned long           j_first, j_last;
+
+       /* Device, blocksize and starting block offset for the location
+        * where we store the journal. */
+       kdev_t                  j_dev;
+       int                     j_blocksize;
+       unsigned int            j_blk_offset;
+
+       /* Total maximum capacity of the journal region on disk. */
+       unsigned int            j_maxlen;
+
+       /* Optional inode where we store the journal.  If present, all
+        * journal block numbers are mapped into this inode via
+        * bmap(). */
+       struct inode *          j_inode;
+
+       /* Sequence number of the oldest transaction in the log */
+       tid_t                   j_tail_sequence;
+       /* Sequence number of the next transaction to grant */
+       tid_t                   j_transaction_sequence;
+       /* Sequence number of the most recently committed transaction */
+       tid_t                   j_commit_sequence;
+       /* Sequence number of the most recent transaction wanting commit */
+       tid_t                   j_commit_request;
+
+       /* Journal uuid: identifies the object (filesystem, LVM volume
+        * etc) backed by this journal.  This will eventually be
+        * replaced by an array of uuids, allowing us to index multiple
+        * devices within a single journal and to perform atomic updates
+        * across them.  */
+
+       __u8                    j_uuid[16];
+
+       /* Maximum number of metadata buffers to allow in a single
+        * compound commit transaction */
+       int                     j_max_transaction_buffers;
+
+       /* What is the maximum transaction lifetime before we begin a
+        * commit? */
+       unsigned long           j_commit_interval;
+
+};
+
+#ifdef __KERNEL__
+
+/* 
+ * Journal flag definitions 
+ */
+#define JFS_UNMOUNT    1       /* Journal thread is being destroyed */
+#define JFS_SYNC       2       /* Perform synchronous transaction commits */
+
+/* 
+ * Journaling internal variables/parameters 
+ */
+
+extern int journal_flush_nr_buffers;
+
+
+/* 
+ * Function declarations for the journaling transaction and buffer
+ * management
+ */
+
+/* Filing buffers */
+extern void journal_unfile_buffer(struct buffer_head *);
+extern void journal_refile_buffer(struct buffer_head *);
+extern void journal_file_buffer(struct buffer_head *, transaction_t *, int);
+extern void journal_clean_data_list(transaction_t *transaction);
+
+/* Log buffer allocation */
+extern struct buffer_head * journal_get_descriptor_buffer(journal_t *);
+extern unsigned long journal_next_log_block(journal_t *);
+
+/* Commit management */
+extern void journal_commit_transaction(journal_t *);
+
+/* Checkpoint list management */
+extern void journal_remove_checkpoint(struct buffer_head *);
+extern void journal_insert_checkpoint(struct buffer_head *, transaction_t *);
+
+/* Buffer IO */
+extern int 
+journal_write_metadata_buffer(transaction_t      *transaction,
+                             struct buffer_head  *bh_in,
+                             struct buffer_head **bh_out,
+                             int                  blocknr);
+
+/* Create and destroy transactions */
+extern transaction_t * get_transaction (journal_t *);
+extern void            put_transaction (transaction_t *);
+
+/* Notify state transitions (called by the log writer thread): */
+extern int             set_transaction_state (transaction_t *, int);
+
+
+/*
+ * Transaction locking
+ *
+ * We need to lock the journal during transaction state changes so that
+ * nobody ever tries to take a handle on the running transaction while
+ * we are in the middle of moving it to the commit phase.
+ *
+ * Note that the locking is completely interrupt unsafe.  We never touch
+ * journal structures from interrupts.
+ */
+
+static inline void __wait_on_journal (journal_t * journal)
+{
+       while (journal->j_locked)
+               sleep_on (&journal->j_wait_lock);
+}
+
+
+/* Journal locking.  In 2.2, we assume that the kernel lock is already
+ * held. */
+static inline void lock_journal (journal_t * journal)
+{
+       if (journal->j_locked)
+               __wait_on_journal(journal);
+       journal->j_locked = 1;
+}
+
+static inline int try_lock_journal (journal_t * journal)
+{
+       if (journal->j_locked)
+               return 1;
+       journal->j_locked = 1;
+       return 0;
+}
+
+static inline void unlock_journal (journal_t * journal)
+{
+       J_ASSERT (journal->j_locked);
+       journal->j_locked = 0;
+       wake_up(&journal->j_wait_lock);
+}
+
+/* This function is gross, but unfortunately we need it as long as
+ * existing filesystems want to guard against races by testing
+ * bh->b_count.  @@@ Remove this?  We no longer abuse b_count so badly!
+ */
+
+static inline int journal_is_buffer_shared(struct buffer_head *bh)
+{
+       int count = bh->b_count;
+       J_ASSERT (count >= 1);
+       return (count > 1);
+}
+
+/* Debugging code only: */
+
+#define jfs_ENOSYS() \
+do {                                                                 \
+       printk (KERN_ERR "JFS unimplemented function " __FUNCTION__); \
+       current->state = TASK_UNINTERRUPTIBLE;                        \
+       schedule();                                                   \
+} while (1)
+
+/* The log thread user interface:
+ *
+ * Request space in the current transaction, and force transaction commit
+ * transitions on demand.
+ */
+
+extern int     log_space_left (journal_t *); /* Called with journal locked */
+extern void    log_start_commit (journal_t *, transaction_t *);
+extern void    log_wait_commit (journal_t *, tid_t);
+extern int     log_do_checkpoint (journal_t *, int);
+
+extern void    log_wait_for_space(journal_t *, int nblocks);
+extern void    journal_drop_transaction(journal_t *, transaction_t *);
+
+
+/* The journaling code user interface:
+ *
+ * Create and destroy handles
+ * Register buffer modifications against the current transaction. 
+ */
+
+extern handle_t *journal_start (journal_t *, int nblocks);
+extern int      journal_restart (handle_t *, int nblocks);
+extern int      journal_extend (handle_t *, int nblocks);
+extern int      journal_get_write_access (handle_t *, struct buffer_head *);
+extern int      journal_get_create_access (handle_t *, struct buffer_head *);
+extern int      journal_get_undo_access (handle_t *, struct buffer_head *);
+extern int      journal_dirty_data (handle_t *, struct buffer_head *);
+extern int      journal_dirty_metadata (handle_t *, struct buffer_head *);
+extern void     journal_release_buffer (handle_t *, struct buffer_head *);
+extern void     journal_forget (handle_t *, struct buffer_head *);
+extern void     journal_sync_buffer (struct buffer_head *);
+extern int      journal_stop (handle_t *);
+extern int      journal_flush (journal_t *);
+
+extern journal_t * journal_init_dev   (kdev_t, int start, int len, int bsize);
+extern journal_t * journal_init_inode (struct inode *);
+extern int        journal_create     (journal_t *);
+extern int        journal_load       (journal_t *);
+extern void       journal_release    (journal_t *);
+extern void       journal_update_superblock (journal_t *, int);
+#endif /* __KERNEL__   */
+extern int        journal_recover    (journal_t *);
+
+#endif /* _LINUX_JFS_H */
diff --git a/e2fsck/jfs_compat.h b/e2fsck/jfs_compat.h
new file mode 100644 (file)
index 0000000..1fa49ca
--- /dev/null
@@ -0,0 +1,54 @@
+
+#ifndef _JFS_COMPAT_H
+#define _JFS_COMPAT_H
+
+#include "e2fsck.h"
+#include <errno.h>
+
+#define printk printf
+#define KERN_ERR ""
+#define KERN_DEBUG ""
+
+#define READ 0
+#define WRITE 1
+
+typedef int tid_t;
+typedef e2fsck_t kdev_t;
+typedef struct journal_s journal_t;
+
+struct buffer_head {
+       char     b_data[8192];
+       e2fsck_t b_ctx;
+       io_channel b_io;
+       int      b_size;
+       blk_t    b_blocknr;
+       int      b_dirty;
+       int      b_uptodate;
+       int      b_err;
+};
+
+struct inode {
+       e2fsck_t i_ctx;
+       ino_t    i_ino;
+       struct ext2_inode i_ext2;
+};
+
+int bmap(struct inode *inode, int block);
+struct buffer_head *getblk(e2fsck_t ctx, blk_t blocknr, int blocksize);
+void ll_rw_block(int rw, int dummy, struct buffer_head *bh);
+void mark_buffer_dirty(struct buffer_head *bh, int dummy);
+void brelse(struct buffer_head *bh);
+int buffer_uptodate(struct buffer_head *bh);
+void wait_on_buffer(struct buffer_head *bh);
+#define fsync_dev(dev) do {} while(0)
+#define buffer_req(bh) 1
+#define do_readahead(journal, start) do {} while(0)
+#define J_ASSERT(assert)                                               \
+       do { if (!(assert)) {                                           \
+               printf ("Assertion failure in %s() at %s line %d: "     \
+                       "\"%s\"\n",                                     \
+                       __FUNCTION__, __FILE__, __LINE__, # assert);    \
+               exit(FSCK_ERROR);                                       \
+       } } while (0)
+
+#endif /* _JFS_COMPAT_H */
index 15b86cc..dc5660c 100644 (file)
 /*
  * journal.c --- code for handling the "ext3" journal
+ *
+ * Copyright (C) 2000 Andreas Dilger
+ * Copyright (C) 2000 Theodore Ts'o
+ *
+ * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
+ * Copyright (C) 1999 Red Hat Software
+ *
+ * This file may be redistributed under the terms of the
+ * GNU General Public License version 2 or at your discretion
+ * any later version.
  */
 
-#include <errno.h>
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#define MNT_FL (MS_MGC_VAL | MS_RDONLY)
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
 
-#include "e2fsck.h"
+#include "jfs.h"
+#include "problem.h"
+#include "uuid/uuid.h"
 
-/*
- * This is a list of directories to try.  The first element may get
- * replaced by a mktemp'ed generated temp directory if possible.
- */
-static char *dirlist[] = { "/mnt", "/tmp", "/root", "/boot", 0 };
+#ifdef JFS_DEBUG
+static int bh_count = 0;
+int jfs_enable_debug = 2;
+#endif
+
+int bmap(struct inode *inode, int block)
+{
+       int retval;
+       blk_t phys;
+
+       retval = ext2fs_bmap(inode->i_ctx->fs, inode->i_ino, &inode->i_ext2,
+                            NULL, 0, block, &phys);
+
+       if (retval)
+               com_err(inode->i_ctx->device_name, retval,
+                       _("bmap journal inode %ld, block %d\n"),
+                       inode->i_ino, block);
+
+       return phys;
+}
+
+struct buffer_head *getblk(e2fsck_t ctx, blk_t blocknr, int blocksize)
+{
+       struct buffer_head *bh;
+
+       bh = e2fsck_allocate_memory(ctx, sizeof(*bh), "block buffer");
+       if (!bh)
+               return NULL;
+
+       jfs_debug(4, "getblk for block %lu (%d bytes)(total %d)\n",
+                 blocknr, blocksize, ++bh_count);
+
+       bh->b_ctx = ctx;
+       bh->b_size = blocksize;
+       bh->b_blocknr = blocknr;
+
+       return bh;
+}
+
+void ll_rw_block(int rw, int dummy, struct buffer_head *bh)
+{
+       int retval;
+
+       if (rw == READ && !bh->b_uptodate) {
+               jfs_debug(3, "reading block %lu/%p\n", bh->b_blocknr, bh);
+               retval = io_channel_read_blk(bh->b_ctx->fs->io, bh->b_blocknr,
+                                            1, bh->b_data);
+               if (retval) {
+                       com_err(bh->b_ctx->device_name, retval,
+                               "while reading block %ld\n", bh->b_blocknr);
+                       bh->b_err = retval;
+                       return;
+               }
+               bh->b_uptodate = 1;
+       } else if (rw == WRITE && bh->b_dirty) {
+               jfs_debug(3, "writing block %lu/%p\n", bh->b_blocknr, bh);
+               retval = io_channel_write_blk(bh->b_ctx->fs->io, bh->b_blocknr,
+                                             1, bh->b_data);
+               if (retval) {
+                       com_err(bh->b_ctx->device_name, retval,
+                               "while writing block %ld\n", bh->b_blocknr);
+                       bh->b_err = retval;
+                       return;
+               }
+               bh->b_dirty = 0;
+               bh->b_uptodate = 1;
+       } else
+               jfs_debug(3, "no-op %s for block %lu\n",
+                         rw == READ ? "read" : "write", bh->b_blocknr);
+}
+
+void mark_buffer_dirty(struct buffer_head *bh, int dummy)
+{
+       bh->b_dirty = dummy | 1; /* use dummy to avoid unused variable */
+}
+
+void brelse(struct buffer_head *bh)
+{
+       if (bh->b_dirty)
+               ll_rw_block(WRITE, 1, bh);
+       jfs_debug(3, "freeing block %lu/%p (total %d)\n",
+                 bh->b_blocknr, bh, --bh_count);
+       ext2fs_free_mem((void **) &bh);
+}
+
+int buffer_uptodate(struct buffer_head *bh)
+{
+       return bh->b_uptodate;
+}
+
+void wait_on_buffer(struct buffer_head *bh)
+{
+       if (!bh->b_uptodate)
+               ll_rw_block(READ, 1, bh);
+}
+
+static void e2fsck_clear_recover(e2fsck_t ctx, int error)
+{
+       struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
+
+       s->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
+
+       /* if we had an error doing journal recovery, we need a full fsck */
+       if (error)
+               s->s_state &= ~EXT2_VALID_FS;
+       ext2fs_mark_super_dirty(ctx->fs);
+}
+
+static int e2fsck_journal_init_inode(e2fsck_t ctx, struct ext2fs_sb *s,
+                                    ino_t journal_inum, journal_t **journal)
+{
+       struct inode *inode;
+       const char *cmdname = ctx->program_name;
+       struct buffer_head *bh;
+       blk_t start;
+       int retval;
+
+       jfs_debug(1, "Using journal inode %lu\n", journal_inum);
+       *journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
+       if (!*journal) {
+               return EXT2_ET_NO_MEMORY;
+       }
+
+       inode = e2fsck_allocate_memory(ctx, sizeof(*inode), "journal inode");
+       if (!inode) {
+               retval = EXT2_ET_NO_MEMORY;
+               goto exit_journal;
+       }
+
+       inode->i_ctx = ctx;
+       inode->i_ino = journal_inum;
+       retval = ext2fs_read_inode(ctx->fs, journal_inum, &inode->i_ext2);
+       if (retval)
+               goto exit_inode;
+
+       (*journal)->j_dev = ctx;
+       (*journal)->j_inode = inode;
+       (*journal)->j_blocksize = ctx->fs->blocksize;
+       (*journal)->j_maxlen = inode->i_ext2.i_size / (*journal)->j_blocksize;
+
+       if (!inode->i_ext2.i_links_count ||
+           !LINUX_S_ISREG(inode->i_ext2.i_mode) ||
+           (*journal)->j_maxlen < JFS_MIN_JOURNAL_BLOCKS ||
+           (start = bmap(inode, 0)) == 0) {
+               retval = EXT2_ET_BAD_INODE_NUM;
+               goto exit_inode;
+       }
+
+       bh = getblk(ctx, start, (*journal)->j_blocksize);
+       if (!bh) {
+               retval = EXT2_ET_NO_MEMORY;
+               goto exit_inode;
+       }
+       (*journal)->j_sb_buffer = bh;
+       (*journal)->j_superblock = (journal_superblock_t *)bh->b_data;
+
+       return 0;
+
+exit_inode:
+       ext2fs_free_mem((void **)&inode);
+exit_journal:
+       ext2fs_free_mem((void **)journal);
+
+       return retval;
+}
+
+static int e2fsck_get_journal(e2fsck_t ctx, journal_t **journal)
+{
+       char uuid_str[40];
+       struct problem_context pctx;
+       struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
+       int recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
+
+       clear_problem_context(&pctx);
+
+       if (s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
+               if (s->s_journal_dev) {
+                       pctx.num = s->s_journal_dev;
+                       /* this problem aborts on -y, -p, unsupported on -n */
+                       if (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_DEV, &pctx))
+                               return EXT2_ET_UNSUPP_FEATURE;
+                       s->s_journal_dev = 0;
+                       s->s_state &= ~EXT2_VALID_FS;
+                       ext2fs_mark_super_dirty(ctx->fs);
+               }
+               if (!uuid_is_null(s->s_journal_uuid)) {
+                       uuid_unparse(s->s_journal_uuid, uuid_str);
+                       pctx.str = uuid_str;
+                       /* this problem aborts on -y, -p, unsupported on -n */
+                       if (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_UUID, &pctx))
+                               return EXT2_ET_UNSUPP_FEATURE;
+                       uuid_clear(s->s_journal_uuid);
+                       s->s_state &= ~EXT2_VALID_FS;
+                       ext2fs_mark_super_dirty(ctx->fs);
+               }
+               if (!s->s_journal_inum)
+                       return EXT2_ET_BAD_INODE_NUM;
+       }
+
+       if (s->s_journal_dev) {
+               pctx.num = s->s_journal_dev;
+               if (!fix_problem(ctx, PR_0_JOURNAL_BAD_DEV, &pctx))
+                       return EXT2_ET_UNSUPP_FEATURE;
+               s->s_journal_dev = 0;
+               s->s_state &= ~EXT2_VALID_FS;
+               ext2fs_mark_super_dirty(ctx->fs);
+       }
+       if (!uuid_is_null(s->s_journal_uuid)) {
+               uuid_unparse(s->s_journal_uuid, uuid_str);
+               pctx.str = uuid_str;
+               if (!fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx))
+                       return EXT2_ET_UNSUPP_FEATURE;
+               uuid_clear(s->s_journal_uuid);
+               s->s_state &= ~EXT2_VALID_FS;
+               ext2fs_mark_super_dirty(ctx->fs);
+       }
+
+       return e2fsck_journal_init_inode(ctx, s, s->s_journal_inum, journal);
+}
+
+static int e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
+                                       struct problem_context *pctx)
+{
+       struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
+       int recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
+       int has_journal = s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+
+       if (has_journal || s->s_journal_inum) {
+               /* The journal inode is bogus, remove and force full fsck */
+               if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
+                       struct ext2fs_sb *s =(struct ext2fs_sb *)ctx->fs->super;
+
+                       if (has_journal && s->s_journal_inum)
+                               printf("*** ext3 journal has been deleted - "
+                                      "filesystem is now ext2 only ***\n\n");
+                       s->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+                       s->s_journal_inum = 0;
+                       e2fsck_clear_recover(ctx, 1);
+                       return 0;
+               }
+               return EXT2_ET_BAD_INODE_NUM;
+       } else if (recover) {
+               if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
+                       e2fsck_clear_recover(ctx, 1);
+                       return 0;
+               }
+               return EXT2_ET_UNSUPP_FEATURE;
+       }
+       return 0;
+}
+
+static int e2fsck_journal_fix_unsupported_super(e2fsck_t ctx,
+                                               struct problem_context *pctx)
+{
+       struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
+
+       /* Unsupported journal superblock - first choice is abort.
+        * Declining that gives the option to reset the superblock.
+        *
+        * Otherwise we get the chance to delete the journal, and
+        * failing that we abort because we can't handle this.
+        */
+       if (s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
+           fix_problem(ctx, PR_0_JOURNAL_UNSUPP_SUPER, pctx))
+               return EXT2_ET_CORRUPT_SUPERBLOCK;
+
+       if (e2fsck_journal_fix_bad_inode(ctx, pctx))
+               return EXT2_ET_UNSUPP_FEATURE;
+
+       return 0;
+}
+
+static int e2fsck_journal_load(journal_t *journal)
+{
+       e2fsck_t ctx = journal->j_dev;
+       journal_superblock_t *jsb;
+       struct buffer_head *jbh = journal->j_sb_buffer;
+       struct problem_context pctx;
+
+       clear_problem_context(&pctx);
+
+       ll_rw_block(READ, 1, jbh);
+       if (jbh->b_err) {
+               com_err(ctx->device_name, jbh->b_err,
+                       _("reading journal superblock\n"));
+               return jbh->b_err;
+       }
+
+       jsb = journal->j_superblock;
+       /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
+       if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
+               return e2fsck_journal_fix_bad_inode(ctx, &pctx);
+
+       if (jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK) ||
+           jsb->s_blocksize != htonl(journal->j_blocksize)) {
+               com_err(ctx->device_name, EXT2_ET_CORRUPT_SUPERBLOCK,
+                       _("%s: no valid journal superblock found\n"));
+               return EXT2_ET_CORRUPT_SUPERBLOCK;
+       }
+
+       if (jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK)) {
+               pctx.num = ntohl(jsb->s_header.h_blocktype);
+               return e2fsck_journal_fix_unsupported_super(ctx, &pctx);
+       }
+
+       if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
+               journal->j_maxlen = ntohl(jsb->s_maxlen);
+       else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
+               com_err(ctx->device_name, EXT2_ET_CORRUPT_SUPERBLOCK,
+                       _("%s: journal too short\n"));
+               return EXT2_ET_CORRUPT_SUPERBLOCK;
+       }
+
+       journal->j_tail_sequence = ntohl(jsb->s_sequence);
+       journal->j_tail = ntohl(jsb->s_start);
+       journal->j_first = ntohl(jsb->s_first);
+       journal->j_last = ntohl(jsb->s_maxlen);
+
+       return 0;
+}
+
+void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
+                                    blk_t size)
+{
+       jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
+       jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK);
+       jsb->s_blocksize = htonl(ctx->fs->blocksize);
+       jsb->s_maxlen = htonl(size);
+       jsb->s_first = 1;
+       jsb->s_sequence = htonl(1);
+}
+
+static int e2fsck_journal_fix_corrupt_super(e2fsck_t ctx, journal_t *journal,
+                                           struct problem_context *pctx)
+{
+       struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
+       int recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
+
+       pctx->num = journal->j_inode->i_ino;
+
+       if (s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
+               if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
+                       journal_superblock_t *jsb = journal->j_superblock;
+
+                       e2fsck_journal_reset_super(ctx, jsb, journal->j_maxlen);
+
+                       journal->j_transaction_sequence = 1;
+                       e2fsck_clear_recover(ctx, recover);
+                       return 0;
+               }
+               return EXT2_ET_CORRUPT_SUPERBLOCK;
+       } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
+               return EXT2_ET_CORRUPT_SUPERBLOCK;
+
+       return 0;
+}
+
+static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal, int reset)
+{
+       journal_superblock_t *jsb;
+
+       if (!(ctx->options & E2F_OPT_READONLY)) {
+               jsb = journal->j_superblock;
+               jsb->s_sequence = htonl(journal->j_transaction_sequence);
+               if (reset)
+                       jsb->s_start = 0; /* this marks the journal as empty */
+               mark_buffer_dirty(journal->j_sb_buffer, 1);
+       }
+       brelse(journal->j_sb_buffer);
+
+       if (journal->j_inode)
+               free(journal->j_inode);
+       ext2fs_free_mem((void **)&journal);
+}
+
+int e2fsck_check_ext3_journal(e2fsck_t ctx)
+{
+       struct ext2fs_sb *s = (struct ext2fs_sb *)ctx->fs->super;
+       journal_t *journal;
+       int recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
+       struct problem_context pctx;
+       int reset = 0;
+       int retval;
+
+       /* If we don't have any journal features, don't do anything more */
+       if (!(s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+           !recover && s->s_journal_inum == 0 && s->s_journal_dev == 0 &&
+           uuid_is_null(s->s_journal_uuid))
+               return 0;
+
+       clear_problem_context(&pctx);
+       pctx.num = s->s_journal_inum;
+
+       retval = e2fsck_get_journal(ctx, &journal);
+       if (retval) {
+               if (retval == EXT2_ET_BAD_INODE_NUM)
+                       return e2fsck_journal_fix_bad_inode(ctx, &pctx);
+               return retval;
+       }
+
+       retval = e2fsck_journal_load(journal);
+       if (retval) {
+               if (retval == EXT2_ET_CORRUPT_SUPERBLOCK)
+                       return e2fsck_journal_fix_corrupt_super(ctx, journal,
+                                                               &pctx);
+               return retval;
+       }
+
+       /*
+        * We want to make the flags consistent here.  We will not leave with
+        * needs_recovery set but has_journal clear.  We can't get in a loop
+        * with -y, -n, or -p, only if a user isn't making up their mind.
+        */
+no_has_journal:
+       if (!(s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
+               recover = s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
+               pctx.str = "inode";
+               if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
+                       if (recover &&
+                           !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
+                               goto no_has_journal;
+                       s->s_journal_inum = 0;
+                       e2fsck_clear_recover(ctx, recover);
+               } else if (!(ctx->options & E2F_OPT_READONLY)) {
+                       s->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+                       ext2fs_mark_super_dirty(ctx->fs);
+               }
+       }
+
+       if (s->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
+           !(s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
+           journal->j_superblock->s_start != 0) {
+               if (fix_problem(ctx, PR_0_JOURNAL_RESET_JOURNAL, &pctx))
+                       reset = 1;
+               /* I refuse to enable recovery for journal */
+       }
+
+       e2fsck_journal_release(ctx, journal, reset);
+       return retval;
+}
+
+static int e2fsck_recover_ext3_journal(e2fsck_t ctx)
+{
+       ext2_filsys fs = ctx->fs;
+       io_manager io_ptr = fs->io->manager;
+       int blocksize = fs->blocksize;
+       journal_t *journal;
+       int retval;
+
+       retval = e2fsck_get_journal(ctx, &journal);
+       if (retval)
+               goto exit;
+       retval = e2fsck_journal_load(journal);
+       if (retval)
+               goto exit;
+
+       retval = -journal_recover(journal);
+
+       e2fsck_journal_release(ctx, journal, 1);
+       if (retval)
+               goto exit;
+
+       /* Reload the filesystem context to get up-to-date data from disk
+        * because journal recovery will change the filesystem under us.
+        */
+       ext2fs_close(fs);
+       retval = ext2fs_open(ctx->device_name, EXT2_FLAG_RW,
+                            ctx->superblock, blocksize, io_ptr, &fs);
+
+       if (retval) {
+               com_err(ctx->program_name, retval,
+                       _("while trying to re-open %s"),
+                       ctx->device_name);
+               exit(FSCK_ERROR);
+       }
+       ctx->fs = fs;
+       fs->priv_data = ctx;
+
+       /* FIXME - In the future we will clean up the ophans here.
+        *         For now, we need to force a full fsck to clean them up.
+        *         We shouldn't have this problem in normal circumstances
+        *         as the kernel recovery code should save us.
+        */
+       if (fs->super->s_last_orphan)
+               fs->super->s_state &= ~EXT2_VALID_FS;
+       else
+               jfs_debug(1, "no orphan inodes to clean up\n");
+
+exit:
+       e2fsck_clear_recover(ctx, retval);
+       ext2fs_close(ctx->fs);
+       return retval;
+}
+
+
+#define TEMPLATE "/tmp/ext3.XXXXXX"
 
 /*
  * This function attempts to mount and unmount an ext3 filesystem,
  * which is a cheap way to force the kernel to run the journal and
- * handle the recovery for us.
+ * handle the recovery for us.  If that fails, we need to recover
+ * the journal ourselves manually.
  */
-int e2fsck_run_ext3_journal(const char *device)
+int e2fsck_run_ext3_journal(e2fsck_t ctx)
 {
-       int     ret = 0;
-       char    **cpp, *dir;
-       char    template[] = "/tmp/ext3.XXXXXX";
+#ifdef __linux__
+       ext2_filsys fs = ctx->fs;
+       char    *dirlist[] = {"/mnt","/lost+found","/tmp","/root","/boot",0};
+       int      retval = 0;
+       int      count = 0;
+       char     template[] = TEMPLATE;
+       struct stat buf;
        char    *tmpdir;
 
+       if (ctx->options & E2F_OPT_READONLY) {
+               printf("%s: won't do journal recovery while read-only\n",
+                      ctx->device_name);
+               return EXT2_ET_FILE_RO;
+       }
+
+       /* For now, non-root users and loop devices can't use kernel recovery */
+       if (geteuid()||stat(ctx->device_name, &buf)||!S_ISBLK(buf.st_mode))
+               goto manual_recover;
+
+       printf(_("%s: trying for ext3 kernel journal recovery\n"),
+              ctx->device_name);
        /*
         * First try to make a temporary directory.  This may fail if
         * the root partition is still mounted read-only.
         */
+newtemp:
        tmpdir = mktemp(template);
        if (tmpdir) {
-               ret = mkdir(template, 0700);
-               if (ret)
-                       tmpdir = 0;
-       }
-       if (tmpdir) {
-               ret = mount(device, tmpdir, "ext3", 0xC0ED, NULL);
-               if (ret) {
-                       ret = errno;
-                       rmdir(tmpdir);
-                       return (ret);
+               jfs_debug(2, "trying %s as ext3 temp mount point\n", tmpdir);
+               retval = mkdir(template, 0700);
+               if (retval) {
+                       if (errno == EROFS) {
+                               tmpdir = NULL;
+                               template[0] = '\0';
+                       } else if (errno == EEXIST && count++ < 10) {
+                               strcpy(template, TEMPLATE);
+                               goto newtemp;
+                       } else
+                               goto manual_recover;
                }
-       } else {
+       }
+
+       /*
+        * OK, creating a temporary directory didn't work.
+        * Let's try a list of possible temporary mountpoints.
+        */
+       if (!tmpdir) {
+               dev_t   rootdev;
+               char    **cpp, *dir;
+
+               if (stat("/", &buf))
+                       goto manual_recover;
+
+               rootdev = buf.st_dev;
+
                /*
-                * OK, creating a temporary directory didn't work.
-                * Let's try a list of possible temporary mountpoints.
+                * Check that dir is on the same device as root (no other
+                * filesystem is mounted there), and it's a directory.
                 */
-               for (cpp = dirlist; dir = *cpp; cpp++) {
-                       ret = mount(device, dir, "ext3", 0xC0ED, NULL);
-                       if (ret == 0)
+               for (cpp = dirlist; (dir = *cpp); cpp++)
+                       if (stat(dir, &buf) == 0 && buf.st_dev == rootdev &&
+                           S_ISDIR(buf.st_mode)) {
+                               tmpdir = dir;
                                break;
+                       }
+       }
+
+       if (tmpdir) {
+               io_manager      io_ptr = fs->io->manager;
+               int             blocksize = fs->blocksize;
+
+               jfs_debug(2, "using %s for ext3 mount\n", tmpdir);
+               ext2fs_close(fs);
+               /* FIXME - need to handle loop devices here */
+               retval = mount(ctx->device_name, tmpdir, "ext3", MNT_FL, NULL);
+               if (retval) {
+                       com_err(ctx->program_name, errno,
+                               "when mounting %s", ctx->device_name);
+                       if (template[0])
+                               rmdir(tmpdir);
+
+                       retval = ext2fs_open(ctx->device_name, EXT2_FLAG_RW,
+                                            ctx->superblock, blocksize, io_ptr,
+                                            &fs);
+
+                       if (retval) {
+                               com_err(ctx->program_name, retval,
+                                       _("while trying to re-open %s"),
+                                       ctx->device_name);
+                               exit(FSCK_ERROR);
+                       }
+                       fs->priv_data = ctx;
+                       ctx->fs = fs;
+                       goto manual_recover;
                }
-               if (!dir)
+               /*
+                * Now that it mounted cleanly, the filesystem will have been
+                * recovered, so we can now unmount it.
+                */
+               retval = umount(tmpdir);
+               if (retval)
                        return errno;
+
+               /*
+                * Remove the temporary directory, if it was created.
+                */
+               if (template[0])
+                       rmdir(tmpdir);
+               return 0;
        }
 
-       /*
-        * Now that it mounted cleanly, the filesystem will have been
-        * recovered, so we can now unmount it.
-        */
-       ret = umount(device);
-       if (ret)
-               return errno;
-       /*
-        * Remove the temporary directory, if it was created.
-        */
-       if (tmpdir)
-               rmdir(tmpdir);
-       return 0;
+manual_recover:
+#endif /* __linux__ */
+       return e2fsck_recover_ext3_journal(ctx);
 }
-
index 3018cc0..529a486 100644 (file)
@@ -44,6 +44,7 @@
  *                     the containing directory.
  *     %s      <str>                   miscellaneous string
  *     %S      backup superblock
+ *     %X      <num> hexadecimal format
  *
  * The following '@' expansions are supported:
  *
@@ -378,6 +379,13 @@ static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
        case 's':
                printf("%s", ctx->str);
                break;
+       case 'X':
+#ifdef EXT2_NO_64_TYPE
+               printf("0x%x", ctx->num);
+#else
+               printf("0x%llx", ctx->num);
+#endif
+               break;
        default:
        no_context:
                printf("%%%c", ch);
index 6ddd7aa..5a05387 100644 (file)
@@ -172,7 +172,62 @@ static const struct e2fsck_problem problem_table[] = {
 
        { PR_0_HURD_CLEAR_FILETYPE,
          N_("The Hurd does not support the filetype feature.\n"),
-         PROMPT_CLEAR, 0 },      
+         PROMPT_CLEAR, 0 },
+
+       /* Journal inode is invalid */
+       { PR_0_JOURNAL_BAD_INODE,
+         N_("@S has a bad ext3 journal (@i %N).\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* Superblock has a journal device (which we can't handle yet) */
+       { PR_0_JOURNAL_UNSUPP_DEV,
+         N_("@S has external ext3 journal device (unsupported).\n"),
+         PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_DEV },
+
+       /* Superblock has a bad journal device */
+       { PR_0_JOURNAL_BAD_DEV,
+         N_("@S has a bad ext3 journal (device %X).\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* Superblock has a journal UUID (which we can't handle yet) */
+       { PR_0_JOURNAL_UNSUPP_UUID,
+         N_("@S has an ext3 journal UUID (unsupported).\n"),
+         PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_UUID },
+
+       /* Superblock has a bad journal UUID */
+       { PR_0_JOURNAL_BAD_UUID,
+         N_("@S has a bad ext3 journal (UUID %s).\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* Journal has an unknown superblock type */
+       { PR_0_JOURNAL_UNSUPP_SUPER,
+         N_("Ext3 journal @S is unknown type %N (unsupported).\n"),
+         PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
+
+       /* Journal superblock is corrupt */
+       { PR_0_JOURNAL_BAD_SUPER,
+         N_("Ext3 journal @S is corrupt.\n"),
+         PROMPT_FIX, PR_PREEN_OK },
+
+       /* Superblock flag should be cleared */
+       { PR_0_JOURNAL_HAS_JOURNAL,
+         N_("@S doesn't have has_journal flag, but has ext3 journal %s.\n"),
+         PROMPT_DELETE, PR_PREEN_OK },
+
+       /* Superblock flag is incorrect */
+       { PR_0_JOURNAL_RECOVER_SET,
+         N_("@S has ext3 needs_recovery flag set, but no journal.\n"),
+         PROMPT_CLEAR, PR_PREEN_OK },
+
+       /* Journal should be reset */
+       { PR_0_JOURNAL_RESET_JOURNAL,
+         N_("*** WARNING *** leaving data in the journal may be DANGEROUS.\n"),
+         PROMPT_NONE, PR_PREEN_NOMSG|PR_AFTER_CODE, PR_0_JOURNAL_RESET_PROMPT},
+
+       /* Journal should be reset */
+       { PR_0_JOURNAL_RESET_PROMPT,
+         N_("ext3 recovery flag clear, but journal has data.\n"),
+         PROMPT_CLEAR, PR_PREEN_OK|PR_PREEN_NOMSG },
 
        /* Pass 1 errors */
        
@@ -1168,11 +1223,11 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
        
        }
 
-       if (ptr->flags & PR_AFTER_CODE)
-               (void) fix_problem(ctx, ptr->second_code, pctx);
-
        if ((ptr->prompt == PROMPT_ABORT) && answer)
                fatal_error(ctx, 0);
 
+       if (ptr->flags & PR_AFTER_CODE)
+               answer = fix_problem(ctx, ptr->second_code, pctx);
+
        return answer;
 }
index ac73db3..323b60b 100644 (file)
@@ -93,7 +93,40 @@ struct problem_context {
 
 /* The Hurd does not support the filetype feature */
 #define PR_0_HURD_CLEAR_FILETYPE 0x00000E
-       
+
+/* Journal inode is invalid */
+#define PR_0_JOURNAL_BAD_INODE 0x00000F
+
+/* Superblock has a journal device (which we can't handle yet) */
+#define PR_0_JOURNAL_UNSUPP_DEV        0x000010
+
+/* Superblock has a bad journal device */
+#define PR_0_JOURNAL_BAD_DEV   0x000011
+
+/* Superblock has a journal UUID (which we can't handle yet) */
+#define PR_0_JOURNAL_UNSUPP_UUID 0x000012
+
+/* Superblock has a bad journal UUID */
+#define PR_0_JOURNAL_BAD_UUID  0x000013
+
+/* Journal has an unknown superblock type */
+#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014
+
+/* Journal superblock is corrupt */
+#define PR_0_JOURNAL_BAD_SUPER 0x000015
+
+/* Journal superblock is corrupt */
+#define PR_0_JOURNAL_HAS_JOURNAL 0x000016
+
+/* Superblock has recovery flag set but no journal */
+#define PR_0_JOURNAL_RECOVER_SET 0x000017
+
+/* Warning message about leaving data in the journal */
+#define PR_0_JOURNAL_RESET_JOURNAL 0x000018
+
+/* Superblock recovery flag clear - journal needs to be reset */
+#define PR_0_JOURNAL_RESET_PROMPT 0x000019
+
 /*
  * Pass 1 errors
  */
diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
new file mode 100644 (file)
index 0000000..06e1246
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * linux/fs/recovery.c
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ *
+ * Copyright 1999 Red Hat Software --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Journal recovery routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.  
+ */
+
+#ifndef __KERNEL__
+#include "jfs.h"
+#else
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jfs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/buffer.h>
+
+
+/* Release readahead buffers after use */
+static void brelse_array(struct buffer_head *b[], int n)
+{
+       while (--n >= 0)
+               brelse (b[n]);
+}
+
+
+/*
+ * When reading from the journal, we are going through the block device
+ * layer directly and so there is no readahead being done for us.  We
+ * need to implement any readahead ourselves if we want it to happen at
+ * all.  Recovery is basically one long sequential read, so make sure we
+ * do the IO in reasonably large chunks.
+ *
+ * This is not so critical that we need to be enormously clever about
+ * the readahead size, though.  128K is a purely arbitrary, good-enough
+ * fixed value.
+ */
+
+static int do_readahead(journal_t *journal, unsigned int start)
+{
+       int err;
+       unsigned int max, nbufs, next, blocknr;
+       struct buffer_head *bh;
+       
+       #define MAXBUF 8
+       struct buffer_head * bufs[MAXBUF];
+       
+       /* Do up to 128K of readahead */
+       max = start + (128 * 1024 / journal->j_blocksize);
+       if (max > journal->j_maxlen)
+               max = journal->j_maxlen;
+
+       /* Do the readahead itself.  We'll submit MAXBUF buffer_heads at
+        * a time to the block device IO layer. */
+       
+       nbufs = 0;
+       
+       for (next = start; next < max; next++) {
+               blocknr = next;
+               if (journal->j_inode)
+                       blocknr = bmap(journal->j_inode, next);
+               if (!blocknr) {
+                       printk (KERN_ERR "JFS: bad block at offset %u\n",
+                               next);
+                       err = -EIO;
+                       goto failed;
+               }
+               
+               bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+               if (!bh) {
+                       printk(KERN_ERR "JFS: readahead getblk failed\n");
+                       err = -ENOMEM;
+                       goto failed;
+               }
+
+               if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
+                       bufs[nbufs++] = bh;
+                       if (nbufs == MAXBUF) {
+                               ll_rw_block(READ, nbufs, bufs);
+                               brelse_array(bufs, nbufs);
+                               nbufs = 0;
+                       }
+               } else
+                       brelse(bh);
+       }
+
+       if (nbufs)
+               ll_rw_block(READ, nbufs, bufs);
+       err = 0;
+       
+failed:        
+       if (nbufs) 
+               brelse_array(bufs, nbufs);
+       return err;
+}
+#endif
+
+/*
+ * Read a block from the journal
+ */
+
+static int jread(struct buffer_head **bhp, journal_t *journal, 
+                unsigned int offset)
+{
+       unsigned int blocknr;
+       struct buffer_head *bh;
+
+       *bhp = NULL;
+
+       if (offset >= journal->j_maxlen)
+               return -EINVAL;
+                       
+       blocknr = offset;
+       if (journal->j_inode)
+               blocknr = bmap(journal->j_inode, offset);
+       
+       if (!blocknr) {
+               printk (KERN_ERR "JFS: bad block at offset %u\n",
+                       offset);
+               return -EIO;
+       }
+       
+       bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+       if (!bh)
+               return -ENOMEM;
+       
+       if (!buffer_uptodate(bh)) {
+               /* If this is a brand new buffer, start readahead.
+                   Otherwise, we assume we are already reading it.  */
+               if (!buffer_req(bh))
+                       do_readahead(journal, offset);
+               wait_on_buffer(bh);
+       }
+       
+       if (!buffer_uptodate(bh)) {
+               printk (KERN_ERR "JFS: Failed to read block at offset %u\n",
+                       offset);
+               brelse(bh);
+               return -EIO;
+       }
+               
+       *bhp = bh;
+       return 0;
+}
+
+
+/*
+ * Count the number of in-use tags in a journal descriptor block.
+ */
+
+int count_tags(struct buffer_head *bh, int size)
+{
+       char *                  tagp;
+       journal_block_tag_t *   tag;
+       int                     nr = 0;
+       
+       tagp = &bh->b_data[sizeof(journal_header_t)];
+       
+       while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
+               tag = (journal_block_tag_t *) tagp;
+               
+               nr++;
+               tagp += sizeof(journal_block_tag_t);
+               if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
+                       tagp += 16;
+
+               if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
+                       break;
+       }
+       
+       return nr;
+}
+
+
+/* Make sure we wrap around the log correctly! */
+#define wrap(journal, var)                                             \
+do {                                                                   \
+       if (var >= (journal)->j_last)                                   \
+               var -= ((journal)->j_last - (journal)->j_first);        \
+} while (0)
+
+/*
+ * journal_recover
+ *
+ * The primary function for recovering the log contents when mounting a
+ * journaled device.  
+ */
+
+int journal_recover(journal_t *journal)
+{
+       unsigned int            first_commit_ID, next_commit_ID;
+       unsigned long           next_log_block;
+       unsigned long           transaction_start;
+       int                     err, success = 0;
+       journal_superblock_t *  jsb;
+       journal_header_t *      tmp;
+       struct buffer_head *    bh;
+
+       /* Precompute the maximum metadata descriptors in a descriptor block */
+       int                     MAX_BLOCKS_PER_DESC;
+       MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
+                              / sizeof(journal_block_tag_t));
+
+       /* 
+        * First thing is to establish what we expect to find in the log
+        * (in terms of transaction IDs), and where (in terms of log
+        * block offsets): query the superblock.  
+        */
+
+       jsb = journal->j_superblock;
+       next_commit_ID = ntohl(jsb->s_sequence);
+       next_log_block = ntohl(jsb->s_start);
+
+       first_commit_ID = next_commit_ID;
+       
+       /* 
+        * The journal superblock's s_start field (the current log head)
+        * is always zero if, and only if, the journal was cleanly
+        * unmounted.  
+        */
+
+       if (!jsb->s_start) {
+               jfs_debug(1, "No recovery required, last transaction %d\n",
+                         ntohl(jsb->s_sequence));
+               journal->j_transaction_sequence = ++next_commit_ID;
+               return 0;
+       }
+       
+       jfs_debug(1, "Starting recovery\n");
+       
+       /*
+        * Now we walk through the log, transaction by transaction,
+        * making sure that each transaction has a commit block in the
+        * expected place.  Each complete transaction gets replayed back
+        * into the main filesystem. 
+        */
+
+       while (1) { 
+               jfs_debug(2, "Looking for commit ID %u at %lu/%lu\n",
+                         next_commit_ID, next_log_block, journal->j_last);
+               transaction_start = next_log_block;
+
+               while (next_log_block < journal->j_last) {
+                       /* Skip over each chunk of the transaction
+                        * looking either the next descriptor block or
+                        * the final commit record. */
+
+                       jfs_debug(3, "JFS: checking block %ld\n", 
+                                 next_log_block);
+                       err = jread(&bh, journal, next_log_block);
+                       if (err)
+                               goto failed;
+                       
+                       /* What kind of buffer is it? 
+                        * 
+                        * If it is a descriptor block, work out the
+                        * expected location of the next and skip to it.
+                        *
+                        * If it is the right commit block, end the
+                        * search and start recovering the transaction.
+                        *
+                        * Any non-control block, or an unexpected
+                        * control block is interpreted as old data from
+                        * a previous wrap of the log: stop recovery at
+                        * this point.  
+                        */
+               
+                       tmp = (journal_header_t *) bh->b_data;
+                       
+                       if (tmp->h_magic == htonl(JFS_MAGIC_NUMBER)) {
+                               int blocktype = ntohl(tmp->h_blocktype);
+                               jfs_debug(3, "Found magic %d\n", blocktype);
+                               
+                               if (blocktype == JFS_DESCRIPTOR_BLOCK) {
+                                       /* Work out where the next descriptor
+                                        * should be. */
+                                       next_log_block++;
+                                       next_log_block += count_tags(bh, journal->j_blocksize);
+                                       wrap(journal, next_log_block);
+                                       brelse(bh);
+                                       continue;
+                               } else if (blocktype == JFS_COMMIT_BLOCK) {
+                                       unsigned int sequence = tmp->h_sequence;
+                                       brelse(bh);
+                                       if (sequence == htonl(next_commit_ID))
+                                               goto commit;
+                                       jfs_debug(2, "found sequence %d, "
+                                                 "expected %d.\n",
+                                                 ntohl(sequence),
+                                                 next_commit_ID);
+                                       goto finished;
+                               }
+                       }
+
+                       /* We didn't recognise it?  OK, we've gone off
+                        * the tail of the log in that case. */
+                       brelse(bh);
+                       break;
+               }
+
+               goto finished;
+               
+       commit:
+               jfs_debug(2, "Found transaction %d\n", next_commit_ID);
+
+               /* OK, we have a transaction to commit.  Rewind to the
+                * start of it, gather up all of the buffers in each
+                * transaction segment, and replay the segments one by
+                * one. */
+
+               next_log_block = transaction_start;
+               
+               while (1) {
+                       int                     flags;
+                       char *                  tagp;
+                       journal_block_tag_t *   tag;
+                       struct buffer_head *    obh;
+                       struct buffer_head *    nbh;
+                       
+                       err = jread(&bh, journal, next_log_block++);
+                       wrap(journal, next_log_block);
+                       if (err)
+                               goto failed;
+
+                       tmp = (journal_header_t *) bh->b_data;
+                       /* should never happen - we just checked above - AED */
+                       J_ASSERT(tmp->h_magic == htonl(JFS_MAGIC_NUMBER));
+
+                       /* If it is the commit block, then we are all done! */
+                       if (tmp->h_blocktype == htonl(JFS_COMMIT_BLOCK)) {
+                               brelse(bh);
+                               break;
+                       }
+                       
+                       /* A descriptor block: we can now write all of
+                        * the data blocks.  Yay, useful work is finally
+                        * getting done here! */
+
+                       tagp = &bh->b_data[sizeof(journal_header_t)];
+                       
+                       while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
+                              <= journal->j_blocksize) {
+                               tag = (journal_block_tag_t *) tagp;
+                               flags = ntohl(tag->t_flags);
+                               
+                               err = jread(&obh, journal, next_log_block++);
+                               wrap(journal, next_log_block);
+                               if (err) {
+                                       /* Recover what we can, but
+                                        * report failure at the end. */
+                                       success = err;
+                                       printk (KERN_ERR 
+                                               "JFS: IO error recovering "
+                                               "block %ld in log\n",
+                                               next_log_block-1);
+                               } else {
+                                       /* can never happen if jread OK - AED */
+                                       J_ASSERT(obh != NULL);
+
+                                       /* And find a buffer for the new data
+                                        * being restored */
+                                       nbh = getblk(journal->j_dev, 
+                                                    ntohl(tag->t_blocknr),
+                                                    journal->j_blocksize);
+                                       if (nbh == NULL) {
+                                               printk(KERN_ERR 
+                                                      "JFS: Out of memory "
+                                                      "during recovery.\n");
+                                               err = -ENOMEM;
+                                               brelse(bh);
+                                               brelse(obh);
+                                               goto failed;
+                                       }
+
+                                       memcpy(nbh->b_data, obh->b_data, 
+                                              journal->j_blocksize);
+                                       if (flags & JFS_FLAG_ESCAPE) {
+                                               * ((unsigned int *) bh->b_data) = htonl(JFS_MAGIC_NUMBER);
+                                       }
+                                       
+                                       mark_buffer_dirty(nbh, 1);
+                                       /* ll_rw_block(WRITE, 1, &nbh); */
+                                       brelse(obh);
+                                       brelse(nbh);
+                               }
+                               
+                               tagp += sizeof(journal_block_tag_t);
+                               if (!(flags & JFS_FLAG_SAME_UUID))
+                                       tagp += 16;
+
+                               if (flags & JFS_FLAG_LAST_TAG)
+                                       break;
+
+                       } /* end of tag loop */
+
+                       brelse(bh);
+                       
+               } /* end of descriptor block loop */
+                       
+               /* We have now replayed that entire transaction: start
+                * looking for the next transaction. */
+               next_commit_ID++;
+       }
+               
+ finished:
+       err = success;
+       fsync_dev(journal->j_dev);
+
+ failed:
+       
+       /* Restart the log at the next transaction ID, thus invalidating
+        * any existing commit records in the log. */
+       jfs_debug(0, "JFS: recovery, exit status %d, "
+                 "recovered transactions %u to %u\n", 
+                 err, first_commit_ID, next_commit_ID);
+       journal->j_transaction_sequence = ++next_commit_ID;
+
+       return err;
+}
index 71a321c..8ab4dcf 100644 (file)
@@ -778,23 +778,48 @@ restart:
        }
 #endif
        s = (struct ext2fs_sb *) fs->super;
+
+       /*
+        * Set the device name, which is used whenever we print error
+        * or informational messages to the user.
+        */
+       if (ctx->device_name == 0 &&
+           (s->s_volume_name[0] != 0)) {
+               char *cp = malloc(sizeof(s->s_volume_name)+1);
+               if (cp) {
+                       strncpy(cp, s->s_volume_name,
+                               sizeof(s->s_volume_name));
+                       cp[sizeof(s->s_volume_name)] = 0;
+                       ctx->device_name = cp;
+               }
+       }
+       if (ctx->device_name == 0)
+               ctx->device_name = ctx->filesystem_name;
+
        /*
         * Check to see if we need to do ext3-style recovery.  If so,
         * do it, and then restart the fsck.
         */
+       retval = e2fsck_check_ext3_journal(ctx);
+       if (retval) {
+               com_err(ctx->program_name, retval,
+                       _("while checking ext3 journal for %s"),
+                       ctx->device_name);
+               ext2fs_close(ctx->fs);
+               exit(FSCK_ERROR);
+       }
+
        if (s->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
-               printf("%s: reading journal for ext3 filesystem...\n",
-                      ctx->filesystem_name);
-               ext2fs_close(fs);
-               retval = e2fsck_run_ext3_journal(ctx->filesystem_name);
+               retval = e2fsck_run_ext3_journal(ctx);
                if (retval) {
                        com_err(ctx->program_name, retval,
-                               ": couldn't load ext3 journal for %s",
-                               ctx->filesystem_name);
+                               _("while recovering ext3 journal of %s"),
+                               ctx->device_name);
                        exit(FSCK_ERROR);
                }
                goto restart;
        }
+
        /*
         * Check for compatibility with the feature sets.  We need to
         * be more stringent than ext2fs_open().
@@ -802,12 +827,12 @@ restart:
        if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
            (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
                com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
-                       "(%s)", ctx->filesystem_name);
+                       "(%s)", ctx->device_name);
                goto get_newer;
        }
        if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
                com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
-                       "(%s)", ctx->filesystem_name);
+                       "(%s)", ctx->device_name);
                goto get_newer;
        }
 #ifdef ENABLE_COMPRESSION
@@ -815,18 +840,6 @@ restart:
                com_err(ctx->program_name, 0,
                        _("Warning: compression support is experimental.\n"));
 #endif
-       if (ctx->device_name == 0 &&
-           (s->s_volume_name[0] != 0)) {
-               char *cp = malloc(sizeof(s->s_volume_name)+1);
-               if (cp) {
-                       strncpy(cp, s->s_volume_name,
-                               sizeof(s->s_volume_name));
-                       cp[sizeof(s->s_volume_name)] = 0;
-                       ctx->device_name = cp;
-               }
-       }
-       if (ctx->device_name == 0)
-               ctx->device_name = ctx->filesystem_name;
        
        /*
         * If the user specified a specific superblock, presumably the