Verify and calculate checksums of htree internal node blocks. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- lib/ext2fs/csum.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ext2fs/dirhash.c | 41 +++++++++++++++++++ lib/ext2fs/ext2fs.h | 8 ++++ 3 files changed, 156 insertions(+), 0 deletions(-) diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c index fdca971..4963524 100644 --- a/lib/ext2fs/csum.c +++ b/lib/ext2fs/csum.c @@ -29,6 +29,113 @@ #define STATIC static #endif +__u32 ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + char *buf = (char *)dirent; + struct ext2_dx_countlimit *c; + int size, count_offset, limit, count; + __u32 crc = 0; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 0; + + c = ext2fs_get_dx_countlimit(fs, dirent, &count_offset); + if (!c) + return 0; + limit = ext2fs_le16_to_cpu(c->limit); + count = ext2fs_le16_to_cpu(c->count); + if (count_offset + (limit * sizeof(struct ext2_dx_entry)) > + fs->blocksize - sizeof(struct ext2_dx_tail)) + return 0; + + size = count_offset + (count * sizeof(struct ext2_dx_entry)); + + /* + * The dx_root/dx_entry structures are always accessed via + * le*_to_cpu helpers and don't need swapping here. However, the + * dirent container fields need to be swapped. + */ +#ifdef WORDS_BIGENDIAN + errcode_t retval; + + retval = ext2fs_get_mem(fs->blocksize, &buf); + if (retval) + return 0; + memcpy(buf, dirent, fs->blocksize); + retval = ext2fs_dirent_swab_out(fs, buf, 0); + if (retval) { + crc = 0; + goto out; + } +#endif + + inum = ext2fs_cpu_to_le32(inum); + crc = crc32c_le(~0, fs->super->s_uuid, sizeof(fs->super->s_uuid)); + crc = crc32c_le(crc, (char *)&inum, sizeof(inum)); + crc = crc32c_le(crc, buf, size); + +#ifdef WORDS_BIGENDIAN +out: + ext2fs_free_mem(&buf); +#endif + + return crc; +} + +int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + struct ext2_dx_countlimit *c; + struct ext2_dx_tail *t; + int count_offset, limit, count; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return 1; + + c = ext2fs_get_dx_countlimit(fs, dirent, &count_offset); + if (!c) + return 1; + limit = ext2fs_le16_to_cpu(c->limit); + count = ext2fs_le16_to_cpu(c->count); + if (count_offset + (limit * sizeof(struct ext2_dx_entry)) > + fs->blocksize - sizeof(struct ext2_dx_tail)) + return 0; + /* htree structs are accessed in LE order */ + t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit); + if (ext2fs_le32_to_cpu(t->checksum) != ext2fs_dx_csum(fs, inum, dirent)) + return 0; + + return 1; +} + +void ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent) +{ + struct ext2_dx_countlimit *c; + struct ext2_dx_tail *t; + int count_offset, limit, count; + + if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + return; + + c = ext2fs_get_dx_countlimit(fs, dirent, &count_offset); + if (!c) + return; + limit = ext2fs_le16_to_cpu(c->limit); + count = ext2fs_le16_to_cpu(c->count); + if (count_offset + (limit * sizeof(struct ext2_dx_entry)) > + fs->blocksize - sizeof(struct ext2_dx_tail)) + return; + t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit); + + /* htree structs are accessed in LE order */ + t->checksum = ext2fs_cpu_to_le32(ext2fs_dx_csum(fs, inum, dirent)); +} + #define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \ (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max))) diff --git a/lib/ext2fs/dirhash.c b/lib/ext2fs/dirhash.c index a069706..76ac274 100644 --- a/lib/ext2fs/dirhash.c +++ b/lib/ext2fs/dirhash.c @@ -255,3 +255,44 @@ errcode_t ext2fs_dirhash(int version, const char *name, int len, *ret_minor_hash = minor_hash; return 0; } + +struct ext2_dx_countlimit *ext2fs_get_dx_countlimit(ext2_filsys fs, + struct ext2_dir_entry *dirent, int *offset) +{ + struct ext2_dir_entry *dp; + struct ext2_dx_root_info *root; + struct ext2_dx_countlimit *c; + int count_offset, max_sane_entries; + unsigned int rec_len; + errcode_t retval; + + retval = ext2fs_get_rec_len(fs, dirent, &rec_len); + if (retval) + return NULL; + + if (rec_len == fs->blocksize && dirent->name_len == 0) + count_offset = 8; + else if (rec_len == 12) { + dp = (struct ext2_dir_entry *)(((void *)dirent) + rec_len); + retval = ext2fs_get_rec_len(fs, dp, &rec_len); + if (retval || rec_len != fs->blocksize - 12) + return NULL; + root = (struct ext2_dx_root_info *)(((void *)dp + 12)); + if (root->reserved_zero || + root->info_length != sizeof(struct ext2_dx_root_info)) + return NULL; + count_offset = 32; + } else + return NULL; + + c = (struct ext2_dx_countlimit *)(((void *)dirent) + count_offset); + max_sane_entries = (fs->blocksize - count_offset) / + sizeof(struct ext2_dx_entry); + if (ext2fs_le16_to_cpu(c->limit) > max_sane_entries || + ext2fs_le16_to_cpu(c->count) > max_sane_entries) + return NULL; + + if (offset) + *offset = count_offset; + return c; +} diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index 1e3e9f1..7110f72 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -894,6 +894,12 @@ extern __u32 crc32c_be(__u32 crc, unsigned char const *p, size_t len); extern __u32 crc32c_le(__u32 crc, unsigned char const *p, size_t len); /* csum.c */ +extern __u32 ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent); +extern int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent); +extern void ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum, + struct ext2_dir_entry *dirent); extern __u32 ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum, struct ext3_extent_header *eh); extern int ext2fs_extent_block_csum_verify(ext2_filsys fs, @@ -983,6 +989,8 @@ extern errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block, void *buf, int flags); /* dirhash.c */ +extern struct ext2_dx_countlimit *ext2fs_get_dx_countlimit(ext2_filsys fs, + struct ext2_dir_entry *dirent, int *offset); extern errcode_t ext2fs_dirhash(int version, const char *name, int len, const __u32 *seed, ext2_dirhash_t *ret_hash, -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html