The dx_tail struct that can be stored at the end of each root block was extended with an additional link to the itree root block. This commit renames the dx_tail to dx_csum_entry and adds dx_itree_entry that holds the 64bit block pointer to itree root. Signed-off-by: Radek Pazdera <rpazdera@xxxxxxxxxx> --- fs/ext4/namei.c | 186 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 145 insertions(+), 41 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 4a22393..a3697a7 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -235,9 +235,30 @@ struct dx_map_entry /* * This goes at the end of each htree block. */ +struct dx_csum_entry { + u32 de_reserved; + __le32 de_checksum; /* crc32c(uuid+inum+dirblock) */ +}; + +/* + * This goes at the end of a htree root block, if there is an itree + * available for that directory. + */ +struct dx_itree_entry { + __le64 de_itree_root; +}; + +/* + * This is a memory-only structure for easier handling the tail of + * dx_node. One or even both members can be set to NULL, which means + * that the node doesn't have the particular entry. + */ struct dx_tail { - u32 dt_reserved; - __le32 dt_checksum; /* crc32c(uuid+inum+dirblock) */ + void *start; + int len; + + struct dx_csum_entry *csum; + struct dx_itree_entry *itree; }; static inline ext4_lblk_t dx_get_block(struct dx_entry *entry); @@ -250,6 +271,10 @@ static void dx_set_count(struct dx_entry *entries, unsigned value); static void dx_set_limit(struct dx_entry *entries, unsigned value); static unsigned dx_root_limit(struct inode *dir, unsigned infosize); static unsigned dx_node_limit(struct inode *dir); +static int dx_get_itree_root(struct inode *inode, struct ext4_dir_entry *dirent, + ext4_fsblk_t *itree_root); +static int dx_set_itree_root(struct inode *inode, struct ext4_dir_entry *dirent, + ext4_fsblk_t itree_root); static struct dx_frame *dx_probe(const struct qstr *d_name, struct inode *dir, struct dx_hash_info *hinfo, @@ -417,80 +442,119 @@ static struct dx_countlimit *get_dx_countlimit(struct inode *inode, return (struct dx_countlimit *)(((void *)dirent) + count_offset); } -static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent, - int count_offset, int count, struct dx_tail *t) +static int dx_get_tail(struct inode *inode, struct ext4_dir_entry *dirent, + struct dx_tail *tail) +{ + struct dx_countlimit *c; + int tail_space, limit, count_offset; + void *tail_ptr; + + c = get_dx_countlimit(inode, dirent, &count_offset); + if (!c) { + EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); + return -EIO; + } + limit = le16_to_cpu(c->limit); + + memset(tail, 0, sizeof(struct dx_tail)); + tail_ptr = tail->start = (void *)(((struct dx_entry *)c) + limit); + tail_space = EXT4_BLOCK_SIZE(inode->i_sb) - + (count_offset + (limit * sizeof(struct dx_entry))); + + if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && + tail_space >= sizeof(struct dx_csum_entry)) { + tail->len += sizeof(struct dx_csum_entry); + tail->csum = (struct dx_csum_entry *)tail_ptr; + tail_ptr += sizeof(struct dx_csum_entry); + tail_space -= sizeof(struct dx_csum_entry); + } + + if (dx_itree(inode) && tail_space >= sizeof(struct dx_itree_entry)) { + tail->len += sizeof(struct dx_itree_entry); + tail->itree = (struct dx_itree_entry *)tail_ptr; + } + + return 0; +} + +static int ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent, + struct dx_tail *tail, __le32 *csum) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_inode_info *ei = EXT4_I(inode); - __u32 csum, old_csum; - int size; + __u32 new_csum, old_csum; + struct dx_countlimit *c; + int size, count, count_offset; + + c = get_dx_countlimit(inode, dirent, &count_offset); + if (!c) + return -EIO; + count = le16_to_cpu(c->count); size = count_offset + (count * sizeof(struct dx_entry)); - old_csum = t->dt_checksum; - t->dt_checksum = 0; - csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size); - csum = ext4_chksum(sbi, csum, (__u8 *)t, sizeof(struct dx_tail)); - t->dt_checksum = old_csum; + old_csum = tail->csum->de_checksum; + tail->csum->de_checksum = 0; + new_csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size); + new_csum = ext4_chksum(sbi, new_csum, (__u8 *)tail->start, tail->len); + tail->csum->de_checksum = old_csum; - return cpu_to_le32(csum); + *csum = cpu_to_le32(new_csum); + return 0; } static int ext4_dx_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent) { - struct dx_countlimit *c; - struct dx_tail *t; - int count_offset, limit, count; + struct dx_tail tail; + int err; + __le32 csum; if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) return 1; - c = get_dx_countlimit(inode, dirent, &count_offset); - if (!c) { - EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); + err = dx_get_tail(inode, dirent, &tail); + if (err) + return err; + + if (!tail.csum) { + warn_no_space_for_csum(inode); return 1; } - limit = le16_to_cpu(c->limit); - count = le16_to_cpu(c->count); - if (count_offset + (limit * sizeof(struct dx_entry)) > - EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) { - warn_no_space_for_csum(inode); + + err = ext4_dx_csum(inode, dirent, &tail, &csum); + if (err) { + EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); return 1; } - t = (struct dx_tail *)(((struct dx_entry *)c) + limit); - if (t->dt_checksum != ext4_dx_csum(inode, dirent, count_offset, - count, t)) + if (tail.csum->de_checksum != csum) return 0; return 1; } static void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry *dirent) { - struct dx_countlimit *c; - struct dx_tail *t; - int count_offset, limit, count; + struct dx_tail tail; + int err; if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) return; - c = get_dx_countlimit(inode, dirent, &count_offset); - if (!c) { - EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); + err = dx_get_tail(inode, dirent, &tail); + if (err) return; - } - limit = le16_to_cpu(c->limit); - count = le16_to_cpu(c->count); - if (count_offset + (limit * sizeof(struct dx_entry)) > - EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) { + + if (!tail.csum) { warn_no_space_for_csum(inode); return; } - t = (struct dx_tail *)(((struct dx_entry *)c) + limit); - t->dt_checksum = ext4_dx_csum(inode, dirent, count_offset, count, t); + err = ext4_dx_csum(inode, dirent, &tail, &(tail.csum->de_checksum)); + if (err) + EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); } static inline int ext4_handle_dirty_dx_node(handle_t *handle, @@ -563,7 +627,9 @@ static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize) if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) - entry_space -= sizeof(struct dx_tail); + entry_space -= sizeof(struct dx_csum_entry); + if (dx_itree(dir)) + entry_space -= sizeof(struct dx_itree_entry); return entry_space / sizeof(struct dx_entry); } @@ -573,10 +639,48 @@ static inline unsigned dx_node_limit(struct inode *dir) if (EXT4_HAS_RO_COMPAT_FEATURE(dir->i_sb, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) - entry_space -= sizeof(struct dx_tail); + entry_space -= sizeof(struct dx_csum_entry); return entry_space / sizeof(struct dx_entry); } +static int dx_get_itree_root(struct inode *inode, struct ext4_dir_entry *dirent, + ext4_fsblk_t *itree_root) +{ + int err; + struct dx_tail tail; + + err = dx_get_tail(inode, dirent, &tail); + if (err) + return err; + + if (!tail.itree) { + EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); + return -EIO; + } + + *itree_root = le64_to_cpu(tail.itree->de_itree_root); + return 0; +} + +static int dx_set_itree_root(struct inode *inode, struct ext4_dir_entry *dirent, + ext4_fsblk_t itree_root) +{ + int err; + struct dx_tail tail; + + err = dx_get_tail(inode, dirent, &tail); + if (err) + return err; + + if (!tail.itree) { + EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); + return -EIO; + } + + tail.itree->de_itree_root = cpu_to_le64(itree_root); + return 0; +} + /* * Debug */ -- 1.7.11.7 -- 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