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 (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_64BIT)) return; if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT) && EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_64BIT)) return; if (journal->j_tail != 0 || EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_RECOVER)) { 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 int count_tags(journal_t *journal, struct buffer_head *bh) { char * tagp; journal_block_tag_t * tag; int nr = 0, size = journal->j_blocksize; int tag_bytes = journal_tag_bytes(journal); if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_CSUM_V2)) size -= sizeof(struct journal_block_tail); tagp = &bh->b_data[sizeof(journal_header_t)]; while ((tagp - bh->b_data + tag_bytes) <= size) { tag = (journal_block_tag_t *) tagp; nr++; tagp += tag_bytes; if (!(tag->t_flags & cpu_to_be16(JFS_FLAG_SAME_UUID))) tagp += 16; if (tag->t_flags & cpu_to_be16(JFS_FLAG_LAST_TAG)) break; } return nr; }
static int scan_revoke_records(journal_t *journal, struct buffer_head *bh, tid_t sequence, struct recovery_info *info) { journal_revoke_header_t *header; int offset, max; int record_len = 4; header = (journal_revoke_header_t *) bh->b_data; offset = sizeof(journal_revoke_header_t); max = be32_to_cpu(header->r_count); if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT)) record_len = 8; while (offset < max) { unsigned long long blocknr; int err; if (record_len == 4) blocknr = ext2fs_be32_to_cpu(*((__be32 *)(bh->b_data + offset))); else blocknr = ext2fs_be64_to_cpu(*((__be64 *)(bh->b_data + offset))); offset += record_len; err = journal_set_revoke(journal, blocknr, sequence); if (err) return err; ++info->nr_revokes; } return 0; }
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_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT) ? 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; }
/* Checksumming functions */ static int e2fsck_journal_verify_csum_type(journal_t *j, journal_superblock_t *jsb) { if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2)) return 1; return jsb->s_checksum_type == JBD2_CRC32C_CHKSUM; }
static inline unsigned long long read_tag_block(journal_t *journal, journal_block_tag_t *tag) { unsigned long long block = ext2fs_be32_to_cpu(tag->t_blocknr); if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT)) block |= (u64)ext2fs_be32_to_cpu(tag->t_blocknr_high) << 32; return block; }
static errcode_t e2fsck_journal_sb_csum_set(journal_t *j, journal_superblock_t *jsb) { __u32 crc; if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2)) return 0; crc = e2fsck_journal_sb_csum(jsb); jsb->s_checksum = ext2fs_cpu_to_be32(crc); return 0; }
static int e2fsck_journal_sb_csum_verify(journal_t *j, journal_superblock_t *jsb) { __u32 provided, calculated; if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2)) return 1; provided = ext2fs_be32_to_cpu(jsb->s_checksum); calculated = e2fsck_journal_sb_csum(jsb); return provided == calculated; }
static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag, void *buf, __u32 sequence) { __u32 provided, calculated; if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2)) return 1; sequence = ext2fs_cpu_to_be32(sequence); calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid, sizeof(j->j_superblock->s_uuid)); calculated = ext2fs_crc32c_le(calculated, (__u8 *)&sequence, sizeof(sequence)); calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize) & 0xffff; provided = ext2fs_be16_to_cpu(tag->t_checksum); return provided == ext2fs_cpu_to_be32(calculated); }
static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag, void *buf, __u32 sequence) { journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag; __u32 csum32; __u32 seq; if (!journal_has_csum_v2or3(j)) return 1; seq = ext2fs_cpu_to_be32(sequence); csum32 = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&seq, sizeof(seq)); csum32 = jbd2_chksum(j, csum32, buf, j->j_blocksize); if (JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V3)) return tag3->t_checksum == ext2fs_cpu_to_be32(csum32); return tag->t_checksum == ext2fs_cpu_to_be16(csum32); }
void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, struct buffer_head *bh, __u32 sequence) { journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag; __u32 csum32; __be32 seq; if (!journal_has_csum_v2or3(j)) return; seq = ext2fs_cpu_to_be32(sequence); csum32 = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&seq, sizeof(seq)); csum32 = jbd2_chksum(j, csum32, bh->b_data, bh->b_size); if (JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V3)) tag3->t_checksum = ext2fs_cpu_to_be32(csum32); else tag->t_checksum = ext2fs_cpu_to_be16(csum32); }
static int jbd2_commit_block_csum_verify(journal_t *j, void *buf) { struct commit_header *h; __u32 provided, calculated; if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2)) return 1; h = buf; provided = h->h_chksum[0]; h->h_chksum[0] = 0; calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid, sizeof(j->j_superblock->s_uuid)); calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize); h->h_chksum[0] = provided; provided = ext2fs_be32_to_cpu(provided); return provided == calculated; }
static int jbd2_revoke_block_csum_verify(journal_t *j, void *buf) { struct journal_revoke_tail *tail; __u32 provided, calculated; if (!JFS_HAS_INCOMPAT_FEATURE(j, JFS_FEATURE_INCOMPAT_CSUM_V2)) return 1; tail = (struct journal_revoke_tail *)(buf + j->j_blocksize - sizeof(struct journal_revoke_tail)); provided = tail->r_checksum; tail->r_checksum = 0; calculated = ext2fs_crc32c_le(~0, j->j_superblock->s_uuid, sizeof(j->j_superblock->s_uuid)); calculated = ext2fs_crc32c_le(calculated, buf, j->j_blocksize); tail->r_checksum = provided; provided = ext2fs_be32_to_cpu(provided); return provided == calculated; }
static void update_64bit_flag(journal_t *journal) { if (journal->j_format_version < 2) return; if (!EXT2_HAS_INCOMPAT_FEATURE(journal->j_fs_dev->k_fs->super, EXT4_FEATURE_INCOMPAT_64BIT)) return; if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT) && EXT2_HAS_INCOMPAT_FEATURE(journal->j_fs_dev->k_fs->super, EXT4_FEATURE_INCOMPAT_64BIT)) return; if (journal->j_tail != 0 || EXT2_HAS_INCOMPAT_FEATURE(journal->j_fs_dev->k_fs->super, EXT3_FEATURE_INCOMPAT_RECOVER)) { printf("Journal needs recovery, will not set 64bit.\n"); return; } journal->j_superblock->s_feature_incompat |= ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_64BIT); }
static errcode_t e2fsck_journal_load(journal_t *journal) { e2fsck_t ctx = journal->j_dev->k_ctx; journal_superblock_t *jsb; struct buffer_head *jbh = journal->j_sb_buffer; struct problem_context pctx; clear_problem_context(&pctx); ll_rw_block(READ, 1, &jbh); if (jbh->b_err) { com_err(ctx->device_name, jbh->b_err, _("reading journal superblock\n")); return jbh->b_err; } jsb = journal->j_superblock; /* If we don't even have JFS_MAGIC, we probably have a wrong inode */ if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER)) return e2fsck_journal_fix_bad_inode(ctx, &pctx); switch (ntohl(jsb->s_header.h_blocktype)) { case JFS_SUPERBLOCK_V1: journal->j_format_version = 1; if (jsb->s_feature_compat || jsb->s_feature_incompat || jsb->s_feature_ro_compat || jsb->s_nr_users) clear_v2_journal_fields(journal); break; case JFS_SUPERBLOCK_V2: journal->j_format_version = 2; if (ntohl(jsb->s_nr_users) > 1 && uuid_is_null(ctx->fs->super->s_journal_uuid)) clear_v2_journal_fields(journal); if (ntohl(jsb->s_nr_users) > 1) { fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx); return EXT2_ET_JOURNAL_UNSUPP_VERSION; } break; /* * These should never appear in a journal super block, so if * they do, the journal is badly corrupted. */ case JFS_DESCRIPTOR_BLOCK: case JFS_COMMIT_BLOCK: case JFS_REVOKE_BLOCK: return EXT2_ET_CORRUPT_SUPERBLOCK; /* If we don't understand the superblock major type, but there * is a magic number, then it is likely to be a new format we * just don't understand, so leave it alone. */ default: return EXT2_ET_JOURNAL_UNSUPP_VERSION; } if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES)) return EXT2_ET_UNSUPP_FEATURE; if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES)) return EXT2_ET_RO_UNSUPP_FEATURE; /* We have now checked whether we know enough about the journal * format to be able to proceed safely, so any other checks that * fail we should attempt to recover from. */ if (jsb->s_blocksize != htonl(journal->j_blocksize)) { com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK, _("%s: no valid journal superblock found\n"), ctx->device_name); return EXT2_ET_CORRUPT_SUPERBLOCK; } if (ntohl(jsb->s_maxlen) < journal->j_maxlen) journal->j_maxlen = ntohl(jsb->s_maxlen); else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) { com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK, _("%s: journal too short\n"), ctx->device_name); return EXT2_ET_CORRUPT_SUPERBLOCK; } journal->j_tail_sequence = ntohl(jsb->s_sequence); journal->j_transaction_sequence = journal->j_tail_sequence; journal->j_tail = ntohl(jsb->s_start); journal->j_first = ntohl(jsb->s_first); journal->j_last = ntohl(jsb->s_maxlen); return 0; }
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; int sz, 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_INCOMPAT_FEATURE(trans->journal, JFS_FEATURE_INCOMPAT_64BIT)) 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_INCOMPAT_FEATURE(trans->journal, JFS_FEATURE_INCOMPAT_64BIT)) *((__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 int do_one_pass(journal_t *journal, struct recovery_info *info, enum passtype pass) { unsigned int first_commit_ID, next_commit_ID; unsigned long long next_log_block; int err, success = 0; journal_superblock_t * sb; journal_header_t * tmp; struct buffer_head * bh; unsigned int sequence; int blocktype; int tag_bytes = journal_tag_bytes(journal); __u32 crc32_sum = ~0; /* Transactional Checksums */ /* Precompute the maximum metadata descriptors in a descriptor block */ int MAX_BLOCKS_PER_DESC; MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t)) / tag_bytes); /* * 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 = be32_to_cpu(sb->s_sequence); next_log_block = be32_to_cpu(sb->s_start); first_commit_ID = next_commit_ID; if (pass == PASS_SCAN) info->start_transaction = first_commit_ID; jbd_debug(1, "Starting recovery pass %d\n", pass); /* * 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) { int flags; char * tagp; journal_block_tag_t * tag; struct buffer_head * obh; struct buffer_head * nbh; cond_resched(); /* If we already know where to stop the log traversal, * check right now that we haven't gone past the end of * the log. */ if (pass != PASS_SCAN) if (tid_geq(next_commit_ID, info->end_transaction)) break; jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n", next_commit_ID, next_log_block, journal->j_last); /* Skip over each chunk of the transaction looking * either the next descriptor block or the final commit * record. */ jbd_debug(3, "JBD: checking block %ld\n", next_log_block); err = jread(&bh, journal, next_log_block); if (err) goto failed; 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 != cpu_to_be32(JFS_MAGIC_NUMBER)) { brelse(bh); break; } blocktype = be32_to_cpu(tmp->h_blocktype); sequence = be32_to_cpu(tmp->h_sequence); jbd_debug(3, "Found magic %d, sequence %d\n", blocktype, sequence); if (sequence != next_commit_ID) { brelse(bh); break; } /* 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: /* If it is a valid descriptor block, replay it * in pass REPLAY; if journal_checksums enabled, then * calculate checksums in PASS_SCAN, otherwise, * just skip over the blocks it describes. */ if (pass != PASS_REPLAY) { if (pass == PASS_SCAN && JFS_HAS_COMPAT_FEATURE(journal, JFS_FEATURE_COMPAT_CHECKSUM) && !info->end_transaction) { if (calc_chksums(journal, bh, &next_log_block, &crc32_sum)) { brelse(bh); break; } brelse(bh); continue; } next_log_block += count_tags(journal, bh); wrap(journal, next_log_block); brelse(bh); continue; } /* A descriptor block: we can now write all of * the data blocks. Yay, useful work is finally * getting done here! */ tagp = &bh->b_data[sizeof(journal_header_t)]; while ((tagp - bh->b_data + tag_bytes) <= journal->j_blocksize) { unsigned long long io_block; tag = (journal_block_tag_t *) tagp; flags = be32_to_cpu(tag->t_flags); io_block = next_log_block++; wrap(journal, next_log_block); err = jread(&obh, journal, io_block); if (err) { /* Recover what we can, but * report failure at the end. */ success = err; printk (KERN_ERR "JBD: IO error %d recovering " "block %llu in log\n", err, io_block); } else { unsigned long long blocknr; J_ASSERT(obh != NULL); blocknr = read_tag_block(tag_bytes, tag); /* If the block has been * revoked, then we're all done * here. */ if (journal_test_revoke (journal, blocknr, next_commit_ID)) { brelse(obh); ++info->nr_revoke_hits; goto skip_write; } /* Find a buffer for the new * data being restored */ nbh = __getblk(journal->j_fs_dev, blocknr, journal->j_blocksize); if (nbh == NULL) { printk(KERN_ERR "JBD: Out of memory " "during recovery.\n"); err = -ENOMEM; brelse(bh); brelse(obh); goto failed; } lock_buffer(nbh); memcpy(nbh->b_data, obh->b_data, journal->j_blocksize); if (flags & JFS_FLAG_ESCAPE) { journal_header_t *header; header = (journal_header_t *) &nbh->b_data[0]; header->h_magic = cpu_to_be32(JFS_MAGIC_NUMBER); } BUFFER_TRACE(nbh, "marking dirty"); set_buffer_uptodate(nbh); mark_buffer_dirty(nbh); BUFFER_TRACE(nbh, "marking uptodate"); ++info->nr_replays; /* ll_rw_block(WRITE, 1, &nbh); */ unlock_buffer(nbh); brelse(obh); brelse(nbh); } skip_write: tagp += tag_bytes; if (!(flags & JFS_FLAG_SAME_UUID)) tagp += 16; if (flags & JFS_FLAG_LAST_TAG) break; } brelse(bh); continue; case JFS_COMMIT_BLOCK: jbd_debug(3, "Commit block for #%u found\n", next_commit_ID); /* How to differentiate between interrupted commit * and journal corruption ? * * {nth transaction} * Checksum Verification Failed * | * ____________________ * | | * async_commit sync_commit * | | * | GO TO NEXT "Journal Corruption" * | TRANSACTION * | * {(n+1)th transanction} * | * _______|______________ * | | * Commit block found Commit block not found * | | * "Journal Corruption" | * _____________|_________ * | | * nth trans corrupt OR nth trans * and (n+1)th interrupted interrupted * before commit block * could reach the disk. * (Cannot find the difference in above * mentioned conditions. Hence assume * "Interrupted Commit".) */ /* Found an expected commit block: if checksums * are present verify them in PASS_SCAN; else not * much to do other than move on to the next sequence * number. */ if (pass == PASS_SCAN && JFS_HAS_COMPAT_FEATURE(journal, JFS_FEATURE_COMPAT_CHECKSUM)) { int chksum_err, chksum_seen; struct commit_header *cbh = (struct commit_header *)bh->b_data; unsigned found_chksum = be32_to_cpu(cbh->h_chksum[0]); chksum_err = chksum_seen = 0; jbd_debug(3, "Checksums %x %x\n", crc32_sum, found_chksum); if (info->end_transaction) { journal->j_failed_commit = info->end_transaction; brelse(bh); break; } if (crc32_sum == found_chksum && cbh->h_chksum_type == JBD2_CRC32_CHKSUM && cbh->h_chksum_size == JBD2_CRC32_CHKSUM_SIZE) chksum_seen = 1; else if (!(cbh->h_chksum_type == 0 && cbh->h_chksum_size == 0 && found_chksum == 0 && !chksum_seen)) /* * If fs is mounted using an old kernel and then * kernel with journal_chksum is used then we * get a situation where the journal flag has * checksum flag set but checksums are not * present i.e chksum = 0, in the individual * commit blocks. * Hence to avoid checksum failures, in this * situation, this extra check is added. */ chksum_err = 1; if (chksum_err) { info->end_transaction = next_commit_ID; jbd_debug(1, "Checksum_err %x %x\n", crc32_sum, found_chksum); if (!JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT)){ journal->j_failed_commit = next_commit_ID; brelse(bh); break; } } crc32_sum = ~0; } brelse(bh); next_commit_ID++; continue; case JFS_REVOKE_BLOCK: /* If we aren't in the REVOKE pass, then we can * just skip over this block. */ if (pass != PASS_REVOKE) { brelse(bh); continue; } err = scan_revoke_records(journal, bh, next_commit_ID, info); brelse(bh); if (err) goto failed; continue; default: jbd_debug(3, "Unrecognised magic %d, end of scan.\n", blocktype); brelse(bh); goto done; } } done: /* * We broke out of the log scan loop: either we came to the * known end of the log or we found an unexpected block in the * log. If the latter happened, then we know that the "current" * transaction marks the end of the valid log. */ if (pass == PASS_SCAN) { if (!info->end_transaction) info->end_transaction = next_commit_ID; } else { /* It's really bad news if different passes end up at * different places (but possible due to IO errors). */ if (info->end_transaction != next_commit_ID) { printk (KERN_ERR "JBD: recovery pass %d ended at " "transaction %u, expected %u\n", pass, next_commit_ID, info->end_transaction); if (!success) success = -EIO; } } return success; failed: 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_INCOMPAT_FEATURE(trans->journal, JFS_FEATURE_INCOMPAT_64BIT)) 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 errcode_t ext2fs_journal_load(journal_t *journal) { ext2_filsys fs = journal->j_dev->k_fs; journal_superblock_t *jsb; struct buffer_head *jbh = journal->j_sb_buffer; ll_rw_block(READ, 1, &jbh); if (jbh->b_err) return jbh->b_err; jsb = journal->j_superblock; /* If we don't even have JFS_MAGIC, we probably have a wrong inode */ if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER)) return ext2fs_journal_fix_bad_inode(fs); switch (ntohl(jsb->s_header.h_blocktype)) { case JFS_SUPERBLOCK_V1: journal->j_format_version = 1; if (jsb->s_feature_compat || jsb->s_feature_incompat || jsb->s_feature_ro_compat || jsb->s_nr_users) clear_v2_journal_fields(journal); break; case JFS_SUPERBLOCK_V2: journal->j_format_version = 2; if (ntohl(jsb->s_nr_users) > 1 && uuid_is_null(fs->super->s_journal_uuid)) clear_v2_journal_fields(journal); if (ntohl(jsb->s_nr_users) > 1) return EXT2_ET_JOURNAL_UNSUPP_VERSION; break; /* * These should never appear in a journal super block, so if * they do, the journal is badly corrupted. */ case JFS_DESCRIPTOR_BLOCK: case JFS_COMMIT_BLOCK: case JFS_REVOKE_BLOCK: return EXT2_ET_CORRUPT_JOURNAL_SB; /* If we don't understand the superblock major type, but there * is a magic number, then it is likely to be a new format we * just don't understand, so leave it alone. */ default: return EXT2_ET_JOURNAL_UNSUPP_VERSION; } if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES)) return EXT2_ET_UNSUPP_FEATURE; if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES)) return EXT2_ET_RO_UNSUPP_FEATURE; /* Checksum v1-3 are mutually exclusive features. */ if (jfs_has_feature_csum2(journal) && jfs_has_feature_csum3(journal)) return EXT2_ET_CORRUPT_JOURNAL_SB; if (journal_has_csum_v2or3(journal) && jfs_has_feature_checksum(journal)) return EXT2_ET_CORRUPT_JOURNAL_SB; if (!ext2fs_journal_verify_csum_type(journal, jsb) || !ext2fs_journal_sb_csum_verify(journal, jsb)) return EXT2_ET_CORRUPT_JOURNAL_SB; if (journal_has_csum_v2or3(journal)) journal->j_csum_seed = jbd2_chksum(journal, ~0, jsb->s_uuid, sizeof(jsb->s_uuid)); /* We have now checked whether we know enough about the journal * format to be able to proceed safely, so any other checks that * fail we should attempt to recover from. */ if (jsb->s_blocksize != htonl(journal->j_blocksize)) return EXT2_ET_CORRUPT_JOURNAL_SB; if (ntohl(jsb->s_maxlen) < journal->j_maxlen) journal->j_maxlen = ntohl(jsb->s_maxlen); else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) return EXT2_ET_CORRUPT_JOURNAL_SB; journal->j_tail_sequence = ntohl(jsb->s_sequence); journal->j_transaction_sequence = journal->j_tail_sequence; journal->j_tail = ntohl(jsb->s_start); journal->j_first = ntohl(jsb->s_first); journal->j_last = ntohl(jsb->s_maxlen); return 0; }