mirror of https://github.com/vitalif/e2fsprogs
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
parent
ae7ecef50f
commit
e5ea6b14eb
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue