Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/R-base' into R
Browse files Browse the repository at this point in the history
* origin/R-base:
  UPSTREAM: ext4: verify dir block before splitting it
  UPSTREAM: ext4: fix use-after-free in ext4_rename_dir_prepare
  BACKPORT: ext4: Only advertise encrypted_casefold when encryption and unicode are enabled
  BACKPORT: ext4: fix no-key deletion for encrypt+casefold
  BACKPORT: ext4: optimize match for casefolded encrypted dirs
  BACKPORT: ext4: handle casefolding with encryption
  Revert "ANDROID: ext4: Handle casefolding with encryption"
  Revert "ANDROID: ext4: Optimize match for casefolded encrypted dirs"

Signed-off-by: atndko <[email protected]>
  • Loading branch information
0wnerDied committed Jun 26, 2022
2 parents fbd0798 + 4e87801 commit bd95cca
Show file tree
Hide file tree
Showing 8 changed files with 587 additions and 121 deletions.
453 changes: 453 additions & 0 deletions Documentation/filesystems/ext4/ondisk/directory.rst

Large diffs are not rendered by default.

35 changes: 15 additions & 20 deletions fs/ext4/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
#include "ext4.h"
#include "xattr.h"

#define DOTDOT_OFFSET 12

static int ext4_dx_readdir(struct file *, struct dir_context *);

/**
Expand All @@ -54,15 +52,14 @@ 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)
static bool is_fake_dir_entry(struct ext4_dir_entry_2 *de)
{
/* Entries in the first block before this value refer to . or .. */
if (lblk == 0 && offset <= DOTDOT_OFFSET)
/* Check if . or .. , or skip if namelen is 0 */
if ((de->name_len > 0) && (de->name_len <= 2) && (de->name[0] == '.') &&
(de->name[1] == '.' || de->name[1] == '\0'))
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))
/* Check if this is a csum entry */
if (de->file_type == EXT4_FT_DIR_CSUM)
return true;
return false;
}
Expand All @@ -79,16 +76,14 @@ 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);
bool fake = is_fake_dir_entry(de);
bool has_csum = ext4_has_metadata_csum(dir->i_sb);

if (unlikely(rlen < ext4_dir_rec_len(1, fake ? NULL : dir)))
error_msg = "rec_len is smaller than minimal";
Expand All @@ -100,7 +95,7 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
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) &&
has_csum ? NULL : dir) &&
next_offset != size))
error_msg = "directory entry too close to block end";
else if (unlikely(le32_to_cpu(de->inode) >
Expand All @@ -112,15 +107,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, size=%d fake=%d",
error_msg, offset, le32_to_cpu(de->inode),
rlen, lblk, size, fake);
rlen, size, fake);
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, size=%d fake=%d",
error_msg, offset, le32_to_cpu(de->inode),
rlen, lblk, size, fake);
rlen, size, fake);

return 1;
}
Expand Down Expand Up @@ -262,7 +257,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
*/
Expand Down Expand Up @@ -664,7 +659,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);
Expand Down
10 changes: 4 additions & 6 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -2517,17 +2517,16 @@ 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,
Expand Down Expand Up @@ -2718,12 +2717,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,
Expand Down
3 changes: 2 additions & 1 deletion fs/ext4/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,8 @@ 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 &&
(!IS_ENCRYPTED(dir) || fscrypt_has_encryption_key(dir))) {
buff = kzalloc(sizeof(char) * PATH_MAX, GFP_KERNEL);
if (!buff)
return -ENOMEM;
Expand Down
5 changes: 1 addition & 4 deletions fs/ext4/ialloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 8 additions & 8 deletions fs/ext4/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,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;
Expand All @@ -1035,7 +1035,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;
Expand Down Expand Up @@ -1422,7 +1422,7 @@ 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;
}
Expand Down Expand Up @@ -1579,7 +1579,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,
Expand Down Expand Up @@ -1671,7 +1671,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)
Expand All @@ -1684,7 +1684,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;

Expand Down Expand Up @@ -1733,7 +1733,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;
Expand Down Expand Up @@ -1817,7 +1817,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"
Expand Down
Loading

0 comments on commit bd95cca

Please sign in to comment.