X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=lwext4%2Fext4_dir_idx.c;h=907b6d222dfedcbb941cc872f1e9c68509c6567c;hb=4c6115ffe7fddaab8fa1c12e7cce7c7902ceb36f;hp=a34fc50c8b58ed16932f0165df84b017b1ee9084;hpb=5e12e58afed7bcda732d379db10d684370a6687a;p=lwext4.git diff --git a/lwext4/ext4_dir_idx.c b/lwext4/ext4_dir_idx.c index a34fc50..907b6d2 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_crc32c.h" #include "ext4_hash.h" #include @@ -202,13 +203,145 @@ static int ext4_dir_dx_hash_string(struct ext4_hash_info *hinfo, int len, &hinfo->hash, &hinfo->minor_hash); } +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) +{ + 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)) { + size = count_offset + + (count * sizeof(struct ext4_directory_dx_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(checksum, dirent, size); + /* Finally calculate crc32 checksum for dx_tail */ + checksum = ext4_crc32c(checksum, t, + sizeof(struct ext4_directory_dx_tail)); + t->checksum = orig_checksum; + } + return checksum; +} + +static struct ext4_directory_dx_countlimit * +ext4_dir_dx_get_countlimit(struct ext4_inode_ref *inode_ref, + struct ext4_directory_entry_ll *dirent, + int *offset) +{ + struct ext4_directory_entry_ll *dp; + struct ext4_directory_dx_root *root; + struct ext4_sblock *sb = &inode_ref->fs->sb; + int count_offset; + + if (ext4_dir_entry_ll_get_entry_length(dirent) == + 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]; + 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)) + return NULL; + count_offset = 32; + } else + return NULL; + + if (offset) + *offset = count_offset; + return (struct ext4_directory_dx_countlimit *)(((char *)dirent) + count_offset); +} + +/* + * BIG FAT NOTES: + * Currently we do not verify the checksum of HTree node. + */ +__unused static int +ext4_dir_dx_checksum_verify(struct ext4_inode_ref *inode_ref, + struct ext4_directory_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 = + ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset); + if (!countlimit) { + /* Directory seems corrupted. */ + return 1; + } + struct ext4_directory_dx_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)) > + ext4_sb_get_block_size(sb) - + sizeof(struct ext4_directory_dx_tail)) { + /* There is no space to hold the checksum */ + return 1; + } + t = (struct ext4_directory_dx_tail *) + (((struct ext4_directory_dx_entry *)countlimit) + limit); + + if (t->checksum != to_le32(ext4_dir_dx_checksum(inode_ref, + dirent, + count_offset, + count, t))) + return 0; + } + return 1; +} + +static void +ext4_dir_set_dx_checksum(struct ext4_inode_ref *inode_ref, + struct ext4_directory_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 = + ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset); + if (!countlimit) { + /* Directory seems corrupted. */ + return; + } + struct ext4_directory_dx_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)) > + ext4_sb_get_block_size(sb) - + sizeof(struct ext4_directory_dx_tail)) { + /* There is no space to hold the checksum */ + return; + } + t = (struct ext4_directory_dx_tail *) + (((struct ext4_directory_dx_entry *)countlimit) + limit); + + t->checksum = + to_le32(ext4_dir_dx_checksum(inode_ref, dirent, count_offset, count, t)); + } +} + /****************************************************************************/ int ext4_dir_dx_init(struct ext4_inode_ref *dir) { /* Load block 0, where will be index root located */ - uint32_t fblock; - int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock); + ext4_fsblk_t fblock; + struct ext4_sblock *sb = &dir->fs->sb; + int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock, false); if (rc != EOK) return rc; @@ -238,8 +371,12 @@ int ext4_dir_dx_init(struct ext4_inode_ref *dir) uint32_t entry_space = block_size - 2 * sizeof(struct ext4_directory_dx_dot_entry) - sizeof(struct ext4_directory_dx_root_info); + if (ext4_sb_has_feature_read_only(sb, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_directory_dx_tail); uint16_t root_limit = entry_space / sizeof(struct ext4_directory_dx_entry); + ext4_dir_dx_countlimit_set_limit(countlimit, root_limit); /* Append new block, where will be new entries inserted in the future */ @@ -261,7 +398,26 @@ int ext4_dir_dx_init(struct ext4_inode_ref *dir) /* Fill the whole block with empty entry */ struct ext4_directory_entry_ll *block_entry = (void *)new_block.data; - ext4_dir_entry_ll_set_entry_length(block_entry, block_size); + if (ext4_sb_has_feature_read_only(sb, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { + ext4_dir_entry_ll_set_entry_length(block_entry, + block_size - + sizeof(struct ext4_directory_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_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); + } else { + ext4_dir_entry_ll_set_entry_length(block_entry, block_size); + } + ext4_dir_entry_ll_set_inode(block_entry, 0); new_block.dirty = true; @@ -275,6 +431,8 @@ int ext4_dir_dx_init(struct ext4_inode_ref *dir) struct ext4_directory_dx_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); block.dirty = true; return ext4_block_set(dir->fs->bdev, &block); @@ -411,9 +569,9 @@ static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo, indirect_level--; - uint32_t fblock; + ext4_fsblk_t fblock; int rc = ext4_fs_get_inode_data_block_index( - inode_ref, next_block, &fblock); + inode_ref, next_block, &fblock, false); if (rc != EOK) return rc; @@ -487,10 +645,10 @@ static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref, /* Fill new path */ while (num_handles--) { uint32_t block_idx = ext4_dir_dx_entry_get_block(p->position); - uint32_t block_addr; + ext4_fsblk_t block_addr; int rc = ext4_fs_get_inode_data_block_index( - inode_ref, block_idx, &block_addr); + inode_ref, block_idx, &block_addr, false); if (rc != EOK) return rc; @@ -506,7 +664,7 @@ static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref, if (rc != EOK) return rc; - memcpy(&p->block, &p->block, sizeof(block)); + memcpy(&p->block, &block, sizeof(block)); p->entries = ((struct ext4_directory_dx_node *)block.data)->entries; p->position = p->entries; @@ -520,10 +678,11 @@ int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result, const char *name) { /* Load direct block 0 (index root) */ - uint32_t root_block_addr; + ext4_fsblk_t root_block_addr; int rc2; int rc = - ext4_fs_get_inode_data_block_index(inode_ref, 0, &root_block_addr); + ext4_fs_get_inode_data_block_index(inode_ref, + 0, &root_block_addr, false); if (rc != EOK) return rc; @@ -561,10 +720,11 @@ int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result, /* Load leaf block */ uint32_t leaf_block_idx = ext4_dir_dx_entry_get_block(dx_block->position); - uint32_t leaf_block_addr; + ext4_fsblk_t leaf_block_addr; rc = ext4_fs_get_inode_data_block_index( - inode_ref, leaf_block_idx, &leaf_block_addr); + inode_ref, leaf_block_idx, &leaf_block_addr, + false); if (rc != EOK) goto cleanup; @@ -679,13 +839,15 @@ static int ext4_dir_dx_entry_comparator(const void *arg1, const void *arg2) /**@brief Insert new index entry to block. * Note that space for new entry must be checked by caller. + * @param inode_ref Directory i-node * @param index_block Block where to insert new entry * @param hash Hash value covered by child node * @param iblock Logical number of child block * */ static void -ext4_dir_dx_insert_entry(struct ext4_directory_dx_block *index_block, +ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref, + struct ext4_directory_dx_block *index_block, uint32_t hash, uint32_t iblock) { struct ext4_directory_dx_entry *old_index_entry = index_block->position; @@ -706,6 +868,8 @@ ext4_dir_dx_insert_entry(struct ext4_directory_dx_block *index_block, 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); index_block->block.dirty = true; } @@ -797,7 +961,7 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, ext4_dir_dx_entry_comparator); #endif /* Allocate new block for store the second part of entries */ - uint32_t new_fblock; + ext4_fsblk_t new_fblock; uint32_t new_iblock; rc = ext4_fs_append_inode_block(inode_ref, &new_fblock, &new_iblock); if (rc != EOK) { @@ -842,6 +1006,10 @@ static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref, uint32_t offset = 0; void *ptr; + if (ext4_sb_has_feature_read_only(&inode_ref->fs->sb, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + block_size -= sizeof(struct ext4_directory_entry_tail); + /* First part - to the old block */ for (i = 0; i < mid; ++i) { ptr = old_data_block->data + offset; @@ -875,14 +1043,29 @@ 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 */ + if (ext4_sb_has_feature_read_only(&inode_ref->fs->sb, + EXT4_FEATURE_RO_COMPAT_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_directory_entry_ll *)old_data_block->data); + ext4_dir_set_checksum(inode_ref, + (struct ext4_directory_entry_ll *)new_data_block_tmp.data); old_data_block->dirty = true; new_data_block_tmp.dirty = true; free(sort_array); free(entry_buffer); - ext4_dir_dx_insert_entry(index_block, new_hash + continued, new_iblock); + ext4_dir_dx_insert_entry(inode_ref, + index_block, + new_hash + continued, new_iblock); *new_data_block = new_data_block_tmp; @@ -901,6 +1084,7 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, struct ext4_directory_dx_block *dx_block, struct ext4_directory_dx_block **new_dx_block) { + struct ext4_sblock *sb = &inode_ref->fs->sb; struct ext4_directory_dx_entry *entries; if (dx_block == dx_blocks) @@ -938,7 +1122,7 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, return ENOSPC; /* Add new block to directory */ - uint32_t new_fblock; + ext4_fsblk_t new_fblock; uint32_t new_iblock; int rc = ext4_fs_append_inode_block(inode_ref, &new_fblock, &new_iblock); @@ -991,9 +1175,13 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, uint32_t entry_space = block_size - sizeof(struct ext4_fake_directory_entry); + if (ext4_sb_has_feature_read_only(sb, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_directory_dx_tail); uint32_t node_limit = entry_space / sizeof(struct ext4_directory_dx_entry); + ext4_dir_dx_countlimit_set_limit(right_countlimit, node_limit); @@ -1001,6 +1189,10 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, uint32_t position_index = (dx_block->position - dx_block->entries); if (position_index >= count_left) { + ext4_dir_set_dx_checksum( + inode_ref, + (struct ext4_directory_entry_ll *) + dx_block->block.data); dx_block->block.dirty = true; struct ext4_block block_tmp = dx_block->block; @@ -1015,11 +1207,21 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, } /* Finally insert new entry */ - ext4_dir_dx_insert_entry(dx_blocks, hash_right, + ext4_dir_dx_insert_entry(inode_ref, + dx_blocks, hash_right, new_iblock); + ext4_dir_set_dx_checksum(inode_ref, + (struct ext4_directory_entry_ll *) + dx_blocks[0].block.data); + ext4_dir_set_dx_checksum(inode_ref, + (struct ext4_directory_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 *) + new_block.data); new_block.dirty = true; return ext4_block_set(inode_ref->fs->bdev, &new_block); } else { @@ -1036,6 +1238,9 @@ ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref, uint32_t entry_space = block_size - sizeof(struct ext4_fake_directory_entry); + if (ext4_sb_has_feature_read_only(sb, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) + entry_space -= sizeof(struct ext4_directory_dx_tail); uint32_t node_limit = entry_space / sizeof(struct ext4_directory_dx_entry); @@ -1064,6 +1269,12 @@ 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 *) + dx_blocks[0].block.data); + ext4_dir_set_dx_checksum(inode_ref, + (struct ext4_directory_entry_ll *) + dx_blocks[1].block.data); dx_blocks[0].block.dirty = true; dx_blocks[1].block.dirty = true; } @@ -1078,9 +1289,11 @@ int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent, int rc2 = EOK; /* Get direct block 0 (index root) */ - uint32_t root_block_addr; + ext4_fsblk_t root_block_addr; int rc = - ext4_fs_get_inode_data_block_index(parent, 0, &root_block_addr); + ext4_fs_get_inode_data_block_index(parent, + 0, &root_block_addr, + false); if (rc != EOK) return rc; @@ -1118,9 +1331,10 @@ int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent, /* Try to insert to existing data block */ uint32_t leaf_block_idx = ext4_dir_dx_entry_get_block(dx_block->position); - uint32_t leaf_block_addr; + ext4_fsblk_t leaf_block_addr; rc = ext4_fs_get_inode_data_block_index(parent, leaf_block_idx, - &leaf_block_addr); + &leaf_block_addr, + false); if (rc != EOK) goto release_index; @@ -1138,7 +1352,7 @@ int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent, goto release_index; /* Check if insert operation passed */ - rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child, name, + rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child, name, name_len); if (rc == EOK) goto release_target_index; @@ -1156,10 +1370,10 @@ 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, &new_block, child, name, + rc = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block, child, name, name_len); else - rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child, + rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child, name, name_len); /* Cleanup */ @@ -1197,8 +1411,8 @@ int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir, uint32_t parent_inode) { /* Load block 0, where will be index root located */ - uint32_t fblock; - int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock); + ext4_fsblk_t fblock; + int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock, false); if (rc != EOK) return rc; @@ -1213,6 +1427,9 @@ int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir, /* 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 *) + block.data); block.dirty = true; return ext4_block_set(dir->fs->bdev, &block);