Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34f40afa authored by Daniel Rosenberg's avatar Daniel Rosenberg Committed by Treehugger Robot
Browse files

Revert "ANDROID: ext4: Handle casefolding with encryption"



This reverts commit 5e0550f5.

Signed-off-by: default avatarDaniel Rosenberg <drosen@google.com>
Change-Id: Ied55a534f9b596534f68d0850113bbc31f081b69
parent bc932227
Loading
Loading
Loading
Loading
+14 −37
Original line number Diff line number Diff line
@@ -30,8 +30,6 @@
#include "ext4.h"
#include "xattr.h"

#define DOTDOT_OFFSET 12

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

/**
@@ -54,19 +52,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
 *
@@ -79,30 +64,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";
@@ -112,15 +92,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;
}
@@ -245,8 +225,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);
@@ -262,7 +241,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
				 */
@@ -287,9 +266,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)
@@ -666,7 +643,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);
+10 −52
Original line number Diff line number Diff line
@@ -1953,17 +1953,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
@@ -1978,22 +1967,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.
@@ -2035,24 +2008,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...
@@ -2109,7 +2068,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)
@@ -2165,7 +2123,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)

/*
@@ -2500,22 +2457,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);
@@ -2699,12 +2655,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,
@@ -3240,6 +3195,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] = {
+4 −20
Original line number Diff line number Diff line
@@ -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;
@@ -257,22 +257,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;
@@ -294,7 +278,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;
@@ -305,12 +289,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);
}
+1 −4
Original line number Diff line number Diff line
@@ -455,9 +455,6 @@ 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.seed = sbi->s_hash_seed;
			ext4fs_dirhash(parent, qstr->name, qstr->len, &hinfo);
+18 −23
Original line number Diff line number Diff line
@@ -1016,7 +1016,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;
@@ -1042,7 +1042,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;
@@ -1051,7 +1051,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);

@@ -1120,7 +1120,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,
@@ -1409,7 +1409,7 @@ 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),
						EXT4_DIR_REC_LEN(fake.name_len),
						inline_size);
			ext4_set_de_type(inode->i_sb, &fake, S_IFDIR);
			de = &fake;
@@ -1419,7 +1419,7 @@ 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),
						EXT4_DIR_REC_LEN(fake.name_len),
						inline_size);
			ext4_set_de_type(inode->i_sb, &fake, S_IFDIR);
			de = &fake;
@@ -1429,18 +1429,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);
		}
		if ((hinfo->hash < start_hash) ||
		    ((hinfo->hash == start_hash) &&
		     (hinfo->minor_hash < start_minor_hash)))
@@ -1522,8 +1517,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;

@@ -1558,7 +1553,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);
@@ -1586,7 +1581,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,
@@ -1678,7 +1673,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)
@@ -1691,7 +1686,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;

@@ -1740,7 +1735,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;
@@ -1824,7 +1819,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"
Loading