ANDROID: ext4: fix encrypt+casefold support

When syncing ext4 with upstream commit f8f4acb6cd ("ext4: use generic
casefolding support"), we forgot to take into account that some
additional checks for the encryption key are needed for the
encrypt+casefold support which isn't upstream yet.

(These checks for the encryption key are still racy since they happen
too late, but apparently they worked well enough...)

This bug made it impossible to delete encrypted+casefolded directories
without the encryption key, due to errors like:

    W         : EXT4-fs warning (device vdc): __ext4fs_dirhash:270: inode #49202: comm Binder:378_4: Siphash requires key

Fixes: 76bfcb2dc2 ("ANDROID: sync generic casefolding code with patches going upstream")
Bug: 161184936
Bug: 172809853
Test: in kvm-xfstests test appliance:
      mkfs.ext4 -F -E encoding=utf8 -O encrypt /dev/vdc
      mount /vdc
      mkdir /vdc/dir
      chattr +F /vdc/dir
      keyid=$(head -c 64 /dev/zero | xfs_io -c add_enckey /vdc | awk '{print $NF}')
      xfs_io -c "set_encpolicy $keyid" /vdc/dir
      for i in `seq 1 100`; do
          mkdir /vdc/dir/$i
      done
      xfs_io -c "rm_enckey $keyid" /vdc
      rm -rf /vdc/dir # fails with the bug
Change-Id: I67fbc9e34778519b229835e1d1b784bd42006ce7
Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
Eric Biggers 2020-11-09 12:49:32 -08:00
parent ab8a8ffd2f
commit 4358dcf4e2
2 changed files with 6 additions and 3 deletions

View File

@ -296,7 +296,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 && IS_CASEFOLDED(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;

View File

@ -1377,7 +1377,8 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
struct dx_hash_info *hinfo = &name->hinfo;
int len;
if (!IS_CASEFOLDED(dir) || !dir->i_sb->s_encoding) {
if (!IS_CASEFOLDED(dir) || !dir->i_sb->s_encoding ||
(IS_ENCRYPTED(dir) && !fscrypt_has_encryption_key(dir))) {
cf_name->name = NULL;
return 0;
}
@ -1428,7 +1429,8 @@ static bool ext4_match(struct inode *parent,
#endif
#ifdef CONFIG_UNICODE
if (parent->i_sb->s_encoding && IS_CASEFOLDED(parent)) {
if (parent->i_sb->s_encoding && IS_CASEFOLDED(parent) &&
(!IS_ENCRYPTED(parent) || fscrypt_has_encryption_key(parent))) {
if (fname->cf_name.name) {
struct qstr cf = {.name = fname->cf_name.name,
.len = fname->cf_name.len};