ext4 crypto: optimize filename encryption

Encrypt the filename as soon it is passed in by the user.  This avoids
our needing to encrypt the filename 2 or 3 times while in the process
of creating a filename.

Similarly, when looking up a directory entry, encrypt the filename
early, or if the encryption key is not available, base-64 decode the
file syystem so that the hash value and the last 16 bytes of the
encrypted filename is available in the new struct ext4_filename data
structure.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
Theodore Ts'o 2015-05-18 13:14:47 -04:00
parent e26081808e
commit 5b643f9ce3
4 changed files with 235 additions and 318 deletions

View file

@ -611,109 +611,82 @@ int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
return -EACCES; return -EACCES;
} }
/* int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname,
* Calculate the htree hash from a filename from user space int lookup, struct ext4_filename *fname)
*/
int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
const struct qstr *iname,
struct dx_hash_info *hinfo)
{ {
struct ext4_str tmp; struct ext4_fname_crypto_ctx *ctx;
int ret = 0; int ret = 0, bigname = 0;
char buf[EXT4_FNAME_CRYPTO_DIGEST_SIZE+1];
if (!ctx || memset(fname, 0, sizeof(struct ext4_filename));
fname->usr_fname = iname;
ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
if ((ctx == NULL) ||
((iname->name[0] == '.') && ((iname->name[0] == '.') &&
((iname->len == 1) || ((iname->len == 1) ||
((iname->name[1] == '.') && (iname->len == 2))))) { ((iname->name[1] == '.') && (iname->len == 2))))) {
ext4fs_dirhash(iname->name, iname->len, hinfo); fname->disk_name.name = (unsigned char *) iname->name;
return 0; fname->disk_name.len = iname->len;
goto out;
} }
if (!ctx->has_valid_key && iname->name[0] == '_') {
if (iname->len != 33)
return -ENOENT;
ret = digest_decode(iname->name+1, iname->len, buf);
if (ret != 24)
return -ENOENT;
memcpy(&hinfo->hash, buf, 4);
memcpy(&hinfo->minor_hash, buf + 4, 4);
return 0;
}
if (!ctx->has_valid_key && iname->name[0] != '_') {
if (iname->len > 43)
return -ENOENT;
ret = digest_decode(iname->name, iname->len, buf);
ext4fs_dirhash(buf, ret, hinfo);
return 0;
}
/* First encrypt the plaintext name */
ret = ext4_fname_crypto_alloc_buffer(ctx, iname->len, &tmp);
if (ret < 0)
return ret;
ret = ext4_fname_encrypt(ctx, iname, &tmp);
if (ret >= 0) {
ext4fs_dirhash(tmp.name, tmp.len, hinfo);
ret = 0;
}
ext4_fname_crypto_free_buffer(&tmp);
return ret;
}
int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr,
int len, const char * const name,
struct ext4_dir_entry_2 *de)
{
int ret = -ENOENT;
int bigname = (*name == '_');
if (ctx->has_valid_key) { if (ctx->has_valid_key) {
if (cstr->name == NULL) { ret = ext4_fname_crypto_alloc_buffer(ctx, iname->len,
struct qstr istr; &fname->crypto_buf);
if (ret < 0)
ret = ext4_fname_crypto_alloc_buffer(ctx, len, cstr); goto out;
if (ret < 0) ret = ext4_fname_encrypt(ctx, iname, &fname->crypto_buf);
goto errout; if (ret < 0)
istr.name = name; goto out;
istr.len = len; fname->disk_name.name = fname->crypto_buf.name;
ret = ext4_fname_encrypt(ctx, &istr, cstr); fname->disk_name.len = fname->crypto_buf.len;
if (ret < 0) ret = 0;
goto errout; goto out;
}
} else {
if (cstr->name == NULL) {
cstr->name = kmalloc(32, GFP_KERNEL);
if (cstr->name == NULL)
return -ENOMEM;
if ((bigname && (len != 33)) ||
(!bigname && (len > 43)))
goto errout;
ret = digest_decode(name+bigname, len-bigname,
cstr->name);
if (ret < 0) {
ret = -ENOENT;
goto errout;
}
cstr->len = ret;
}
if (bigname) {
if (de->name_len < 16)
return 0;
ret = memcmp(de->name + de->name_len - 16,
cstr->name + 8, 16);
return (ret == 0) ? 1 : 0;
}
} }
if (de->name_len != cstr->len) if (!lookup) {
return 0; ret = -EACCES;
ret = memcmp(de->name, cstr->name, cstr->len); goto out;
return (ret == 0) ? 1 : 0; }
errout:
kfree(cstr->name); /* We don't have the key and we are doing a lookup; decode the
cstr->name = NULL; * user-supplied name
*/
if (iname->name[0] == '_')
bigname = 1;
if ((bigname && (iname->len != 33)) ||
(!bigname && (iname->len > 43))) {
ret = -ENOENT;
}
fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
if (fname->crypto_buf.name == NULL) {
ret = -ENOMEM;
goto out;
}
ret = digest_decode(iname->name + bigname, iname->len - bigname,
fname->crypto_buf.name);
if (ret < 0) {
ret = -ENOENT;
goto out;
}
fname->crypto_buf.len = ret;
if (bigname) {
memcpy(&fname->hinfo.hash, fname->crypto_buf.name, 4);
memcpy(&fname->hinfo.minor_hash, fname->crypto_buf.name + 4, 4);
} else {
fname->disk_name.name = fname->crypto_buf.name;
fname->disk_name.len = fname->crypto_buf.len;
}
ret = 0;
out:
ext4_put_fname_crypto_ctx(&ctx);
return ret; return ret;
} }
void ext4_fname_free_filename(struct ext4_filename *fname)
{
kfree(fname->crypto_buf.name);
fname->crypto_buf.name = NULL;
fname->usr_fname = NULL;
fname->disk_name.name = NULL;
}

