From mboxrd@z Thu Jan 1 00:00:00 1970 From: Vyacheslav Dubeyko Subject: [PATCH v3 12/15] hfsplus: implement functionality of transaction's block check Date: Wed, 12 Feb 2014 18:29:17 +0400 Message-ID: <1392215357.12337.108.camel@ubuntu> Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 7bit Cc: Al Viro , ChristophHellwig , Hin-Tak Leung , Andrew Morton To: Linux FS devel list Return-path: Received: from gproxy4-pub.mail.unifiedlayer.com ([69.89.23.142]:55769 "HELO gproxy4-pub.mail.unifiedlayer.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S1751354AbaBLO3b (ORCPT ); Wed, 12 Feb 2014 09:29:31 -0500 Sender: linux-fsdevel-owner@vger.kernel.org List-ID: From: Vyacheslav Dubeyko Subject: [PATCH v3 12/15] hfsplus: implement functionality of transaction's block check A group of related changes is called a transaction. When all of the changes of a transaction have been written to their normal locations on disk, that transaction has been committed, and is removed from the journal. The journal may contain several transactions. Copying changes from all transactions to their normal locations on disk is called replaying the journal. A single transaction consists of several blocks, including both the data to be written, and the location where that data is to be written. This is represented on disk by a block list header, which describes the number and sizes of the blocks, immediately followed by the contents of those blocks. This patch implements method of checking description of transaction's block before replaying of it. Signed-off-by: Vyacheslav Dubeyko CC: Al Viro CC: Christoph Hellwig Tested-by: Hin-Tak Leung --- fs/hfsplus/journal.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/fs/hfsplus/journal.c b/fs/hfsplus/journal.c index c40ef77..f44291d 100644 --- a/fs/hfsplus/journal.c +++ b/fs/hfsplus/journal.c @@ -502,6 +502,102 @@ struct hfsplus_block_info *hfsplus_get_binfo(struct super_block *sb, return &desc->binfo[diff]; } +static int hfsplus_check_transaction_block(struct super_block *sb, int i, + struct hfsplus_blist_desc *desc) +{ + struct hfsplus_journal *jnl = HFSPLUS_SB(sb)->jnl; + struct hfsplus_journal_header *jh = jnl->jh; + struct hfsplus_blhdr *blhdr = &desc->blhdr; + struct hfsplus_block_info *binfo; + sector_t sec; + u32 size; + u32 checksum = 0; + + hfs_dbg(JREPLAY, "check binfo[%u]\n", i); + + binfo = hfsplus_get_binfo(sb, i, desc); + if (!binfo) { + pr_err("unable to get binfo[%u]\n", i); + return -EIO; + } + + if (BNUM(binfo) == ~(u64)0) { + hfs_dbg(JREPLAY, "don't add *killed* block %llu\n", + BNUM(binfo)); + return 0; /* don't add "killed" blocks */ + } + + BUG_ON(BSIZE(binfo) % HFSPLUS_SECTOR_SIZE); + + if ((BSIZE(binfo) / HFSPLUS_SECTOR_SIZE) == 0) { + pr_warn("transaction block size is not aligned\n"); + return -EINVAL; + } + + sec = JOURNAL_OFF_TO_SEC(sb); + size = BSIZE(binfo); + while (size > 0) { + void *block_ptr; + size_t check_size = HFSPLUS_SECTOR_SIZE; + size_t rest_bytes; + int err; + + if (need_to_wrap_journal(jh, TR_START(jh), check_size)) { + hfsplus_wrap_journal(sb, TR_START(jh), check_size); + sec = JOURNAL_OFF_TO_SEC(sb); + } + + err = hfsplus_submit_bio(sb, sec, desc->src_buf, + (void **)&block_ptr, + READA, &rest_bytes); + if (err) { + pr_err("unable to read sector %llu of blist\n", + (unsigned long long)sec); + return err; + } + + BUG_ON(rest_bytes < HFSPLUS_SECTOR_SIZE); + + if (TR_START(jh) > LAST_TR_END(jh)) { + if ((TR_START(jh) + check_size) > JOURNAL_SIZE(jh)) + err = -EIO; + } else if (TR_START(jh) < LAST_TR_END(jh)) { + if ((TR_START(jh) + check_size) > LAST_TR_END(jh)) + err = -EIO; + } else /* start == end */ + err = -EIO; + + if (err) { + pr_err("invalid transaction: start %llu, end %llu, journal size %llu, bsize %zu\n", + TR_START(jh), LAST_TR_END(jh), + JOURNAL_SIZE(jh), check_size); + return err; + } + + if (NEED_CHECK_CSUM(blhdr)) { + checksum = calc_internal_checksum(checksum, + block_ptr, + check_size); + } + + size -= check_size; + le64_add_cpu(&jh->start, check_size); + sec = JOURNAL_OFF_TO_SEC(sb); + } + + if (NEED_CHECK_CSUM(blhdr)) { + if (HFSPLUS_CHECKSUM(checksum) != binfo->block.checksum) { + pr_err("invalid transaction's checksum\n"); + hfs_dbg(JREPLAY, "calc checksum: %#x, checksum: %#x\n", + le32_to_cpu(HFSPLUS_CHECKSUM(checksum)), + le32_to_cpu(binfo->block.checksum)); + return -EIO; + } + } + + return 0; +} + static inline bool hfsplus_journal_empty(struct hfsplus_journal_header *jh) { @@ -585,6 +681,7 @@ static int hfsplus_replay_journal(struct super_block *sb) while (!hfsplus_journal_empty(jh)) { struct hfsplus_blhdr *blhdr; struct hfsplus_block_info *binfo; + u32 really_used = 0; u32 i; if (TR_START(jh) == JOURNAL_SIZE(jh)) @@ -614,6 +711,8 @@ static int hfsplus_replay_journal(struct super_block *sb) } else le64_add_cpu(&jh->start, BLHDR_SIZE(jh)); + really_used += BLHDR_SIZE(jh); + /* Check transaction */ for (i = 1; i < TR_BLOCKS(blhdr); i++) { binfo = hfsplus_get_binfo(sb, i, &desc); @@ -623,7 +722,13 @@ static int hfsplus_replay_journal(struct super_block *sb) goto failed_journal_replay; } - /* TODO: check transaction block */ + err = hfsplus_check_transaction_block(sb, i, &desc); + if (err) { + pr_err("binfo[%u] is corrupted\n", i); + goto failed_journal_replay; + } + + really_used += BSIZE(binfo); } } -- 1.7.9.5