X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=lwext4%2Fext4_dir_idx.c;h=df1d999d06459daa5fd7b26ff3f4e89e296e1e5b;hb=69eb74b93f64af428c2cb7a7da7bd03d3929a106;hp=97878cae49442abad2df5ebffcf90cc0703c046e;hpb=baef42af22f58e2b2668467a2efffbc868b470df;p=lwext4.git diff --git a/lwext4/ext4_dir_idx.c b/lwext4/ext4_dir_idx.c index 97878ca..df1d999 100644 --- a/lwext4/ext4_dir_idx.c +++ b/lwext4/ext4_dir_idx.c @@ -40,6 +40,7 @@ #include "ext4_blockdev.h" #include "ext4_fs.h" #include "ext4_super.h" +#include "ext4_inode.h" #include "ext4_crc32c.h" #include "ext4_hash.h" @@ -51,7 +52,7 @@ * @return Hash algorithm version */ static inline uint8_t ext4_dir_dx_root_info_get_hash_version( - struct ext4_directory_dx_root_info *root_info) + struct ext4_dir_idx_root_info *root_info) { return root_info->hash_version; } @@ -61,7 +62,7 @@ static inline uint8_t ext4_dir_dx_root_info_get_hash_version( * @param v Hash algorithm version */ static inline void ext4_dir_dx_root_info_set_hash_version( - struct ext4_directory_dx_root_info *root_info, uint8_t v) + struct ext4_dir_idx_root_info *root_info, uint8_t v) { root_info->hash_version = v; } @@ -71,7 +72,7 @@ static inline void ext4_dir_dx_root_info_set_hash_version( * @return Length of the structure */ static inline uint8_t ext4_dir_dx_root_info_get_info_length( - struct ext4_directory_dx_root_info *root_info) + struct ext4_dir_idx_root_info *root_info) { return root_info->info_length; } @@ -81,7 +82,7 @@ static inline uint8_t ext4_dir_dx_root_info_get_info_length( * @param info_length Length of the structure */ static inline void ext4_dir_dx_root_info_set_info_length( - struct ext4_directory_dx_root_info *root_info, uint8_t len) + struct ext4_dir_idx_root_info *root_info, uint8_t len) { root_info->info_length = len; } @@ -91,7 +92,7 @@ static inline void ext4_dir_dx_root_info_set_info_length( * @return Height of HTree (actually only 0 or 1) */ static inline uint8_t ext4_dir_dx_root_info_get_indirect_levels( - struct ext4_directory_dx_root_info *root_info) + struct ext4_dir_idx_root_info *root_info) { return root_info->indirect_levels; } @@ -101,7 +102,7 @@ static inline uint8_t ext4_dir_dx_root_info_get_indirect_levels( * @param lvl Height of HTree (actually only 0 or 1) */ static inline void ext4_dir_dx_root_info_set_indirect_levels( - struct ext4_directory_dx_root_info *root_info, uint8_t lvl) + struct ext4_dir_idx_root_info *root_info, uint8_t lvl) { root_info->indirect_levels = lvl; } @@ -111,7 +112,7 @@ static inline void ext4_dir_dx_root_info_set_indirect_levels( * @return Maximum of entries in node */ static inline uint16_t -ext4_dir_dx_countlimit_get_limit(struct ext4_directory_dx_countlimit *climit) +ext4_dir_dx_countlimit_get_limit(struct ext4_dir_idx_countlimit *climit) { return to_le16(climit->limit); } @@ -121,7 +122,7 @@ ext4_dir_dx_countlimit_get_limit(struct ext4_directory_dx_countlimit *climit) * @param limit Maximum of entries in node */ static inline void -ext4_dir_dx_countlimit_set_limit(struct ext4_directory_dx_countlimit *climit, +ext4_dir_dx_countlimit_set_limit(struct ext4_dir_idx_countlimit *climit, uint16_t limit) { climit->limit = to_le16(limit); @@ -132,7 +133,7 @@ ext4_dir_dx_countlimit_set_limit(struct ext4_directory_dx_countlimit *climit, * @return Number of entries in node */ static inline uint16_t -ext4_dir_dx_countlimit_get_count(struct ext4_directory_dx_countlimit *climit) +ext4_dir_dx_countlimit_get_count(struct ext4_dir_idx_countlimit *climit) { return to_le16(climit->count); } @@ -142,7 +143,7 @@ ext4_dir_dx_countlimit_get_count(struct ext4_directory_dx_countlimit *climit) * @param count Number of entries in node */ static inline void -ext4_dir_dx_countlimit_set_count(struct ext4_directory_dx_countlimit *climit, +ext4_dir_dx_countlimit_set_count(struct ext4_dir_idx_countlimit *climit, uint16_t count) { climit->count = to_le16(count); @@ -153,7 +154,7 @@ ext4_dir_dx_countlimit_set_count(struct ext4_directory_dx_countlimit *climit, * @return Hash value */ static inline uint32_t -ext4_dir_dx_entry_get_hash(struct ext4_directory_dx_entry *entry) +ext4_dir_dx_entry_get_hash(struct ext4_dir_idx_entry *entry) { return to_le32(entry->hash); } @@ -163,7 +164,7 @@ ext4_dir_dx_entry_get_hash(struct ext4_directory_dx_entry *entry) * @param hash Hash value */ static inline void -ext4_dir_dx_entry_set_hash(struct ext4_directory_dx_entry *entry, uint32_t hash) +ext4_dir_dx_entry_set_hash(struct ext4_dir_idx_entry *entry, uint32_t hash) { entry->hash = to_le32(hash); } @@ -173,7 +174,7 @@ ext4_dir_dx_entry_set_hash(struct ext4_directory_dx_entry *entry, uint32_t hash) * @return Block address of child node */ static inline uint32_t -ext4_dir_dx_entry_get_block(struct ext4_directory_dx_entry *entry) +ext4_dir_dx_entry_get_block(struct ext4_dir_idx_entry *entry) { return to_le32(entry->block); } @@ -183,7 +184,7 @@ ext4_dir_dx_entry_get_block(struct ext4_directory_dx_entry *entry) * @param block Block address of child node */ static inline void -ext4_dir_dx_entry_set_block(struct ext4_directory_dx_entry *entry, +ext4_dir_dx_entry_set_block(struct ext4_dir_idx_entry *entry, uint32_t block) { entry->block = to_le32(block); @@ -203,41 +204,52 @@ static int ext4_dir_dx_hash_string(struct ext4_hash_info *hinfo, int len, &hinfo->hash, &hinfo->minor_hash); } +#if CONFIG_META_CSUM_ENABLE static uint32_t ext4_dir_dx_checksum(struct ext4_inode_ref *inode_ref, void *dirent, - int count_offset, int count, struct ext4_directory_dx_tail *t) + int count_offset, int count, struct ext4_dir_idx_tail *t) { uint32_t orig_checksum, checksum = 0; struct ext4_sblock *sb = &inode_ref->fs->sb; int size; /* Compute the checksum only if the filesystem supports it */ - if (ext4_sb_has_feature_read_only(sb, - EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + uint32_t ino_index = to_le32(inode_ref->index); + uint32_t ino_gen = + to_le32(ext4_inode_get_generation(inode_ref->inode)); + size = count_offset + - (count * sizeof(struct ext4_directory_dx_tail)); + (count * sizeof(struct ext4_dir_idx_tail)); orig_checksum = t->checksum; t->checksum = 0; /* First calculate crc32 checksum against fs uuid */ - checksum = ext4_crc32c(~0, sb->uuid, sizeof(sb->uuid)); - /* Then calculate crc32 checksum against all the dx_entry */ + checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, + sizeof(sb->uuid)); + /* Then calculate crc32 checksum against inode number + * and inode generation */ + checksum = ext4_crc32c(checksum, &ino_index, + sizeof(ino_index)); + checksum = ext4_crc32c(checksum, &ino_gen, + sizeof(ino_gen)); + /* After that calculate crc32 checksum against all the dx_entry */ checksum = ext4_crc32c(checksum, dirent, size); /* Finally calculate crc32 checksum for dx_tail */ checksum = ext4_crc32c(checksum, t, - sizeof(struct ext4_directory_dx_tail)); + sizeof(struct ext4_dir_idx_tail)); t->checksum = orig_checksum; } return checksum; } -static struct ext4_directory_dx_countlimit * +static struct ext4_dir_idx_countlimit * ext4_dir_dx_get_countlimit(struct ext4_inode_ref *inode_ref, - struct ext4_directory_entry_ll *dirent, + struct ext4_dir_entry_ll *dirent, int *offset) { - struct ext4_directory_entry_ll *dp; - struct ext4_directory_dx_root *root; + struct ext4_dir_entry_ll *dp; + struct ext4_dir_idx_root *root; struct ext4_sblock *sb = &inode_ref->fs->sb; int count_offset; @@ -245,13 +257,13 @@ ext4_dir_dx_get_countlimit(struct ext4_inode_ref *inode_ref, ext4_sb_get_block_size(sb)) count_offset = 8; else if (ext4_dir_entry_ll_get_entry_length(dirent) == 12) { - root = (struct ext4_directory_dx_root *)dirent; - dp = (struct ext4_directory_entry_ll *)&root->dots[1]; + root = (struct ext4_dir_idx_root *)dirent; + dp = (struct ext4_dir_entry_ll *)&root->dots[1]; if (ext4_dir_entry_ll_get_entry_length(dp) != ext4_sb_get_block_size(sb) - 12) return NULL; if (root->info.reserved_zero || - root->info.info_length != sizeof(struct ext4_directory_dx_root_info)) + root->info.info_length != sizeof(struct ext4_dir_idx_root_info)) return NULL; count_offset = 32; } else @@ -259,100 +271,118 @@ ext4_dir_dx_get_countlimit(struct ext4_inode_ref *inode_ref, if (offset) *offset = count_offset; - return (struct ext4_directory_dx_countlimit *)(((char *)dirent) + count_offset); + return (struct ext4_dir_idx_countlimit *)(((char *)dirent) + count_offset); } /* * BIG FAT NOTES: * Currently we do not verify the checksum of HTree node. */ -__unused static int +static bool ext4_dir_dx_checksum_verify(struct ext4_inode_ref *inode_ref, - struct ext4_directory_entry_ll *dirent) + struct ext4_dir_entry_ll *dirent) { struct ext4_sblock *sb = &inode_ref->fs->sb; int count_offset, limit, count; - if (ext4_sb_has_feature_read_only(sb, - EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { - struct ext4_directory_dx_countlimit *countlimit = + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + struct ext4_dir_idx_countlimit *countlimit = ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset); if (!countlimit) { /* Directory seems corrupted. */ - return 1; + return true; } - struct ext4_directory_dx_tail *t; + struct ext4_dir_idx_tail *t; limit = ext4_dir_dx_countlimit_get_limit(countlimit); count = ext4_dir_dx_countlimit_get_count(countlimit); - if (count_offset + (limit * sizeof(struct ext4_directory_dx_entry)) > + if (count_offset + (limit * sizeof(struct ext4_dir_idx_entry)) > ext4_sb_get_block_size(sb) - - sizeof(struct ext4_directory_dx_tail)) { + sizeof(struct ext4_dir_idx_tail)) { /* There is no space to hold the checksum */ - return 1; + return true; } - t = (struct ext4_directory_dx_tail *) - (((struct ext4_directory_dx_entry *)countlimit) + limit); + t = (void *)(((struct ext4_dir_idx_entry *)countlimit) + limit); if (t->checksum != to_le32(ext4_dir_dx_checksum(inode_ref, dirent, count_offset, count, t))) - return 0; + return false; } - return 1; + return true; } + static void ext4_dir_set_dx_checksum(struct ext4_inode_ref *inode_ref, - struct ext4_directory_entry_ll *dirent) + struct ext4_dir_entry_ll *dirent) { int count_offset, limit, count; struct ext4_sblock *sb = &inode_ref->fs->sb; - if (ext4_sb_has_feature_read_only(sb, - EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { - struct ext4_directory_dx_countlimit *countlimit = + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + struct ext4_dir_idx_countlimit *countlimit = ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset); if (!countlimit) { /* Directory seems corrupted. */ return; } - struct ext4_directory_dx_tail *t; + struct ext4_dir_idx_tail *t; limit = ext4_dir_dx_countlimit_get_limit(countlimit); count = ext4_dir_dx_countlimit_get_count(countlimit); - if (count_offset + (limit * sizeof(struct ext4_directory_dx_entry)) > + if (count_offset + (limit * sizeof(struct ext4_dir_idx_entry)) > ext4_sb_get_block_size(sb) - - sizeof(struct ext4_directory_dx_tail)) { + sizeof(struct ext4_dir_idx_tail)) { /* There is no space to hold the checksum */ return; } - t = (struct ext4_directory_dx_tail *) - (((struct ext4_directory_dx_entry *)countlimit) + limit); + t = (struct ext4_dir_idx_tail *) + (((struct ext4_dir_idx_entry *)countlimit) + limit); - t->checksum = - to_le32(ext4_dir_dx_checksum(inode_ref, dirent, count_offset, count, t)); + t->checksum = to_le32(ext4_dir_dx_checksum(inode_ref, dirent, + count_offset, count, t)); } } +#else +#define ext4_dir_dx_checksum_verify(...) true +#define ext4_dir_set_dx_checksum(...) +#endif /****************************************************************************/ -int ext4_dir_dx_init(struct ext4_inode_ref *dir) +int ext4_dir_dx_init(struct ext4_inode_ref *dir, struct ext4_inode_ref *parent) { /* Load block 0, where will be index root located */ ext4_fsblk_t fblock; + uint32_t iblock; struct ext4_sblock *sb = &dir->fs->sb; - int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock, false); + int rc = ext4_fs_append_inode_block(dir, &fblock, &iblock); if (rc != EOK) return rc; struct ext4_block block; - rc = ext4_block_get(dir->fs->bdev, &block, fblock); + rc = ext4_block_get_noread(dir->fs->bdev, &block, fblock); if (rc != EOK) return rc; /* Initialize pointers to data structures */ - struct ext4_directory_dx_root *root = (void *)block.data; - struct ext4_directory_dx_root_info *info = &(root->info); + struct ext4_dir_idx_root *root = (void *)block.data; + struct ext4_dir_idx_root_info *info = &(root->info); + + /* Initialize dot entries */ + ext4_dir_write_entry(&dir->fs->sb, + (struct ext4_dir_entry_ll *)root->dots, + 12, + dir, + ".", + strlen(".")); + + ext4_dir_write_entry(&dir->fs->sb, + (struct ext4_dir_entry_ll *)(root->dots + 1), + ext4_sb_get_block_size(sb) - 12, + parent, + "..", + strlen("..")); /* Initialize root info structure */ uint8_t hash_version = ext4_get8(&dir->fs->sb, default_hash_version); @@ -362,21 +392,24 @@ int ext4_dir_dx_init(struct ext4_inode_ref *dir) ext4_dir_dx_root_info_set_info_length(info, 8); /* Set limit and current number of entries */ - struct ext4_directory_dx_countlimit *countlimit = - (struct ext4_directory_dx_countlimit *)&root->entries; + struct ext4_dir_idx_countlimit *countlimit = + (struct ext4_dir_idx_countlimit *)&root->entries; ext4_dir_dx_countlimit_set_count(countlimit, 1); uint32_t block_size = ext4_sb_get_block_size(&dir->fs->sb); uint32_t entry_space = block_size - - 2 * sizeof(struct ext4_directory_dx_dot_entry) - - sizeof(struct ext4_directory_dx_root_info); + 2 * sizeof(struct ext4_dir_idx_dot_entry) - + sizeof(struct ext4_dir_idx_root_info); + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_dir_idx_tail); + uint16_t root_limit = - entry_space / sizeof(struct ext4_directory_dx_entry); + entry_space / sizeof(struct ext4_dir_idx_entry); + ext4_dir_dx_countlimit_set_limit(countlimit, root_limit); /* Append new block, where will be new entries inserted in the future */ - uint32_t iblock; rc = ext4_fs_append_inode_block(dir, &fblock, &iblock); if (rc != EOK) { ext4_block_set(dir->fs->bdev, &block); @@ -385,27 +418,30 @@ int ext4_dir_dx_init(struct ext4_inode_ref *dir) struct ext4_block new_block; - rc = ext4_block_get(dir->fs->bdev, &new_block, fblock); + rc = ext4_block_get_noread(dir->fs->bdev, &new_block, fblock); if (rc != EOK) { ext4_block_set(dir->fs->bdev, &block); return rc; } /* Fill the whole block with empty entry */ - struct ext4_directory_entry_ll *block_entry = (void *)new_block.data; + struct ext4_dir_entry_ll *block_entry = (void *)new_block.data; - if (ext4_sb_has_feature_read_only(sb, - EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { ext4_dir_entry_ll_set_entry_length(block_entry, - sizeof(struct ext4_directory_entry_tail)); + block_size - + sizeof(struct ext4_dir_entry_tail)); ext4_dir_entry_ll_set_name_length(sb, block_entry, 0); ext4_dir_entry_ll_set_inode_type(sb, block_entry, - EXT4_DIRENTRY_DIR_CSUM); + EXT4_DIRENTRY_UNKNOWN); + + initialize_dir_tail(EXT4_DIRENT_TAIL(block_entry, + ext4_sb_get_block_size(sb))); ext4_dir_set_checksum(dir, - (struct ext4_directory_entry_ll *)new_block.data); + (struct ext4_dir_entry_ll *)new_block.data); } else { ext4_dir_entry_ll_set_entry_length(block_entry, block_size); } @@ -420,11 +456,11 @@ int ext4_dir_dx_init(struct ext4_inode_ref *dir) } /* Connect new block to the only entry in index */ - struct ext4_directory_dx_entry *entry = root->entries; + struct ext4_dir_idx_entry *entry = root->entries; ext4_dir_dx_entry_set_block(entry, iblock); ext4_dir_set_dx_checksum(dir, - (struct ext4_directory_entry_ll *)block.data); + (struct ext4_dir_entry_ll *)block.data); block.dirty = true; return ext4_block_set(dir->fs->bdev, &block); @@ -443,8 +479,8 @@ static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo, struct ext4_sblock *sb, size_t name_len, const char *name) { - struct ext4_directory_dx_root *root = - (struct ext4_directory_dx_root *)root_block->data; + struct ext4_dir_idx_root *root = + (struct ext4_dir_idx_root *)root_block->data; if ((root->info.hash_version != EXT2_HTREE_LEGACY) && (root->info.hash_version != EXT2_HTREE_HALF_MD4) && @@ -462,12 +498,14 @@ static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo, /* Check if node limit is correct */ uint32_t block_size = ext4_sb_get_block_size(sb); uint32_t entry_space = block_size; - entry_space -= 2 * sizeof(struct ext4_directory_dx_dot_entry); - entry_space -= sizeof(struct ext4_directory_dx_root_info); - entry_space = entry_space / sizeof(struct ext4_directory_dx_entry); + entry_space -= 2 * sizeof(struct ext4_dir_idx_dot_entry); + entry_space -= sizeof(struct ext4_dir_idx_root_info); + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_dir_idx_tail); + entry_space = entry_space / sizeof(struct ext4_dir_idx_entry); uint16_t limit = ext4_dir_dx_countlimit_get_limit( - (struct ext4_directory_dx_countlimit *)&root->entries); + (struct ext4_dir_idx_countlimit *)&root->entries); if (limit != entry_space) return EXT4_ERR_BAD_DX_DIR; @@ -502,30 +540,30 @@ static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo, static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo, struct ext4_inode_ref *inode_ref, struct ext4_block *root_block, - struct ext4_directory_dx_block **dx_block, - struct ext4_directory_dx_block *dx_blocks) + struct ext4_dir_idx_block **dx_block, + struct ext4_dir_idx_block *dx_blocks) { - struct ext4_directory_dx_block *tmp_dx_block = dx_blocks; - struct ext4_directory_dx_root *root = - (struct ext4_directory_dx_root *)root_block->data; - struct ext4_directory_dx_entry *entries = - (struct ext4_directory_dx_entry *)&root->entries; + struct ext4_dir_idx_block *tmp_dx_block = dx_blocks; + struct ext4_dir_idx_root *root = + (struct ext4_dir_idx_root *)root_block->data; + struct ext4_dir_idx_entry *entries = + (struct ext4_dir_idx_entry *)&root->entries; uint16_t limit = ext4_dir_dx_countlimit_get_limit( - (struct ext4_directory_dx_countlimit *)entries); + (struct ext4_dir_idx_countlimit *)entries); uint8_t indirect_level = ext4_dir_dx_root_info_get_indirect_levels(&root->info); struct ext4_block *tmp_block = root_block; - struct ext4_directory_dx_entry *p; - struct ext4_directory_dx_entry *q; - struct ext4_directory_dx_entry *m; - struct ext4_directory_dx_entry *at; + struct ext4_dir_idx_entry *p; + struct ext4_dir_idx_entry *q; + struct ext4_dir_idx_entry *m; + struct ext4_dir_idx_entry *at; /* Walk through the index tree */ while (true) { uint16_t count = ext4_dir_dx_countlimit_get_count( - (struct ext4_directory_dx_countlimit *)entries); + (struct ext4_dir_idx_countlimit *)entries); if ((count == 0) || (count > limit)) return EXT4_ERR_BAD_DX_DIR; @@ -572,22 +610,34 @@ static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo, return rc; entries = - ((struct ext4_directory_dx_node *)tmp_block->data)->entries; + ((struct ext4_dir_idx_node *)tmp_block->data)->entries; limit = ext4_dir_dx_countlimit_get_limit( - (struct ext4_directory_dx_countlimit *)entries); + (struct ext4_dir_idx_countlimit *)entries); - uint16_t entry_space = - ext4_sb_get_block_size(&inode_ref->fs->sb) - - sizeof(struct ext4_fake_directory_entry); + struct ext4_sblock *sb = &inode_ref->fs->sb; + uint16_t entry_space = ext4_sb_get_block_size(sb) - + sizeof(struct ext4_fake_dir_entry); - entry_space = - entry_space / sizeof(struct ext4_directory_dx_entry); + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_dir_idx_tail); + + entry_space = entry_space / sizeof(struct ext4_dir_idx_entry); if (limit != entry_space) { ext4_block_set(inode_ref->fs->bdev, tmp_block); return EXT4_ERR_BAD_DX_DIR; } + if (!ext4_dir_dx_checksum_verify(inode_ref, + (struct ext4_dir_entry_ll *)tmp_block->data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + inode_ref->index, + next_block); + } + ++tmp_dx_block; } @@ -604,17 +654,17 @@ static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo, */ static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref, uint32_t hash, - struct ext4_directory_dx_block *dx_block, - struct ext4_directory_dx_block *dx_blocks) + struct ext4_dir_idx_block *dx_block, + struct ext4_dir_idx_block *dx_blocks) { uint32_t num_handles = 0; - struct ext4_directory_dx_block *p = dx_block; + struct ext4_dir_idx_block *p = dx_block; /* Try to find data block with next bunch of entries */ while (true) { p->position++; uint16_t count = ext4_dir_dx_countlimit_get_count( - (struct ext4_directory_dx_countlimit *)p->entries); + (struct ext4_dir_idx_countlimit *)p->entries); if (p->position < p->entries + count) break; @@ -649,6 +699,16 @@ static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref, if (rc != EOK) return rc; + if (!ext4_dir_dx_checksum_verify(inode_ref, + (struct ext4_dir_entry_ll *)block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + inode_ref->index, + block_idx); + } + p++; /* Don't forget to put old block (prevent memory leak) */ @@ -658,14 +718,14 @@ static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref, memcpy(&p->block, &block, sizeof(block)); p->entries = - ((struct ext4_directory_dx_node *)block.data)->entries; + ((struct ext4_dir_idx_node *)block.data)->entries; p->position = p->entries; } return ENOENT; } -int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result, +int ext4_dir_dx_find_entry(struct ext4_dir_search_result *result, struct ext4_inode_ref *inode_ref, size_t name_len, const char *name) { @@ -685,6 +745,16 @@ int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result, if (rc != EOK) return rc; + if (!ext4_dir_dx_checksum_verify(inode_ref, + (struct ext4_dir_entry_ll *)root_block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree root checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + inode_ref->index, + (uint32_t)0); + } + /* Initialize hash info (compute hash value) */ struct ext4_hash_info hinfo; rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name); @@ -697,9 +767,9 @@ int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result, * Hardcoded number 2 means maximum height of index tree, * specified in the Linux driver. */ - struct ext4_directory_dx_block dx_blocks[2]; - struct ext4_directory_dx_block *dx_block; - struct ext4_directory_dx_block *tmp; + struct ext4_dir_idx_block dx_blocks[2]; + struct ext4_dir_idx_block *dx_block; + struct ext4_dir_idx_block *tmp; rc = ext4_dir_dx_get_leaf(&hinfo, inode_ref, &root_block, &dx_block, dx_blocks); @@ -725,8 +795,18 @@ int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result, if (rc != EOK) goto cleanup; + if (!ext4_dir_checksum_verify(inode_ref, + (struct ext4_dir_entry_ll *)leaf_block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree leaf block checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + inode_ref->index, + leaf_block_idx); + } + /* Linear search inside block */ - struct ext4_directory_entry_ll *res_dentry; + struct ext4_dir_entry_ll *res_dentry; rc = ext4_dir_find_in_block(&leaf_block, &fs->sb, name_len, name, &res_dentry); @@ -838,18 +918,18 @@ static int ext4_dir_dx_entry_comparator(const void *arg1, const void *arg2) * */ static void -ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref, - struct ext4_directory_dx_block *index_block, +ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref __unused, + struct ext4_dir_idx_block *index_block, uint32_t hash, uint32_t iblock) { - struct ext4_directory_dx_entry *old_index_entry = index_block->position; - struct ext4_directory_dx_entry *new_index_entry = old_index_entry + 1; + struct ext4_dir_idx_entry *old_index_entry = index_block->position; + struct ext4_dir_idx_entry *new_index_entry = old_index_entry + 1; - struct ext4_directory_dx_countlimit *countlimit = - (struct ext4_directory_dx_countlimit *)index_block->entries; + struct ext4_dir_idx_countlimit *countlimit = + (struct ext4_dir_idx_countlimit *)index_block->entries; uint32_t count = ext4_dir_dx_countlimit_get_count(countlimit); - struct ext4_directory_dx_entry *start_index = index_block->entries; + struct ext4_dir_idx_entry *start_index = index_block->entries; size_t bytes = (uint8_t *)(start_index + count) - (uint8_t *)(new_index_entry); @@ -861,7 +941,7 @@ ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref, ext4_dir_dx_countlimit_set_count(countlimit, count + 1); ext4_dir_set_dx_checksum(inode_ref, - (struct ext4_directory_entry_ll *)index_block->block.data); + (struct ext4_dir_entry_ll *)index_block->block.data); index_block->block.dirty = true; } @@ -875,7 +955,7 @@ ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref, static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, struct ext4_hash_info *hinfo, struct ext4_block *old_data_block, - struct ext4_directory_dx_block *index_block, + struct ext4_dir_idx_block *index_block, struct ext4_block *new_data_block) { int rc = EOK; @@ -889,7 +969,7 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, /* dot entry has the smallest size available */ uint32_t max_entry_count = - block_size / sizeof(struct ext4_directory_dx_dot_entry); + block_size / sizeof(struct ext4_dir_idx_dot_entry); /* Allocate sort entry */ struct ext4_dx_sort_entry *sort_array = @@ -908,12 +988,11 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, memcpy(&tmp_hinfo, hinfo, sizeof(struct ext4_hash_info)); /* Load all valid entries to the buffer */ - struct ext4_directory_entry_ll *dentry = (void *)old_data_block->data; + struct ext4_dir_entry_ll *dentry = (void *)old_data_block->data; uint8_t *entry_buffer_ptr = entry_buffer; while ((void *)dentry < (void *)(old_data_block->data + block_size)) { /* Read only valid entries */ - if (ext4_dir_entry_ll_get_inode(dentry) && - dentry->name_length) { + if (ext4_dir_entry_ll_get_inode(dentry) && dentry->name_length) { uint8_t len = ext4_dir_entry_ll_get_name_length( &inode_ref->fs->sb, dentry); @@ -964,8 +1043,8 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, /* Load new block */ struct ext4_block new_data_block_tmp; - rc = ext4_block_get(inode_ref->fs->bdev, &new_data_block_tmp, - new_fblock); + rc = ext4_block_get_noread(inode_ref->fs->bdev, &new_data_block_tmp, + new_fblock); if (rc != EOK) { free(sort_array); free(entry_buffer); @@ -997,13 +1076,16 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, uint32_t offset = 0; void *ptr; + struct ext4_sblock *sb = &inode_ref->fs->sb; + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + block_size -= sizeof(struct ext4_dir_entry_tail); /* First part - to the old block */ for (i = 0; i < mid; ++i) { ptr = old_data_block->data + offset; memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len); - struct ext4_directory_entry_ll *tmp = ptr; + struct ext4_dir_entry_ll *tmp = ptr; if (i < (mid - 1)) ext4_dir_entry_ll_set_entry_length( tmp, sort_array[i].rec_len); @@ -1020,7 +1102,7 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, ptr = new_data_block_tmp.data + offset; memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len); - struct ext4_directory_entry_ll *tmp = ptr; + struct ext4_dir_entry_ll *tmp = ptr; if (i < (idx - 1)) ext4_dir_entry_ll_set_entry_length( tmp, sort_array[i].rec_len); @@ -1031,11 +1113,20 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, offset += sort_array[i].rec_len; } + block_size = ext4_sb_get_block_size(&inode_ref->fs->sb); + /* Do some steps to finish operation */ - ext4_dir_set_dx_checksum(inode_ref, - (struct ext4_directory_entry_ll *)old_data_block->data); - ext4_dir_set_dx_checksum(inode_ref, - (struct ext4_directory_entry_ll *)new_data_block_tmp.data); + sb = &inode_ref->fs->sb; + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { + initialize_dir_tail(EXT4_DIRENT_TAIL(old_data_block->data, + block_size)); + initialize_dir_tail(EXT4_DIRENT_TAIL(new_data_block_tmp.data, + block_size)); + } + ext4_dir_set_checksum(inode_ref, + (struct ext4_dir_entry_ll *)old_data_block->data); + ext4_dir_set_checksum(inode_ref, + (struct ext4_dir_entry_ll *)new_data_block_tmp.data); old_data_block->dirty = true; new_data_block_tmp.dirty = true; @@ -1059,23 +1150,24 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, */ static int ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, - struct ext4_directory_dx_block *dx_blocks, - struct ext4_directory_dx_block *dx_block, - struct ext4_directory_dx_block **new_dx_block) + struct ext4_dir_idx_block *dx_blocks, + struct ext4_dir_idx_block *dx_block, + struct ext4_dir_idx_block **new_dx_block) { - struct ext4_directory_dx_entry *entries; + struct ext4_sblock *sb = &inode_ref->fs->sb; + struct ext4_dir_idx_entry *entries; if (dx_block == dx_blocks) entries = - ((struct ext4_directory_dx_root *)dx_block->block.data) + ((struct ext4_dir_idx_root *)dx_block->block.data) ->entries; else entries = - ((struct ext4_directory_dx_node *)dx_block->block.data) + ((struct ext4_dir_idx_node *)dx_block->block.data) ->entries; - struct ext4_directory_dx_countlimit *countlimit = - (struct ext4_directory_dx_countlimit *)entries; + struct ext4_dir_idx_countlimit *countlimit = + (struct ext4_dir_idx_countlimit *)entries; uint16_t leaf_limit = ext4_dir_dx_countlimit_get_limit(countlimit); uint16_t leaf_count = ext4_dir_dx_countlimit_get_count(countlimit); @@ -1084,12 +1176,12 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, if (leaf_limit == leaf_count) { size_t levels = dx_block - dx_blocks; - struct ext4_directory_dx_entry *root_entries = - ((struct ext4_directory_dx_root *)dx_blocks[0].block.data) + struct ext4_dir_idx_entry *root_entries = + ((struct ext4_dir_idx_root *)dx_blocks[0].block.data) ->entries; - struct ext4_directory_dx_countlimit *root_countlimit = - (struct ext4_directory_dx_countlimit *)root_entries; + struct ext4_dir_idx_countlimit *root_countlimit = + (struct ext4_dir_idx_countlimit *)root_entries; uint16_t root_limit = ext4_dir_dx_countlimit_get_limit(root_countlimit); uint16_t root_count = @@ -1110,16 +1202,17 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, /* load new block */ struct ext4_block new_block; rc = - ext4_block_get(inode_ref->fs->bdev, &new_block, new_fblock); + ext4_block_get_noread(inode_ref->fs->bdev, + &new_block, new_fblock); if (rc != EOK) return rc; - struct ext4_directory_dx_node *new_node = + struct ext4_dir_idx_node *new_node = (void *)new_block.data; - struct ext4_directory_dx_entry *new_entries = new_node->entries; + struct ext4_dir_idx_entry *new_entries = new_node->entries; memset(&new_node->fake, 0, - sizeof(struct ext4_fake_directory_entry)); + sizeof(struct ext4_fake_dir_entry)); uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb); @@ -1137,13 +1230,13 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, memcpy((void *)new_entries, (void *)(entries + count_left), count_right * - sizeof(struct ext4_directory_dx_entry)); + sizeof(struct ext4_dir_idx_entry)); /* Initialize new node */ - struct ext4_directory_dx_countlimit *left_countlimit = - (struct ext4_directory_dx_countlimit *)entries; - struct ext4_directory_dx_countlimit *right_countlimit = - (struct ext4_directory_dx_countlimit *)new_entries; + struct ext4_dir_idx_countlimit *left_countlimit = + (struct ext4_dir_idx_countlimit *)entries; + struct ext4_dir_idx_countlimit *right_countlimit = + (struct ext4_dir_idx_countlimit *)new_entries; ext4_dir_dx_countlimit_set_count(left_countlimit, count_left); @@ -1152,10 +1245,13 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, uint32_t entry_space = block_size - - sizeof(struct ext4_fake_directory_entry); + sizeof(struct ext4_fake_dir_entry); + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_dir_idx_tail); + uint32_t node_limit = - entry_space / - sizeof(struct ext4_directory_dx_entry); + entry_space / sizeof(struct ext4_dir_idx_entry); + ext4_dir_dx_countlimit_set_limit(right_countlimit, node_limit); @@ -1165,7 +1261,7 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, if (position_index >= count_left) { ext4_dir_set_dx_checksum( inode_ref, - (struct ext4_directory_entry_ll *) + (struct ext4_dir_entry_ll *) dx_block->block.data); dx_block->block.dirty = true; @@ -1185,16 +1281,16 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, dx_blocks, hash_right, new_iblock); ext4_dir_set_dx_checksum(inode_ref, - (struct ext4_directory_entry_ll *) + (struct ext4_dir_entry_ll *) dx_blocks[0].block.data); ext4_dir_set_dx_checksum(inode_ref, - (struct ext4_directory_entry_ll *) + (struct ext4_dir_entry_ll *) dx_blocks[1].block.data); dx_blocks[0].block.dirty = true; dx_blocks[1].block.dirty = true; ext4_dir_set_dx_checksum(inode_ref, - (struct ext4_directory_entry_ll *) + (struct ext4_dir_entry_ll *) new_block.data); new_block.dirty = true; return ext4_block_set(inode_ref->fs->bdev, &new_block); @@ -1204,30 +1300,32 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, /* Copy data from root to child block */ memcpy((void *)new_entries, (void *)entries, leaf_count * - sizeof(struct ext4_directory_dx_entry)); + sizeof(struct ext4_dir_idx_entry)); - struct ext4_directory_dx_countlimit *new_countlimit = - (struct ext4_directory_dx_countlimit *)new_entries; + struct ext4_dir_idx_countlimit *new_countlimit = + (struct ext4_dir_idx_countlimit *)new_entries; uint32_t entry_space = block_size - - sizeof(struct ext4_fake_directory_entry); + sizeof(struct ext4_fake_dir_entry); + if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_dir_idx_tail); + uint32_t node_limit = - entry_space / - sizeof(struct ext4_directory_dx_entry); + entry_space / sizeof(struct ext4_dir_idx_entry); ext4_dir_dx_countlimit_set_limit(new_countlimit, node_limit); /* Set values in root node */ - struct ext4_directory_dx_countlimit + struct ext4_dir_idx_countlimit *new_root_countlimit = - (struct ext4_directory_dx_countlimit *)entries; + (struct ext4_dir_idx_countlimit *)entries; ext4_dir_dx_countlimit_set_count(new_root_countlimit, 1); ext4_dir_dx_entry_set_block(entries, new_iblock); - ((struct ext4_directory_dx_root *)dx_blocks[0] + ((struct ext4_dir_idx_root *)dx_blocks[0] .block.data) ->info.indirect_levels = 1; @@ -1241,10 +1339,10 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, *new_dx_block = dx_block; ext4_dir_set_dx_checksum(inode_ref, - (struct ext4_directory_entry_ll *) + (struct ext4_dir_entry_ll *) dx_blocks[0].block.data); ext4_dir_set_dx_checksum(inode_ref, - (struct ext4_directory_entry_ll *) + (struct ext4_dir_entry_ll *) dx_blocks[1].block.data); dx_blocks[0].block.dirty = true; dx_blocks[1].block.dirty = true; @@ -1275,6 +1373,16 @@ int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent, if (rc != EOK) return rc; + if (!ext4_dir_dx_checksum_verify(parent, + (struct ext4_dir_entry_ll *)root_block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree root checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + parent->index, + (uint32_t)0); + } + /* Initialize hinfo structure (mainly compute hash) */ uint32_t name_len = strlen(name); struct ext4_hash_info hinfo; @@ -1288,9 +1396,9 @@ int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent, * Hardcoded number 2 means maximum height of index * tree defined in Linux. */ - struct ext4_directory_dx_block dx_blocks[2]; - struct ext4_directory_dx_block *dx_block; - struct ext4_directory_dx_block *dx_it; + struct ext4_dir_idx_block dx_blocks[2]; + struct ext4_dir_idx_block *dx_block; + struct ext4_dir_idx_block *dx_it; rc = ext4_dir_dx_get_leaf(&hinfo, parent, &root_block, &dx_block, dx_blocks); @@ -1322,9 +1430,19 @@ int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent, if (rc != EOK) goto release_index; + if (!ext4_dir_checksum_verify(parent, + (struct ext4_dir_entry_ll *)target_block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree leaf block checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + parent->index, + leaf_block_idx); + } + /* Check if insert operation passed */ - rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child, name, - name_len); + rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child, + name, name_len); if (rc == EOK) goto release_target_index; @@ -1341,11 +1459,11 @@ int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent, uint32_t new_block_hash = ext4_dir_dx_entry_get_hash(dx_block->position + 1); if (hinfo.hash >= new_block_hash) - rc = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block, child, name, - name_len); + rc = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block, + child, name, name_len); else - rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child, - name, name_len); + rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, + child, name, name_len); /* Cleanup */ rc = ext4_block_set(fs->bdev, &new_block); @@ -1392,14 +1510,24 @@ int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir, if (rc != EOK) return rc; + if (!ext4_dir_dx_checksum_verify(dir, + (struct ext4_dir_entry_ll *)block.data)) { + ext4_dbg(DEBUG_DIR_IDX, + DBG_WARN "HTree root checksum failed." + "Inode: %" PRIu32", " + "Block: %" PRIu32"\n", + dir->index, + (uint32_t)0); + } + /* Initialize pointers to data structures */ - struct ext4_directory_dx_root *root = (void *)block.data; + struct ext4_dir_idx_root *root = (void *)block.data; /* Fill the inode field with a new parent ino. */ ext4_dx_dot_entry_set_inode(&root->dots[1], parent_inode); ext4_dir_set_dx_checksum(dir, - (struct ext4_directory_entry_ll *) + (struct ext4_dir_entry_ll *) block.data); block.dirty = true;