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:
parent
e26081808e
commit
5b643f9ce3
4 changed files with 235 additions and 318 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
294
fs/ext4/namei.c
294
fs/ext4/namei.c
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue