static void ext4_sb_set_csum(struct ext4_sblock *s) { if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM)) return; s->checksum = to_le32(ext4_sb_csum(s)); }
bool ext4_sb_is_super_in_bg(struct ext4_sblock *s, uint32_t group) { if (ext4_sb_feature_ro_com(s, EXT4_FRO_COM_SPARSE_SUPER) && !ext4_sb_sparse(group)) return false; return true; }
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_feature_ro_com(sb, EXT4_FRO_COM_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)); } }
static bool ext4_sb_verify_csum(struct ext4_sblock *s) { if (!ext4_sb_feature_ro_com(s, EXT4_FRO_COM_METADATA_CSUM)) return true; if (s->checksum_type != to_le32(EXT4_CHECKSUM_CRC32C)) return false; return s->checksum == to_le32(ext4_sb_csum(s)); }
/**@brief Initialize hash info structure necessary for index operations. * @param hinfo Pointer to hinfo to be initialized * @param root_block Root block (number 0) of index * @param sb Pointer to superblock * @param name_len Length of name to be computed hash value from * @param name Name to be computed hash value from * @return Standard error code */ static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo, struct ext4_block *root_block, 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; if ((root->info.hash_version != EXT2_HTREE_LEGACY) && (root->info.hash_version != EXT2_HTREE_HALF_MD4) && (root->info.hash_version != EXT2_HTREE_TEA)) return EXT4_ERR_BAD_DX_DIR; /* Check unused flags */ if (root->info.unused_flags != 0) return EXT4_ERR_BAD_DX_DIR; /* Check indirect levels */ if (root->info.indirect_levels > 1) return EXT4_ERR_BAD_DX_DIR; /* 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); if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) entry_space -= sizeof(struct ext4_directory_dx_tail); entry_space = entry_space / sizeof(struct ext4_directory_dx_entry); uint16_t limit = ext4_dir_dx_countlimit_get_limit( (struct ext4_directory_dx_countlimit *)&root->entries); if (limit != entry_space) return EXT4_ERR_BAD_DX_DIR; /* Check hash version and modify if necessary */ hinfo->hash_version = ext4_dir_dx_root_info_get_hash_version(&root->info); if ((hinfo->hash_version <= EXT2_HTREE_TEA) && (ext4_sb_check_flag(sb, EXT4_SUPERBLOCK_FLAGS_UNSIGNED_HASH))) { /* Use unsigned hash */ hinfo->hash_version += 3; } /* Load hash seed from superblock */ hinfo->seed = ext4_get8(sb, hash_seed); /* Compute hash value of name */ if (name) return ext4_dir_dx_hash_string(hinfo, name_len, name); return EOK; }
static uint32_t ext4_balloc_bitmap_csum(struct ext4_sblock *sb, void *bitmap) { uint32_t checksum = 0; if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) { uint32_t blocks_per_group = ext4_get32(sb, blocks_per_group); /* First calculate crc32 checksum against fs uuid */ checksum = ext4_crc32c(EXT4_CRC32_INIT, sb->uuid, sizeof(sb->uuid)); /* Then calculate crc32 checksum against block_group_desc */ checksum = ext4_crc32c(checksum, bitmap, blocks_per_group / 8); } return checksum; }
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_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)); orig_checksum = t->checksum; t->checksum = 0; /* First calculate crc32 checksum against fs uuid */ 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)); t->checksum = orig_checksum; } return checksum; }
/**@brief Walk through index tree and load leaf with corresponding hash value. * @param hinfo Initialized hash info structure * @param inode_ref Current i-node * @param root_block Root block (iblock 0), where is root node located * @param dx_block Pointer to leaf node in dx_blocks array * @param dx_blocks Array with the whole path from root to leaf * @return Standard error code */ 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_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; uint16_t limit = ext4_dir_dx_countlimit_get_limit( (struct ext4_directory_dx_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; /* Walk through the index tree */ while (true) { uint16_t count = ext4_dir_dx_countlimit_get_count( (struct ext4_directory_dx_countlimit *)entries); if ((count == 0) || (count > limit)) return EXT4_ERR_BAD_DX_DIR; /* Do binary search in every node */ p = entries + 1; q = entries + count - 1; while (p <= q) { m = p + (q - p) / 2; if (ext4_dir_dx_entry_get_hash(m) > hinfo->hash) q = m - 1; else p = m + 1; } at = p - 1; /* Write results */ memcpy(&tmp_dx_block->block, tmp_block, sizeof(struct ext4_block)); tmp_dx_block->entries = entries; tmp_dx_block->position = at; /* Is algorithm in the leaf? */ if (indirect_level == 0) { *dx_block = tmp_dx_block; return EOK; } /* Goto child node */ uint32_t next_block = ext4_dir_dx_entry_get_block(at); indirect_level--; ext4_fsblk_t fblock; int rc = ext4_fs_get_inode_data_block_index( inode_ref, next_block, &fblock, false); if (rc != EOK) return rc; rc = ext4_block_get(inode_ref->fs->bdev, tmp_block, fblock); if (rc != EOK) return rc; entries = ((struct ext4_directory_dx_node *)tmp_block->data)->entries; limit = ext4_dir_dx_countlimit_get_limit( (struct ext4_directory_dx_countlimit *)entries); uint16_t entry_space = ext4_sb_get_block_size(&inode_ref->fs->sb) - sizeof(struct ext4_fake_directory_entry); if (ext4_sb_feature_ro_com(&inode_ref->fs->sb, EXT4_FRO_COM_METADATA_CSUM)) entry_space -= sizeof(struct ext4_directory_dx_tail); entry_space = entry_space / sizeof(struct ext4_directory_dx_entry); if (limit != entry_space) { ext4_block_set(inode_ref->fs->bdev, tmp_block); return EXT4_ERR_BAD_DX_DIR; } ++tmp_dx_block; } /* Unreachable */ return EOK; }
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_append_inode_block(dir, &fblock, &iblock); if (rc != EOK) return rc; struct ext4_block block; rc = ext4_block_get(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); /* Initialize dot entries */ ext4_dir_write_entry(&dir->fs->sb, (struct ext4_directory_entry_ll *)root->dots, 12, dir, ".", strlen(".")); ext4_dir_write_entry(&dir->fs->sb, (struct ext4_directory_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); ext4_dir_dx_root_info_set_hash_version(info, hash_version); ext4_dir_dx_root_info_set_indirect_levels(info, 0); 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; 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); if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_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 */ rc = ext4_fs_append_inode_block(dir, &fblock, &iblock); if (rc != EOK) { ext4_block_set(dir->fs->bdev, &block); return rc; } struct ext4_block new_block; rc = ext4_block_get(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; if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_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; rc = ext4_block_set(dir->fs->bdev, &new_block); if (rc != EOK) { ext4_block_set(dir->fs->bdev, &block); return rc; } /* Connect new block to the only entry in index */ 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); }