| /* |
| * Copyright 2002-2004 by Hans Reiser, licensing governed by |
| * reiserfsprogs/README |
| */ |
| |
| #define _GNU_SOURCE |
| |
| #include "includes.h" |
| #include "progbar.h" |
| |
| /* compares description block with commit block. returns 0 if they differ, 1 |
| if they match */ |
| static int does_desc_match_commit (struct buffer_head *d_bh, |
| struct buffer_head *c_bh) |
| { |
| return (get_commit_trans_id (c_bh) == get_desc_trans_id (d_bh) && |
| get_commit_trans_len (c_bh) == get_desc_trans_len (d_bh)); |
| } |
| |
| |
| /* d_bh is descriptor, return number of block where commit block of this |
| transaction is to be */ |
| unsigned long commit_expected (reiserfs_filsys_t * fs, struct buffer_head * d_bh) |
| { |
| unsigned long offset; |
| struct journal_params * sb_jp; |
| |
| |
| sb_jp = sb_jp (fs->fs_ondisk_sb); |
| //desc = (struct reiserfs_journal_desc *)d_bh->b_data; |
| offset = d_bh->b_blocknr - get_jp_journal_1st_block (sb_jp); |
| return get_jp_journal_1st_block (sb_jp) + |
| ((offset + get_desc_trans_len (d_bh) + 1) % get_jp_journal_size (sb_jp)); |
| } |
| |
| |
| /* d_bh contains journal descriptor, returns number of block where descriptor |
| block of next transaction should be */ |
| unsigned long next_desc_expected (reiserfs_filsys_t * fs, struct buffer_head * d_bh) |
| { |
| unsigned long offset; |
| struct journal_params * sb_jp; |
| |
| |
| sb_jp = sb_jp (fs->fs_ondisk_sb); |
| //desc = (struct reiserfs_journal_desc *)d_bh->b_data; |
| offset = d_bh->b_blocknr - get_jp_journal_1st_block (sb_jp); |
| return get_jp_journal_1st_block (sb_jp) + |
| ((offset + get_desc_trans_len (d_bh) + 2) % get_jp_journal_size (sb_jp)); |
| } |
| |
| /* common checks for validness of a transaction */ |
| int transaction_check_content (reiserfs_filsys_t * fs, reiserfs_trans_t * trans) { |
| struct buffer_head *d_bh, *c_bh; |
| struct reiserfs_journal_desc * desc; |
| struct reiserfs_journal_commit * commit; |
| unsigned long block; |
| unsigned int trans_half, i; |
| |
| d_bh = bread (fs->fs_journal_dev, trans->desc_blocknr, fs->fs_blocksize); |
| |
| if (!d_bh || who_is_this (d_bh->b_data, d_bh->b_size) != THE_JDESC) |
| goto error_desc_brelse; |
| |
| /* read expected commit block and compare with descriptor block */ |
| c_bh = bread (fs->fs_journal_dev, commit_expected (fs, d_bh), fs->fs_blocksize); |
| if (!c_bh) |
| goto error_desc_brelse; |
| |
| if (!does_desc_match_commit (d_bh, c_bh)) |
| goto error_commit_brelse; |
| |
| /* Check that all target blocks are journalable */ |
| desc = (struct reiserfs_journal_desc *)(d_bh->b_data); |
| commit = (struct reiserfs_journal_commit *)(c_bh->b_data); |
| |
| trans_half = journal_trans_half (d_bh->b_size); |
| for (i = 0; i < get_desc_trans_len(d_bh); i++) { |
| if (i < trans_half) |
| block = le32_to_cpu (desc->j2_realblock[i]); |
| else |
| block = le32_to_cpu (commit->j3_realblock[i - trans_half]); |
| |
| if (not_journalable(fs, block)) |
| goto error_commit_brelse; |
| } |
| |
| brelse (d_bh); |
| brelse (c_bh); |
| return 1; |
| |
| error_commit_brelse: |
| brelse (c_bh); |
| error_desc_brelse: |
| brelse(d_bh); |
| return 0; |
| } |
| |
| /* common checks for validness of a transaction */ |
| int transaction_check_desc(reiserfs_filsys_t * fs, struct buffer_head * d_bh) { |
| struct buffer_head * c_bh; |
| int ret = 1; |
| |
| if (!d_bh || who_is_this (d_bh->b_data, d_bh->b_size) != THE_JDESC) |
| return 0; |
| |
| /* read expected commit block and compare with descriptor block */ |
| c_bh = bread (fs->fs_journal_dev, commit_expected (fs, d_bh), fs->fs_blocksize); |
| if (!c_bh) |
| return 0; |
| |
| if (!does_desc_match_commit (d_bh, c_bh)) |
| ret = 0; |
| |
| brelse (c_bh); |
| return ret; |
| } |
| |
| |
| /* read the journal and find the oldest and newest transactions, return number |
| of transactions found */ |
| int get_boundary_transactions (reiserfs_filsys_t * fs, |
| reiserfs_trans_t * oldest, |
| reiserfs_trans_t * newest) |
| { |
| struct reiserfs_super_block * sb; |
| unsigned long j_cur; |
| unsigned long j_start; |
| unsigned long j_size; |
| struct buffer_head * d_bh; |
| __u32 newest_trans_id, oldest_trans_id, trans_id; |
| int trans_nr; |
| |
| sb = fs->fs_ondisk_sb; |
| |
| j_start = get_jp_journal_1st_block (sb_jp (sb)); |
| j_size = get_jp_journal_size (sb_jp (sb)); |
| |
| oldest_trans_id = 0xffffffff; |
| newest_trans_id = 0; |
| |
| trans_nr = 0; |
| for (j_cur = 0; j_cur < j_size; j_cur ++) { |
| d_bh = bread (fs->fs_journal_dev, j_start + j_cur, fs->fs_blocksize); |
| if (!transaction_check_desc (fs, d_bh)) { |
| brelse (d_bh); |
| continue; |
| } |
| |
| trans_nr ++; |
| |
| trans_id = get_desc_trans_id (d_bh); |
| if (trans_id < oldest_trans_id) { |
| oldest_trans_id = trans_id; |
| |
| oldest->mount_id = get_desc_mount_id (d_bh); |
| oldest->trans_id = get_desc_trans_id (d_bh); |
| oldest->desc_blocknr = d_bh->b_blocknr; |
| oldest->trans_len = get_desc_trans_len (d_bh); |
| oldest->commit_blocknr = commit_expected (fs, d_bh); |
| oldest->next_trans_offset = next_desc_expected (fs, d_bh) - j_start; |
| } |
| |
| if (trans_id > newest_trans_id) { |
| newest_trans_id = trans_id; |
| |
| newest->mount_id = get_desc_mount_id (d_bh); |
| newest->trans_id = get_desc_trans_id (d_bh); |
| newest->desc_blocknr = d_bh->b_blocknr; |
| newest->trans_len = get_desc_trans_len (d_bh); |
| newest->commit_blocknr = commit_expected (fs, d_bh); |
| newest->next_trans_offset = next_desc_expected (fs, d_bh) - j_start; |
| } |
| |
| j_cur += get_desc_trans_len (d_bh) + 1; |
| brelse (d_bh); |
| } |
| |
| return trans_nr; |
| } |
| |
| #define TRANS_FOUND 1 |
| #define TRANS_NOT_FOUND 0 |
| |
| /* trans is a valid transaction. Look for valid transaction with smallest |
| trans id which is greater than the id of the current one */ |
| int next_transaction (reiserfs_filsys_t * fs, reiserfs_trans_t * trans, reiserfs_trans_t break_trans) |
| { |
| struct buffer_head * d_bh, * next_d_bh; |
| int found; |
| unsigned long j_start; |
| unsigned long j_offset; |
| unsigned long block; |
| |
| |
| j_start = get_jp_journal_1st_block (sb_jp (fs->fs_ondisk_sb)); |
| |
| found = TRANS_NOT_FOUND; |
| |
| if (trans->trans_id == break_trans.trans_id) |
| return found; |
| |
| /* make sure that 'trans' is a valid transaction */ |
| d_bh = bread (fs->fs_journal_dev, trans->desc_blocknr, fs->fs_blocksize); |
| if (!transaction_check_desc (fs, d_bh)) |
| die ("next_transaction: valid transaction is expected"); |
| |
| block = next_desc_expected (fs, d_bh); |
| j_offset = block - j_start; |
| |
| while (1) { |
| next_d_bh = bread (fs->fs_journal_dev, block, fs->fs_blocksize); |
| if (transaction_check_desc (fs, next_d_bh)) |
| break; |
| |
| brelse (next_d_bh); |
| j_offset ++; |
| block = j_start + (j_offset % get_jp_journal_size (sb_jp (fs->fs_ondisk_sb))); |
| } |
| |
| //next_desc = (struct reiserfs_journal_desc *)next_d_bh->b_data; |
| |
| if (break_trans.trans_id >= get_desc_trans_id (next_d_bh)) { |
| /* found transaction is newer */ |
| trans->mount_id = get_desc_mount_id (next_d_bh); |
| trans->trans_id = get_desc_trans_id (next_d_bh); |
| trans->desc_blocknr = next_d_bh->b_blocknr; |
| trans->trans_len = get_desc_trans_len (next_d_bh); |
| trans->commit_blocknr = commit_expected (fs, next_d_bh); |
| trans->next_trans_offset = next_desc_expected (fs, next_d_bh) - j_start; |
| found = TRANS_FOUND; |
| } |
| |
| brelse (d_bh); |
| brelse (next_d_bh); |
| return found; |
| } |
| |
| static void read_journal_write_in_place (reiserfs_filsys_t * fs, reiserfs_trans_t * trans, unsigned int index, |
| unsigned long in_journal, unsigned long in_place) |
| { |
| struct buffer_head * j_bh, * bh; |
| |
| j_bh = bread (fs->fs_journal_dev, in_journal, fs->fs_blocksize); |
| if (!j_bh) { |
| fprintf (stderr, "replay_one_transaction: transaction %lu: reading %lu block failed\n", |
| trans->trans_id, in_journal); |
| return; |
| } |
| if (not_journalable (fs, in_place)) { |
| fprintf (stderr, "replay_one_transaction: transaction %lu: block %ld should not be journalled (%lu)\n", |
| trans->trans_id, in_journal, in_place); |
| brelse (j_bh); |
| return; |
| } |
| |
| bh = getblk (fs->fs_dev, in_place, fs->fs_blocksize); |
| |
| memcpy (bh->b_data, j_bh->b_data, bh->b_size); |
| mark_buffer_dirty (bh); |
| mark_buffer_uptodate (bh, 1); |
| bwrite (bh); |
| brelse (bh); |
| brelse (j_bh); |
| |
| } |
| |
| |
| /* go through all blocks of transaction and call 'action' each of them */ |
| void for_each_block (reiserfs_filsys_t * fs, reiserfs_trans_t * trans, |
| action_on_block_t action) |
| { |
| struct buffer_head * d_bh, * c_bh; |
| struct reiserfs_journal_desc * desc; |
| struct reiserfs_journal_commit * commit; |
| unsigned long j_start, j_offset, j_size; |
| unsigned int i, trans_half; |
| unsigned long block; |
| |
| d_bh = bread (fs->fs_journal_dev, trans->desc_blocknr, fs->fs_blocksize); |
| if (!d_bh) { |
| reiserfs_warning (stdout, "reading descriptor block %lu failed\n", trans->desc_blocknr); |
| return; |
| } |
| |
| c_bh = bread (fs->fs_journal_dev, trans->commit_blocknr, fs->fs_blocksize); |
| if (!c_bh) { |
| reiserfs_warning (stdout, "reading commit block %lu failed\n", trans->commit_blocknr); |
| brelse (d_bh); |
| return; |
| } |
| |
| desc = (struct reiserfs_journal_desc *)(d_bh->b_data); |
| commit = (struct reiserfs_journal_commit *)(c_bh->b_data); |
| |
| /* first block of journal and size of journal */ |
| j_start = get_jp_journal_1st_block (sb_jp (fs->fs_ondisk_sb)); |
| j_size = get_jp_journal_size (sb_jp (fs->fs_ondisk_sb)); |
| |
| /* offset in the journal where the transaction starts */ |
| j_offset = trans->desc_blocknr - j_start + 1; |
| |
| trans_half = journal_trans_half (d_bh->b_size); |
| for (i = 0; i < trans->trans_len; i ++, j_offset ++) { |
| if (i < trans_half) |
| block = le32_to_cpu (desc->j2_realblock[i]); |
| else |
| block = le32_to_cpu (commit->j3_realblock[i - trans_half]); |
| action (fs, trans, i, j_start + (j_offset % j_size), block); |
| } |
| |
| brelse (d_bh); |
| brelse (c_bh); |
| } |
| |
| |
| /* transaction is supposed to be valid */ |
| int replay_one_transaction (reiserfs_filsys_t * fs, |
| reiserfs_trans_t * trans) |
| { |
| for_each_block (fs, trans, read_journal_write_in_place); |
| fsync(fs->fs_dev); |
| return 0; |
| } |
| |
| |
| void for_each_transaction (reiserfs_filsys_t * fs, action_on_trans_t action) |
| { |
| reiserfs_trans_t oldest, newest; |
| int ret = 0; |
| |
| if (!get_boundary_transactions (fs, &oldest, &newest)) |
| return; |
| |
| while (1) { |
| action (fs, &oldest); |
| if ((ret = next_transaction (fs, &oldest, newest)) == TRANS_NOT_FOUND) |
| break; |
| } |
| } |
| |
| unsigned long get_size_of_journal_or_reserved_area( |
| struct reiserfs_super_block * sb) |
| { |
| if (is_reiserfs_jr_magic_string (sb)) |
| return get_sb_reserved_for_journal (sb); |
| |
| /* with standard journal */ |
| return get_jp_journal_size (sb_jp (sb)) + 1; |
| } |
| |
| |
| __u32 advise_journal_max_trans_len (__u32 desired, __u32 journal_size /* no j_header */, |
| int blocksize, int verbose) |
| { |
| __u32 saved; |
| __u32 ratio = 1; |
| |
| if (blocksize < 4096) |
| ratio = 4096/blocksize; |
| |
| saved = desired; |
| if (!desired) |
| desired = JOURNAL_TRANS_MAX/ratio; |
| |
| if (journal_size / desired < JOURNAL_MIN_RATIO) |
| desired = journal_size / JOURNAL_MIN_RATIO; |
| |
| if (desired > JOURNAL_TRANS_MAX/ratio) |
| desired = JOURNAL_TRANS_MAX/ratio; |
| |
| if (desired < JOURNAL_TRANS_MIN/ratio) |
| desired = JOURNAL_TRANS_MIN/ratio; |
| |
| if (verbose) { |
| if (saved && saved != desired) |
| reiserfs_warning (stderr, |
| "WARNING: wrong transaction max size (%u). Changed to %u\n", |
| saved, desired); |
| } |
| |
| return desired; |
| } |
| #if 0 |
| __u32 ret_val; |
| ret_val = 0; |
| if (!desired) ret_val = JOURNAL_TRANS_MAX; |
| if (desired<journal_size/8) ret_val = journal_size/8; |
| if (desired>journal_size/2) ret_val = journal_size/2; |
| if (desired>JOURNAL_TRANS_MAX) ret_val = JOURNAL_TRANS_MAX; |
| if (ret_val) { |
| reiserfs_warning (stderr, "WARNING: Journal max trans length is wrong seting: %u, resetting to available possible %u\n", |
| desired, ret_val); |
| } else { |
| ret_val = desired; |
| } |
| return ret_val; |
| } |
| #endif |
| |
| __u32 advise_journal_max_batch (unsigned long journal_trans_max) |
| { |
| return journal_trans_max*JOURNAL_MAX_BATCH/JOURNAL_TRANS_MAX; |
| } |
| |
| |
| __u32 advise_journal_max_commit_age (void) |
| { |
| return JOURNAL_MAX_COMMIT_AGE; |
| } |
| |
| |
| __u32 advise_journal_max_trans_age (void) |
| { |
| return JOURNAL_MAX_TRANS_AGE; |
| } |
| |
| |
| int reiserfs_journal_params_check (reiserfs_filsys_t * fs) { |
| struct reiserfs_journal_header * j_head; |
| struct reiserfs_super_block * sb = fs->fs_ondisk_sb; |
| |
| j_head = (struct reiserfs_journal_header *)(fs->fs_jh_bh->b_data); |
| |
| /* Check the superblock's journal parameters. */ |
| if (!is_reiserfs_jr_magic_string (sb)) { |
| if (get_jp_journal_dev (sb_jp(sb)) != 0 || |
| get_jp_journal_1st_block (sb_jp(sb)) != get_journal_start_must (fs) || |
| get_jp_journal_size (sb_jp(sb)) != |
| journal_default_size(fs->fs_super_bh->b_blocknr, fs->fs_blocksize)) |
| { |
| reiserfs_warning (stderr, |
| "\nreiserfs_open_journal: wrong journal parameters found in the " |
| "super block. \nYou should run reiserfsck with --rebuild-sb to " |
| "check your superblock consistency.\n\n"); |
| |
| return 1; |
| } |
| } |
| |
| if (memcmp(&j_head->jh_journal, sb_jp (sb), sizeof(struct journal_params))) { |
| if (!is_reiserfs_jr_magic_string (sb)) { |
| reiserfs_warning (stderr, |
| "\nreiserfs_open_journal: journal parameters from the superblock " |
| "does not match \nto the journal headers ones. It looks like that " |
| "you created your fs with old\nreiserfsprogs. Journal header is " |
| "fixed.\n\n", fs->fs_j_file_name); |
| |
| memcpy(&j_head->jh_journal, sb_jp(sb), sizeof(struct journal_params)); |
| mark_buffer_dirty(fs->fs_jh_bh); |
| bwrite(fs->fs_jh_bh); |
| } else { |
| reiserfs_warning (stderr, |
| "\nreiserfs_open_journal: journal parameters from the super block " |
| "does not match \nto journal parameters from the journal. You should " |
| "run reiserfsck with --rebuild-sb to check your superblock consistency.\n\n"); |
| |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* read journal header and make sure that it matches with the filesystem |
| opened */ |
| int reiserfs_open_journal (reiserfs_filsys_t * fs, char * j_filename, int flags) { |
| struct reiserfs_super_block * sb; |
| __u64 count; |
| |
| sb = fs->fs_ondisk_sb; |
| |
| if (!j_filename) { |
| /* |
| if (is_reiserfs_jr_magic_string (sb)) { |
| // create a special file to access journal |
| return 1; |
| } */ |
| |
| j_filename = fs->fs_file_name; |
| } else if (!is_reiserfs_jr_magic_string (sb)) { |
| /* make sure that name specified is a correct name */ |
| if (strcmp (j_filename, fs->fs_file_name)) { |
| reiserfs_warning (stderr, "Filesystem with standard journal found, " |
| "wrong name of specified journal device %s \n", j_filename); |
| return 2; |
| } |
| } |
| |
| fs->fs_journal_dev = open (j_filename, flags |
| #if defined(O_LARGEFILE) |
| | O_LARGEFILE |
| #endif |
| ); |
| if (fs->fs_journal_dev == -1) |
| return -1; |
| |
| asprintf (&fs->fs_j_file_name, "%s", j_filename); |
| |
| if (get_jp_journal_size(sb_jp(sb)) < JOURNAL_MIN_SIZE) { |
| reiserfs_warning (stderr, "Journal of (%lu) block size found on " |
| "specified journal device %s.\nMust be not less than (%lu).\n", |
| get_jp_journal_size (sb_jp (sb)) + 1, j_filename, |
| JOURNAL_MIN_SIZE + 1); |
| close(fs->fs_journal_dev); |
| return 1; |
| } |
| |
| if (!(count = count_blocks (j_filename, fs->fs_blocksize))) { |
| close(fs->fs_journal_dev); |
| return -1; |
| } |
| |
| if (get_jp_journal_1st_block (sb_jp (sb)) + |
| get_jp_journal_size (sb_jp (sb)) + 1 > count) |
| { |
| reiserfs_warning (stderr, "Detected journal on specified device %s " |
| "does not fit to the device.\nStart block (%lu) + " |
| "size (%lu) less than device size (%lu).\n", |
| j_filename, get_jp_journal_1st_block(sb_jp (sb)), |
| get_jp_journal_size(sb_jp (sb)) + 1, count); |
| close(fs->fs_journal_dev); |
| return 1; |
| } |
| |
| /* read journal header */ |
| fs->fs_jh_bh = bread (fs->fs_journal_dev, |
| get_jp_journal_1st_block (sb_jp (sb)) + |
| get_jp_journal_size (sb_jp (sb)), |
| fs->fs_blocksize); |
| |
| if (!fs->fs_jh_bh) { |
| reiserfs_warning (stderr, "reiserfs_open_journal: bread failed " |
| "reading journal header.\n"); |
| close(fs->fs_journal_dev); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* initialize super block's journal related fields and journal header fields. |
| * If len is 0 - make journal of default size */ |
| int reiserfs_create_journal( |
| reiserfs_filsys_t * fs, |
| char * j_device, /* journal device name */ |
| unsigned long offset, /* journal offset on the j_device */ |
| unsigned long len, /* including journal header */ |
| int transaction_max_size, |
| int force) |
| { |
| struct stat st; |
| struct buffer_head * bh; |
| struct reiserfs_journal_header * jh; |
| struct reiserfs_super_block * sb; |
| unsigned long blocks; |
| |
| sb = fs->fs_ondisk_sb; |
| |
| if (!j_device || !strcmp (j_device, fs->fs_file_name)) { |
| /* Journal is to be on the host device, check the amount space for the |
| * journal on it. */ |
| len = len ? len : journal_default_size(fs->fs_super_bh->b_blocknr, |
| fs->fs_blocksize) + 1; |
| |
| offset = offset ? offset : get_journal_start_must(fs); |
| |
| if (offset < get_journal_start_must(fs)) { |
| reiserfs_warning (stderr, "reiserfs_create_journal: offset is " |
| "%lu, but it cannot be less then %llu on the device %s\n", |
| offset, get_journal_start_must(fs), j_device); |
| return 0; |
| } |
| |
| if (!is_block_count_correct(offset, fs->fs_blocksize, |
| get_sb_block_count(sb), len)) |
| { |
| /* host device does not contain enough blocks */ |
| reiserfs_warning (stderr, "reiserfs_create_journal: cannot create " |
| "a journal of %lu blocks with %lu offset on %u blocks\n", |
| len, offset, get_sb_block_count(sb)); |
| return 0; |
| } |
| |
| j_device = fs->fs_file_name; |
| |
| |
| st.st_rdev = 0; |
| } else { |
| /* journal is to be on separate device */ |
| if (!(blocks = count_blocks (j_device, fs->fs_blocksize))) |
| return 0; |
| |
| if (!len) { |
| /* default size of a journal on a separate device is whole device */ |
| if (blocks < offset) { |
| reiserfs_warning (stderr, "reiserfs_create_journal: offset is " |
| "%lu, blocks on device %lu\n", offset, blocks); |
| return 0; |
| } |
| /* XXX jdm: This can end up being huge and could result |
| * in an unmountable file system: |
| * len = blocks - offset; */ |
| len = journal_default_size(fs->fs_super_bh->b_blocknr, |
| fs->fs_blocksize) + 1; |
| |
| } |
| |
| if (!force && len > journal_default_size (fs->fs_super_bh->b_blocknr, |
| fs->fs_blocksize) + 1) |
| { |
| unsigned long journal_max = journal_max_size (fs->fs_super_bh->b_blocknr, fs->fs_blocksize); |
| fflush(stderr); |
| |
| reiserfs_warning (stdout, "\n*** You've specified a journal " |
| "size larger than the default size of " |
| "%lu\n*** blocks. This may slow down " |
| "journal initialization and mounting " |
| "of\n*** the file system.%s", |
| journal_default_size(fs->fs_super_bh->b_blocknr, fs->fs_blocksize) + 1, |
| len > journal_max ? " " : "\n"); |
| if (len > journal_max) |
| reiserfs_warning (stdout, "On 32-bit systems, and on " |
| "64-bit systems with\n*** limited " |
| "memory, this may also cause the file " |
| "system to be unmountable.\n*** Please " |
| "consider using a journal size " |
| "<= %lu blocks.\n\nFile system creation " |
| "failed. You may override this behavior " |
| "with the -f option.\n", journal_max); |
| return 0; |
| } |
| |
| if (len < blocks) |
| reiserfs_warning (stdout, "\n\n*** Your journal device is %lu " |
| "blocks, but your journal is only %lu " |
| "blocks.\n*** You may want to consider " |
| "resizing the journal device to avoid " |
| "wasting space.\n\n", blocks, len); |
| |
| if (blocks < offset + len) { |
| reiserfs_warning (stderr, "reiserfs_create_journal: no enough " |
| "blocks on device %lu, needed %lu\n", blocks, offset + len); |
| return 0; |
| } |
| |
| if (stat (j_device, &st) == -1) { |
| reiserfs_warning (stderr, "reiserfs_create_journal: stat %s failed" |
| ": %s\n", j_device, strerror(errno)); |
| return 0; |
| } |
| /* |
| if (!S_ISBLK (st.st_mode)) { |
| reiserfs_warning (stderr, "reiserfs_create_journal: " |
| "%s is not a block device (%x)\n", j_device, st.st_rdev); |
| return 0; |
| } |
| */ |
| } |
| |
| fs->fs_journal_dev = open (j_device, O_RDWR |
| #if defined(O_LARGEFILE) |
| | O_LARGEFILE |
| #endif |
| ); |
| if (fs->fs_journal_dev == -1) { |
| reiserfs_warning (stderr, "reiserfs_create_journal: could not open " |
| "%s: %s\n", j_device, strerror(errno)); |
| return 0; |
| } |
| |
| asprintf (&fs->fs_j_file_name, "%s", j_device); |
| |
| if (len < JOURNAL_MIN_SIZE + 1) { |
| reiserfs_warning (stderr, "WARNING: Journal size (%u) is less, than " |
| "minimal supported journal size (%u).\n", len, JOURNAL_MIN_SIZE + 1); |
| return 0; |
| } |
| /* get journal header */ |
| bh = getblk (fs->fs_journal_dev, offset + len - 1, fs->fs_blocksize); |
| if (!bh) { |
| reiserfs_warning (stderr, "reiserfs_create_journal: getblk failed\n"); |
| return 0; |
| } |
| |
| /* fill journal header */ |
| jh = (struct reiserfs_journal_header *)bh->b_data; |
| set_jp_journal_1st_block(&jh->jh_journal, offset); |
| set_jp_journal_dev(&jh->jh_journal, st.st_rdev); |
| set_jp_journal_magic(&jh->jh_journal, get_random()); |
| |
| set_jp_journal_size(&jh->jh_journal, len - 1); |
| set_jp_journal_max_trans_len(&jh->jh_journal, advise_journal_max_trans_len( |
| transaction_max_size, len - 1, fs->fs_blocksize, 1)); |
| set_jp_journal_max_batch(&jh->jh_journal, advise_journal_max_batch( |
| get_jp_journal_max_trans_len(&jh->jh_journal))); |
| set_jp_journal_max_commit_age(&jh->jh_journal, |
| advise_journal_max_commit_age()); |
| set_jp_journal_max_trans_age(&jh->jh_journal, |
| advise_journal_max_trans_age ()); |
| |
| mark_buffer_uptodate (bh, 1); |
| mark_buffer_dirty (bh); |
| |
| fs->fs_jh_bh = bh; |
| |
| /* make a copy of journal header in the super block */ |
| memcpy (sb_jp (sb), &jh->jh_journal, sizeof (struct journal_params)); |
| mark_buffer_dirty (fs->fs_super_bh); |
| |
| return 1; |
| } |
| |
| |
| /* brelse journal header, flush all dirty buffers, close device, open, read |
| journal header */ |
| void reiserfs_reopen_journal (reiserfs_filsys_t * fs, int flag) |
| { |
| unsigned long jh_block; |
| |
| |
| if (!reiserfs_journal_opened (fs)) |
| return; |
| |
| jh_block = fs->fs_jh_bh->b_blocknr; |
| brelse (fs->fs_jh_bh); |
| flush_buffers (fs->fs_journal_dev); |
| invalidate_buffers (fs->fs_journal_dev); |
| if (close (fs->fs_journal_dev)) |
| die ("reiserfs_reopen_journal: closed failed: %s", strerror(errno)); |
| |
| fs->fs_journal_dev = open (fs->fs_j_file_name, flag |
| #if defined(O_LARGEFILE) |
| | O_LARGEFILE |
| #endif |
| ); |
| if (fs->fs_journal_dev == -1) |
| die ("reiserfs_reopen_journal: could not reopen journal device"); |
| |
| fs->fs_jh_bh = bread (fs->fs_journal_dev, jh_block, fs->fs_blocksize); |
| if (!fs->fs_jh_bh) |
| die ("reiserfs_reopen_journal: reading journal header failed"); |
| } |
| |
| |
| int reiserfs_journal_opened (reiserfs_filsys_t * fs) |
| { |
| return fs->fs_jh_bh ? 1 : 0; |
| } |
| |
| |
| void reiserfs_flush_journal (reiserfs_filsys_t * fs) |
| { |
| if (!reiserfs_journal_opened (fs)) |
| return; |
| flush_buffers (fs->fs_journal_dev); |
| } |
| |
| |
| void reiserfs_free_journal (reiserfs_filsys_t * fs) |
| { |
| if (!reiserfs_journal_opened (fs)) |
| return; |
| brelse (fs->fs_jh_bh); |
| fs->fs_jh_bh = 0; |
| free (fs->fs_j_file_name); |
| fs->fs_j_file_name = 0; |
| } |
| |
| |
| void reiserfs_close_journal (reiserfs_filsys_t * fs) |
| { |
| reiserfs_flush_journal (fs); |
| reiserfs_free_journal (fs); |
| |
| } |
| |
| /* update journal header */ |
| static void update_journal_header (reiserfs_filsys_t * fs, |
| struct buffer_head * bh_jh, |
| reiserfs_trans_t *trans) |
| { |
| struct reiserfs_journal_header * j_head; |
| |
| j_head = (struct reiserfs_journal_header *)(bh_jh->b_data); |
| |
| /* update journal header */ |
| set_jh_last_flushed (j_head, trans->trans_id); |
| set_jh_mount_id (j_head, trans->mount_id); |
| set_jh_replay_start_offset (j_head, trans->next_trans_offset); |
| mark_buffer_dirty (bh_jh); |
| bwrite (bh_jh); |
| fsync(fs->fs_journal_dev); |
| } |
| |
| /* fixme: what should be done when not all transactions can be replayed in proper order? */ |
| int replay_journal (reiserfs_filsys_t * fs) |
| { |
| struct buffer_head * bh; |
| struct reiserfs_journal_header * j_head; |
| reiserfs_trans_t cur, newest, control; |
| int replayed, ret; |
| struct progbar progbar; |
| int trans_count; |
| |
| if (!reiserfs_journal_opened (fs)) |
| reiserfs_panic ("replay_journal: journal is not opened"); |
| |
| if (!is_opened_rw (fs)) |
| reiserfs_panic ("replay_journal: fs is not opened with write perms"); |
| |
| |
| reiserfs_warning (stderr, "Replaying journal: "); |
| bh = fs->fs_jh_bh; |
| |
| j_head = (struct reiserfs_journal_header *)(bh->b_data); |
| control.mount_id = get_jh_mount_id (j_head); |
| control.trans_id = get_jh_last_flushed (j_head); |
| control.desc_blocknr = get_jh_replay_start_offset (j_head); |
| |
| trans_count = get_boundary_transactions (fs, &cur, &newest); |
| if (!trans_count) { |
| reiserfs_warning (stderr, "No transactions found\n"); |
| return 0; |
| } |
| |
| /* Smth strange with journal header or journal. We cannot say for sure what was the last |
| replaied transaction, but relying on JH data is preferable. */ |
| |
| replayed = 0; |
| ret = TRANS_FOUND; |
| |
| progbar_init(&progbar, " trans", stderr); |
| /* Looking to the first valid not replayed transaction. */ |
| while (1) { |
| if (cur.mount_id == control.mount_id && |
| cur.trans_id > control.trans_id) |
| break; |
| |
| if ((ret = next_transaction (fs, &cur, newest)) != TRANS_FOUND) |
| break; |
| } |
| |
| while (ret == TRANS_FOUND) { |
| /* If not the next transaction to be replayed, break out here. */ |
| if ((cur.mount_id != control.mount_id) || |
| (cur.trans_id != control.trans_id + 1 && control.trans_id)) |
| break; |
| |
| if (!transaction_check_content(fs, &cur)) { |
| progbar_clear(&progbar); |
| reiserfs_warning (stderr, "Trans broken: mountid %lu, transid %lu, desc %lu, " |
| "len %lu, commit %lu, next trans offset %lu\n", cur.mount_id, cur.trans_id, |
| cur.desc_blocknr, cur.trans_len, cur.commit_blocknr, cur.next_trans_offset); |
| break; |
| } |
| |
| reiserfs_warning (stderr, "Trans replayed: mountid %lu, transid %lu, desc %lu, " |
| "len %lu, commit %lu, next trans offset %lu\n", |
| cur.mount_id, cur.trans_id, cur.desc_blocknr, cur.trans_len, |
| cur.commit_blocknr, cur.next_trans_offset); |
| replay_one_transaction (fs, &cur); |
| update_journal_header (fs, bh, &cur); |
| control = cur; |
| replayed ++; |
| |
| progbar_update(&progbar, "Replaying journal", replayed, trans_count, |
| replayed); |
| |
| ret = next_transaction (fs, &cur, newest); |
| } |
| progbar_clear(&progbar); |
| |
| reiserfs_warning (stderr, "\rReplaying journal: Done.\nReiserfs journal '%s' in blocks [%u..%u]: %d " |
| "transactions replayed\n", fs->fs_j_file_name, |
| get_jp_journal_1st_block(sb_jp(fs->fs_ondisk_sb)), |
| get_jp_journal_1st_block(sb_jp(fs->fs_ondisk_sb)) + |
| get_jp_journal_size(sb_jp(fs->fs_ondisk_sb)) + 1, |
| replayed); |
| |
| mark_buffer_dirty (fs->fs_super_bh); |
| bwrite (fs->fs_super_bh); |
| |
| update_journal_header (fs, bh, &newest); |
| |
| return 0; |
| } |