| /* |
| * do_journal.c --- Scribble onto the journal! |
| * |
| * Copyright (C) 2014 Oracle. This file may be redistributed |
| * under the terms of the GNU Public License. |
| */ |
| |
| #include "config.h" |
| #include <stdio.h> |
| #ifdef HAVE_GETOPT_H |
| #include <getopt.h> |
| #else |
| extern int optind; |
| extern char *optarg; |
| #endif |
| #include <ctype.h> |
| #include <unistd.h> |
| #ifdef HAVE_SYS_TIME_H |
| #include <sys/time.h> |
| #endif |
| |
| #include "debugfs.h" |
| #include "ext2fs/kernel-jbd.h" |
| #include "journal.h" |
| |
| #undef DEBUG |
| |
| #ifdef DEBUG |
| # define dbg_printf(f, a...) do {printf("JFS DEBUG: " f, ## a); \ |
| fflush(stdout); \ |
| } while (0) |
| #else |
| # define dbg_printf(f, a...) |
| #endif |
| |
| #define JOURNAL_CHECK_TRANS_MAGIC(x) \ |
| do { \ |
| if ((x)->magic != J_TRANS_MAGIC) \ |
| return EXT2_ET_INVALID_ARGUMENT; \ |
| } while (0) |
| |
| #define J_TRANS_MAGIC 0xD15EA5ED |
| #define J_TRANS_OPEN 1 |
| #define J_TRANS_COMMITTED 2 |
| struct journal_transaction_s { |
| unsigned int magic; |
| ext2_filsys fs; |
| journal_t *journal; |
| blk64_t block; |
| blk64_t start, end; |
| tid_t tid; |
| int flags; |
| }; |
| |
| typedef struct journal_transaction_s journal_transaction_t; |
| |
| static journal_t *current_journal = NULL; |
| |
| static void journal_dump_trans(journal_transaction_t *trans EXT2FS_ATTR((unused)), |
| const char *tag EXT2FS_ATTR((unused))) |
| { |
| dbg_printf("TRANS %p(%s): tid=%d start=%llu block=%llu end=%llu " |
| "flags=0x%x\n", trans, tag, trans->tid, trans->start, |
| trans->block, trans->end, trans->flags); |
| } |
| |
| static errcode_t journal_commit_trans(journal_transaction_t *trans) |
| { |
| struct buffer_head *bh, *cbh = NULL; |
| struct commit_header *commit; |
| #ifdef HAVE_SYS_TIME_H |
| struct timeval tv; |
| #endif |
| errcode_t err; |
| |
| JOURNAL_CHECK_TRANS_MAGIC(trans); |
| |
| if ((trans->flags & J_TRANS_COMMITTED) || |
| !(trans->flags & J_TRANS_OPEN)) |
| return EXT2_ET_INVALID_ARGUMENT; |
| |
| bh = getblk(trans->journal->j_dev, 0, trans->journal->j_blocksize); |
| if (bh == NULL) |
| return ENOMEM; |
| |
| /* write the descriptor block header */ |
| commit = (struct commit_header *)bh->b_data; |
| commit->h_magic = ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER); |
| commit->h_blocktype = ext2fs_cpu_to_be32(JFS_COMMIT_BLOCK); |
| commit->h_sequence = ext2fs_cpu_to_be32(trans->tid); |
| if (jfs_has_feature_checksum(trans->journal)) { |
| __u32 csum_v1 = ~0; |
| blk64_t cblk; |
| |
| cbh = getblk(trans->journal->j_dev, 0, |
| trans->journal->j_blocksize); |
| if (cbh == NULL) { |
| err = ENOMEM; |
| goto error; |
| } |
| |
| for (cblk = trans->start; cblk < trans->block; cblk++) { |
| err = journal_bmap(trans->journal, cblk, |
| &cbh->b_blocknr); |
| if (err) |
| goto error; |
| mark_buffer_uptodate(cbh, 0); |
| ll_rw_block(READ, 1, &cbh); |
| err = cbh->b_err; |
| if (err) |
| goto error; |
| csum_v1 = ext2fs_crc32_be(csum_v1, |
| (unsigned char const *)cbh->b_data, |
| cbh->b_size); |
| } |
| |
| commit->h_chksum_type = JFS_CRC32_CHKSUM; |
| commit->h_chksum_size = JFS_CRC32_CHKSUM_SIZE; |
| commit->h_chksum[0] = ext2fs_cpu_to_be32(csum_v1); |
| } else { |
| commit->h_chksum_type = 0; |
| commit->h_chksum_size = 0; |
| commit->h_chksum[0] = 0; |
| } |
| #ifdef HAVE_SYS_TIME_H |
| gettimeofday(&tv, NULL); |
| commit->h_commit_sec = ext2fs_cpu_to_be32(tv.tv_sec); |
| commit->h_commit_nsec = ext2fs_cpu_to_be32(tv.tv_usec * 1000); |
| #else |
| commit->h_commit_sec = 0; |
| commit->h_commit_nsec = 0; |
| #endif |
| |
| /* Write block */ |
| jbd2_commit_block_csum_set(trans->journal, bh); |
| err = journal_bmap(trans->journal, trans->block, &bh->b_blocknr); |
| if (err) |
| goto error; |
| |
| dbg_printf("Writing commit block at %llu:%llu\n", trans->block, |
| bh->b_blocknr); |
| mark_buffer_dirty(bh); |
| ll_rw_block(WRITE, 1, &bh); |
| err = bh->b_err; |
| if (err) |
| goto error; |
| trans->flags |= J_TRANS_COMMITTED; |
| trans->flags &= ~J_TRANS_OPEN; |
| trans->block++; |
| |
| ext2fs_set_feature_journal_needs_recovery(trans->fs->super); |
| ext2fs_mark_super_dirty(trans->fs); |
| error: |
| if (cbh) |
| brelse(cbh); |
| brelse(bh); |
| return err; |
| } |
| |
| static errcode_t journal_add_revoke_to_trans(journal_transaction_t *trans, |
| blk64_t *revoke_list, |
| size_t revoke_len) |
| { |
| journal_revoke_header_t *jrb; |
| void *buf; |
| size_t i, offset; |
| blk64_t curr_blk; |
| unsigned int sz; |
| unsigned csum_size = 0; |
| struct buffer_head *bh; |
| errcode_t err; |
| |
| JOURNAL_CHECK_TRANS_MAGIC(trans); |
| |
| if ((trans->flags & J_TRANS_COMMITTED) || |
| !(trans->flags & J_TRANS_OPEN)) |
| return EXT2_ET_INVALID_ARGUMENT; |
| |
| if (revoke_len == 0) |
| return 0; |
| |
| /* Do we need to leave space at the end for a checksum? */ |
| if (journal_has_csum_v2or3(trans->journal)) |
| csum_size = sizeof(struct journal_revoke_tail); |
| |
| curr_blk = trans->block; |
| |
| bh = getblk(trans->journal->j_dev, curr_blk, |
| trans->journal->j_blocksize); |
| if (bh == NULL) |
| return ENOMEM; |
| jrb = buf = bh->b_data; |
| jrb->r_header.h_magic = ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER); |
| jrb->r_header.h_blocktype = ext2fs_cpu_to_be32(JFS_REVOKE_BLOCK); |
| jrb->r_header.h_sequence = ext2fs_cpu_to_be32(trans->tid); |
| offset = sizeof(*jrb); |
| |
| if (jfs_has_feature_64bit(trans->journal)) |
| sz = 8; |
| else |
| sz = 4; |
| |
| for (i = 0; i < revoke_len; i++) { |
| /* Block full, write to journal */ |
| if (offset + sz > trans->journal->j_blocksize - csum_size) { |
| jrb->r_count = ext2fs_cpu_to_be32(offset); |
| jbd2_revoke_csum_set(trans->journal, bh); |
| |
| err = journal_bmap(trans->journal, curr_blk, |
| &bh->b_blocknr); |
| if (err) |
| goto error; |
| dbg_printf("Writing revoke block at %llu:%llu\n", |
| curr_blk, bh->b_blocknr); |
| mark_buffer_dirty(bh); |
| ll_rw_block(WRITE, 1, &bh); |
| err = bh->b_err; |
| if (err) |
| goto error; |
| |
| offset = sizeof(*jrb); |
| curr_blk++; |
| } |
| |
| if (revoke_list[i] >= |
| ext2fs_blocks_count(trans->journal->j_fs_dev->k_fs->super)) { |
| err = EXT2_ET_BAD_BLOCK_NUM; |
| goto error; |
| } |
| |
| if (jfs_has_feature_64bit(trans->journal)) |
| *((__u64 *)(&((char *)buf)[offset])) = |
| ext2fs_cpu_to_be64(revoke_list[i]); |
| else |
| *((__u32 *)(&((char *)buf)[offset])) = |
| ext2fs_cpu_to_be32(revoke_list[i]); |
| offset += sz; |
| } |
| |
| if (offset > 0) { |
| jrb->r_count = ext2fs_cpu_to_be32(offset); |
| jbd2_revoke_csum_set(trans->journal, bh); |
| |
| err = journal_bmap(trans->journal, curr_blk, &bh->b_blocknr); |
| if (err) |
| goto error; |
| dbg_printf("Writing revoke block at %llu:%llu\n", |
| curr_blk, bh->b_blocknr); |
| mark_buffer_dirty(bh); |
| ll_rw_block(WRITE, 1, &bh); |
| err = bh->b_err; |
| if (err) |
| goto error; |
| curr_blk++; |
| } |
| |
| error: |
| trans->block = curr_blk; |
| brelse(bh); |
| return err; |
| } |
| |
| static errcode_t journal_add_blocks_to_trans(journal_transaction_t *trans, |
| blk64_t *block_list, size_t block_len, |
| FILE *fp) |
| { |
| blk64_t curr_blk, jdb_blk; |
| size_t i, j; |
| int csum_size = 0; |
| journal_header_t *jdb; |
| journal_block_tag_t *jdbt; |
| int tag_bytes; |
| void *buf = NULL, *jdb_buf = NULL; |
| struct buffer_head *bh = NULL, *data_bh; |
| errcode_t err; |
| |
| JOURNAL_CHECK_TRANS_MAGIC(trans); |
| |
| if ((trans->flags & J_TRANS_COMMITTED) || |
| !(trans->flags & J_TRANS_OPEN)) |
| return EXT2_ET_INVALID_ARGUMENT; |
| |
| if (block_len == 0) |
| return 0; |
| |
| /* Do we need to leave space at the end for a checksum? */ |
| if (journal_has_csum_v2or3(trans->journal)) |
| csum_size = sizeof(struct journal_block_tail); |
| |
| curr_blk = jdb_blk = trans->block; |
| |
| data_bh = getblk(trans->journal->j_dev, curr_blk, |
| trans->journal->j_blocksize); |
| if (data_bh == NULL) |
| return ENOMEM; |
| buf = data_bh->b_data; |
| |
| /* write the descriptor block header */ |
| bh = getblk(trans->journal->j_dev, curr_blk, |
| trans->journal->j_blocksize); |
| if (bh == NULL) { |
| err = ENOMEM; |
| goto error; |
| } |
| jdb = jdb_buf = bh->b_data; |
| jdb->h_magic = ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER); |
| jdb->h_blocktype = ext2fs_cpu_to_be32(JFS_DESCRIPTOR_BLOCK); |
| jdb->h_sequence = ext2fs_cpu_to_be32(trans->tid); |
| jdbt = (journal_block_tag_t *)(jdb + 1); |
| |
| curr_blk++; |
| for (i = 0; i < block_len; i++) { |
| j = fread(data_bh->b_data, trans->journal->j_blocksize, 1, fp); |
| if (j != 1) { |
| err = errno; |
| goto error; |
| } |
| |
| tag_bytes = journal_tag_bytes(trans->journal); |
| |
| /* No space left in descriptor block, write it out */ |
| if ((char *)jdbt + tag_bytes > |
| (char *)jdb_buf + trans->journal->j_blocksize - csum_size) { |
| jbd2_descr_block_csum_set(trans->journal, bh); |
| err = journal_bmap(trans->journal, jdb_blk, |
| &bh->b_blocknr); |
| if (err) |
| goto error; |
| dbg_printf("Writing descriptor block at %llu:%llu\n", |
| jdb_blk, bh->b_blocknr); |
| mark_buffer_dirty(bh); |
| ll_rw_block(WRITE, 1, &bh); |
| err = bh->b_err; |
| if (err) |
| goto error; |
| |
| jdbt = (journal_block_tag_t *)(jdb + 1); |
| jdb_blk = curr_blk; |
| curr_blk++; |
| } |
| |
| if (block_list[i] >= |
| ext2fs_blocks_count(trans->journal->j_fs_dev->k_fs->super)) { |
| err = EXT2_ET_BAD_BLOCK_NUM; |
| goto error; |
| } |
| |
| /* Fill out the block tag */ |
| jdbt->t_blocknr = ext2fs_cpu_to_be32(block_list[i] & 0xFFFFFFFF); |
| jdbt->t_flags = 0; |
| if (jdbt != (journal_block_tag_t *)(jdb + 1)) |
| jdbt->t_flags |= ext2fs_cpu_to_be16(JFS_FLAG_SAME_UUID); |
| else { |
| memcpy(jdbt + tag_bytes, |
| trans->journal->j_superblock->s_uuid, |
| sizeof(trans->journal->j_superblock->s_uuid)); |
| tag_bytes += 16; |
| } |
| if (i == block_len - 1) |
| jdbt->t_flags |= ext2fs_cpu_to_be16(JFS_FLAG_LAST_TAG); |
| if (*((__u32 *)buf) == ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER)) { |
| *((__u32 *)buf) = 0; |
| jdbt->t_flags |= ext2fs_cpu_to_be16(JFS_FLAG_ESCAPE); |
| } |
| if (jfs_has_feature_64bit(trans->journal)) |
| jdbt->t_blocknr_high = ext2fs_cpu_to_be32(block_list[i] >> 32); |
| jbd2_block_tag_csum_set(trans->journal, jdbt, data_bh, |
| trans->tid); |
| |
| /* Write the data block */ |
| err = journal_bmap(trans->journal, curr_blk, |
| &data_bh->b_blocknr); |
| if (err) |
| goto error; |
| dbg_printf("Writing data block %llu at %llu:%llu tag %d\n", |
| block_list[i], curr_blk, data_bh->b_blocknr, |
| tag_bytes); |
| mark_buffer_dirty(data_bh); |
| ll_rw_block(WRITE, 1, &data_bh); |
| err = data_bh->b_err; |
| if (err) |
| goto error; |
| |
| curr_blk++; |
| jdbt = (journal_block_tag_t *)(((char *)jdbt) + tag_bytes); |
| } |
| |
| /* Write out the last descriptor block */ |
| if (jdbt != (journal_block_tag_t *)(jdb + 1)) { |
| jbd2_descr_block_csum_set(trans->journal, bh); |
| err = journal_bmap(trans->journal, jdb_blk, &bh->b_blocknr); |
| if (err) |
| goto error; |
| dbg_printf("Writing descriptor block at %llu:%llu\n", |
| jdb_blk, bh->b_blocknr); |
| mark_buffer_dirty(bh); |
| ll_rw_block(WRITE, 1, &bh); |
| err = bh->b_err; |
| if (err) |
| goto error; |
| } |
| |
| error: |
| trans->block = curr_blk; |
| if (bh) |
| brelse(bh); |
| brelse(data_bh); |
| return err; |
| } |
| |
| static blk64_t journal_guess_blocks(journal_t *journal, blk64_t data_blocks, |
| blk64_t revoke_blocks) |
| { |
| blk64_t ret = 1; |
| unsigned int bs, sz; |
| |
| /* Estimate # of revoke blocks */ |
| bs = journal->j_blocksize; |
| if (journal_has_csum_v2or3(journal)) |
| bs -= sizeof(struct journal_revoke_tail); |
| sz = jfs_has_feature_64bit(journal) ? sizeof(__u64) : sizeof(__u32); |
| ret += revoke_blocks * sz / bs; |
| |
| /* Estimate # of data blocks */ |
| bs = journal->j_blocksize - 16; |
| if (journal_has_csum_v2or3(journal)) |
| bs -= sizeof(struct journal_block_tail); |
| sz = journal_tag_bytes(journal); |
| ret += data_blocks * sz / bs; |
| |
| ret += data_blocks; |
| |
| return ret; |
| } |
| |
| static errcode_t journal_open_trans(journal_t *journal, |
| journal_transaction_t *trans, |
| blk64_t blocks) |
| { |
| trans->fs = journal->j_fs_dev->k_fs; |
| trans->journal = journal; |
| trans->flags = J_TRANS_OPEN; |
| |
| if (journal->j_tail == 0) { |
| /* Clean journal, start at the tail */ |
| trans->tid = journal->j_tail_sequence; |
| trans->start = journal->j_first; |
| } else { |
| /* Put new transaction at the head of the list */ |
| trans->tid = journal->j_transaction_sequence; |
| trans->start = journal->j_head; |
| } |
| |
| trans->block = trans->start; |
| if (trans->start + blocks > journal->j_last) |
| return ENOSPC; |
| trans->end = trans->block + blocks; |
| journal_dump_trans(trans, "new transaction"); |
| |
| trans->magic = J_TRANS_MAGIC; |
| return 0; |
| } |
| |
| static errcode_t journal_close_trans(journal_transaction_t *trans) |
| { |
| journal_t *journal; |
| |
| JOURNAL_CHECK_TRANS_MAGIC(trans); |
| |
| if (!(trans->flags & J_TRANS_COMMITTED)) |
| return 0; |
| |
| journal = trans->journal; |
| if (journal->j_tail == 0) { |
| /* Update the tail */ |
| journal->j_tail_sequence = trans->tid; |
| journal->j_tail = trans->start; |
| journal->j_superblock->s_start = ext2fs_cpu_to_be32(trans->start); |
| } |
| |
| /* Update the head */ |
| journal->j_head = trans->end + 1; |
| journal->j_transaction_sequence = trans->tid + 1; |
| |
| trans->magic = 0; |
| |
| /* Mark ourselves as needing recovery */ |
| if (!ext2fs_has_feature_journal_needs_recovery(trans->fs->super)) { |
| ext2fs_set_feature_journal_needs_recovery(trans->fs->super); |
| ext2fs_mark_super_dirty(trans->fs); |
| } |
| |
| return 0; |
| } |
| |
| #define JOURNAL_WRITE_NO_COMMIT 1 |
| static errcode_t journal_write(journal_t *journal, |
| int flags, blk64_t *block_list, |
| size_t block_len, blk64_t *revoke_list, |
| size_t revoke_len, FILE *fp) |
| { |
| blk64_t blocks; |
| journal_transaction_t trans; |
| errcode_t err; |
| |
| if (revoke_len > 0) { |
| jfs_set_feature_revoke(journal); |
| mark_buffer_dirty(journal->j_sb_buffer); |
| } |
| |
| blocks = journal_guess_blocks(journal, block_len, revoke_len); |
| err = journal_open_trans(journal, &trans, blocks); |
| if (err) |
| goto error; |
| |
| err = journal_add_blocks_to_trans(&trans, block_list, block_len, fp); |
| if (err) |
| goto error; |
| |
| err = journal_add_revoke_to_trans(&trans, revoke_list, revoke_len); |
| if (err) |
| goto error; |
| |
| if (!(flags & JOURNAL_WRITE_NO_COMMIT)) { |
| err = journal_commit_trans(&trans); |
| if (err) |
| goto error; |
| } |
| |
| err = journal_close_trans(&trans); |
| if (err) |
| goto error; |
| error: |
| return err; |
| } |
| |
| void do_journal_write(int argc, char *argv[]) |
| { |
| blk64_t *blist = NULL, *rlist = NULL; |
| size_t bn = 0, rn = 0; |
| FILE *fp = NULL; |
| int opt; |
| int flags = 0; |
| errcode_t err; |
| |
| if (current_journal == NULL) { |
| printf("Journal not open.\n"); |
| return; |
| } |
| |
| reset_getopt(); |
| while ((opt = getopt(argc, argv, "b:r:c")) != -1) { |
| switch (opt) { |
| case 'b': |
| err = read_list(optarg, &blist, &bn); |
| if (err) |
| com_err(argv[0], err, |
| "while reading block list"); |
| break; |
| case 'r': |
| err = read_list(optarg, &rlist, &rn); |
| if (err) |
| com_err(argv[0], err, |
| "while reading revoke list"); |
| break; |
| case 'c': |
| flags |= JOURNAL_WRITE_NO_COMMIT; |
| break; |
| default: |
| printf("%s [-b blocks] [-r revoke] [-c] file\n", |
| argv[0]); |
| printf("-b: Write these blocks into transaction.\n"); |
| printf("-c: Do not commit transaction.\n"); |
| printf("-r: Revoke these blocks from transaction.\n"); |
| |
| goto out; |
| } |
| } |
| |
| if (bn > 0 && optind != argc - 1) { |
| printf("Need a file to read blocks from.\n"); |
| return; |
| } |
| |
| if (bn > 0) { |
| fp = fopen(argv[optind], "r"); |
| if (fp == NULL) { |
| com_err(argv[0], errno, |
| "while opening journal data file"); |
| goto out; |
| } |
| } |
| |
| err = journal_write(current_journal, flags, blist, bn, |
| rlist, rn, fp); |
| if (err) |
| com_err("journal_write", err, "while writing journal"); |
| |
| if (fp) |
| fclose(fp); |
| out: |
| if (blist) |
| free(blist); |
| if (rlist) |
| free(rlist); |
| } |
| |
| /* Make sure we wrap around the log correctly! */ |
| #define wrap(journal, var) \ |
| do { \ |
| if (var >= (journal)->j_last) \ |
| var -= ((journal)->j_last - (journal)->j_first); \ |
| } while (0) |
| |
| /* |
| * Count the number of in-use tags in a journal descriptor block. |
| */ |
| |
| static int count_tags(journal_t *journal, char *buf) |
| { |
| char *tagp; |
| journal_block_tag_t *tag; |
| int nr = 0, size = journal->j_blocksize; |
| int tag_bytes = journal_tag_bytes(journal); |
| |
| if (journal_has_csum_v2or3(journal)) |
| size -= sizeof(struct journal_block_tail); |
| |
| tagp = buf + sizeof(journal_header_t); |
| |
| while ((tagp - buf + tag_bytes) <= size) { |
| tag = (journal_block_tag_t *) tagp; |
| |
| nr++; |
| tagp += tag_bytes; |
| if (!(tag->t_flags & ext2fs_cpu_to_be16(JFS_FLAG_SAME_UUID))) |
| tagp += 16; |
| |
| if (tag->t_flags & ext2fs_cpu_to_be16(JFS_FLAG_LAST_TAG)) |
| break; |
| } |
| |
| return nr; |
| } |
| |
| static errcode_t journal_find_head(journal_t *journal) |
| { |
| unsigned int next_commit_ID; |
| blk64_t next_log_block, head_block; |
| int err; |
| journal_superblock_t *sb; |
| journal_header_t *tmp; |
| struct buffer_head *bh; |
| unsigned int sequence; |
| int blocktype; |
| |
| /* |
| * First thing is to establish what we expect to find in the log |
| * (in terms of transaction IDs), and where (in terms of log |
| * block offsets): query the superblock. |
| */ |
| |
| sb = journal->j_superblock; |
| next_commit_ID = ext2fs_be32_to_cpu(sb->s_sequence); |
| next_log_block = ext2fs_be32_to_cpu(sb->s_start); |
| head_block = next_log_block; |
| |
| if (next_log_block == 0) |
| return 0; |
| |
| bh = getblk(journal->j_dev, 0, journal->j_blocksize); |
| if (bh == NULL) |
| return ENOMEM; |
| |
| /* |
| * Now we walk through the log, transaction by transaction, |
| * making sure that each transaction has a commit block in the |
| * expected place. Each complete transaction gets replayed back |
| * into the main filesystem. |
| */ |
| while (1) { |
| dbg_printf("Scanning for sequence ID %u at %lu/%lu\n", |
| next_commit_ID, (unsigned long)next_log_block, |
| journal->j_last); |
| |
| /* Skip over each chunk of the transaction looking |
| * either the next descriptor block or the final commit |
| * record. */ |
| err = journal_bmap(journal, next_log_block, &bh->b_blocknr); |
| if (err) |
| goto err; |
| mark_buffer_uptodate(bh, 0); |
| ll_rw_block(READ, 1, &bh); |
| err = bh->b_err; |
| if (err) |
| goto err; |
| |
| next_log_block++; |
| wrap(journal, next_log_block); |
| |
| /* What kind of buffer is it? |
| * |
| * If it is a descriptor block, check that it has the |
| * expected sequence number. Otherwise, we're all done |
| * here. */ |
| |
| tmp = (journal_header_t *)bh->b_data; |
| |
| if (tmp->h_magic != ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER)) { |
| dbg_printf("JBD2: wrong magic 0x%x\n", tmp->h_magic); |
| goto err; |
| } |
| |
| blocktype = ext2fs_be32_to_cpu(tmp->h_blocktype); |
| sequence = ext2fs_be32_to_cpu(tmp->h_sequence); |
| dbg_printf("Found magic %d, sequence %d\n", |
| blocktype, sequence); |
| |
| if (sequence != next_commit_ID) { |
| dbg_printf("JBD2: Wrong sequence %d (wanted %d)\n", |
| sequence, next_commit_ID); |
| goto err; |
| } |
| |
| /* OK, we have a valid descriptor block which matches |
| * all of the sequence number checks. What are we going |
| * to do with it? That depends on the pass... */ |
| |
| switch (blocktype) { |
| case JFS_DESCRIPTOR_BLOCK: |
| next_log_block += count_tags(journal, bh->b_data); |
| wrap(journal, next_log_block); |
| continue; |
| |
| case JFS_COMMIT_BLOCK: |
| head_block = next_log_block; |
| next_commit_ID++; |
| continue; |
| |
| case JFS_REVOKE_BLOCK: |
| continue; |
| |
| default: |
| dbg_printf("Unrecognised magic %d, end of scan.\n", |
| blocktype); |
| err = -EINVAL; |
| goto err; |
| } |
| } |
| |
| err: |
| if (err == 0) { |
| dbg_printf("head seq=%d blk=%llu\n", next_commit_ID, |
| head_block); |
| journal->j_transaction_sequence = next_commit_ID; |
| journal->j_head = head_block; |
| } |
| brelse(bh); |
| return err; |
| } |
| |
| static void update_journal_csum(journal_t *journal, int ver) |
| { |
| journal_superblock_t *jsb; |
| |
| if (journal->j_format_version < 2) |
| return; |
| |
| if (journal->j_tail != 0 || |
| ext2fs_has_feature_journal_needs_recovery( |
| journal->j_fs_dev->k_fs->super)) { |
| printf("Journal needs recovery, will not add csums.\n"); |
| return; |
| } |
| |
| /* metadata_csum implies journal csum v3 */ |
| jsb = journal->j_superblock; |
| if (ext2fs_has_feature_metadata_csum(journal->j_fs_dev->k_fs->super)) { |
| printf("Setting csum v%d\n", ver); |
| switch (ver) { |
| case 2: |
| jfs_clear_feature_csum3(journal); |
| jfs_set_feature_csum2(journal); |
| jfs_clear_feature_checksum(journal); |
| break; |
| case 3: |
| jfs_set_feature_csum3(journal); |
| jfs_clear_feature_csum2(journal); |
| jfs_clear_feature_checksum(journal); |
| break; |
| default: |
| printf("Unknown checksum v%d\n", ver); |
| break; |
| } |
| journal->j_superblock->s_checksum_type = JBD2_CRC32C_CHKSUM; |
| journal->j_csum_seed = jbd2_chksum(journal, ~0, jsb->s_uuid, |
| sizeof(jsb->s_uuid)); |
| } else { |
| jfs_clear_feature_csum3(journal); |
| jfs_clear_feature_csum2(journal); |
| jfs_set_feature_checksum(journal); |
| } |
| } |
| |
| static void update_uuid(journal_t *journal) |
| { |
| size_t z; |
| ext2_filsys fs; |
| |
| if (journal->j_format_version < 2) |
| return; |
| |
| for (z = 0; z < sizeof(journal->j_superblock->s_uuid); z++) |
| if (journal->j_superblock->s_uuid[z]) |
| break; |
| if (z == 0) |
| return; |
| |
| fs = journal->j_fs_dev->k_fs; |
| if (!ext2fs_has_feature_64bit(fs->super)) |
| return; |
| |
| if (jfs_has_feature_64bit(journal) && |
| ext2fs_has_feature_64bit(fs->super)) |
| return; |
| |
| if (journal->j_tail != 0 || |
| ext2fs_has_feature_journal_needs_recovery(fs->super)) { |
| printf("Journal needs recovery, will not set 64bit.\n"); |
| return; |
| } |
| |
| memcpy(journal->j_superblock->s_uuid, fs->super->s_uuid, |
| sizeof(fs->super->s_uuid)); |
| } |
| |
| static void update_64bit_flag(journal_t *journal) |
| { |
| if (journal->j_format_version < 2) |
| return; |
| |
| if (!ext2fs_has_feature_64bit(journal->j_fs_dev->k_fs->super)) |
| return; |
| |
| if (jfs_has_feature_64bit(journal) && |
| ext2fs_has_feature_64bit(journal->j_fs_dev->k_fs->super)) |
| return; |
| |
| if (journal->j_tail != 0 || |
| ext2fs_has_feature_journal_needs_recovery( |
| journal->j_fs_dev->k_fs->super)) { |
| printf("Journal needs recovery, will not set 64bit.\n"); |
| return; |
| } |
| |
| jfs_set_feature_64bit(journal); |
| } |
| |
| void do_journal_open(int argc, char *argv[]) |
| { |
| int opt, enable_csum = 0, csum_ver = 3; |
| journal_t *journal; |
| errcode_t err; |
| |
| if (check_fs_open(argv[0])) |
| return; |
| if (check_fs_read_write(argv[0])) |
| return; |
| if (check_fs_bitmaps(argv[0])) |
| return; |
| if (current_journal) { |
| printf("Journal is already open.\n"); |
| return; |
| } |
| if (!ext2fs_has_feature_journal(current_fs->super)) { |
| printf("Journalling is not enabled on this filesystem.\n"); |
| return; |
| } |
| |
| reset_getopt(); |
| while ((opt = getopt(argc, argv, "cv:f:")) != -1) { |
| switch (opt) { |
| case 'c': |
| enable_csum = 1; |
| break; |
| case 'f': |
| if (current_fs->journal_name) |
| free(current_fs->journal_name); |
| current_fs->journal_name = strdup(optarg); |
| break; |
| case 'v': |
| csum_ver = atoi(optarg); |
| if (csum_ver != 2 && csum_ver != 3) { |
| printf("Unknown journal csum v%d\n", csum_ver); |
| csum_ver = 3; |
| } |
| break; |
| default: |
| printf("%s: [-c] [-v ver] [-f ext_jnl]\n", argv[0]); |
| printf("-c: Enable journal checksumming.\n"); |
| printf("-v: Use this version checksum format.\n"); |
| printf("-j: Load this external journal.\n"); |
| } |
| } |
| |
| err = ext2fs_open_journal(current_fs, ¤t_journal); |
| if (err) { |
| com_err(argv[0], err, "while opening journal"); |
| return; |
| } |
| journal = current_journal; |
| |
| dbg_printf("JOURNAL: seq=%d tailseq=%d start=%lu first=%lu " |
| "maxlen=%lu\n", journal->j_tail_sequence, |
| journal->j_transaction_sequence, journal->j_tail, |
| journal->j_first, journal->j_last); |
| |
| update_uuid(journal); |
| update_64bit_flag(journal); |
| if (enable_csum) |
| update_journal_csum(journal, csum_ver); |
| |
| err = journal_find_head(journal); |
| if (err) |
| com_err(argv[0], err, "while examining journal"); |
| } |
| |
| void do_journal_close(int argc EXT2FS_ATTR((unused)), |
| char *argv[] EXT2FS_ATTR((unused))) |
| { |
| if (current_journal == NULL) { |
| printf("Journal not open.\n"); |
| return; |
| } |
| |
| ext2fs_close_journal(current_fs, ¤t_journal); |
| } |
| |
| void do_journal_run(int argc EXT2FS_ATTR((unused)), char *argv[]) |
| { |
| errcode_t err; |
| |
| if (check_fs_open(argv[0])) |
| return; |
| if (check_fs_read_write(argv[0])) |
| return; |
| if (check_fs_bitmaps(argv[0])) |
| return; |
| if (current_journal) { |
| printf("Please close the journal before recovering it.\n"); |
| return; |
| } |
| |
| err = ext2fs_run_ext3_journal(¤t_fs); |
| if (err) |
| com_err("journal_run", err, "while recovering journal"); |
| else { |
| ext2fs_clear_feature_journal_needs_recovery(current_fs->super); |
| ext2fs_mark_super_dirty(current_fs); |
| } |
| } |