/* * 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[], int sci_idx EXT2FS_ATTR((unused)), void *infop EXT2FS_ATTR((unused))) { 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 sci_idx EXT2FS_ATTR((unused)), void *infop EXT2FS_ATTR((unused))) { 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("-f: 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)), int sci_idx EXT2FS_ATTR((unused)), void *infop 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[], int sci_idx EXT2FS_ATTR((unused)), void *infop EXT2FS_ATTR((unused))) { 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); } }