Revert "ANDROID: ext4: Handle casefolding with encryption"

This reverts commit b10c4acd41e8b15cff1119e2100adf36b013b840.

Signed-off-by: Daniel Rosenberg <drosen@google.com>
Change-Id: I6e036e92492d34e2a7bafeab98eae780a7aa2355
This commit is contained in:
Daniel Rosenberg 2022-06-24 18:13:32 -07:00
parent 0bedb20065
commit bfec958d8b
7 changed files with 151 additions and 348 deletions

View File

@ -29,8 +29,6 @@
#include "ext4.h"
#include "xattr.h"
#define DOTDOT_OFFSET 12
static int ext4_dx_readdir(struct file *, struct dir_context *);
/**
@ -53,19 +51,6 @@ static int is_dx_dir(struct inode *inode)
return 0;
}
static bool is_fake_entry(struct inode *dir, ext4_lblk_t lblk,
unsigned int offset, unsigned int blocksize)
{
/* Entries in the first block before this value refer to . or .. */
if (lblk == 0 && offset <= DOTDOT_OFFSET)
return true;
/* Check if this is likely the csum entry */
if (ext4_has_metadata_csum(dir->i_sb) && offset % blocksize ==
blocksize - sizeof(struct ext4_dir_entry_tail))
return true;
return false;
}
/*
* Return 0 if the directory entry is OK, and 1 if there is a problem
*
@ -78,30 +63,25 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
struct inode *dir, struct file *filp,
struct ext4_dir_entry_2 *de,
struct buffer_head *bh, char *buf, int size,
ext4_lblk_t lblk,
unsigned int offset)
{
const char *error_msg = NULL;
const int rlen = ext4_rec_len_from_disk(de->rec_len,
dir->i_sb->s_blocksize);
const int next_offset = ((char *) de - buf) + rlen;
unsigned int blocksize = dir->i_sb->s_blocksize;
bool fake = is_fake_entry(dir, lblk, offset, blocksize);
bool next_fake = is_fake_entry(dir, lblk, next_offset, blocksize);
if (unlikely(rlen < ext4_dir_rec_len(1, fake ? NULL : dir)))
if (unlikely(rlen < EXT4_DIR_REC_LEN(1)))
error_msg = "rec_len is smaller than minimal";
else if (unlikely(rlen % 4 != 0))
error_msg = "rec_len % 4 != 0";
else if (unlikely(rlen < ext4_dir_rec_len(de->name_len,
fake ? NULL : dir)))
else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
error_msg = "rec_len is too small for name_len";
else if (unlikely(((char *) de - buf) + rlen > size))
error_msg = "directory entry overrun";
else if (unlikely(next_offset > size - ext4_dir_rec_len(1,
next_fake ? NULL : dir) &&
next_offset != size))
else if (unlikely(((char *) de - buf) + rlen >
size - EXT4_DIR_REC_LEN(1) &&
((char *) de - buf) + rlen != size)) {
error_msg = "directory entry too close to block end";
}
else if (unlikely(le32_to_cpu(de->inode) >
le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
error_msg = "inode out of bounds";
@ -111,15 +91,15 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
if (filp)
ext4_error_file(filp, function, line, bh->b_blocknr,
"bad entry in directory: %s - offset=%u, "
"inode=%u, rec_len=%d, lblk=%d, size=%d fake=%d",
"inode=%u, rec_len=%d, name_len=%d, size=%d",
error_msg, offset, le32_to_cpu(de->inode),
rlen, lblk, size, fake);
rlen, de->name_len, size);
else
ext4_error_inode(dir, function, line, bh->b_blocknr,
"bad entry in directory: %s - offset=%u, "
"inode=%u, rec_len=%d, lblk=%d, size=%d fake=%d",
"inode=%u, rec_len=%d, name_len=%d, size=%d",
error_msg, offset, le32_to_cpu(de->inode),
rlen, lblk, size, fake);
rlen, de->name_len, size);
return 1;
}
@ -244,8 +224,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
* failure will be detected in the
* dirent test below. */
if (ext4_rec_len_from_disk(de->rec_len,
sb->s_blocksize) < ext4_dir_rec_len(1,
inode))
sb->s_blocksize) < EXT4_DIR_REC_LEN(1))
break;
i += ext4_rec_len_from_disk(de->rec_len,
sb->s_blocksize);
@ -261,7 +240,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
if (ext4_check_dir_entry(inode, file, de, bh,
bh->b_data, bh->b_size,
map.m_lblk, offset)) {
offset)) {
/*
* On error, skip to the next block
*/
@ -286,9 +265,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
/* Directory is encrypted */
err = fscrypt_fname_disk_to_usr(inode,
EXT4_DIRENT_HASH(de),
EXT4_DIRENT_MINOR_HASH(de),
&de_name, &fstr);
0, 0, &de_name, &fstr);
de_name = fstr;
fstr.len = save_len;
if (err)
@ -663,7 +640,7 @@ int ext4_check_all_de(struct inode *dir, struct buffer_head *bh, void *buf,
top = buf + buf_size;
while ((char *) de < top) {
if (ext4_check_dir_entry(dir, NULL, de, bh,
buf, buf_size, 0, offset))
buf, buf_size, offset))
return -EFSCORRUPTED;
rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
de = (struct ext4_dir_entry_2 *)((char *)de + rlen);

View File