View file

@ -1838,6 +1838,17 @@ struct dx_hash_info
*/ */
#define HASH_NB_ALWAYS 1 #define HASH_NB_ALWAYS 1
struct ext4_filename {
const struct qstr *usr_fname;
struct ext4_str disk_name;
struct dx_hash_info hinfo;
#ifdef CONFIG_EXT4_FS_ENCRYPTION
struct ext4_str crypto_buf;
#endif
};
#define fname_name(p) ((p)->disk_name.name)
#define fname_len(p) ((p)->disk_name.len)
/* /*
* Describe an inode's exact location on disk and in memory * Describe an inode's exact location on disk and in memory
@ -2098,21 +2109,16 @@ int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx, int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
const struct qstr *iname, const struct qstr *iname,
struct ext4_str *oname); struct ext4_str *oname);
int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
const struct qstr *iname,
struct dx_hash_info *hinfo);
int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx, int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
u32 namelen); u32 namelen);
int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr,
int len, const char * const name,
struct ext4_dir_entry_2 *de);
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx); void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx);
struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode, struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode,
u32 max_len); u32 max_len);
void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str); void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str);
int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname,
int lookup, struct ext4_filename *fname);
void ext4_fname_free_filename(struct ext4_filename *fname);
#else #else
static inline static inline
void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx) { } void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx) { }
@ -2123,6 +2129,16 @@ struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode,
return NULL; return NULL;
} }
static inline void ext4_fname_crypto_free_buffer(struct ext4_str *p) { } static inline void ext4_fname_crypto_free_buffer(struct ext4_str *p) { }
static inline int ext4_fname_setup_filename(struct inode *dir,
const struct qstr *iname,
int lookup, struct ext4_filename *fname)
{
fname->usr_fname = iname;
fname->disk_name.name = (unsigned char *) iname->name;
fname->disk_name.len = iname->len;
return 0;
}
static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
#endif #endif
@ -2156,14 +2172,13 @@ extern void ext4_htree_free_dir_info(struct dir_private_info *p);
extern int ext4_find_dest_de(struct inode *dir, struct inode *inode, extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
struct buffer_head *bh, struct buffer_head *bh,
void *buf, int buf_size, void *buf, int buf_size,
const char *name, int namelen, struct ext4_filename *fname,
struct ext4_dir_entry_2 **dest_de); struct ext4_dir_entry_2 **dest_de);
int ext4_insert_dentry(struct inode *dir, int ext4_insert_dentry(struct inode *dir,
struct inode *inode, struct inode *inode,
struct ext4_dir_entry_2 *de, struct ext4_dir_entry_2 *de,
int buf_size, int buf_size,
const struct qstr *iname, struct ext4_filename *fname);
const char *name, int namelen);
static inline void ext4_update_dx_flag(struct inode *inode) static inline void ext4_update_dx_flag(struct inode *inode)
{ {
if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb, if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb,
@ -2317,13 +2332,14 @@ extern int ext4_orphan_add(handle_t *, struct inode *);
extern int ext4_orphan_del(handle_t *, struct inode *); extern int ext4_orphan_del(handle_t *, struct inode *);
extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
__u32 start_minor_hash, __u32 *next_hash); __u32 start_minor_hash, __u32 *next_hash);
extern int search_dir(struct buffer_head *bh, extern int ext4_search_dir(struct buffer_head *bh,
char *search_buf, char *search_buf,
int buf_size, int buf_size,
struct inode *dir, struct inode *dir,
const struct qstr *d_name, struct ext4_filename *fname,
unsigned int offset, const struct qstr *d_name,
struct ext4_dir_entry_2 **res_dir); unsigned int offset,
struct ext4_dir_entry_2 **res_dir);
extern int ext4_generic_delete_entry(handle_t *handle, extern int ext4_generic_delete_entry(handle_t *handle,
struct inode *dir, struct inode *dir,
struct ext4_dir_entry_2 *de_del, struct ext4_dir_entry_2 *de_del,
@ -2768,7 +2784,9 @@ extern int ext4_da_write_inline_data_begin(struct address_space *mapping,
extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos, extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
unsigned len, unsigned copied, unsigned len, unsigned copied,
struct page *page); struct page *page);
extern int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, extern int ext4_try_add_inline_entry(handle_t *handle,
struct ext4_filename *fname,
struct dentry *dentry,
struct inode *inode); struct inode *inode);
extern int ext4_try_create_inline_dir(handle_t *handle, extern int ext4_try_create_inline_dir(handle_t *handle,
struct inode *parent, struct inode *parent,
@ -2782,6 +2800,7 @@ extern int htree_inlinedir_to_tree(struct file *dir_file,
__u32 start_hash, __u32 start_minor_hash, __u32 start_hash, __u32 start_minor_hash,
int *has_inline_data); int *has_inline_data);
extern struct buffer_head *ext4_find_inline_entry(struct inode *dir, extern struct buffer_head *ext4_find_inline_entry(struct inode *dir,
struct ext4_filename *fname,
const struct qstr *d_name, const struct qstr *d_name,
struct ext4_dir_entry_2 **res_dir, struct ext4_dir_entry_2 **res_dir,
int *has_inline_data); int *has_inline_data);

View file

@ -995,20 +995,18 @@ void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
* and -EEXIST if directory entry already exists. * and -EEXIST if directory entry already exists.
*/ */
static int ext4_add_dirent_to_inline(handle_t *handle, static int ext4_add_dirent_to_inline(handle_t *handle,
struct ext4_filename *fname,
struct dentry *dentry, struct dentry *dentry,
struct inode *inode, struct inode *inode,
struct ext4_iloc *iloc, struct ext4_iloc *iloc,
void *inline_start, int inline_size) void *inline_start, int inline_size)
{ {
struct inode *dir = d_inode(dentry->d_parent); struct inode *dir = d_inode(dentry->d_parent);
const char *name = dentry->d_name.name;
int namelen = dentry->d_name.len;
int err; int err;
struct ext4_dir_entry_2 *de; struct ext4_dir_entry_2 *de;
err = ext4_find_dest_de(dir, inode, iloc->bh, err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start,
inline_start, inline_size, inline_size, fname, &de);
name, namelen, &de);
if (err) if (err)
return err; return err;
@ -1016,8 +1014,7 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
err = ext4_journal_get_write_access(handle, iloc->bh); err = ext4_journal_get_write_access(handle, iloc->bh);
if (err) if (err)
return err; return err;
ext4_insert_dentry(dir, inode, de, inline_size, &dentry->d_name, ext4_insert_dentry(dir, inode, de, inline_size, fname);
name, namelen);
ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size); ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size);
@ -1248,8 +1245,8 @@ static int ext4_convert_inline_data_nolock(handle_t *handle,
* If succeeds, return 0. If not, extended the inline dir and copied data to * If succeeds, return 0. If not, extended the inline dir and copied data to
* the new created block. * the new created block.
*/ */
int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
struct inode *inode) struct dentry *dentry, struct inode *inode)
{ {
int ret, inline_size; int ret, inline_size;
void *inline_start; void *inline_start;
@ -1268,7 +1265,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry,
EXT4_INLINE_DOTDOT_SIZE; EXT4_INLINE_DOTDOT_SIZE;
inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE; inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE;
ret = ext4_add_dirent_to_inline(handle, dentry, inode, &iloc, ret = ext4_add_dirent_to_inline(handle, fname, dentry, inode, &iloc,
inline_start, inline_size); inline_start, inline_size);
if (ret != -ENOSPC) if (ret != -ENOSPC)
goto out; goto out;
@ -1289,8 +1286,9 @@ int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry,
if (inline_size) { if (inline_size) {
inline_start = ext4_get_inline_xattr_pos(dir, &iloc); inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
ret = ext4_add_dirent_to_inline(handle, dentry, inode, &iloc, ret = ext4_add_dirent_to_inline(handle, fname, dentry,
inline_start, inline_size); inode, &iloc, inline_start,
inline_size);
if (ret != -ENOSPC) if (ret != -ENOSPC)
goto out; goto out;
@ -1611,6 +1609,7 @@ int ext4_try_create_inline_dir(handle_t *handle, struct inode *parent,
} }
struct buffer_head *ext4_find_inline_entry(struct inode *dir, struct buffer_head *ext4_find_inline_entry(struct inode *dir,
struct ext4_filename *fname,
const struct qstr *d_name, const struct qstr *d_name,
struct ext4_dir_entry_2 **res_dir, struct ext4_dir_entry_2 **res_dir,
int *has_inline_data) int *has_inline_data)
@ -1632,8 +1631,8 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
inline_start = (void *)ext4_raw_inode(&iloc)->i_block + inline_start = (void *)ext4_raw_inode(&iloc)->i_block +
EXT4_INLINE_DOTDOT_SIZE; EXT4_INLINE_DOTDOT_SIZE;
inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE; inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE;
ret = search_dir(iloc.bh, inline_start, inline_size, ret = ext4_search_dir(iloc.bh, inline_start, inline_size,
dir, d_name, 0, res_dir); dir, fname, d_name, 0, res_dir);
if (ret == 1) if (ret == 1)
goto out_find; goto out_find;
if (ret < 0) if (ret < 0)
@ -1645,8 +1644,8 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
inline_start = ext4_get_inline_xattr_pos(dir, &iloc); inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
inline_size = ext4_get_inline_size(dir) - EXT4_MIN_INLINE_DATA_SIZE; inline_size = ext4_get_inline_size(dir) - EXT4_MIN_INLINE_DATA_SIZE;
ret = search_dir(iloc.bh, inline_start, inline_size, ret = ext4_search_dir(iloc.bh, inline_start, inline_size,
dir, d_name, 0, res_dir); dir, fname, d_name, 0, res_dir);
if (ret == 1) if (ret == 1)
goto out_find; goto out_find;

View file

@ -248,7 +248,7 @@ static void dx_set_count(struct dx_entry *entries, unsigned value);
static void dx_set_limit(struct dx_entry *entries, unsigned value); static void dx_set_limit(struct dx_entry *entries, unsigned value);
static unsigned dx_root_limit(struct inode *dir, unsigned infosize); static unsigned dx_root_limit(struct inode *dir, unsigned infosize);
static unsigned dx_node_limit(struct inode *dir); static unsigned dx_node_limit(struct inode *dir);
static struct dx_frame *dx_probe(const struct qstr *d_name, static struct dx_frame *dx_probe(struct ext4_filename *fname,
struct inode *dir, struct inode *dir,
struct dx_hash_info *hinfo, struct dx_hash_info *hinfo,
struct dx_frame *frame); struct dx_frame *frame);
@ -267,10 +267,10 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
struct dx_frame *frames, struct dx_frame *frames,
__u32 *start_hash); __u32 *start_hash);
static struct buffer_head * ext4_dx_find_entry(struct inode *dir, static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
const struct qstr *d_name, struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir); struct ext4_dir_entry_2 **res_dir);
static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
struct inode *inode); struct dentry *dentry, struct inode *inode);
/* checksumming functions */ /* checksumming functions */
void initialize_dirent_tail(struct ext4_dir_entry_tail *t, void initialize_dirent_tail(struct ext4_dir_entry_tail *t,
@ -724,7 +724,7 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
* back to userspace. * back to userspace.
*/ */
static struct dx_frame * static struct dx_frame *
dx_probe(const struct qstr *d_name, struct inode *dir, dx_probe(struct ext4_filename *fname, struct inode *dir,
struct dx_hash_info *hinfo, struct dx_frame *frame_in) struct dx_hash_info *hinfo, struct dx_frame *frame_in)
{ {
unsigned count, indirect; unsigned count, indirect;
@ -746,32 +746,14 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
root->info.hash_version); root->info.hash_version);
goto fail; goto fail;
} }
if (fname)
hinfo = &fname->hinfo;
hinfo->hash_version = root->info.hash_version; hinfo->hash_version = root->info.hash_version;
if (hinfo->hash_version <= DX_HASH_TEA) if (hinfo->hash_version <= DX_HASH_TEA)
hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed; hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
#ifdef CONFIG_EXT4_FS_ENCRYPTION if (fname && fname_name(fname))
if (d_name) { ext4fs_dirhash(fname_name(fname), fname_len(fname), hinfo);
struct ext4_fname_crypto_ctx *ctx = NULL;
int res;
/* Check if the directory is encrypted */
ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
if (IS_ERR(ctx)) {
ret_err = ERR_PTR(PTR_ERR(ctx));
goto fail;
}
res = ext4_fname_usr_to_hash(ctx, d_name, hinfo);
if (res < 0) {
ret_err = ERR_PTR(res);
goto fail;
}
ext4_put_fname_crypto_ctx(&ctx);
}
#else
if (d_name)
ext4fs_dirhash(d_name->name, d_name->len, hinfo);
#endif
hash = hinfo->hash; hash = hinfo->hash;
if (root->info.unused_flags & 1) { if (root->info.unused_flags & 1) {
@ -1155,12 +1137,13 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
static inline int search_dirblock(struct buffer_head *bh, static inline int search_dirblock(struct buffer_head *bh,
struct inode *dir, struct inode *dir,
struct ext4_filename *fname,
const struct qstr *d_name, const struct qstr *d_name,
unsigned int offset, unsigned int offset,
struct ext4_dir_entry_2 **res_dir) struct ext4_dir_entry_2 **res_dir)
{ {
return search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir, return ext4_search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir,
d_name, offset, res_dir); fname, d_name, offset, res_dir);
} }
/* /*
@ -1242,54 +1225,54 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
* `len <= EXT4_NAME_LEN' is guaranteed by caller. * `len <= EXT4_NAME_LEN' is guaranteed by caller.
* `de != NULL' is guaranteed by caller. * `de != NULL' is guaranteed by caller.
*/ */
static inline int ext4_match(struct ext4_fname_crypto_ctx *ctx, static inline int ext4_match(struct ext4_filename *fname,
struct ext4_str *fname_crypto_str,
int len, const char * const name,
struct ext4_dir_entry_2 *de) struct ext4_dir_entry_2 *de)
{ {
int res; const void *name = fname_name(fname);
u32 len = fname_len(fname);
if (!de->inode) if (!de->inode)
return 0; return 0;
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
if (ctx) if (unlikely(!name)) {
return ext4_fname_match(ctx, fname_crypto_str, len, name, de); if (fname->usr_fname->name[0] == '_') {
int ret;
if (de->name_len < 16)
return 0;
ret = memcmp(de->name + de->name_len - 16,
fname->crypto_buf.name + 8, 16);
return (ret == 0) ? 1 : 0;
}
name = fname->crypto_buf.name;
len = fname->crypto_buf.len;
}
#endif #endif
if (len != de->name_len) if (de->name_len != len)
return 0; return 0;
res = memcmp(name, de->name, len); return (memcmp(de->name, name, len) == 0) ? 1 : 0;
return (res == 0) ? 1 : 0;
} }
/* /*
* Returns 0 if not found, -1 on failure, and 1 on success * Returns 0 if not found, -1 on failure, and 1 on success
*/ */
int search_dir(struct buffer_head *bh, char *search_buf, int buf_size, int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
struct inode *dir, const struct qstr *d_name, struct inode *dir, struct ext4_filename *fname,
unsigned int offset, struct ext4_dir_entry_2 **res_dir) const struct qstr *d_name,
unsigned int offset, struct ext4_dir_entry_2 **res_dir)
{ {
struct ext4_dir_entry_2 * de; struct ext4_dir_entry_2 * de;
char * dlimit; char * dlimit;
int de_len; int de_len;
const char *name = d_name->name;
int namelen = d_name->len;
struct ext4_fname_crypto_ctx *ctx = NULL;
struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
int res; int res;
ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
if (IS_ERR(ctx))
return -1;
de = (struct ext4_dir_entry_2 *)search_buf; de = (struct ext4_dir_entry_2 *)search_buf;
dlimit = search_buf + buf_size; dlimit = search_buf + buf_size;
while ((char *) de < dlimit) { while ((char *) de < dlimit) {
/* this code is executed quadratically often */ /* this code is executed quadratically often */
/* do minimal checking `by hand' */ /* do minimal checking `by hand' */
if ((char *) de + de->name_len <= dlimit) { if ((char *) de + de->name_len <= dlimit) {
res = ext4_match(ctx, &fname_crypto_str, namelen, res = ext4_match(fname, de);
name, de);
if (res < 0) { if (res < 0) {
res = -1; res = -1;
goto return_result; goto return_result;
@ -1322,8 +1305,6 @@ int search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
res = 0; res = 0;
return_result: return_result:
ext4_put_fname_crypto_ctx(&ctx);
ext4_fname_crypto_free_buffer(&fname_crypto_str);
return res; return res;
} }
@ -1370,7 +1351,8 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
buffer */ buffer */
int num = 0; int num = 0;
ext4_lblk_t nblocks; ext4_lblk_t nblocks;
int i, namelen; int i, namelen, retval;
struct ext4_filename fname;
*res_dir = NULL; *res_dir = NULL;
sb = dir->i_sb; sb = dir->i_sb;
@ -1378,14 +1360,18 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
if (namelen > EXT4_NAME_LEN) if (namelen > EXT4_NAME_LEN)
return NULL; return NULL;
retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
if (retval)
return ERR_PTR(retval);
if (ext4_has_inline_data(dir)) { if (ext4_has_inline_data(dir)) {
int has_inline_data = 1; int has_inline_data = 1;
ret = ext4_find_inline_entry(dir, d_name, res_dir, ret = ext4_find_inline_entry(dir, &fname, d_name, res_dir,
&has_inline_data); &has_inline_data);
if (has_inline_data) { if (has_inline_data) {
if (inlined) if (inlined)
*inlined = 1; *inlined = 1;
return ret; goto cleanup_and_exit;
} }
} }
@ -1400,14 +1386,14 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
goto restart; goto restart;
} }
if (is_dx(dir)) { if (is_dx(dir)) {
bh = ext4_dx_find_entry(dir, d_name, res_dir); ret = ext4_dx_find_entry(dir, &fname, res_dir);
/* /*
* On success, or if the error was file not found, * On success, or if the error was file not found,
* return. Otherwise, fall back to doing a search the * return. Otherwise, fall back to doing a search the
* old fashioned way. * old fashioned way.
*/ */
if (!IS_ERR(bh) || PTR_ERR(bh) != ERR_BAD_DX_DIR) if (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR)
return bh; goto cleanup_and_exit;
dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, " dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, "
"falling back\n")); "falling back\n"));
} }
@ -1438,8 +1424,10 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
num++; num++;
bh = ext4_getblk(NULL, dir, b++, 0); bh = ext4_getblk(NULL, dir, b++, 0);
if (unlikely(IS_ERR(bh))) { if (unlikely(IS_ERR(bh))) {
if (ra_max == 0) if (ra_max == 0) {
return bh; ret = bh;
goto cleanup_and_exit;
}
break; break;
} }
bh_use[ra_max] = bh; bh_use[ra_max] = bh;
@ -1469,7 +1457,7 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
goto next; goto next;
} }
set_buffer_verified(bh); set_buffer_verified(bh);
i = search_dirblock(bh, dir, d_name, i = search_dirblock(bh, dir, &fname, d_name,
block << EXT4_BLOCK_SIZE_BITS(sb), res_dir); block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
if (i == 1) { if (i == 1) {
EXT4_I(dir)->i_dir_start_lookup = block; EXT4_I(dir)->i_dir_start_lookup = block;
@ -1500,15 +1488,17 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
/* Clean up the read-ahead blocks */ /* Clean up the read-ahead blocks */
for (; ra_ptr < ra_max; ra_ptr++) for (; ra_ptr < ra_max; ra_ptr++)
brelse(bh_use[ra_ptr]); brelse(bh_use[ra_ptr]);
ext4_fname_free_filename(&fname);
return ret; return ret;
} }
static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct qstr *d_name, static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
struct ext4_dir_entry_2 **res_dir) struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir)
{ {
struct super_block * sb = dir->i_sb; struct super_block * sb = dir->i_sb;
struct dx_hash_info hinfo;
struct dx_frame frames[2], *frame; struct dx_frame frames[2], *frame;
const struct qstr *d_name = fname->usr_fname;
struct buffer_head *bh; struct buffer_head *bh;
ext4_lblk_t block; ext4_lblk_t block;
int retval; int retval;
@ -1516,7 +1506,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
#ifdef CONFIG_EXT4_FS_ENCRYPTION #ifdef CONFIG_EXT4_FS_ENCRYPTION
*res_dir = NULL; *res_dir = NULL;
#endif #endif
frame = dx_probe(d_name, dir, &hinfo, frames); frame = dx_probe(fname, dir, NULL, frames);
if (IS_ERR(frame)) if (IS_ERR(frame))
return (struct buffer_head *) frame; return (struct buffer_head *) frame;
do { do {
@ -1525,7 +1515,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
if (IS_ERR(bh)) if (IS_ERR(bh))
goto errout; goto errout;
retval = search_dirblock(bh, dir, d_name, retval = search_dirblock(bh, dir, fname, d_name,
block << EXT4_BLOCK_SIZE_BITS(sb), block << EXT4_BLOCK_SIZE_BITS(sb),
res_dir); res_dir);
if (retval == 1) if (retval == 1)
@ -1537,7 +1527,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
} }
/* Check to see if we should continue to search */ /* Check to see if we should continue to search */
retval = ext4_htree_next_block(dir, hinfo.hash, frame, retval = ext4_htree_next_block(dir, fname->hinfo.hash, frame,
frames, NULL); frames, NULL);
if (retval < 0) { if (retval < 0) {
ext4_warning(sb, ext4_warning(sb,
@ -1796,32 +1786,16 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
int ext4_find_dest_de(struct inode *dir, struct inode *inode, int ext4_find_dest_de(struct inode *dir, struct inode *inode,
struct buffer_head *bh, struct buffer_head *bh,
void *buf, int buf_size, void *buf, int buf_size,
const char *name, int namelen, struct ext4_filename *fname,
struct ext4_dir_entry_2 **dest_de) struct ext4_dir_entry_2 **dest_de)
{ {
struct ext4_dir_entry_2 *de; struct ext4_dir_entry_2 *de;
unsigned short reclen = EXT4_DIR_REC_LEN(namelen); unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname));
int nlen, rlen; int nlen, rlen;
unsigned int offset = 0; unsigned int offset = 0;
char *top; char *top;
struct ext4_fname_crypto_ctx *ctx = NULL;
struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
int res; int res;
ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
if (IS_ERR(ctx))
return -1;
if (ctx != NULL) {
/* Calculate record length needed to store the entry */
res = ext4_fname_crypto_namelen_on_disk(ctx, namelen);
if (res < 0) {
ext4_put_fname_crypto_ctx(&ctx);
return res;
}
reclen = EXT4_DIR_REC_LEN(res);
}
de = (struct ext4_dir_entry_2 *)buf; de = (struct ext4_dir_entry_2 *)buf;
top = buf + buf_size - reclen; top = buf + buf_size - reclen;
while ((char *) de <= top) { while ((char *) de <= top) {
@ -1831,7 +1805,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
goto return_result; goto return_result;
} }
/* Provide crypto context and crypto buffer to ext4 match */ /* Provide crypto context and crypto buffer to ext4 match */
res = ext4_match(ctx, &fname_crypto_str, namelen, name, de); res = ext4_match(fname, de);
if (res < 0) if (res < 0)
goto return_result; goto return_result;
if (res > 0) { if (res > 0) {
@ -1853,8 +1827,6 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
res = 0; res = 0;
} }
return_result: return_result:
ext4_put_fname_crypto_ctx(&ctx);
ext4_fname_crypto_free_buffer(&fname_crypto_str);
return res; return res;
} }
@ -1862,39 +1834,10 @@ int ext4_insert_dentry(struct inode *dir,
struct inode *inode, struct inode *inode,
struct ext4_dir_entry_2 *de, struct ext4_dir_entry_2 *de,
int buf_size, int buf_size,
const struct qstr *iname, struct ext4_filename *fname)
const char *name, int namelen)
{ {
int nlen, rlen; int nlen, rlen;
struct ext4_fname_crypto_ctx *ctx = NULL;
struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
struct ext4_str tmp_str;
int res;
ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
if (IS_ERR(ctx))
return -EIO;
/* By default, the input name would be written to the disk */
tmp_str.name = (unsigned char *)name;
tmp_str.len = namelen;
if (ctx != NULL) {
/* Directory is encrypted */
res = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN,
&fname_crypto_str);
if (res < 0) {
ext4_put_fname_crypto_ctx(&ctx);
return -ENOMEM;
}
res = ext4_fname_usr_to_disk(ctx, iname, &fname_crypto_str);
if (res < 0) {
ext4_put_fname_crypto_ctx(&ctx);
ext4_fname_crypto_free_buffer(&fname_crypto_str);
return res;
}
tmp_str.name = fname_crypto_str.name;
tmp_str.len = fname_crypto_str.len;
}
nlen = EXT4_DIR_REC_LEN(de->name_len); nlen = EXT4_DIR_REC_LEN(de->name_len);
rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
@ -1908,11 +1851,8 @@ int ext4_insert_dentry(struct inode *dir,
de->file_type = EXT4_FT_UNKNOWN; de->file_type = EXT4_FT_UNKNOWN;
de->inode = cpu_to_le32(inode->i_ino); de->inode = cpu_to_le32(inode->i_ino);
ext4_set_de_type(inode->i_sb, de, inode->i_mode); ext4_set_de_type(inode->i_sb, de, inode->i_mode);
de->name_len = tmp_str.len; de->name_len = fname_len(fname);
memcpy(de->name, fname_name(fname), fname_len(fname));
memcpy(de->name, tmp_str.name, tmp_str.len);
ext4_put_fname_crypto_ctx(&ctx);
ext4_fname_crypto_free_buffer(&fname_crypto_str);
return 0; return 0;
} }
@ -1924,13 +1864,11 @@ int ext4_insert_dentry(struct inode *dir,
* space. It will return -ENOSPC if no space is available, and -EIO * space. It will return -ENOSPC if no space is available, and -EIO
* and -EEXIST if directory entry already exists. * and -EEXIST if directory entry already exists.
*/ */
static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
struct inode *dir,
struct inode *inode, struct ext4_dir_entry_2 *de, struct inode *inode, struct ext4_dir_entry_2 *de,
struct buffer_head *bh) struct buffer_head *bh)
{ {
struct inode *dir = d_inode(dentry->d_parent);
const char *name = dentry->d_name.name;
int namelen = dentry->d_name.len;
unsigned int blocksize = dir->i_sb->s_blocksize; unsigned int blocksize = dir->i_sb->s_blocksize;
int csum_size = 0; int csum_size = 0;
int err; int err;
@ -1939,9 +1877,8 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
csum_size = sizeof(struct ext4_dir_entry_tail); csum_size = sizeof(struct ext4_dir_entry_tail);
if (!de) { if (!de) {
err = ext4_find_dest_de(dir, inode, err = ext4_find_dest_de(dir, inode, bh, bh->b_data,
bh, bh->b_data, blocksize - csum_size, blocksize - csum_size, fname, &de);
name, namelen, &de);
if (err) if (err)
return err; return err;
} }
@ -1954,8 +1891,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
/* By now the buffer is marked for journaling. Due to crypto operations, /* By now the buffer is marked for journaling. Due to crypto operations,
* the following function call may fail */ * the following function call may fail */
err = ext4_insert_dentry(dir, inode, de, blocksize, &dentry->d_name, err = ext4_insert_dentry(dir, inode, de, blocksize, fname);
name, namelen);
if (err < 0) if (err < 0)
return err; return err;
@ -1985,17 +1921,11 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
* This converts a one block unindexed directory to a 3 block indexed * This converts a one block unindexed directory to a 3 block indexed
* directory, and adds the dentry to the indexed directory. * directory, and adds the dentry to the indexed directory.
*/ */
static int make_indexed_dir(handle_t *handle, struct dentry *dentry, static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
struct dentry *dentry,
struct inode *inode, struct buffer_head *bh) struct inode *inode, struct buffer_head *bh)
{ {
struct inode *dir = d_inode(dentry->d_parent); struct inode *dir = d_inode(dentry->d_parent);
#ifdef CONFIG_EXT4_FS_ENCRYPTION
struct ext4_fname_crypto_ctx *ctx = NULL;
int res;
#else
const char *name = dentry->d_name.name;
int namelen = dentry->d_name.len;
#endif
struct buffer_head *bh2; struct buffer_head *bh2;
struct dx_root *root; struct dx_root *root;
struct dx_frame frames[2], *frame; struct dx_frame frames[2], *frame;
@ -2006,17 +1936,10 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
unsigned len; unsigned len;
int retval; int retval;
unsigned blocksize; unsigned blocksize;
struct dx_hash_info hinfo;
ext4_lblk_t block; ext4_lblk_t block;
struct fake_dirent *fde; struct fake_dirent *fde;
int csum_size = 0; int csum_size = 0;
#ifdef CONFIG_EXT4_FS_ENCRYPTION
ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
#endif
if (ext4_has_metadata_csum(inode->i_sb)) if (ext4_has_metadata_csum(inode->i_sb))
csum_size = sizeof(struct ext4_dir_entry_tail); csum_size = sizeof(struct ext4_dir_entry_tail);
@ -2078,22 +2001,12 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
dx_set_limit(entries, dx_root_limit(dir, sizeof(root->info))); dx_set_limit(entries, dx_root_limit(dir, sizeof(root->info)));
/* Initialize as for dx_probe */ /* Initialize as for dx_probe */
hinfo.hash_version = root->info.hash_version; fname->hinfo.hash_version = root->info.hash_version;
if (hinfo.hash_version <= DX_HASH_TEA) if (fname->hinfo.hash_version <= DX_HASH_TEA)
hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; fname->hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
#ifdef CONFIG_EXT4_FS_ENCRYPTION ext4fs_dirhash(fname_name(fname), fname_len(fname), &fname->hinfo);
res = ext4_fname_usr_to_hash(ctx, &dentry->d_name, &hinfo);
if (res < 0) {
ext4_put_fname_crypto_ctx(&ctx);
ext4_mark_inode_dirty(handle, dir);
brelse(bh);
return res;
}
ext4_put_fname_crypto_ctx(&ctx);
#else
ext4fs_dirhash(name, namelen, &hinfo);
#endif
memset(frames, 0, sizeof(frames)); memset(frames, 0, sizeof(frames));
frame = frames; frame = frames;
frame->entries = entries; frame->entries = entries;
@ -2108,14 +2021,14 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
if (retval) if (retval)
goto out_frames; goto out_frames;
de = do_split(handle,dir, &bh, frame, &hinfo); de = do_split(handle,dir, &bh, frame, &fname->hinfo);
if (IS_ERR(de)) { if (IS_ERR(de)) {
retval = PTR_ERR(de); retval = PTR_ERR(de);
goto out_frames; goto out_frames;
} }
dx_release(frames); dx_release(frames);
retval = add_dirent_to_buf(handle, dentry, inode, de, bh); retval = add_dirent_to_buf(handle, fname, dir, inode, de, bh);
brelse(bh); brelse(bh);
return retval; return retval;
out_frames: out_frames:
@ -2147,6 +2060,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
struct ext4_dir_entry_2 *de; struct ext4_dir_entry_2 *de;
struct ext4_dir_entry_tail *t; struct ext4_dir_entry_tail *t;
struct super_block *sb; struct super_block *sb;
struct ext4_filename fname;
int retval; int retval;
int dx_fallback=0; int dx_fallback=0;
unsigned blocksize; unsigned blocksize;
@ -2161,10 +2075,15 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
if (!dentry->d_name.len) if (!dentry->d_name.len)
return -EINVAL; return -EINVAL;
retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname);
if (retval)
return retval;
if (ext4_has_inline_data(dir)) { if (ext4_has_inline_data(dir)) {
retval = ext4_try_add_inline_entry(handle, dentry, inode); retval = ext4_try_add_inline_entry(handle, &fname,
dentry, inode);
if (retval < 0) if (retval < 0)
return retval; goto out;
if (retval == 1) { if (retval == 1) {
retval = 0; retval = 0;
goto out; goto out;
@ -2172,7 +2091,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
} }
if (is_dx(dir)) { if (is_dx(dir)) {
retval = ext4_dx_add_entry(handle, dentry, inode); retval = ext4_dx_add_entry(handle, &fname, dentry, inode);
if (!retval || (retval != ERR_BAD_DX_DIR)) if (!retval || (retval != ERR_BAD_DX_DIR))
goto out; goto out;
ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); ext4_clear_inode_flag(dir, EXT4_INODE_INDEX);
@ -2182,24 +2101,31 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
blocks = dir->i_size >> sb->s_blocksize_bits; blocks = dir->i_size >> sb->s_blocksize_bits;
for (block = 0; block < blocks; block++) { for (block = 0; block < blocks; block++) {
bh = ext4_read_dirblock(dir, block, DIRENT); bh = ext4_read_dirblock(dir, block, DIRENT);
if (IS_ERR(bh)) if (IS_ERR(bh)) {
return PTR_ERR(bh); retval = PTR_ERR(bh);
bh = NULL;
retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); goto out;
}
retval = add_dirent_to_buf(handle, &fname, dir, inode,
NULL, bh);
if (retval != -ENOSPC) if (retval != -ENOSPC)
goto out; goto out;
if (blocks == 1 && !dx_fallback && if (blocks == 1 && !dx_fallback &&
EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) { EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
retval = make_indexed_dir(handle, dentry, inode, bh); retval = make_indexed_dir(handle, &fname, dentry,
inode, bh);
bh = NULL; /* make_indexed_dir releases bh */ bh = NULL; /* make_indexed_dir releases bh */
goto out; goto out;
} }
brelse(bh); brelse(bh);
} }
bh = ext4_append(handle, dir, &block); bh = ext4_append(handle, dir, &block);
if (IS_ERR(bh)) if (IS_ERR(bh)) {
return PTR_ERR(bh); retval = PTR_ERR(bh);
bh = NULL;
goto out;
}
de = (struct ext4_dir_entry_2 *) bh->b_data; de = (struct ext4_dir_entry_2 *) bh->b_data;
de->inode = 0; de->inode = 0;
de->rec_len = ext4_rec_len_to_disk(blocksize - csum_size, blocksize); de->rec_len = ext4_rec_len_to_disk(blocksize - csum_size, blocksize);
@ -2209,8 +2135,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
initialize_dirent_tail(t, blocksize); initialize_dirent_tail(t, blocksize);
} }
retval = add_dirent_to_buf(handle, dentry, inode, de, bh); retval = add_dirent_to_buf(handle, &fname, dir, inode, de, bh);
out: out:
ext4_fname_free_filename(&fname);
brelse(bh); brelse(bh);
if (retval == 0) if (retval == 0)
ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY); ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY);
@ -2220,19 +2147,18 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
/* /*
* Returns 0 for success, or a negative error value * Returns 0 for success, or a negative error value
*/ */
static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
struct inode *inode) struct dentry *dentry, struct inode *inode)
{ {
struct dx_frame frames[2], *frame; struct dx_frame frames[2], *frame;
struct dx_entry *entries, *at; struct dx_entry *entries, *at;
struct dx_hash_info hinfo;
struct buffer_head *bh; struct buffer_head *bh;
struct inode *dir = d_inode(dentry->d_parent); struct inode *dir = d_inode(dentry->d_parent);
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
struct ext4_dir_entry_2 *de; struct ext4_dir_entry_2 *de;
int err; int err;
frame = dx_probe(&dentry->d_name, dir, &hinfo, frames); frame = dx_probe(fname, dir, NULL, frames);
if (IS_ERR(frame)) if (IS_ERR(frame))
return PTR_ERR(frame); return PTR_ERR(frame);
entries = frame->entries; entries = frame->entries;
@ -2249,7 +2175,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
if (err) if (err)
goto journal_error; goto journal_error;
err = add_dirent_to_buf(handle, dentry, inode, NULL, bh); err = add_dirent_to_buf(handle, fname, dir, inode, NULL, bh);
if (err != -ENOSPC) if (err != -ENOSPC)
goto cleanup; goto cleanup;
@ -2345,12 +2271,12 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
goto cleanup; goto cleanup;
} }
} }
de = do_split(handle, dir, &bh, frame, &hinfo); de = do_split(handle, dir, &bh, frame, &fname->hinfo);
if (IS_ERR(de)) { if (IS_ERR(de)) {
err = PTR_ERR(de); err = PTR_ERR(de);
goto cleanup; goto cleanup;
} }
err = add_dirent_to_buf(handle, dentry, inode, de, bh); err = add_dirent_to_buf(handle, fname, dir, inode, de, bh);
goto cleanup; goto cleanup;
journal_error: journal_error: