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;
}
Example #2
0
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);
}