@ -1967,17 +1967,6 @@ struct ext4_dir_entry {
char name[EXT4_NAME_LEN]; /* File name */
};
/*
* Encrypted Casefolded entries require saving the hash on disk. This structure
* followed ext4_dir_entry_2's name[name_len] at the next 4 byte aligned
* boundary.
*/
struct ext4_dir_entry_hash {
__le32 hash;
__le32 minor_hash;
};
/*
* The new version of the directory entry. Since EXT4 structures are
* stored in intel byte order, and the name_len field could never be
@ -1992,22 +1981,6 @@ struct ext4_dir_entry_2 {
char name[EXT4_NAME_LEN]; /* File name */
};
/*
* Access the hashes at the end of ext4_dir_entry_2
*/
#define EXT4_DIRENT_HASHES(entry) \
((struct ext4_dir_entry_hash *) \
(((void *)(entry)) + \
((8 + (entry)->name_len + EXT4_DIR_ROUND) & ~EXT4_DIR_ROUND)))
#define EXT4_DIRENT_HASH(entry) le32_to_cpu(EXT4_DIRENT_HASHES(de)->hash)
#define EXT4_DIRENT_MINOR_HASH(entry) \
le32_to_cpu(EXT4_DIRENT_HASHES(de)->minor_hash)
static inline bool ext4_hash_in_dirent(const struct inode *inode)
{
return IS_CASEFOLDED(inode) && IS_ENCRYPTED(inode);
}
/*
* This is a bogus directory entry at the end of each leaf block that
* records checksums.
@ -2049,24 +2022,10 @@ struct ext4_dir_entry_tail {
*/
#define EXT4_DIR_PAD 4
#define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1)
#define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & \
~EXT4_DIR_ROUND)
#define EXT4_MAX_REC_LEN ((1<<16)-1)
/*
* The rec_len is dependent on the type of directory. Directories that are
* casefolded and encrypted need to store the hash as well, so we add room for
* ext4_extended_dir_entry_2. For all entries related to '.' or '..' you should
* pass NULL for dir, as those entries do not use the extra fields.
*/
static inline unsigned int ext4_dir_rec_len(__u8 name_len,
const struct inode *dir)
{
int rec_len = (name_len + 8 + EXT4_DIR_ROUND);
if (dir && ext4_hash_in_dirent(dir))
rec_len += sizeof(struct ext4_dir_entry_hash);
return (rec_len & ~EXT4_DIR_ROUND);
}
/*
* If we ever get support for fs block sizes > page_size, we'll need
* to remove the #if statements in the next two functions...
@ -2123,7 +2082,6 @@ static inline __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize)
#define DX_HASH_LEGACY_UNSIGNED 3
#define DX_HASH_HALF_MD4_UNSIGNED 4
#define DX_HASH_TEA_UNSIGNED 5
#define DX_HASH_SIPHASH 6
static inline u32 ext4_chksum(struct ext4_sb_info *sbi, u32 crc,
const void *address, unsigned int length)
@ -2181,7 +2139,6 @@ struct ext4_filename {
};
#define fname_name(p) ((p)->disk_name.name)
#define fname_usr_name(p) ((p)->usr_fname->name)
#define fname_len(p) ((p)->disk_name.len)
/*
@ -2516,22 +2473,21 @@ extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
struct file *,
struct ext4_dir_entry_2 *,
struct buffer_head *, char *, int,
ext4_lblk_t, unsigned int);
#define ext4_check_dir_entry(dir, filp, de, bh, buf, size, lblk, offset) \
unsigned int);
#define ext4_check_dir_entry(dir, filp, de, bh, buf, size, offset) \
unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (filp), \
(de), (bh), (buf), (size), (lblk), (offset)))
(de), (bh), (buf), (size), (offset)))
extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
__u32 minor_hash,
struct ext4_dir_entry_2 *dirent,
struct fscrypt_str *ent_name);
extern void ext4_htree_free_dir_info(struct dir_private_info *p);
extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
ext4_lblk_t lblk,
struct buffer_head *bh,
void *buf, int buf_size,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **dest_de);
void ext4_insert_dentry(struct inode *dir, struct inode *inode,
void ext4_insert_dentry(struct inode *inode,
struct ext4_dir_entry_2 *de,
int buf_size,
struct ext4_filename *fname);
@ -2717,12 +2673,11 @@ extern int ext4_search_dir(struct buffer_head *bh,
int buf_size,
struct inode *dir,
struct ext4_filename *fname,
ext4_lblk_t lblk, unsigned int offset,
unsigned int offset,
struct ext4_dir_entry_2 **res_dir);
extern int ext4_generic_delete_entry(handle_t *handle,
struct inode *dir,
struct ext4_dir_entry_2 *de_del,
ext4_lblk_t lblk,
struct buffer_head *bh,
void *entry_buf,
int buf_size,
@ -3248,6 +3203,9 @@ extern void initialize_dirent_tail(struct ext4_dir_entry_tail *t,
extern int ext4_handle_dirty_dirent_node(handle_t *handle,
struct inode *inode,
struct buffer_head *bh);
extern int ext4_ci_compare(const struct inode *parent,
const struct qstr *fname,
const struct qstr *entry, bool quick);
#define S_SHIFT 12
static const unsigned char ext4_type_by_mode[(S_IFMT >> S_SHIFT) + 1] = {

View File

@ -201,7 +201,7 @@ static void str2hashbuf_unsigned(const char *msg, int len, __u32 *buf, int num)
* represented, and whether or not the returned hash is 32 bits or 64
* bits. 32 bit hashes will return 0 for the minor hash.
*/
static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len,
static int __ext4fs_dirhash(const char *name, int len,
struct dx_hash_info *hinfo)
{
__u32 hash;
@ -261,22 +261,6 @@ static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len,
hash = buf[0];
minor_hash = buf[1];
break;
case DX_HASH_SIPHASH:
{
struct qstr qname = QSTR_INIT(name, len);
__u64 combined_hash;
if (fscrypt_has_encryption_key(dir)) {
combined_hash = fscrypt_fname_siphash(dir, &qname);
} else {
ext4_warning_inode(dir, "Siphash requires key");
return -1;
}
hash = (__u32)(combined_hash >> 32);
minor_hash = (__u32)combined_hash;
break;
}
default:
hinfo->hash = 0;
return -1;
@ -298,7 +282,7 @@ int ext4fs_dirhash(const struct inode *dir, const char *name, int len,
unsigned char *buff;
struct qstr qstr = {.name = name, .len = len };
if (len && needs_casefold(dir) && um) {
if (len && IS_CASEFOLDED(dir) && um) {
buff = kzalloc(sizeof(char) * PATH_MAX, GFP_KERNEL);
if (!buff)
return -ENOMEM;
@ -309,12 +293,12 @@ int ext4fs_dirhash(const struct inode *dir, const char *name, int len,
goto opaque_seq;
}
r = __ext4fs_dirhash(dir, buff, dlen, hinfo);
r = __ext4fs_dirhash(buff, dlen, hinfo);
kfree(buff);
return r;
}
opaque_seq:
#endif
return __ext4fs_dirhash(dir, name, len, hinfo);
return __ext4fs_dirhash(name, len, hinfo);
}

View File

@ -463,10 +463,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent,
int ret = -1;
if (qstr) {
if (ext4_hash_in_dirent(parent))
hinfo.hash_version = DX_HASH_SIPHASH;
else
hinfo.hash_version = DX_HASH_HALF_MD4;
hinfo.hash_version = DX_HASH_HALF_MD4;
hinfo.seed = sbi->s_hash_seed;
ext4fs_dirhash(parent, qstr->name, qstr->len, &hinfo);
grp = hinfo.hash;

View File

@ -1022,7 +1022,7 @@ void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
offset, de_len, de->name_len, de->name,
de->name_len, le32_to_cpu(de->inode));
if (ext4_check_dir_entry(dir, NULL, de, bh,
inline_start, inline_size, 0, offset))
inline_start, inline_size, offset))
BUG();
offset += de_len;
@ -1048,7 +1048,7 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
int err;
struct ext4_dir_entry_2 *de;
err = ext4_find_dest_de(dir, inode, 0, iloc->bh, inline_start,
err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start,
inline_size, fname, &de);
if (err)
return err;
@ -1057,7 +1057,7 @@ static int ext4_add_dirent_to_inline(handle_t *handle,
err = ext4_journal_get_write_access(handle, iloc->bh);
if (err)
return err;
ext4_insert_dentry(dir, inode, de, inline_size, fname);
ext4_insert_dentry(inode, de, inline_size, fname);
ext4_show_inline_dir(dir, iloc->bh, inline_start, inline_size);
@ -1126,7 +1126,7 @@ static int ext4_update_inline_dir(handle_t *handle, struct inode *dir,
int old_size = EXT4_I(dir)->i_inline_size - EXT4_MIN_INLINE_DATA_SIZE;
int new_size = get_max_inline_xattr_value_size(dir, iloc);
if (new_size - old_size <= ext4_dir_rec_len(1, NULL))
if (new_size - old_size <= EXT4_DIR_REC_LEN(1))
return -ENOSPC;
ret = ext4_update_inline_data(handle, dir,
@ -1415,8 +1415,8 @@ int htree_inlinedir_to_tree(struct file *dir_file,
fake.name_len = 1;
strcpy(fake.name, ".");
fake.rec_len = ext4_rec_len_to_disk(
ext4_dir_rec_len(fake.name_len, NULL),
inline_size);
EXT4_DIR_REC_LEN(fake.name_len),
inline_size);
ext4_set_de_type(inode->i_sb, &fake, S_IFDIR);
de = &fake;
pos = EXT4_INLINE_DOTDOT_OFFSET;
@ -1425,8 +1425,8 @@ int htree_inlinedir_to_tree(struct file *dir_file,
fake.name_len = 2;
strcpy(fake.name, "..");
fake.rec_len = ext4_rec_len_to_disk(
ext4_dir_rec_len(fake.name_len, NULL),
inline_size);
EXT4_DIR_REC_LEN(fake.name_len),
inline_size);
ext4_set_de_type(inode->i_sb, &fake, S_IFDIR);
de = &fake;
pos = EXT4_INLINE_DOTDOT_SIZE;
@ -1435,18 +1435,13 @@ int htree_inlinedir_to_tree(struct file *dir_file,
pos += ext4_rec_len_from_disk(de->rec_len, inline_size);
if (ext4_check_dir_entry(inode, dir_file, de,
iloc.bh, dir_buf,
inline_size, block, pos)) {
inline_size, pos)) {
ret = count;
goto out;
}
}
if (ext4_hash_in_dirent(dir)) {
hinfo->hash = EXT4_DIRENT_HASH(de);
hinfo->minor_hash = EXT4_DIRENT_MINOR_HASH(de);
} else {
ext4fs_dirhash(dir, de->name, de->name_len, hinfo);
}
ext4fs_dirhash(dir, de->name, de->name_len, hinfo);
if ((hinfo->hash < start_hash) ||
((hinfo->hash == start_hash) &&
(hinfo->minor_hash < start_minor_hash)))
@ -1528,8 +1523,8 @@ int ext4_read_inline_dir(struct file *file,
* So we will use extra_offset and extra_size to indicate them
* during the inline dir iteration.
*/
dotdot_offset = ext4_dir_rec_len(1, NULL);
dotdot_size = dotdot_offset + ext4_dir_rec_len(2, NULL);
dotdot_offset = EXT4_DIR_REC_LEN(1);
dotdot_size = dotdot_offset + EXT4_DIR_REC_LEN(2);
extra_offset = dotdot_size - EXT4_INLINE_DOTDOT_SIZE;
extra_size = extra_offset + inline_size;
@ -1564,7 +1559,7 @@ int ext4_read_inline_dir(struct file *file,
* failure will be detected in the
* dirent test below. */
if (ext4_rec_len_from_disk(de->rec_len, extra_size)
< ext4_dir_rec_len(1, NULL))
< EXT4_DIR_REC_LEN(1))
break;
i += ext4_rec_len_from_disk(de->rec_len,
extra_size);
@ -1592,7 +1587,7 @@ int ext4_read_inline_dir(struct file *file,
de = (struct ext4_dir_entry_2 *)
(dir_buf + ctx->pos - extra_offset);
if (ext4_check_dir_entry(inode, file, de, iloc.bh, dir_buf,
extra_size, 0, ctx->pos))
extra_size, ctx->pos))
goto out;
if (le32_to_cpu(de->inode)) {
if (!dir_emit(ctx, de->name, de->name_len,
@ -1684,7 +1679,7 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
EXT4_INLINE_DOTDOT_SIZE;
inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE;
ret = ext4_search_dir(iloc.bh, inline_start, inline_size,
dir, fname, 0, 0, res_dir);
dir, fname, 0, res_dir);
if (ret == 1)
goto out_find;
if (ret < 0)
@ -1697,7 +1692,7 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
inline_size = ext4_get_inline_size(dir) - EXT4_MIN_INLINE_DATA_SIZE;
ret = ext4_search_dir(iloc.bh, inline_start, inline_size,
dir, fname, 0, 0, res_dir);
dir, fname, 0, res_dir);
if (ret == 1)
goto out_find;
@ -1746,7 +1741,7 @@ int ext4_delete_inline_entry(handle_t *handle,
if (err)
goto out;
err = ext4_generic_delete_entry(handle, dir, de_del, 0, bh,
err = ext4_generic_delete_entry(handle, dir, de_del, bh,
inline_start, inline_size, 0);
if (err)
goto out;
@ -1830,7 +1825,7 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
&inline_pos, &inline_size);
if (ext4_check_dir_entry(dir, NULL, de,
iloc.bh, inline_pos,
inline_size, 0, offset)) {
inline_size, offset)) {
ext4_warning(dir->i_sb,
"bad inline directory (dir #%lu) - "
"inode %u, rec_len %u, name_len %d"

View File

@ -276,11 +276,9 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
unsigned blocksize, struct dx_hash_info *hinfo,
struct dx_map_entry map[]);
static void dx_sort_map(struct dx_map_entry *map, unsigned count);
static struct ext4_dir_entry_2 *dx_move_dirents(struct inode *dir, char *from,
char *to, struct dx_map_entry *offsets,
int count, unsigned int blocksize);
static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base,
unsigned int blocksize);
static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
struct dx_map_entry *offsets, int count, unsigned blocksize);
static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize);
static void dx_insert_block(struct dx_frame *frame,
u32 hash, ext4_lblk_t block);
static int ext4_htree_next_block(struct inode *dir, __u32 hash,
@ -289,7 +287,7 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
__u32 *start_hash);
static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir, ext4_lblk_t *lblk);
struct ext4_dir_entry_2 **res_dir);
static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
struct inode *dir, struct inode *inode);
@ -571,9 +569,8 @@ static inline void dx_set_limit(struct dx_entry *entries, unsigned value)
static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize)
{
unsigned int entry_space = dir->i_sb->s_blocksize -
ext4_dir_rec_len(1, NULL) -
ext4_dir_rec_len(2, NULL) - infosize;
unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(1) -
EXT4_DIR_REC_LEN(2) - infosize;
if (ext4_has_metadata_csum(dir->i_sb))
entry_space -= sizeof(struct dx_tail);
@ -582,8 +579,7 @@ static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize)
static inline unsigned dx_node_limit(struct inode *dir)
{
unsigned int entry_space = dir->i_sb->s_blocksize -
ext4_dir_rec_len(0, dir);
unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0);
if (ext4_has_metadata_csum(dir->i_sb))
entry_space -= sizeof(struct dx_tail);
@ -679,10 +675,7 @@ static struct stats dx_show_leaf(struct inode *dir,
name = fname_crypto_str.name;
len = fname_crypto_str.len;
}
if (IS_CASEFOLDED(dir))
h.hash = EXT4_DIRENT_HASH(de);
else
ext4fs_dirhash(dir, de->name,
ext4fs_dirhash(dir, de->name,
de->name_len, &h);
printk("%*.s:(E)%x.%u ", len, name,
h.hash, (unsigned) ((char *) de
@ -698,7 +691,7 @@ static struct stats dx_show_leaf(struct inode *dir,
(unsigned) ((char *) de - base));
#endif
}
space += ext4_dir_rec_len(de->name_len, dir);
space += EXT4_DIR_REC_LEN(de->name_len);
names++;
}
de = ext4_next_entry(de, size);
@ -772,25 +765,11 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
root = (struct dx_root *) frame->bh->b_data;
if (root->info.hash_version != DX_HASH_TEA &&
root->info.hash_version != DX_HASH_HALF_MD4 &&
root->info.hash_version != DX_HASH_LEGACY &&
root->info.hash_version != DX_HASH_SIPHASH) {
root->info.hash_version != DX_HASH_LEGACY) {
ext4_warning_inode(dir, "Unrecognised inode hash code %u",
root->info.hash_version);
goto fail;
}
if (ext4_hash_in_dirent(dir)) {
if (root->info.hash_version != DX_HASH_SIPHASH) {
ext4_warning_inode(dir,
"Hash in dirent, but hash is not SIPHASH");
goto fail;
}
} else {
if (root->info.hash_version == DX_HASH_SIPHASH) {
ext4_warning_inode(dir,
"Hash code is SIPHASH, but hash not in dirent");
goto fail;
}
}
if (fname)
hinfo = &fname->hinfo;
hinfo->hash_version = root->info.hash_version;
@ -1026,7 +1005,6 @@ static int htree_dirblock_to_tree(struct file *dir_file,
struct ext4_dir_entry_2 *de, *top;
int err = 0, count = 0;
struct fscrypt_str fname_crypto_str = FSTR_INIT(NULL, 0), tmp_str;
int csum = ext4_has_metadata_csum(dir->i_sb);
dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n",
(unsigned long)block));
@ -1035,11 +1013,9 @@ static int htree_dirblock_to_tree(struct file *dir_file,
return PTR_ERR(bh);
de = (struct ext4_dir_entry_2 *) bh->b_data;
/* csum entries are not larger in the casefolded encrypted case */
top = (struct ext4_dir_entry_2 *) ((char *) de +
dir->i_sb->s_blocksize -
ext4_dir_rec_len(0,
csum ? NULL : dir));
EXT4_DIR_REC_LEN(0));
#ifdef CONFIG_FS_ENCRYPTION
/* Check if the directory is encrypted */
if (IS_ENCRYPTED(dir)) {
@ -1058,23 +1034,13 @@ static int htree_dirblock_to_tree(struct file *dir_file,
#endif
for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
if (ext4_check_dir_entry(dir, NULL, de, bh,
bh->b_data, bh->b_size, block,
bh->b_data, bh->b_size,
(block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))
+ ((char *)de - bh->b_data))) {
/* silently ignore the rest of the block */
break;
}
if (ext4_hash_in_dirent(dir)) {
if (de->name_len && de->inode) {
hinfo->hash = EXT4_DIRENT_HASH(de);
hinfo->minor_hash = EXT4_DIRENT_MINOR_HASH(de);
} else {
hinfo->hash = 0;
hinfo->minor_hash = 0;
}
} else {
ext4fs_dirhash(dir, de->name, de->name_len, hinfo);
}
ext4fs_dirhash(dir, de->name, de->name_len, hinfo);
if ((hinfo->hash < start_hash) ||
((hinfo->hash == start_hash) &&
(hinfo->minor_hash < start_minor_hash)))
@ -1145,11 +1111,7 @@ int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
start_hash, start_minor_hash));
dir = file_inode(dir_file);
if (!(ext4_test_inode_flag(dir, EXT4_INODE_INDEX))) {
if (ext4_hash_in_dirent(dir))
hinfo.hash_version = DX_HASH_SIPHASH;
else
hinfo.hash_version =
EXT4_SB(dir->i_sb)->s_def_hash_version;
hinfo.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version;
if (hinfo.hash_version <= DX_HASH_TEA)
hinfo.hash_version +=
EXT4_SB(dir->i_sb)->s_hash_unsigned;
@ -1242,12 +1204,11 @@ errout:
static inline int search_dirblock(struct buffer_head *bh,
struct inode *dir,
struct ext4_filename *fname,
ext4_lblk_t lblk,
unsigned int offset,
struct ext4_dir_entry_2 **res_dir)
{
return ext4_search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir,
fname, lblk, offset, res_dir);
fname, offset, res_dir);
}
/*
@ -1268,10 +1229,7 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
while ((char *) de < base + blocksize) {
if (de->name_len && de->inode) {
if (ext4_hash_in_dirent(dir))
h.hash = EXT4_DIRENT_HASH(de);
else
ext4fs_dirhash(dir, de->name, de->name_len, &h);
ext4fs_dirhash(dir, de->name, de->name_len, &h);
map_tail--;
map_tail->hash = h.hash;
map_tail->offs = ((char *) de - base)>>2;
@ -1335,47 +1293,31 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
* Returns: 0 if the directory entry matches, more than 0 if it
* doesn't match or less than zero on error.
*/
static int ext4_ci_compare(const struct inode *parent, const struct qstr *name,
u8 *de_name, size_t de_name_len, bool quick)
int ext4_ci_compare(const struct inode *parent, const struct qstr *name,
const struct qstr *entry, bool quick)
{
const struct super_block *sb = parent->i_sb;
const struct unicode_map *um = sb->s_encoding;
struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len);
struct qstr entry = QSTR_INIT(de_name, de_name_len);
int ret;
if (IS_ENCRYPTED(parent)) {
const struct fscrypt_str encrypted_name =
FSTR_INIT(de_name, de_name_len);
decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL);
if (!decrypted_name.name)
return -ENOMEM;
ret = fscrypt_fname_disk_to_usr(parent, 0, 0, &encrypted_name,
&decrypted_name);
if (ret < 0)
goto out;
entry.name = decrypted_name.name;
entry.len = decrypted_name.len;
}
if (quick)
ret = utf8_strncasecmp_folded(um, name, &entry);
ret = utf8_strncasecmp_folded(um, name, entry);
else
ret = utf8_strncasecmp(um, name, &entry);
ret = utf8_strncasecmp(um, name, entry);
if (ret < 0) {
/* Handle invalid character sequence as either an error
* or as an opaque byte sequence.
*/
if (sb_has_enc_strict_mode(sb))
ret = -EINVAL;
else if (name->len != entry.len)
ret = 1;
else
ret = !!memcmp(name->name, entry.name, entry.len);
return -EINVAL;
if (name->len != entry->len)
return 1;
return !!memcmp(name->name, entry->name, name->len);
}
out:
kfree(decrypted_name.name);
return ret;
}
@ -1411,11 +1353,14 @@ void ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
*
* Return: %true if the directory entry matches, otherwise %false.
*/
static bool ext4_match(struct inode *parent,
static inline bool ext4_match(const struct inode *parent,
const struct ext4_filename *fname,
struct ext4_dir_entry_2 *de)
const struct ext4_dir_entry_2 *de)
{
struct fscrypt_name f;
#ifdef CONFIG_UNICODE
const struct qstr entry = {.name = de->name, .len = de->name_len};
#endif
if (!de->inode)
return false;
@ -1431,23 +1376,10 @@ static bool ext4_match(struct inode *parent,
if (fname->cf_name.name) {
struct qstr cf = {.name = fname->cf_name.name,
.len = fname->cf_name.len};
if (IS_ENCRYPTED(parent)) {
struct dx_hash_info hinfo;
hinfo.hash_version = DX_HASH_SIPHASH;
hinfo.seed = NULL;
ext4fs_dirhash(parent, fname->cf_name.name,
fname_len(fname), &hinfo);
if (hinfo.hash != EXT4_DIRENT_HASH(de) ||
hinfo.minor_hash !=
EXT4_DIRENT_MINOR_HASH(de))
return 0;
}
return !ext4_ci_compare(parent, &cf, de->name,
de->name_len, true);
return !ext4_ci_compare(parent, &cf, &entry, true);
}
return !ext4_ci_compare(parent, fname->usr_fname, de->name,
de->name_len, false);
return !ext4_ci_compare(parent, fname->usr_fname, &entry,
false);
}
#endif
@ -1459,8 +1391,7 @@ static bool ext4_match(struct inode *parent,
*/
int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
struct inode *dir, struct ext4_filename *fname,
ext4_lblk_t lblk, unsigned int offset,
struct ext4_dir_entry_2 **res_dir)
unsigned int offset, struct ext4_dir_entry_2 **res_dir)
{
struct ext4_dir_entry_2 * de;
char * dlimit;
@ -1476,7 +1407,7 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
/* found a match - just to be sure, do
* a full check */
if (ext4_check_dir_entry(dir, NULL, de, bh, search_buf,
buf_size, lblk, offset))
buf_size, offset))
return -1;
*res_dir = de;
return 1;
@ -1522,7 +1453,7 @@ static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
static struct buffer_head *__ext4_find_entry(struct inode *dir,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir,
int *inlined, ext4_lblk_t *lblk)
int *inlined)
{
struct super_block *sb;
struct buffer_head *bh_use[NAMEI_RA_SIZE];
@ -1546,8 +1477,6 @@ static struct buffer_head *__ext4_find_entry(struct inode *dir,
int has_inline_data = 1;
ret = ext4_find_inline_entry(dir, fname, res_dir,
&has_inline_data);
if (lblk)
*lblk = 0;
if (has_inline_data) {
if (inlined)
*inlined = 1;
@ -1566,7 +1495,7 @@ static struct buffer_head *__ext4_find_entry(struct inode *dir,
goto restart;
}
if (is_dx(dir)) {
ret = ext4_dx_find_entry(dir, fname, res_dir, lblk);
ret = ext4_dx_find_entry(dir, fname, res_dir);
/*
* On success, or if the error was file not found,
* return. Otherwise, fall back to doing a search the
@ -1631,11 +1560,9 @@ restart:
goto cleanup_and_exit;
}
set_buffer_verified(bh);
i = search_dirblock(bh, dir, fname, block,
i = search_dirblock(bh, dir, fname,
block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
if (i == 1) {
if (lblk)
*lblk = block;
EXT4_I(dir)->i_dir_start_lookup = block;
ret = bh;
goto cleanup_and_exit;
@ -1670,7 +1597,7 @@ cleanup_and_exit:
static struct buffer_head *ext4_find_entry(struct inode *dir,
const struct qstr *d_name,
struct ext4_dir_entry_2 **res_dir,
int *inlined, ext4_lblk_t *lblk)
int *inlined)
{
int err;
struct ext4_filename fname;
@ -1682,7 +1609,7 @@ static struct buffer_head *ext4_find_entry(struct inode *dir,
if (err)
return ERR_PTR(err);
bh = __ext4_find_entry(dir, &fname, res_dir, inlined, lblk);
bh = __ext4_find_entry(dir, &fname, res_dir, inlined);
ext4_fname_free_filename(&fname);
return bh;
@ -1703,7 +1630,7 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir,
if (err)
return ERR_PTR(err);
bh = __ext4_find_entry(dir, &fname, res_dir, NULL, NULL);
bh = __ext4_find_entry(dir, &fname, res_dir, NULL);
ext4_fname_free_filename(&fname);
return bh;
@ -1711,7 +1638,7 @@ static struct buffer_head *ext4_lookup_entry(struct inode *dir,
static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir, ext4_lblk_t *lblk)
struct ext4_dir_entry_2 **res_dir)
{
struct super_block * sb = dir->i_sb;
struct dx_frame frames[EXT4_HTREE_LEVEL], *frame;
@ -1727,13 +1654,11 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
return (struct buffer_head *) frame;
do {
block = dx_get_block(frame->at);
if (lblk)
*lblk = block;
bh = ext4_read_dirblock(dir, block, DIRENT_HTREE);
if (IS_ERR(bh))
goto errout;
retval = search_dirblock(bh, dir, fname, block,
retval = search_dirblock(bh, dir, fname,
block << EXT4_BLOCK_SIZE_BITS(sb),
res_dir);
if (retval == 1)
@ -1828,7 +1753,7 @@ struct dentry *ext4_get_parent(struct dentry *child)
struct ext4_dir_entry_2 * de;
struct buffer_head *bh;
bh = ext4_find_entry(d_inode(child), &dotdot, &de, NULL, NULL);
bh = ext4_find_entry(d_inode(child), &dotdot, &de, NULL);
if (IS_ERR(bh))
return (struct dentry *) bh;
if (!bh)
@ -1850,8 +1775,7 @@ struct dentry *ext4_get_parent(struct dentry *child)
* Returns pointer to last entry moved.
*/
static struct ext4_dir_entry_2 *
dx_move_dirents(struct inode *dir, char *from, char *to,
struct dx_map_entry *map, int count,
dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count,
unsigned blocksize)
{
unsigned rec_len = 0;
@ -1859,8 +1783,7 @@ dx_move_dirents(struct inode *dir, char *from, char *to,
while (count--) {
struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)
(from + (map->offs<<2));
rec_len = ext4_dir_rec_len(de->name_len, dir);
rec_len = EXT4_DIR_REC_LEN(de->name_len);
memcpy (to, de, rec_len);
((struct ext4_dir_entry_2 *) to)->rec_len =
ext4_rec_len_to_disk(rec_len, blocksize);
@ -1875,8 +1798,7 @@ dx_move_dirents(struct inode *dir, char *from, char *to,
* Compact each dir entry in the range to the minimal rec_len.
* Returns pointer to last entry in range.
*/
static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base,
unsigned int blocksize)
static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize)
{
struct ext4_dir_entry_2 *next, *to, *prev, *de = (struct ext4_dir_entry_2 *) base;
unsigned rec_len = 0;
@ -1885,7 +1807,7 @@ static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base,
while ((char*)de < base + blocksize) {
next = ext4_next_entry(de, blocksize);
if (de->inode && de->name_len) {
rec_len = ext4_dir_rec_len(de->name_len, dir);
rec_len = EXT4_DIR_REC_LEN(de->name_len);
if (de > to)
memmove(to, de, rec_len);
to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize);
@ -1903,12 +1825,13 @@ static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base,
* Returns pointer to de in block into which the new entry will be inserted.
*/
static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
struct buffer_head **bh, struct dx_frame *frame,
struct dx_hash_info *hinfo, ext4_lblk_t *newblock)
struct buffer_head **bh,struct dx_frame *frame,
struct dx_hash_info *hinfo)
{
unsigned blocksize = dir->i_sb->s_blocksize;
unsigned count, continued;
struct buffer_head *bh2;
ext4_lblk_t newblock;
u32 hash2;
struct dx_map_entry *map;
char *data1 = (*bh)->b_data, *data2;
@ -1921,7 +1844,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
if (ext4_has_metadata_csum(dir->i_sb))
csum_size = sizeof(struct ext4_dir_entry_tail);
bh2 = ext4_append(handle, dir, newblock);
bh2 = ext4_append(handle, dir, &newblock);
if (IS_ERR(bh2)) {
brelse(*bh);
*bh = NULL;
@ -1975,9 +1898,9 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
hash2, split, count-split));
/* Fancy dance to stay within two buffers */
de2 = dx_move_dirents(dir, data1, data2, map + split, count - split,
de2 = dx_move_dirents(data1, data2, map + split, count - split,
blocksize);
de = dx_pack_dirents(dir, data1, blocksize);
de = dx_pack_dirents(data1, blocksize);
de->rec_len = ext4_rec_len_to_disk(data1 + (blocksize - csum_size) -
(char *) de,
blocksize);
@ -2002,7 +1925,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
swap(*bh, bh2);
de = de2;
}
dx_insert_block(frame, hash2 + continued, *newblock);
dx_insert_block(frame, hash2 + continued, newblock);
err = ext4_handle_dirty_dirent_node(handle, dir, bh2);
if (err)
goto journal_error;
@ -2022,14 +1945,13 @@ journal_error:
}
int ext4_find_dest_de(struct inode *dir, struct inode *inode,
ext4_lblk_t lblk,
struct buffer_head *bh,
void *buf, int buf_size,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **dest_de)
{
struct ext4_dir_entry_2 *de;
unsigned short reclen = ext4_dir_rec_len(fname_len(fname), dir);
unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname));
int nlen, rlen;
unsigned int offset = 0;
char *top;
@ -2038,11 +1960,11 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
top = buf + buf_size - reclen;
while ((char *) de <= top) {
if (ext4_check_dir_entry(dir, NULL, de, bh,
buf, buf_size, lblk, offset))
buf, buf_size, offset))
return -EFSCORRUPTED;
if (ext4_match(dir, fname, de))
return -EEXIST;
nlen = ext4_dir_rec_len(de->name_len, dir);
nlen = EXT4_DIR_REC_LEN(de->name_len);
rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
if ((de->inode ? rlen - nlen : rlen) >= reclen)
break;
@ -2056,8 +1978,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
return 0;
}
void ext4_insert_dentry(struct inode *dir,
struct inode *inode,
void ext4_insert_dentry(struct inode *inode,
struct ext4_dir_entry_2 *de,
int buf_size,
struct ext4_filename *fname)
@ -2065,7 +1986,7 @@ void ext4_insert_dentry(struct inode *dir,
int nlen, rlen;
nlen = ext4_dir_rec_len(de->name_len, dir);
nlen = EXT4_DIR_REC_LEN(de->name_len);
rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
if (de->inode) {
struct ext4_dir_entry_2 *de1 =
@ -2079,17 +2000,6 @@ void ext4_insert_dentry(struct inode *dir,
ext4_set_de_type(inode->i_sb, de, inode->i_mode);
de->name_len = fname_len(fname);
memcpy(de->name, fname_name(fname), fname_len(fname));
if (ext4_hash_in_dirent(dir)) {
struct dx_hash_info hinfo;
hinfo.hash_version = DX_HASH_SIPHASH;
hinfo.seed = NULL;
ext4fs_dirhash(dir, fname_usr_name(fname),
fname_len(fname), &hinfo);
EXT4_DIRENT_HASHES(de)->hash = cpu_to_le32(hinfo.hash);
EXT4_DIRENT_HASHES(de)->minor_hash =
cpu_to_le32(hinfo.minor_hash);
}
}
/*
@ -2103,7 +2013,6 @@ void ext4_insert_dentry(struct inode *dir,
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,
ext4_lblk_t blk,
struct buffer_head *bh)
{
unsigned int blocksize = dir->i_sb->s_blocksize;
@ -2114,7 +2023,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
csum_size = sizeof(struct ext4_dir_entry_tail);
if (!de) {
err = ext4_find_dest_de(dir, inode, blk, bh, bh->b_data,
err = ext4_find_dest_de(dir, inode, bh, bh->b_data,
blocksize - csum_size, fname, &de);
if (err)
return err;
@ -2127,7 +2036,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
}
/* By now the buffer is marked for journaling */
ext4_insert_dentry(dir, inode, de, blocksize, fname);
ext4_insert_dentry(inode, de, blocksize, fname);
/*
* XXX shouldn't update any times until successful
@ -2223,16 +2132,11 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
/* Initialize the root; the dot dirents already exist */
de = (struct ext4_dir_entry_2 *) (&root->dotdot);
de->rec_len = ext4_rec_len_to_disk(
blocksize - ext4_dir_rec_len(2, NULL), blocksize);
de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(2),
blocksize);
memset (&root->info, 0, sizeof(root->info));
root->info.info_length = sizeof(root->info);
if (ext4_hash_in_dirent(dir))
root->info.hash_version = DX_HASH_SIPHASH;
else
root->info.hash_version =
EXT4_SB(dir->i_sb)->s_def_hash_version;
root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version;
entries = root->entries;
dx_set_block(entries, 1);
dx_set_count(entries, 1);
@ -2243,12 +2147,7 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
if (fname->hinfo.hash_version <= DX_HASH_TEA)
fname->hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
if (ext4_hash_in_dirent(dir))
ext4fs_dirhash(dir, fname_usr_name(fname),
fname_len(fname), &fname->hinfo);
else
ext4fs_dirhash(dir, fname_name(fname),
fname_len(fname), &fname->hinfo);
ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), &fname->hinfo);
memset(frames, 0, sizeof(frames));
frame = frames;
@ -2263,13 +2162,13 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
if (retval)
goto out_frames;
de = do_split(handle, dir, &bh2, frame, &fname->hinfo, &block);
de = do_split(handle,dir, &bh2, frame, &fname->hinfo);
if (IS_ERR(de)) {
retval = PTR_ERR(de);
goto out_frames;
}
retval = add_dirent_to_buf(handle, fname, dir, inode, de, block, bh2);
retval = add_dirent_to_buf(handle, fname, dir, inode, de, bh2);
out_frames:
/*
* Even if the block split failed, we have to properly write
@ -2365,7 +2264,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
goto out;
}
retval = add_dirent_to_buf(handle, &fname, dir, inode,
NULL, block, bh);
NULL, bh);
if (retval != -ENOSPC)
goto out;
@ -2394,7 +2293,7 @@ add_to_new_block:
initialize_dirent_tail(t, blocksize);
}
retval = add_dirent_to_buf(handle, &fname, dir, inode, de, block, bh);
retval = add_dirent_to_buf(handle, &fname, dir, inode, de, bh);
out:
ext4_fname_free_filename(&fname);
brelse(bh);
@ -2416,7 +2315,6 @@ static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
struct ext4_dir_entry_2 *de;
int restart;
int err;
ext4_lblk_t lblk;
again:
restart = 0;
@ -2425,8 +2323,7 @@ again:
return PTR_ERR(frame);
entries = frame->entries;
at = frame->at;
lblk = dx_get_block(frame->at);
bh = ext4_read_dirblock(dir, lblk, DIRENT_HTREE);
bh = ext4_read_dirblock(dir, dx_get_block(frame->at), DIRENT_HTREE);
if (IS_ERR(bh)) {
err = PTR_ERR(bh);
bh = NULL;
@ -2438,7 +2335,7 @@ again:
if (err)
goto journal_error;
err = add_dirent_to_buf(handle, fname, dir, inode, NULL, lblk, bh);
err = add_dirent_to_buf(handle, fname, dir, inode, NULL, bh);
if (err != -ENOSPC)
goto cleanup;
@ -2557,12 +2454,12 @@ again:
goto journal_error;
}
}
de = do_split(handle, dir, &bh, frame, &fname->hinfo, &lblk);
de = do_split(handle, dir, &bh, frame, &fname->hinfo);
if (IS_ERR(de)) {
err = PTR_ERR(de);
goto cleanup;
}
err = add_dirent_to_buf(handle, fname, dir, inode, de, lblk, bh);
err = add_dirent_to_buf(handle, fname, dir, inode, de, bh);
goto cleanup;
journal_error:
@ -2585,7 +2482,6 @@ cleanup:
int ext4_generic_delete_entry(handle_t *handle,
struct inode *dir,
struct ext4_dir_entry_2 *de_del,
ext4_lblk_t lblk,
struct buffer_head *bh,
void *entry_buf,
int buf_size,
@ -2600,7 +2496,7 @@ int ext4_generic_delete_entry(handle_t *handle,
de = (struct ext4_dir_entry_2 *)entry_buf;
while (i < buf_size - csum_size) {
if (ext4_check_dir_entry(dir, NULL, de, bh,
entry_buf, buf_size, lblk, i))
entry_buf, buf_size, i))
return -EFSCORRUPTED;
if (de == de_del) {
if (pde)
@ -2625,7 +2521,6 @@ int ext4_generic_delete_entry(handle_t *handle,
static int ext4_delete_entry(handle_t *handle,
struct inode *dir,
struct ext4_dir_entry_2 *de_del,
ext4_lblk_t lblk,
struct buffer_head *bh)
{
int err, csum_size = 0;
@ -2646,7 +2541,7 @@ static int ext4_delete_entry(handle_t *handle,
if (unlikely(err))
goto out;
err = ext4_generic_delete_entry(handle, dir, de_del, lblk,
err = ext4_generic_delete_entry(handle, dir, de_del,
bh, bh->b_data,
dir->i_sb->s_blocksize, csum_size);
if (err)
@ -2829,7 +2724,7 @@ struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode,
{
de->inode = cpu_to_le32(inode->i_ino);
de->name_len = 1;
de->rec_len = ext4_rec_len_to_disk(ext4_dir_rec_len(de->name_len, NULL),
de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len),
blocksize);
strcpy(de->name, ".");
ext4_set_de_type(inode->i_sb, de, S_IFDIR);
@ -2839,12 +2734,11 @@ struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode,
de->name_len = 2;
if (!dotdot_real_len)
de->rec_len = ext4_rec_len_to_disk(blocksize -
(csum_size + ext4_dir_rec_len(1, NULL)),
(csum_size + EXT4_DIR_REC_LEN(1)),
blocksize);
else
de->rec_len = ext4_rec_len_to_disk(
ext4_dir_rec_len(de->name_len, NULL),
blocksize);
EXT4_DIR_REC_LEN(de->name_len), blocksize);
strcpy(de->name, "..");
ext4_set_de_type(inode->i_sb, de, S_IFDIR);
@ -2972,8 +2866,7 @@ bool ext4_empty_dir(struct inode *inode)
}
sb = inode->i_sb;
if (inode->i_size < ext4_dir_rec_len(1, NULL) +
ext4_dir_rec_len(2, NULL)) {
if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2)) {
EXT4_ERROR_INODE(inode, "invalid size");
return true;
}
@ -2985,7 +2878,7 @@ bool ext4_empty_dir(struct inode *inode)
return true;
de = (struct ext4_dir_entry_2 *) bh->b_data;
if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size, 0,
if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size,
0) ||
le32_to_cpu(de->inode) != inode->i_ino || strcmp(".", de->name)) {
ext4_warning_inode(inode, "directory missing '.'");
@ -2994,7 +2887,7 @@ bool ext4_empty_dir(struct inode *inode)
}
offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
de = ext4_next_entry(de, sb->s_blocksize);
if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size, 0,
if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size,
offset) ||
le32_to_cpu(de->inode) == 0 || strcmp("..", de->name)) {
ext4_warning_inode(inode, "directory missing '..'");
@ -3018,7 +2911,7 @@ bool ext4_empty_dir(struct inode *inode)
de = (struct ext4_dir_entry_2 *) (bh->b_data +
(offset & (sb->s_blocksize - 1)));
if (ext4_check_dir_entry(inode, NULL, de, bh,
bh->b_data, bh->b_size, 0, offset)) {
bh->b_data, bh->b_size, offset)) {
offset = (offset | (sb->s_blocksize - 1)) + 1;
continue;
}
@ -3213,8 +3106,6 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
struct buffer_head *bh;
struct ext4_dir_entry_2 *de;
handle_t *handle = NULL;
ext4_lblk_t lblk;
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
return -EIO;
@ -3229,7 +3120,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
return retval;
retval = -ENOENT;
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, &lblk);
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
if (IS_ERR(bh))
return PTR_ERR(bh);
if (!bh)
@ -3256,7 +3147,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
if (IS_DIRSYNC(dir))
ext4_handle_sync(handle);
retval = ext4_delete_entry(handle, dir, de, lblk, bh);
retval = ext4_delete_entry(handle, dir, de, bh);
if (retval)
goto end_rmdir;
if (!EXT4_DIR_LINK_EMPTY(inode))
@ -3302,7 +3193,6 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
struct buffer_head *bh;
struct ext4_dir_entry_2 *de;
handle_t *handle = NULL;
ext4_lblk_t lblk;
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
return -EIO;
@ -3318,7 +3208,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
return retval;
retval = -ENOENT;
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, &lblk);
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
if (IS_ERR(bh))
return PTR_ERR(bh);
if (!bh)
@ -3341,7 +3231,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
if (IS_DIRSYNC(dir))
ext4_handle_sync(handle);
retval = ext4_delete_entry(handle, dir, de, lblk, bh);
retval = ext4_delete_entry(handle, dir, de, bh);
if (retval)
goto end_unlink;
dir->i_ctime = dir->i_mtime = current_time(dir);
@ -3603,7 +3493,6 @@ struct ext4_renament {
int dir_nlink_delta;
/* entry for "dentry" */
ext4_lblk_t lblk;
struct buffer_head *bh;
struct ext4_dir_entry_2 *de;
int inlined;
@ -3696,7 +3585,7 @@ static void ext4_resetent(handle_t *handle, struct ext4_renament *ent,
* so the old->de may no longer valid and need to find it again
* before reset old inode info.
*/
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL, NULL);
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
if (IS_ERR(old.bh))
retval = PTR_ERR(old.bh);
if (!old.bh)
@ -3716,13 +3605,12 @@ static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
int retval = -ENOENT;
struct buffer_head *bh;
struct ext4_dir_entry_2 *de;
ext4_lblk_t lblk;
bh = ext4_find_entry(dir, d_name, &de, NULL, &lblk);
bh = ext4_find_entry(dir, d_name, &de, NULL);
if (IS_ERR(bh))
return PTR_ERR(bh);
if (bh) {
retval = ext4_delete_entry(handle, dir, de, lblk, bh);
retval = ext4_delete_entry(handle, dir, de, bh);
brelse(bh);
}
return retval;
@ -3746,8 +3634,7 @@ static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent,
retval = ext4_find_delete_entry(handle, ent->dir,
&ent->dentry->d_name);
} else {
retval = ext4_delete_entry(handle, ent->dir, ent->de,
ent->lblk, ent->bh);
retval = ext4_delete_entry(handle, ent->dir, ent->de, ent->bh);
if (retval == -ENOENT) {
retval = ext4_find_delete_entry(handle, ent->dir,
&ent->dentry->d_name);
@ -3860,8 +3747,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
return retval;
}
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL,
&old.lblk);
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
if (IS_ERR(old.bh))
return PTR_ERR(old.bh);
/*
@ -3875,7 +3761,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
goto release_bh;
new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
&new.de, &new.inlined, NULL);
&new.de, &new.inlined);
if (IS_ERR(new.bh)) {
retval = PTR_ERR(new.bh);
new.bh = NULL;
@ -4059,7 +3945,7 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
return retval;
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name,
&old.de, &old.inlined, NULL);
&old.de, &old.inlined);
if (IS_ERR(old.bh))
return PTR_ERR(old.bh);
/*
@ -4073,7 +3959,7 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
goto end_rename;
new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
&new.de, &new.inlined, NULL);
&new.de, &new.inlined);
if (IS_ERR(new.bh)) {
retval = PTR_ERR(new.bh);
new.bh = NULL;

View File

@ -3890,6 +3890,12 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
struct unicode_map *encoding;
__u16 encoding_flags;
if (ext4_has_feature_encrypt(sb)) {
ext4_msg(sb, KERN_ERR,
"Can't mount with encoding and encryption");
goto failed_mount;
}
if (ext4_sb_read_encoding(es, &encoding_info,
&encoding_flags)) {
ext4_msg(sb, KERN_ERR,