/* * Copyright 2000 Hans Reiser */ #include "fsck.h" #include /*#include */ #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; }