blob: 656dc4dc1a3d94cd463ecc0ac39d0c8f40dc76dc [file] [log] [blame]
/*
* 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;
}