220 lines
6.3 KiB
C
220 lines
6.3 KiB
C
/*
|
|
* Copyright 2000 Hans Reiser
|
|
*/
|
|
|
|
#include "fsck.h"
|
|
#include <limits.h>
|
|
/*#include <stdlib.h>*/
|
|
|
|
|
|
|
|
|
|
|
|
#define bh_desc(bh) ((struct reiserfs_journal_desc *)((bh)->b_data))
|
|
#define bh_commit(bh) ((struct reiserfs_journal_commit *)((bh)->b_data))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int next_expected_desc (struct super_block * s, struct buffer_head * d_bh)
|
|
{
|
|
int offset;
|
|
struct reiserfs_journal_desc * desc;
|
|
|
|
desc = (struct reiserfs_journal_desc *)d_bh->b_data;
|
|
offset = d_bh->b_blocknr - SB_JOURNAL_BLOCK (s);
|
|
return SB_JOURNAL_BLOCK (s) + ((offset + desc->j_len + 1 + 1) % JOURNAL_BLOCK_COUNT);
|
|
}
|
|
|
|
|
|
static int is_valid_transaction (struct super_block * s, struct buffer_head * d_bh)
|
|
{
|
|
struct buffer_head * c_bh;
|
|
int offset;
|
|
struct reiserfs_journal_desc *desc = (struct reiserfs_journal_desc *)d_bh->b_data;
|
|
struct reiserfs_journal_commit *commit ;
|
|
__u32 block, start_block;
|
|
|
|
|
|
offset = d_bh->b_blocknr - SB_JOURNAL_BLOCK (s);
|
|
|
|
/* ok, we have a journal description block, lets see if the transaction was valid */
|
|
block = next_expected_desc (s, d_bh) - 1;
|
|
start_block = d_bh->b_blocknr;
|
|
while(!(c_bh = bread (s->s_dev, block, s->s_blocksize))){
|
|
if (++block == SB_JOURNAL_BLOCK (s) + JOURNAL_BLOCK_COUNT)
|
|
block = SB_JOURNAL_BLOCK (s);
|
|
if (block == start_block)
|
|
return 0;
|
|
}
|
|
|
|
commit = (struct reiserfs_journal_commit *)c_bh->b_data ;
|
|
if (does_desc_match_commit (desc, commit)) {
|
|
// if (journal_compare_desc_commit (s, desc, commit)) {
|
|
/* printf ("desc and commit block do not match\n");*/
|
|
brelse (c_bh) ;
|
|
return 0;
|
|
}
|
|
brelse (c_bh);
|
|
return 1;
|
|
}
|
|
|
|
|
|
int reiserfs_replay_journal (struct super_block * s)
|
|
{
|
|
struct buffer_head * d_bh, * c_bh, * jh_bh;
|
|
struct reiserfs_journal_header * j_head;
|
|
struct reiserfs_journal_desc * j_desc;
|
|
struct reiserfs_journal_commit * j_commit;
|
|
unsigned long latest_mount_id;
|
|
unsigned long j_cur;
|
|
unsigned long j_start;
|
|
unsigned long j_size;
|
|
unsigned long mount_id, trans_id;
|
|
unsigned long t_first, t_last, t_count, t_flushed;
|
|
unsigned long t_offset;
|
|
int i;
|
|
|
|
fsck_progress ("Analyzing journal..");
|
|
|
|
j_start = SB_JOURNAL_BLOCK (s);
|
|
j_cur = 0;
|
|
j_size = rs_journal_size (s->s_rs);
|
|
t_first = 0;
|
|
t_last = 0;
|
|
latest_mount_id = 0;
|
|
|
|
/* look for the transactions with the most recent mount_id */
|
|
for (j_cur = 0; j_cur < j_size; ) {
|
|
d_bh = bread (s->s_dev, j_start + j_cur, s->s_blocksize);
|
|
if (d_bh && who_is_this (d_bh->b_data, d_bh->b_size) == THE_JDESC && is_valid_transaction (s, d_bh)) {
|
|
j_desc = (struct reiserfs_journal_desc *)d_bh->b_data;
|
|
|
|
mount_id = le32_to_cpu (j_desc->j_mount_id);
|
|
trans_id = le32_to_cpu (j_desc->j_trans_id);
|
|
|
|
if (mount_id > latest_mount_id) {
|
|
/* more recent mount_id found */
|
|
latest_mount_id = mount_id;
|
|
t_first = t_last = trans_id;
|
|
t_offset = j_cur;
|
|
t_count = 1;
|
|
} else if (mount_id == latest_mount_id) {
|
|
t_count ++;
|
|
if (trans_id > t_last)
|
|
t_last = trans_id;
|
|
if (trans_id < t_first) {
|
|
t_first = trans_id;
|
|
t_offset = j_cur;
|
|
}
|
|
}
|
|
j_cur += le32_to_cpu (j_desc->j_len) + 1;
|
|
}
|
|
j_cur ++;
|
|
brelse (d_bh);
|
|
}
|
|
|
|
/* replay only if journal header looks resonable */
|
|
jh_bh = bread (s->s_dev, j_start + j_size, s->s_blocksize);
|
|
j_head = (struct reiserfs_journal_header *)(jh_bh->b_data);
|
|
|
|
if (latest_mount_id != le32_to_cpu (j_head->j_mount_id)) {
|
|
fsck_progress ("nothing to replay (no transactions match to latest mount id)\n");
|
|
brelse (jh_bh);
|
|
return 0;
|
|
}
|
|
/* last transaction flushed - which should not be replayed */
|
|
t_flushed = le32_to_cpu (j_head->j_last_flush_trans_id);
|
|
if (t_flushed >= t_last) {
|
|
fsck_progress ("nothing to replay (no transactions older than last flushed one found)\n");
|
|
brelse (jh_bh);
|
|
return 0;
|
|
}
|
|
if (t_first > t_flushed + 1) {
|
|
if (t_flushed)
|
|
fsck_progress ("last flushed trans %lu, the oldest but newer is %lu\n",
|
|
t_flushed, t_first);
|
|
} else {
|
|
/* start replaying with first not flushed transaction */
|
|
t_first = t_flushed + 1;
|
|
t_offset = le32_to_cpu (j_head->j_first_unflushed_offset);
|
|
}
|
|
|
|
fsck_progress ("last flushed trans %lu, mount_id %lu, "
|
|
"will replay from %lu up to %lu:Yes?",
|
|
t_flushed, latest_mount_id, t_first, t_last);
|
|
if (!fsck_user_confirmed (fs, "", "Yes\n", 1))
|
|
die ("");
|
|
|
|
/* replay transactions we have found */
|
|
for (j_cur = t_offset; t_first <= t_last; t_first ++) {
|
|
unsigned long offset;
|
|
|
|
d_bh = bread (s->s_dev, j_start + j_cur, s->s_blocksize);
|
|
j_desc = (struct reiserfs_journal_desc *)d_bh->b_data;
|
|
if (who_is_this (d_bh->b_data, d_bh->b_size) != THE_JDESC ||
|
|
le32_to_cpu (j_desc->j_mount_id) != latest_mount_id ||
|
|
le32_to_cpu (j_desc->j_trans_id) != t_first)
|
|
die ("reiserfs_replay_journal: desc block not found");
|
|
|
|
offset = j_cur + 1;
|
|
j_cur += le32_to_cpu (j_desc->j_len) + 1;
|
|
j_cur %= j_size;
|
|
c_bh = bread (s->s_dev, j_start + j_cur, s->s_blocksize);
|
|
j_commit = (struct reiserfs_journal_commit *)c_bh->b_data;
|
|
if (does_desc_match_commit (j_desc, j_commit))
|
|
die ("reiserfs_replay_journal: commit block not found");
|
|
|
|
fsck_log ("Mount_id %lu, transaction %lu, desc block %lu, commit block %lu: (",
|
|
latest_mount_id, t_first, d_bh->b_blocknr, c_bh->b_blocknr);
|
|
|
|
/* replay one transaction */
|
|
for (i = 0; i < le32_to_cpu (j_desc->j_len); i ++) {
|
|
struct buffer_head * in_place, * log;
|
|
unsigned long block;
|
|
|
|
log = bread (s->s_dev, j_start + ((offset + i) % j_size), s->s_blocksize);
|
|
|
|
if (i < JOURNAL_TRANS_HALF) {
|
|
block = le32_to_cpu (j_desc->j_realblock[i]);
|
|
} else {
|
|
block = le32_to_cpu (j_commit->j_realblock[i - JOURNAL_TRANS_HALF]);
|
|
}
|
|
|
|
if (not_journalable (s, block)) {
|
|
fsck_log ("transaction %lu, block %d could not be replayed (%lu)\n",
|
|
t_first, i, block);
|
|
} else {
|
|
fsck_log (" %lu", block);
|
|
|
|
in_place = getblk (s->s_dev, block, s->s_blocksize) ;
|
|
memcpy (in_place->b_data, log->b_data, s->s_blocksize);
|
|
mark_buffer_dirty (in_place);
|
|
mark_buffer_uptodate (in_place, 1);
|
|
bwrite (in_place);
|
|
brelse (in_place);
|
|
}
|
|
brelse (log);
|
|
|
|
}
|
|
fsck_log (")\n");
|
|
|
|
brelse (d_bh);
|
|
brelse (c_bh);
|
|
j_cur ++;
|
|
j_cur %= j_size;
|
|
|
|
/* update journal header */
|
|
j_head->j_last_flush_trans_id = cpu_to_le32 (t_first);
|
|
mark_buffer_dirty (jh_bh);
|
|
bwrite (jh_bh);
|
|
}
|
|
|
|
brelse (jh_bh);
|
|
fsck_progress ("Journal replaied\n");
|
|
return 0;
|
|
}
|