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

Commit 8515bb68 authored by Daniel Rosenberg's avatar Daniel Rosenberg Committed by Greg Kroah-Hartman
Browse files

Revert "ANDROID: ext4: Handle casefolding with encryption"



This reverts commit 268041d7.

Replacing with upstream version

Signed-off-by: default avatarDaniel Rosenberg <drosen@google.com>
Change-Id: Icedfb2bada2722e8325abd3962403d61f6ffd885
parent 91b58da1
Loading
Loading
Loading
Loading
+0 −27
Original line number Diff line number Diff line
@@ -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:
+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 *);

/**
@@ -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);
+10 −52
Original line number Diff line number Diff line
@@ -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] = {
+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;
@@ -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);
}
+1 −4
Original line number Diff line number Diff line
@@ -450,9 +450,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);
Loading