static int ext4_finish_convert_inline_dir(handle_t *handle, struct inode *inode, struct buffer_head *dir_block, void *buf, int inline_size) { int err, csum_size = 0, header_size = 0; struct ext4_dir_entry_2 *de; struct ext4_dir_entry_tail *t; void *target = dir_block->b_data; /* * First create "." and ".." and then copy the dir information * back to the block. */ de = (struct ext4_dir_entry_2 *)target; de = ext4_init_dot_dotdot(inode, de, inode->i_sb->s_blocksize, csum_size, le32_to_cpu(((struct ext4_dir_entry_2 *)buf)->inode), 1); header_size = (void *)de - target; memcpy((void *)de, buf + EXT4_INLINE_DOTDOT_SIZE, inline_size - EXT4_INLINE_DOTDOT_SIZE); if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext4_dir_entry_tail); inode->i_size = inode->i_sb->s_blocksize; i_size_write(inode, inode->i_sb->s_blocksize); EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize; ext4_update_final_de(dir_block->b_data, inline_size - EXT4_INLINE_DOTDOT_SIZE + header_size, inode->i_sb->s_blocksize - csum_size); if (csum_size) { t = EXT4_DIRENT_TAIL(dir_block->b_data, inode->i_sb->s_blocksize); initialize_dirent_tail(t, inode->i_sb->s_blocksize); } set_buffer_uptodate(dir_block); err = ext4_handle_dirty_dirent_node(handle, inode, dir_block); if (err) goto out; set_buffer_verified(dir_block); out: return err; }
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); }