Revert "ANDROID: ext4: Handle casefolding with encryption"

This reverts commit 268041d71e.

Replacing with upstream version

Signed-off-by: Daniel Rosenberg <drosen@google.com>
Change-Id: Icedfb2bada2722e8325abd3962403d61f6ffd885
This commit is contained in:
Daniel Rosenberg
2022-06-22 17:58:38 -07:00
committed by Greg Kroah-Hartman
parent 91b58da1b3
commit 8515bb68ee
8 changed files with 151 additions and 375 deletions

View File

@@ -121,31 +121,6 @@ The directory file type is one of the following values:
* - 0x7
- Symbolic link.
To support directories that are both encrypted and casefolded directories, we
must also include hash information in the directory entry. We append
``ext4_extended_dir_entry_2`` to ``ext4_dir_entry_2`` except for the entries
for dot and dotdot, which are kept the same. The structure follows immediately
after ``name`` and is included in the size listed by ``rec_len`` If a directory
entry uses this extension, it may be up to 271 bytes.
.. list-table::
:widths: 8 8 24 40
:header-rows: 1
* - Offset
- Size
- Name
- Description
* - 0x0
- \_\_le32
- hash
- The hash of the directory name
* - 0x4
- \_\_le32
- minor\_hash
- The minor hash of the directory name
In order to add checksums to these classic directory blocks, a phony
``struct ext4_dir_entry`` is placed at the end of each leaf block to
hold the checksum. The directory entry is 12 bytes long. The inode
@@ -347,8 +322,6 @@ The directory hash is one of the following values:
- Half MD4, unsigned.
* - 0x5
- Tea, unsigned.
* - 0x6
- Siphash.
Interior nodes of an htree are recorded as ``struct dx_node``, which is
also the full length of a data block:

View File

@@ -30,8 +30,6 @@
#include "ext4.h"
#include "xattr.h"
#define DOTDOT_OFFSET 12
static int ext4_dx_readdir(struct file *, struct dir_context *);
/**
@@ -57,19 +55,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
*
@@ -82,30 +67,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";
@@ -115,15 +95,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;
}
@@ -247,8 +227,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);
@@ -264,7 +243,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
*/
@@ -289,9 +268,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)
@@ -668,7 +645,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

@@ -1971,17 +1971,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
@@ -1996,22 +1985,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.
@@ -2053,24 +2026,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...
@@ -2127,7 +2086,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)
@@ -2182,7 +2140,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)
/*
@@ -2517,22 +2474,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,
@@ -3257,6 +3212,9 @@ extern void ext4_initialize_dirent_tail(struct buffer_head *bh,
unsigned int blocksize);
extern int ext4_handle_dirty_dirblock(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

@@ -197,7 +197,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;
@@ -259,22 +259,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;
@@ -296,7 +280,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;
@@ -307,12 +291,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

@@ -450,10 +450,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

@@ -1011,7 +1011,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;
@@ -1037,7 +1037,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;
@@ -1046,7 +1046,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);
@@ -1115,7 +1115,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,
@@ -1401,8 +1401,8 @@ int ext4_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;
@@ -1411,8 +1411,8 @@ int ext4_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;
@@ -1421,18 +1421,13 @@ int ext4_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)))
@@ -1514,8 +1509,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;
@@ -1550,7 +1545,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);
@@ -1578,7 +1573,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,
@@ -1670,7 +1665,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)
@@ -1683,7 +1678,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;
@@ -1732,7 +1727,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;
@@ -1816,7 +1811,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

@@ -277,11 +277,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,
@@ -290,7 +288,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);
@@ -573,9 +571,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);
@@ -584,8 +581,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);
@@ -681,10 +677,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
@@ -700,7 +693,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);
@@ -774,25 +767,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;
@@ -1028,7 +1007,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));
@@ -1037,11 +1015,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)) {
@@ -1060,23 +1036,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)))
@@ -1147,11 +1113,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;
@@ -1244,12 +1206,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);
}
/*
@@ -1270,10 +1231,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;
@@ -1337,47 +1295,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;
}
@@ -1413,11 +1355,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;
@@ -1433,23 +1378,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
@@ -1461,8 +1393,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;
@@ -1478,7 +1409,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;
@@ -1524,7 +1455,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];
@@ -1548,8 +1479,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;
@@ -1568,7 +1497,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
@@ -1632,11 +1561,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;
@@ -1671,7 +1598,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;
@@ -1683,7 +1610,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;
@@ -1704,7 +1631,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;
@@ -1712,7 +1639,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;
@@ -1728,13 +1655,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)
@@ -1829,7 +1754,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 ERR_CAST(bh);
if (!bh)
@@ -1851,8 +1776,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;
@@ -1860,8 +1784,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);
@@ -1876,8 +1799,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;
@@ -1886,7 +1808,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);
@@ -1904,13 +1826,14 @@ 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 continued;
int count;
struct buffer_head *bh2;
ext4_lblk_t newblock;
u32 hash2;
struct dx_map_entry *map;
char *data1 = (*bh)->b_data, *data2;
@@ -1922,7 +1845,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;
@@ -1976,9 +1899,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);
@@ -2000,7 +1923,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_dirblock(handle, dir, bh2);
if (err)
goto journal_error;
@@ -2020,14 +1943,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;
@@ -2036,11 +1958,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;
@@ -2054,8 +1976,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)
@@ -2063,7 +1984,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 =
@@ -2077,17 +1998,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);
}
}
/*
@@ -2101,7 +2011,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;
@@ -2112,7 +2021,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;
@@ -2125,7 +2034,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
@@ -2217,16 +2126,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);
@@ -2237,12 +2141,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;
@@ -2257,13 +2156,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
@@ -2361,7 +2260,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;
@@ -2388,7 +2287,7 @@ add_to_new_block:
if (csum_size)
ext4_initialize_dirent_tail(bh, 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);
@@ -2410,7 +2309,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;
@@ -2419,8 +2317,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;
@@ -2432,7 +2329,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;
@@ -2551,12 +2448,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:
@@ -2579,7 +2476,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,
@@ -2594,7 +2490,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)
@@ -2619,7 +2515,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;
@@ -2640,7 +2535,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)
@@ -2823,7 +2718,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);
@@ -2833,12 +2728,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);
@@ -2963,8 +2857,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;
}
@@ -2976,7 +2869,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 '.'");
@@ -2985,7 +2878,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 '..'");
@@ -3009,7 +2902,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;
}
@@ -3204,8 +3097,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;
@@ -3220,7 +3111,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)
@@ -3247,7 +3138,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))
@@ -3293,7 +3184,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;
@@ -3309,7 +3199,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)
@@ -3332,7 +3222,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);
@@ -3594,7 +3484,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;
@@ -3685,7 +3574,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)
@@ -3705,13 +3594,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;
@@ -3735,8 +3623,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);
@@ -3849,8 +3736,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);
/*
@@ -3864,7 +3750,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;
@@ -4048,7 +3934,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);
/*
@@ -4062,7 +3948,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

@@ -3972,6 +3972,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,