e2fsck: Fix potential data corruptor bug in journal recovery

While synchronizing e2fsck's recovery.c with the latest 2.6 kernel
sources, I discovered a serious bug that apparently had been fixed in
the kernel sometime between Deceber 2003 and April 2005, but which had
not been carried over to e2fsprogs.  Specifically, when blocks whose
first 4 bytes are JFS_MAGIC_NUMBER (0xc03b3998) are written into the
journal, the first 4 bytes zero'ed out.  A one character typo meant
that when the blocks were replayed by e2fsck, the JFS_MAGIC_NUMBER
would not be restored.

Oops.

Fortunately, it is *highly* unlikely that ext4 metadata blocks will
contain that magic number in the first four bytes, and data=journalled
is a relatively rarely used.

This commit fixes this bug, as well as updating e2fsck's recovery.c to
be in sync with 2.6.25.

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
bitmap-optimize
Theodore Ts'o 2008-05-20 14:51:14 -04:00
parent ae7ecef50f
commit e5ea6b14eb
2 changed files with 92 additions and 83 deletions

View File

@ -59,6 +59,10 @@ typedef struct {
#define kmalloc(len,flags) malloc(len)
#define kfree(p) free(p)
#define cond_resched() do { } while (0)
typedef unsigned int __be32;
/*
* We use the standard libext2fs portability tricks for inline
* functions.

View File

@ -1,5 +1,5 @@
/*
* linux/fs/recovery.c
* linux/fs/jbd/recovery.c
*
* Written by Stephen C. Tweedie <sct@redhat.com>, 1999
*
@ -46,7 +46,7 @@ static int scan_revoke_records(journal_t *, struct buffer_head *,
#ifdef __KERNEL__
/* Release readahead buffers after use */
void journal_brelse_array(struct buffer_head *b[], int n)
static void journal_brelse_array(struct buffer_head *b[], int n)
{
while (--n >= 0)
brelse (b[n]);
@ -137,7 +137,10 @@ static int jread(struct buffer_head **bhp, journal_t *journal,
*bhp = NULL;
J_ASSERT (offset < journal->j_maxlen);
if (offset >= journal->j_maxlen) {
printk(KERN_ERR "JBD: corrupted journal superblock\n");
return -EIO;
}
err = journal_bmap(journal, offset, &blocknr);
@ -188,10 +191,10 @@ static int count_tags(struct buffer_head *bh, int size)
nr++;
tagp += sizeof(journal_block_tag_t);
if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
if (!(tag->t_flags & cpu_to_be32(JFS_FLAG_SAME_UUID)))
tagp += 16;
if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
if (tag->t_flags & cpu_to_be32(JFS_FLAG_LAST_TAG))
break;
}
@ -207,7 +210,7 @@ do { \
} while (0)
/**
* int journal_recover(journal_t *journal) - recovers a on-disk journal
* journal_recover - recovers a on-disk journal
* @journal: the journal to recover
*
* The primary function for recovering the log contents when mounting a
@ -236,8 +239,8 @@ int journal_recover(journal_t *journal)
if (!sb->s_start) {
jbd_debug(1, "No recovery required, last transaction %d\n",
(int)ntohl(sb->s_sequence));
journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
be32_to_cpu(sb->s_sequence));
journal->j_transaction_sequence = be32_to_cpu(sb->s_sequence) + 1;
return 0;
}
@ -247,10 +250,10 @@ int journal_recover(journal_t *journal)
if (!err)
err = do_one_pass(journal, &info, PASS_REPLAY);
jbd_debug(0, "JBD: recovery, exit status %d, "
jbd_debug(1, "JBD: recovery, exit status %d, "
"recovered transactions %u to %u\n",
err, info.start_transaction, info.end_transaction);
jbd_debug(0, "JBD: Replayed %d and revoked %d/%d blocks\n",
jbd_debug(1, "JBD: Replayed %d and revoked %d/%d blocks\n",
info.nr_replays, info.nr_revoke_hits, info.nr_revokes);
/* Restart the log at the next transaction ID, thus invalidating
@ -263,7 +266,7 @@ int journal_recover(journal_t *journal)
}
/**
* int journal_skip_recovery() - Start journal and wipe exiting records
* journal_skip_recovery - Start journal and wipe exiting records
* @journal: journal to startup
*
* Locate any valid recovery information from the journal and set up the
@ -292,9 +295,9 @@ int journal_skip_recovery(journal_t *journal)
++journal->j_transaction_sequence;
} else {
#ifdef CONFIG_JBD_DEBUG
int dropped = info.end_transaction - ntohl(sb->s_sequence);
int dropped = info.end_transaction - be32_to_cpu(sb->s_sequence);
#endif
jbd_debug(0,
jbd_debug(1,
"JBD: ignoring %d transaction%s from the journal.\n",
dropped, (dropped == 1) ? "" : "s");
journal->j_transaction_sequence = ++info.end_transaction;
@ -328,8 +331,8 @@ static int do_one_pass(journal_t *journal,
*/
sb = journal->j_superblock;
next_commit_ID = ntohl(sb->s_sequence);
next_log_block = ntohl(sb->s_start);
next_commit_ID = be32_to_cpu(sb->s_sequence);
next_log_block = be32_to_cpu(sb->s_start);
first_commit_ID = next_commit_ID;
if (pass == PASS_SCAN)
@ -351,6 +354,8 @@ static int do_one_pass(journal_t *journal,
struct buffer_head * obh;
struct buffer_head * nbh;
cond_resched();
/* If we already know where to stop the log traversal,
* check right now that we haven't gone past the end of
* the log. */
@ -382,13 +387,13 @@ static int do_one_pass(journal_t *journal,
tmp = (journal_header_t *)bh->b_data;
if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
if (tmp->h_magic != cpu_to_be32(JFS_MAGIC_NUMBER)) {
brelse(bh);
break;
}
blocktype = ntohl(tmp->h_blocktype);
sequence = ntohl(tmp->h_sequence);
blocktype = be32_to_cpu(tmp->h_blocktype);
sequence = be32_to_cpu(tmp->h_sequence);
jbd_debug(3, "Found magic %d, sequence %d\n",
blocktype, sequence);
@ -424,7 +429,7 @@ static int do_one_pass(journal_t *journal,
unsigned long io_block;
tag = (journal_block_tag_t *) tagp;
flags = ntohl(tag->t_flags);
flags = be32_to_cpu(tag->t_flags);
io_block = next_log_block++;
wrap(journal, next_log_block);
@ -435,13 +440,13 @@ static int do_one_pass(journal_t *journal,
success = err;
printk (KERN_ERR
"JBD: IO error %d recovering "
"block %lu in log\n",
"block %ld in log\n",
err, io_block);
} else {
unsigned long blocknr;
J_ASSERT(obh != NULL);
blocknr = ntohl(tag->t_blocknr);
blocknr = be32_to_cpu(tag->t_blocknr);
/* If the block has been
* revoked, then we're all done
@ -473,8 +478,8 @@ static int do_one_pass(journal_t *journal,
memcpy(nbh->b_data, obh->b_data,
journal->j_blocksize);
if (flags & JFS_FLAG_ESCAPE) {
*((unsigned int *)bh->b_data) =
htonl(JFS_MAGIC_NUMBER);
*((__be32 *)nbh->b_data) =
cpu_to_be32(JFS_MAGIC_NUMBER);
}
BUFFER_TRACE(nbh, "marking dirty");
@ -570,13 +575,13 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
header = (journal_revoke_header_t *) bh->b_data;
offset = sizeof(journal_revoke_header_t);
max = ntohl(header->r_count);
max = be32_to_cpu(header->r_count);
while (offset < max) {
unsigned long blocknr;
int err;
blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
blocknr = be32_to_cpu(* ((__be32 *) (bh->b_data+offset)));
offset += 4;
err = journal_set_revoke(journal, blocknr, sequence);
if (err)