From 39f5659ae3a0d9105ecddddfda2800b7002febc3 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 2 Aug 2012 20:47:44 -0400 Subject: [PATCH] libext2fs: verify and calculate extended attribute block checksums Calculate and verify the checksum for separate (i.e. not in the inode) extended attribute blocks; the checksum lives in the header. [ Merged in change from Tao so that we always use the fs checksum seed for the xattr blocks. ] Signed-off-by: Darrick J. Wong Signed-off-by: Tao Ma Signed-off-by: Theodore Ts'o --- e2fsck/pass1.c | 10 ++++---- e2fsck/pass1b.c | 4 ++-- e2fsck/pass2.c | 6 ++--- e2fsck/super.c | 6 ++--- lib/ext2fs/csum.c | 56 ++++++++++++++++++++++++++++++++++++++++++++ lib/ext2fs/ext2_err.et.in | 3 +++ lib/ext2fs/ext2fs.h | 14 +++++++++++ lib/ext2fs/ext_attr.c | 59 ++++++++++++++++++++++++++++++++++++----------- lib/ext2fs/swapfs.c | 3 ++- 9 files changed, 134 insertions(+), 27 deletions(-) diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 2d39d03..06a5314 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -1518,7 +1518,8 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, if ((blk = ea_refcount_intr_next(refcount, &count)) == 0) break; pctx.blk = blk; - pctx.errcode = ext2fs_read_ext_attr2(fs, blk, block_buf); + pctx.errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, + pctx.ino); if (pctx.errcode) { fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx); return; @@ -1529,8 +1530,9 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount, pctx.num = should_be; if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) { header->h_refcount = should_be; - pctx.errcode = ext2fs_write_ext_attr2(fs, blk, - block_buf); + pctx.errcode = ext2fs_write_ext_attr3(fs, blk, + block_buf, + pctx.ino); if (pctx.errcode) { fix_problem(ctx, PR_1_EXTATTR_WRITE_ABORT, &pctx); @@ -1628,7 +1630,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx, * validate it */ pctx->blk = blk; - pctx->errcode = ext2fs_read_ext_attr2(fs, blk, block_buf); + pctx->errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, pctx->ino); if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx)) goto clear_extattr; header = (struct ext2_ext_attr_header *) block_buf; diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c index 93fb630..05cbd10 100644 --- a/e2fsck/pass1b.c +++ b/e2fsck/pass1b.c @@ -653,9 +653,9 @@ static void delete_file(e2fsck_t ctx, ext2_ino_t ino, if (ext2fs_file_acl_block(fs, &inode) && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { count = 1; - pctx.errcode = ext2fs_adjust_ea_refcount2(fs, + pctx.errcode = ext2fs_adjust_ea_refcount3(fs, ext2fs_file_acl_block(fs, &inode), - block_buf, -1, &count); + block_buf, -1, &count, ino); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index be950de..e28af61 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -1290,9 +1290,9 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) if (ext2fs_file_acl_block(fs, &inode) && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { - pctx.errcode = ext2fs_adjust_ea_refcount2(fs, - ext2fs_file_acl_block(fs, &inode), - block_buf, -1, &count); + pctx.errcode = ext2fs_adjust_ea_refcount3(fs, + ext2fs_file_acl_block(fs, &inode), + block_buf, -1, &count, ino); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; diff --git a/e2fsck/super.c b/e2fsck/super.c index a5e8005..a8fe571 100644 --- a/e2fsck/super.c +++ b/e2fsck/super.c @@ -199,9 +199,9 @@ static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, ext2fs_iblk_sub_blocks(fs, inode, pb.truncated_blocks); if (ext2fs_file_acl_block(fs, inode)) { - retval = ext2fs_adjust_ea_refcount2(fs, - ext2fs_file_acl_block(fs, inode), - block_buf, -1, &count); + retval = ext2fs_adjust_ea_refcount3(fs, + ext2fs_file_acl_block(fs, inode), + block_buf, -1, &count, ino); if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) { retval = 0; count = 1; diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c index e92bac5..8325fb5 100644 --- a/lib/ext2fs/csum.c +++ b/lib/ext2fs/csum.c @@ -30,6 +30,62 @@ #define STATIC static #endif +static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, ext2_ino_t inum, + blk64_t block, + struct ext2_ext_attr_header *hdr, + __u32 *crc) +{ + errcode_t retval; + char *buf = (char *)hdr; + __u32 gen, old_crc = hdr->h_checksum; + struct ext2_inode inode; + + hdr->h_checksum = 0; + block = ext2fs_cpu_to_le64(block); + *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&block, + sizeof(block)); + *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, fs->blocksize); + hdr->h_checksum = old_crc; + + return 0; +} + +int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, + blk64_t block, + struct ext2_ext_attr_header *hdr) +{ + __u32 calculated; + errcode_t retval; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + + retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &calculated); + if (retval) + return 0; + + return ext2fs_le32_to_cpu(hdr->h_checksum) == calculated; +} + +errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs, ext2_ino_t inum, + blk64_t block, + struct ext2_ext_attr_header *hdr) +{ + errcode_t retval; + __u32 crc; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &crc); + if (retval) + return retval; + hdr->h_checksum = ext2fs_cpu_to_le32(crc); + return 0; +} + static __u16 do_nothing16(__u16 x) { return x; diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in index 430d567..90be193 100644 --- a/lib/ext2fs/ext2_err.et.in +++ b/lib/ext2fs/ext2_err.et.in @@ -461,4 +461,7 @@ ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM, ec EXT2_ET_DIR_CSUM_INVALID, "Directory block checksum does not match directory block" +ec EXT2_ET_EXT_ATTR_CSUM_INVALID, + "Extended attribute block checksum does not match block" + end diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index cc47399..b793885 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -943,6 +943,12 @@ extern __u32 ext2fs_crc32c_be(__u32 crc, unsigned char const *p, size_t len); extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len); /* csum.c */ +extern errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs, + ext2_ino_t inum, blk64_t block, + struct ext2_ext_attr_header *hdr); +extern int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, + blk64_t block, + struct ext2_ext_attr_header *hdr); #define EXT2_DIRENT_TAIL(block, blocksize) \ ((struct ext2_dir_entry_tail *)(((void *)(block)) + \ (blocksize) - sizeof(struct ext2_dir_entry_tail))) @@ -1097,16 +1103,24 @@ extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf); +extern errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, + void *buf, ext2_ino_t inum); extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *buf); +extern errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block, + void *buf, ext2_ino_t inum); extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, char *block_buf, int adjust, __u32 *newcount); extern errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, char *block_buf, int adjust, __u32 *newcount); +extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk, + char *block_buf, + int adjust, __u32 *newcount, + ext2_ino_t inum); /* extent.c */ extern errcode_t ext2fs_extent_header_verify(void *ptr, int size); diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c index 1889824..9649a14 100644 --- a/lib/ext2fs/ext_attr.c +++ b/lib/ext2fs/ext_attr.c @@ -61,17 +61,29 @@ __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data) #undef NAME_HASH_SHIFT #undef VALUE_HASH_SHIFT -errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf) +errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf, + ext2_ino_t inum) { errcode_t retval; retval = io_channel_read_blk64(fs->io, block, 1, buf); if (retval) return retval; + + if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && + !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf)) + retval = EXT2_ET_EXT_ATTR_CSUM_INVALID; + #ifdef WORDS_BIGENDIAN ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); #endif - return 0; + + return retval; +} + +errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf) +{ + return ext2fs_read_ext_attr3(fs, block, buf, 0); } errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) @@ -79,30 +91,40 @@ errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) return ext2fs_read_ext_attr2(fs, block, buf); } -errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf) +errcode_t ext2fs_write_ext_attr3(ext2_filsys fs, blk64_t block, void *inbuf, + ext2_ino_t inum) { errcode_t retval; char *write_buf; -#ifdef WORDS_BIGENDIAN - char *buf = NULL; - retval = ext2fs_get_mem(fs->blocksize, &buf); +#ifdef WORDS_BIGENDIAN + retval = ext2fs_get_mem(fs->blocksize, &write_buf); if (retval) return retval; - write_buf = buf; - ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); + ext2fs_swap_ext_attr(write_buf, inbuf, fs->blocksize, 1); #else write_buf = (char *) inbuf; #endif + + retval = ext2fs_ext_attr_block_csum_set(fs, inum, block, + (struct ext2_ext_attr_header *)write_buf); + if (retval) + return retval; + retval = io_channel_write_blk64(fs->io, block, 1, write_buf); #ifdef WORDS_BIGENDIAN - ext2fs_free_mem(&buf); + ext2fs_free_mem(&write_buf); #endif if (!retval) ext2fs_mark_changed(fs); return retval; } +errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf) +{ + return ext2fs_write_ext_attr3(fs, block, inbuf, 0); +} + errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) { return ext2fs_write_ext_attr2(fs, block, inbuf); @@ -111,9 +133,9 @@ errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) /* * This function adjusts the reference count of the EA block. */ -errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, +errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk, char *block_buf, int adjust, - __u32 *newcount) + __u32 *newcount, ext2_ino_t inum) { errcode_t retval; struct ext2_ext_attr_header *header; @@ -130,7 +152,7 @@ errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, block_buf = buf; } - retval = ext2fs_read_ext_attr2(fs, blk, block_buf); + retval = ext2fs_read_ext_attr3(fs, blk, block_buf, inum); if (retval) goto errout; @@ -139,7 +161,7 @@ errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, if (newcount) *newcount = header->h_refcount; - retval = ext2fs_write_ext_attr2(fs, blk, block_buf); + retval = ext2fs_write_ext_attr3(fs, blk, block_buf, inum); if (retval) goto errout; @@ -149,9 +171,18 @@ errout: return retval; } +errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk, + char *block_buf, int adjust, + __u32 *newcount) +{ + return ext2fs_adjust_ea_refcount3(fs, blk, block_buf, adjust, + newcount, 0); +} + errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, char *block_buf, int adjust, __u32 *newcount) { - return ext2fs_adjust_ea_refcount(fs, blk, block_buf, adjust, newcount); + return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust, + newcount); } diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c index 69916e5..de178a4 100644 --- a/lib/ext2fs/swapfs.c +++ b/lib/ext2fs/swapfs.c @@ -157,7 +157,8 @@ void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header, to_header->h_blocks = ext2fs_swab32(from_header->h_blocks); to_header->h_refcount = ext2fs_swab32(from_header->h_refcount); to_header->h_hash = ext2fs_swab32(from_header->h_hash); - for (n = 0; n < 4; n++) + to_header->h_checksum = ext2fs_swab32(from_header->h_checksum); + for (n = 0; n < 3; n++) to_header->h_reserved[n] = ext2fs_swab32(from_header->h_reserved[n]); } -- 1.8.3.1