/*
* 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);
}
}