static void print_journal_information(ext2_filsys fs) { errcode_t retval; char buf[1024]; char str[80]; unsigned int i; journal_superblock_t *jsb; /* Get the journal superblock */ if ((retval = io_channel_read_blk64(fs->io, fs->super->s_first_data_block + 1, -1024, buf))) { com_err(program_name, retval, "%s", _("while reading journal superblock")); exit(1); } jsb = (journal_superblock_t *) buf; if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) { com_err(program_name, 0, "%s", _("Couldn't find journal superblock magic numbers")); exit(1); } if (jsb->s_feature_compat & ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM)) printf(_("Journal checksum type: crc32\n")); if (jsb->s_feature_incompat & ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2)) printf(_("Journal checksum type: %s\n" "Journal checksum: 0x%08x\n"), journal_checksum_type_str(jsb->s_checksum_type), ext2fs_be32_to_cpu(jsb->s_checksum)); printf(_("\nJournal block size: %u\n" "Journal length: %u\n" "Journal first block: %u\n" "Journal sequence: 0x%08x\n" "Journal start: %u\n" "Journal number of users: %u\n"), (unsigned int)ntohl(jsb->s_blocksize), (unsigned int)ntohl(jsb->s_maxlen), (unsigned int)ntohl(jsb->s_first), (unsigned int)ntohl(jsb->s_sequence), (unsigned int)ntohl(jsb->s_start), (unsigned int)ntohl(jsb->s_nr_users)); for (i=0; i < ntohl(jsb->s_nr_users); i++) { uuid_unparse(&jsb->s_users[i*16], str); printf(i ? " %s\n" : _("Journal users: %s\n"), str); } }
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 (!(EXT2_HAS_INCOMPAT_FEATURE(trans->fs->super, EXT3_FEATURE_INCOMPAT_RECOVER))) { trans->fs->super->s_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER; ext2fs_mark_super_dirty(trans->fs); } return 0; }
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); }
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_feature_csum3(j)) tag3->t_checksum = ext2fs_cpu_to_be32(csum32); else tag->t_checksum = ext2fs_cpu_to_be16(csum32); }
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_feature_csum3(j)) return tag3->t_checksum == ext2fs_cpu_to_be32(csum32); return tag->t_checksum == ext2fs_cpu_to_be16(csum32); }
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 || EXT2_HAS_INCOMPAT_FEATURE(journal->j_fs_dev->k_fs->super, EXT3_FEATURE_INCOMPAT_RECOVER)) { printf("Journal needs recovery, will not add csums.\n"); return; } /* metadata_csum implies journal csum v3 */ jsb = journal->j_superblock; if (EXT2_HAS_RO_COMPAT_FEATURE(journal->j_fs_dev->k_fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { printf("Setting csum v%d\n", ver); switch (ver) { case 2: journal->j_superblock->s_feature_incompat &= ext2fs_cpu_to_be32(~JFS_FEATURE_INCOMPAT_CSUM_V3); journal->j_superblock->s_feature_incompat |= ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2); journal->j_superblock->s_feature_compat &= ext2fs_cpu_to_be32(~JFS_FEATURE_COMPAT_CHECKSUM); break; case 3: journal->j_superblock->s_feature_incompat &= ext2fs_cpu_to_be32(~JFS_FEATURE_INCOMPAT_CSUM_V2); journal->j_superblock->s_feature_incompat |= ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V3); journal->j_superblock->s_feature_compat &= ext2fs_cpu_to_be32(~JFS_FEATURE_COMPAT_CHECKSUM); 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 { journal->j_superblock->s_feature_compat |= ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM); journal->j_superblock->s_feature_incompat &= ext2fs_cpu_to_be32(~(JFS_FEATURE_INCOMPAT_CSUM_V2 | JFS_FEATURE_INCOMPAT_CSUM_V3)); } }
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 errcode_t e2fsck_journal_sb_csum_set(journal_t *j, journal_superblock_t *jsb) { __u32 crc; if (!journal_has_csum_v2or3(j)) return 0; crc = e2fsck_journal_sb_csum(jsb); jsb->s_checksum = ext2fs_cpu_to_be32(crc); return 0; }
void jbd2_descr_block_csum_set(journal_t *j, struct buffer_head *bh) { struct journal_block_tail *tail; __u32 csum; if (!journal_has_csum_v2or3(j)) return; tail = (struct journal_block_tail *)(bh->b_data + j->j_blocksize - sizeof(struct journal_block_tail)); tail->t_checksum = 0; csum = jbd2_chksum(j, j->j_csum_seed, bh->b_data, j->j_blocksize); tail->t_checksum = ext2fs_cpu_to_be32(csum); }
void jbd2_commit_block_csum_set(journal_t *j, struct buffer_head *bh) { struct commit_header *h; __u32 csum; if (!journal_has_csum_v2or3(j)) return; h = (struct commit_header *)(bh->b_data); h->h_chksum_type = 0; h->h_chksum_size = 0; h->h_chksum[0] = 0; csum = jbd2_chksum(j, j->j_csum_seed, bh->b_data, j->j_blocksize); h->h_chksum[0] = ext2fs_cpu_to_be32(csum); }
static int jbd2_commit_block_csum_verify(journal_t *j, void *buf) { struct commit_header *h; __u32 provided; __u32 calculated; if (!journal_has_csum_v2or3(j)) return 1; h = buf; provided = h->h_chksum[0]; h->h_chksum[0] = 0; calculated = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize); h->h_chksum[0] = provided; return provided == ext2fs_cpu_to_be32(calculated); }
static int jbd2_revoke_block_csum_verify(journal_t *j, void *buf) { struct journal_revoke_tail *tail; __u32 provided; __u32 calculated; if (!journal_has_csum_v2or3(j)) 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 = jbd2_chksum(j, j->j_csum_seed, buf, j->j_blocksize); tail->r_checksum = provided; return provided == ext2fs_cpu_to_be32(calculated); }
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) { journal->j_superblock->s_feature_incompat |= ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_REVOKE); 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; }
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 void print_inline_journal_information(ext2_filsys fs) { journal_superblock_t *jsb; struct ext2_inode inode; ext2_file_t journal_file; errcode_t retval; ino_t ino = fs->super->s_journal_inum; char buf[1024]; __u32 *mask_ptr, mask, m; int i, j, size, printed = 0; if (fs->flags & EXT2_FLAG_IMAGE_FILE) return; retval = ext2fs_read_inode(fs, ino, &inode); if (retval) { com_err(program_name, retval, "%s", _("while reading journal inode")); exit(1); } retval = ext2fs_file_open2(fs, ino, &inode, 0, &journal_file); if (retval) { com_err(program_name, retval, "%s", _("while opening journal inode")); exit(1); } retval = ext2fs_file_read(journal_file, buf, sizeof(buf), 0); if (retval) { com_err(program_name, retval, "%s", _("while reading journal super block")); exit(1); } ext2fs_file_close(journal_file); jsb = (journal_superblock_t *) buf; if (be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) { fprintf(stderr, "%s", _("Journal superblock magic number invalid!\n")); exit(1); } printf("%s", _("Journal features: ")); for (i=0, mask_ptr=&jsb->s_feature_compat; i <3; i++,mask_ptr++) { mask = be32_to_cpu(*mask_ptr); for (j=0,m=1; j < 32; j++, m<<=1) { if (mask & m) { printf(" %s", e2p_jrnl_feature2string(i, m)); printed++; } } } if (printed == 0) printf(" (none)"); printf("\n"); fputs(_("Journal size: "), stdout); if ((fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && (inode.i_flags & EXT4_HUGE_FILE_FL)) size = inode.i_blocks / (fs->blocksize / 1024); else size = inode.i_blocks >> 1; if (size < 8192) printf("%uk\n", size); else printf("%uM\n", size >> 10); printf(_("Journal length: %u\n" "Journal sequence: 0x%08x\n" "Journal start: %u\n"), (unsigned int)ntohl(jsb->s_maxlen), (unsigned int)ntohl(jsb->s_sequence), (unsigned int)ntohl(jsb->s_start)); if (jsb->s_feature_compat & ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM)) printf(_("Journal checksum type: crc32\n")); if (jsb->s_feature_incompat & ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2)) printf(_("Journal checksum type: %s\n" "Journal checksum: 0x%08x\n"), journal_checksum_type_str(jsb->s_checksum_type), ext2fs_be32_to_cpu(jsb->s_checksum)); if (jsb->s_errno != 0) printf(_("Journal errno: %d\n"), (int) ntohl(jsb->s_errno)); }
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 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 */ int descr_csum_size = 0; int block_error = 0; /* * 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); 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, "JBD2: 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 != ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER)) { brelse(bh); break; } blocktype = ext2fs_be32_to_cpu(tmp->h_blocktype); sequence = ext2fs_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: /* Verify checksum first */ if (journal_has_csum_v2or3(journal)) descr_csum_size = sizeof(struct journal_block_tail); if (descr_csum_size > 0 && !jbd2_descr_block_csum_verify(journal, bh->b_data)) { err = -EFSBADCRC; brelse(bh); goto failed; } /* 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_feature_checksum(journal) && !info->end_transaction) { if (calc_chksums(journal, bh, &next_log_block, &crc32_sum)) { put_bh(bh); break; } put_bh(bh); continue; } next_log_block += count_tags(journal, bh); wrap(journal, next_log_block); put_bh(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 - descr_csum_size) { unsigned long io_block; tag = (journal_block_tag_t *) tagp; flags = ext2fs_be16_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 "JBD2: IO error %d recovering " "block %ld in log\n", err, io_block); } else { unsigned long long blocknr; J_ASSERT(obh != NULL); blocknr = read_tag_block(journal, 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; } /* Look for block corruption */ if (!jbd2_block_tag_csum_verify( journal, tag, obh->b_data, ext2fs_be32_to_cpu(tmp->h_sequence))) { brelse(obh); success = -EFSBADCRC; printk(KERN_ERR "JBD2: Invalid " "checksum recovering " "block %llu in log\n", blocknr); block_error = 1; 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 "JBD2: 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) { *((__u32 *)nbh->b_data) = ext2fs_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: /* 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_feature_checksum(journal)) { int chksum_err, chksum_seen; struct commit_header *cbh = (struct commit_header *)bh->b_data; unsigned found_chksum = ext2fs_be32_to_cpu(cbh->h_chksum[0]); chksum_err = chksum_seen = 0; if (info->end_transaction) { journal->j_failed_commit = info->end_transaction; brelse(bh); break; } if (crc32_sum == found_chksum && cbh->h_chksum_type == JFS_CRC32_CHKSUM && cbh->h_chksum_size == JFS_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; if (!jfs_has_feature_async_commit(journal)){ journal->j_failed_commit = next_commit_ID; brelse(bh); break; } } crc32_sum = ~0; } if (pass == PASS_SCAN && !jbd2_commit_block_csum_verify(journal, bh->b_data)) { info->end_transaction = next_commit_ID; if (!jfs_has_feature_async_commit(journal)) { journal->j_failed_commit = next_commit_ID; brelse(bh); break; } } 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 "JBD2: recovery pass %d ended at " "transaction %u, expected %u\n", pass, next_commit_ID, info->end_transaction); if (!success) success = -EIO; } } if (block_error && success == 0) 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; }
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 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 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_COMPAT_FEATURE(trans->journal, JFS_FEATURE_COMPAT_CHECKSUM)) { __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++; trans->fs->super->s_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER; ext2fs_mark_super_dirty(trans->fs); error: if (cbh) brelse(cbh); brelse(bh); return err; }