--- /dev/null
+From foo@baz Sun Nov 1 11:35:18 AM CET 2020
+From: Eric Biggers <ebiggers@kernel.org>
+Date: Sat, 31 Oct 2020 15:05:51 -0700
+Subject: fs, fscrypt: clear DCACHE_ENCRYPTED_NAME when unaliasing directory
+To: stable@vger.kernel.org
+Cc: linux-fscrypt@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, Sarthak Kukreti <sarthakkukreti@chromium.org>, Theodore Ts'o <tytso@mit.edu>
+Message-ID: <20201031220553.1085782-4-ebiggers@kernel.org>
+
+From: Eric Biggers <ebiggers@google.com>
+
+commit 0bf3d5c1604ecbbd4e49e9f5b3c79152b87adb0d upstream.
+
+Make __d_move() clear DCACHE_ENCRYPTED_NAME on the source dentry. This
+is needed for when d_splice_alias() moves a directory's encrypted alias
+to its decrypted alias as a result of the encryption key being added.
+
+Otherwise, the decrypted alias will incorrectly be invalidated on the
+next lookup, causing problems such as unmounting a mount the user just
+mount()ed there.
+
+Note that we don't have to support arbitrary moves of this flag because
+fscrypt doesn't allow dentries with DCACHE_ENCRYPTED_NAME to be the
+source or target of a rename().
+
+Fixes: 28b4c263961c ("ext4 crypto: revalidate dentry after adding or removing the key")
+Reported-by: Sarthak Kukreti <sarthakkukreti@chromium.org>
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/dcache.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -2713,6 +2713,20 @@ static void copy_name(struct dentry *den
+ }
+
+ /*
++ * When d_splice_alias() moves a directory's encrypted alias to its decrypted
++ * alias as a result of the encryption key being added, DCACHE_ENCRYPTED_NAME
++ * must be cleared. Note that we don't have to support arbitrary moves of this
++ * flag because fscrypt doesn't allow encrypted aliases to be the source or
++ * target of a rename().
++ */
++static inline void fscrypt_handle_d_move(struct dentry *dentry)
++{
++#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
++ dentry->d_flags &= ~DCACHE_ENCRYPTED_NAME;
++#endif
++}
++
++/*
+ * __d_move - move a dentry
+ * @dentry: entry to move
+ * @target: new dentry
+@@ -2787,6 +2801,7 @@ static void __d_move(struct dentry *dent
+ list_move(&dentry->d_child, &dentry->d_parent->d_subdirs);
+ __d_rehash(dentry);
+ fsnotify_update_flags(dentry);
++ fscrypt_handle_d_move(dentry);
+
+ write_seqcount_end(&target->d_seq);
+ write_seqcount_end(&dentry->d_seq);
--- /dev/null
+From foo@baz Sun Nov 1 11:35:18 AM CET 2020
+From: Eric Biggers <ebiggers@kernel.org>
+Date: Sat, 31 Oct 2020 15:05:49 -0700
+Subject: fscrypt: clean up and improve dentry revalidation
+To: stable@vger.kernel.org
+Cc: linux-fscrypt@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, Theodore Ts'o <tytso@mit.edu>
+Message-ID: <20201031220553.1085782-2-ebiggers@kernel.org>
+
+From: Eric Biggers <ebiggers@google.com>
+
+commit 6cc248684d3d23bbd073ae2fa73d3416c0558909 upstream.
+
+Make various improvements to fscrypt dentry revalidation:
+
+- Don't try to handle the case where the per-directory key is removed,
+ as this can't happen without the inode (and dentries) being evicted.
+
+- Flag ciphertext dentries rather than plaintext dentries, since it's
+ ciphertext dentries that need the special handling.
+
+- Avoid doing unnecessary work for non-ciphertext dentries.
+
+- When revalidating ciphertext dentries, try to set up the directory's
+ i_crypt_info to make sure the key is really still absent, rather than
+ invalidating all negative dentries as the previous code did. An old
+ comment suggested we can't do this for locking reasons, but AFAICT
+ this comment was outdated and it actually works fine.
+
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/crypto/crypto.c | 58 ++++++++++++++++++++++++------------------------
+ fs/crypto/hooks.c | 4 +--
+ include/linux/dcache.h | 2 -
+ include/linux/fscrypt.h | 6 +---
+ 4 files changed, 35 insertions(+), 35 deletions(-)
+
+--- a/fs/crypto/crypto.c
++++ b/fs/crypto/crypto.c
+@@ -314,45 +314,47 @@ int fscrypt_decrypt_page(const struct in
+ EXPORT_SYMBOL(fscrypt_decrypt_page);
+
+ /*
+- * Validate dentries for encrypted directories to make sure we aren't
+- * potentially caching stale data after a key has been added or
+- * removed.
++ * Validate dentries in encrypted directories to make sure we aren't potentially
++ * caching stale dentries after a key has been added.
+ */
+ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
+ {
+ struct dentry *dir;
+- int dir_has_key, cached_with_key;
++ int err;
++ int valid;
++
++ /*
++ * Plaintext names are always valid, since fscrypt doesn't support
++ * reverting to ciphertext names without evicting the directory's inode
++ * -- which implies eviction of the dentries in the directory.
++ */
++ if (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME))
++ return 1;
++
++ /*
++ * Ciphertext name; valid if the directory's key is still unavailable.
++ *
++ * Although fscrypt forbids rename() on ciphertext names, we still must
++ * use dget_parent() here rather than use ->d_parent directly. That's
++ * because a corrupted fs image may contain directory hard links, which
++ * the VFS handles by moving the directory's dentry tree in the dcache
++ * each time ->lookup() finds the directory and it already has a dentry
++ * elsewhere. Thus ->d_parent can be changing, and we must safely grab
++ * a reference to some ->d_parent to prevent it from being freed.
++ */
+
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+
+ dir = dget_parent(dentry);
+- if (!IS_ENCRYPTED(d_inode(dir))) {
+- dput(dir);
+- return 0;
+- }
+-
+- spin_lock(&dentry->d_lock);
+- cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY;
+- spin_unlock(&dentry->d_lock);
+- dir_has_key = (d_inode(dir)->i_crypt_info != NULL);
++ err = fscrypt_get_encryption_info(d_inode(dir));
++ valid = !fscrypt_has_encryption_key(d_inode(dir));
+ dput(dir);
+
+- /*
+- * If the dentry was cached without the key, and it is a
+- * negative dentry, it might be a valid name. We can't check
+- * if the key has since been made available due to locking
+- * reasons, so we fail the validation so ext4_lookup() can do
+- * this check.
+- *
+- * We also fail the validation if the dentry was created with
+- * the key present, but we no longer have the key, or vice versa.
+- */
+- if ((!cached_with_key && d_is_negative(dentry)) ||
+- (!cached_with_key && dir_has_key) ||
+- (cached_with_key && !dir_has_key))
+- return 0;
+- return 1;
++ if (err < 0)
++ return err;
++
++ return valid;
+ }
+
+ const struct dentry_operations fscrypt_d_ops = {
+--- a/fs/crypto/hooks.c
++++ b/fs/crypto/hooks.c
+@@ -101,9 +101,9 @@ int __fscrypt_prepare_lookup(struct inod
+ if (err)
+ return err;
+
+- if (fscrypt_has_encryption_key(dir)) {
++ if (!fscrypt_has_encryption_key(dir)) {
+ spin_lock(&dentry->d_lock);
+- dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY;
++ dentry->d_flags |= DCACHE_ENCRYPTED_NAME;
+ spin_unlock(&dentry->d_lock);
+ }
+
+--- a/include/linux/dcache.h
++++ b/include/linux/dcache.h
+@@ -210,7 +210,7 @@ struct dentry_operations {
+
+ #define DCACHE_MAY_FREE 0x00800000
+ #define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
+-#define DCACHE_ENCRYPTED_WITH_KEY 0x02000000 /* dir is encrypted with a valid key */
++#define DCACHE_ENCRYPTED_NAME 0x02000000 /* Encrypted name (dir key was unavailable) */
+ #define DCACHE_OP_REAL 0x04000000
+
+ #define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */
+--- a/include/linux/fscrypt.h
++++ b/include/linux/fscrypt.h
+@@ -145,10 +145,8 @@ static inline int fscrypt_prepare_rename
+ * filenames are presented in encrypted form. Therefore, we'll try to set up
+ * the directory's encryption key, but even without it the lookup can continue.
+ *
+- * To allow invalidating stale dentries if the directory's encryption key is
+- * added later, we also install a custom ->d_revalidate() method and use the
+- * DCACHE_ENCRYPTED_WITH_KEY flag to indicate whether a given dentry is a
+- * plaintext name (flag set) or a ciphertext name (flag cleared).
++ * This also installs a custom ->d_revalidate() method which will invalidate the
++ * dentry if it was created without the key and the key is later added.
+ *
+ * Return: 0 on success, -errno if a problem occurred while setting up the
+ * encryption key
--- /dev/null
+From foo@baz Sun Nov 1 11:35:18 AM CET 2020
+From: Eric Biggers <ebiggers@kernel.org>
+Date: Sat, 31 Oct 2020 15:05:50 -0700
+Subject: fscrypt: fix race allowing rename() and link() of ciphertext dentries
+To: stable@vger.kernel.org
+Cc: linux-fscrypt@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, Theodore Ts'o <tytso@mit.edu>
+Message-ID: <20201031220553.1085782-3-ebiggers@kernel.org>
+
+From: Eric Biggers <ebiggers@google.com>
+
+commit 968dd6d0c6d6b6a989c6ddb9e2584a031b83e7b5 upstream.
+
+Close some race conditions where fscrypt allowed rename() and link() on
+ciphertext dentries that had been looked up just prior to the key being
+concurrently added. It's better to return -ENOKEY in this case.
+
+This avoids doing the nonsensical thing of encrypting the names a second
+time when searching for the actual on-disk dir entries. It also
+guarantees that DCACHE_ENCRYPTED_NAME dentries are never rename()d, so
+the dcache won't have support all possible combinations of moving
+DCACHE_ENCRYPTED_NAME around during __d_move().
+
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/crypto/hooks.c | 12 +++++++++++-
+ include/linux/fscrypt.h | 2 +-
+ include/linux/fscrypt_notsupp.h | 4 ++--
+ include/linux/fscrypt_supp.h | 3 ++-
+ 4 files changed, 16 insertions(+), 5 deletions(-)
+
+--- a/fs/crypto/hooks.c
++++ b/fs/crypto/hooks.c
+@@ -49,7 +49,8 @@ int fscrypt_file_open(struct inode *inod
+ }
+ EXPORT_SYMBOL_GPL(fscrypt_file_open);
+
+-int __fscrypt_prepare_link(struct inode *inode, struct inode *dir)
++int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
++ struct dentry *dentry)
+ {
+ int err;
+
+@@ -57,6 +58,10 @@ int __fscrypt_prepare_link(struct inode
+ if (err)
+ return err;
+
++ /* ... in case we looked up ciphertext name before key was added */
++ if (dentry->d_flags & DCACHE_ENCRYPTED_NAME)
++ return -ENOKEY;
++
+ if (!fscrypt_has_permitted_context(dir, inode))
+ return -EXDEV;
+
+@@ -78,6 +83,11 @@ int __fscrypt_prepare_rename(struct inod
+ if (err)
+ return err;
+
++ /* ... in case we looked up ciphertext name(s) before key was added */
++ if ((old_dentry->d_flags | new_dentry->d_flags) &
++ DCACHE_ENCRYPTED_NAME)
++ return -ENOKEY;
++
+ if (old_dir != new_dir) {
+ if (IS_ENCRYPTED(new_dir) &&
+ !fscrypt_has_permitted_context(new_dir,
+--- a/include/linux/fscrypt.h
++++ b/include/linux/fscrypt.h
+@@ -97,7 +97,7 @@ static inline int fscrypt_prepare_link(s
+ struct dentry *dentry)
+ {
+ if (IS_ENCRYPTED(dir))
+- return __fscrypt_prepare_link(d_inode(old_dentry), dir);
++ return __fscrypt_prepare_link(d_inode(old_dentry), dir, dentry);
+ return 0;
+ }
+
+--- a/include/linux/fscrypt_notsupp.h
++++ b/include/linux/fscrypt_notsupp.h
+@@ -183,8 +183,8 @@ static inline int fscrypt_file_open(stru
+ return 0;
+ }
+
+-static inline int __fscrypt_prepare_link(struct inode *inode,
+- struct inode *dir)
++static inline int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
++ struct dentry *dentry)
+ {
+ return -EOPNOTSUPP;
+ }
+--- a/include/linux/fscrypt_supp.h
++++ b/include/linux/fscrypt_supp.h
+@@ -184,7 +184,8 @@ extern int fscrypt_zeroout_range(const s
+
+ /* hooks.c */
+ extern int fscrypt_file_open(struct inode *inode, struct file *filp);
+-extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir);
++extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
++ struct dentry *dentry);
+ extern int __fscrypt_prepare_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct inode *new_dir,
--- /dev/null
+From foo@baz Sun Nov 1 11:35:18 AM CET 2020
+From: Eric Biggers <ebiggers@kernel.org>
+Date: Sat, 31 Oct 2020 15:05:53 -0700
+Subject: fscrypt: fix race where ->lookup() marks plaintext dentry as ciphertext
+To: stable@vger.kernel.org
+Cc: linux-fscrypt@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, Theodore Ts'o <tytso@mit.edu>
+Message-ID: <20201031220553.1085782-6-ebiggers@kernel.org>
+
+From: Eric Biggers <ebiggers@google.com>
+
+commit b01531db6cec2aa330dbc91bfbfaaef4a0d387a4 upstream.
+
+->lookup() in an encrypted directory begins as follows:
+
+1. fscrypt_prepare_lookup():
+ a. Try to load the directory's encryption key.
+ b. If the key is unavailable, mark the dentry as a ciphertext name
+ via d_flags.
+2. fscrypt_setup_filename():
+ a. Try to load the directory's encryption key.
+ b. If the key is available, encrypt the name (treated as a plaintext
+ name) to get the on-disk name. Otherwise decode the name
+ (treated as a ciphertext name) to get the on-disk name.
+
+But if the key is concurrently added, it may be found at (2a) but not at
+(1a). In this case, the dentry will be wrongly marked as a ciphertext
+name even though it was actually treated as plaintext.
+
+This will cause the dentry to be wrongly invalidated on the next lookup,
+potentially causing problems. For example, if the racy ->lookup() was
+part of sys_mount(), then the new mount will be detached when anything
+tries to access it. This is despite the mountpoint having a plaintext
+path, which should remain valid now that the key was added.
+
+Of course, this is only possible if there's a userspace race. Still,
+the additional kernel-side race is confusing and unexpected.
+
+Close the kernel-side race by changing fscrypt_prepare_lookup() to also
+set the on-disk filename (step 2b), consistent with the d_flags update.
+
+Fixes: 28b4c263961c ("ext4 crypto: revalidate dentry after adding or removing the key")
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/crypto/fname.c | 1
+ fs/crypto/hooks.c | 11 +++--
+ fs/ext4/ext4.h | 62 ++++++++++++++++++++++++--------
+ fs/ext4/namei.c | 76 +++++++++++++++++++++++++++-------------
+ fs/f2fs/namei.c | 17 +++++---
+ fs/ubifs/dir.c | 8 +---
+ include/linux/fscrypt.h | 22 +++++++----
+ include/linux/fscrypt_notsupp.h | 5 +-
+ include/linux/fscrypt_supp.h | 3 +
+ 9 files changed, 139 insertions(+), 66 deletions(-)
+
+--- a/fs/crypto/fname.c
++++ b/fs/crypto/fname.c
+@@ -354,6 +354,7 @@ int fscrypt_setup_filename(struct inode
+ }
+ if (!lookup)
+ return -ENOKEY;
++ fname->is_ciphertext_name = true;
+
+ /*
+ * We don't have the key and we are doing a lookup; decode the
+--- a/fs/crypto/hooks.c
++++ b/fs/crypto/hooks.c
+@@ -104,20 +104,21 @@ int __fscrypt_prepare_rename(struct inod
+ }
+ EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename);
+
+-int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry)
++int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
++ struct fscrypt_name *fname)
+ {
+- int err = fscrypt_get_encryption_info(dir);
++ int err = fscrypt_setup_filename(dir, &dentry->d_name, 1, fname);
+
+- if (err)
++ if (err && err != -ENOENT)
+ return err;
+
+- if (!fscrypt_has_encryption_key(dir)) {
++ if (fname->is_ciphertext_name) {
+ spin_lock(&dentry->d_lock);
+ dentry->d_flags |= DCACHE_ENCRYPTED_NAME;
+ spin_unlock(&dentry->d_lock);
+ d_set_d_op(dentry, &fscrypt_d_ops);
+ }
+- return 0;
++ return err;
+ }
+ EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);
+
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -2326,23 +2326,47 @@ static inline bool ext4_encrypted_inode(
+ }
+
+ #ifdef CONFIG_EXT4_FS_ENCRYPTION
++static inline void ext4_fname_from_fscrypt_name(struct ext4_filename *dst,
++ const struct fscrypt_name *src)
++{
++ memset(dst, 0, sizeof(*dst));
++
++ dst->usr_fname = src->usr_fname;
++ dst->disk_name = src->disk_name;
++ dst->hinfo.hash = src->hash;
++ dst->hinfo.minor_hash = src->minor_hash;
++ dst->crypto_buf = src->crypto_buf;
++}
++
+ static inline int ext4_fname_setup_filename(struct inode *dir,
+- const struct qstr *iname,
+- int lookup, struct ext4_filename *fname)
++ const struct qstr *iname,
++ int lookup,
++ struct ext4_filename *fname)
+ {
+ struct fscrypt_name name;
+ int err;
+
+- memset(fname, 0, sizeof(struct ext4_filename));
+-
+ err = fscrypt_setup_filename(dir, iname, lookup, &name);
++ if (err)
++ return err;
+
+- fname->usr_fname = name.usr_fname;
+- fname->disk_name = name.disk_name;
+- fname->hinfo.hash = name.hash;
+- fname->hinfo.minor_hash = name.minor_hash;
+- fname->crypto_buf = name.crypto_buf;
+- return err;
++ ext4_fname_from_fscrypt_name(fname, &name);
++ return 0;
++}
++
++static inline int ext4_fname_prepare_lookup(struct inode *dir,
++ struct dentry *dentry,
++ struct ext4_filename *fname)
++{
++ struct fscrypt_name name;
++ int err;
++
++ err = fscrypt_prepare_lookup(dir, dentry, &name);
++ if (err)
++ return err;
++
++ ext4_fname_from_fscrypt_name(fname, &name);
++ return 0;
+ }
+
+ static inline void ext4_fname_free_filename(struct ext4_filename *fname)
+@@ -2356,19 +2380,27 @@ static inline void ext4_fname_free_filen
+ fname->usr_fname = NULL;
+ fname->disk_name.name = NULL;
+ }
+-#else
++#else /* !CONFIG_EXT4_FS_ENCRYPTION */
+ static inline int ext4_fname_setup_filename(struct inode *dir,
+- const struct qstr *iname,
+- int lookup, struct ext4_filename *fname)
++ const struct qstr *iname,
++ int lookup,
++ struct ext4_filename *fname)
+ {
+ fname->usr_fname = iname;
+ fname->disk_name.name = (unsigned char *) iname->name;
+ fname->disk_name.len = iname->len;
+ return 0;
+ }
+-static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
+
+-#endif
++static inline int ext4_fname_prepare_lookup(struct inode *dir,
++ struct dentry *dentry,
++ struct ext4_filename *fname)
++{
++ return ext4_fname_setup_filename(dir, &dentry->d_name, 1, fname);
++}
++
++static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
++#endif /* !CONFIG_EXT4_FS_ENCRYPTION */
+
+ /* dir.c */
+ extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
+--- a/fs/ext4/namei.c
++++ b/fs/ext4/namei.c
+@@ -1343,7 +1343,7 @@ static int is_dx_internal_node(struct in
+ }
+
+ /*
+- * ext4_find_entry()
++ * __ext4_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+@@ -1353,39 +1353,32 @@ static int is_dx_internal_node(struct in
+ * The returned buffer_head has ->b_count elevated. The caller is expected
+ * to brelse() it when appropriate.
+ */
+-static struct buffer_head * ext4_find_entry (struct inode *dir,
+- const struct qstr *d_name,
+- struct ext4_dir_entry_2 **res_dir,
+- int *inlined)
++static struct buffer_head *__ext4_find_entry(struct inode *dir,
++ struct ext4_filename *fname,
++ struct ext4_dir_entry_2 **res_dir,
++ int *inlined)
+ {
+ struct super_block *sb;
+ struct buffer_head *bh_use[NAMEI_RA_SIZE];
+ struct buffer_head *bh, *ret = NULL;
+ ext4_lblk_t start, block;
+- const u8 *name = d_name->name;
++ const u8 *name = fname->usr_fname->name;
+ size_t ra_max = 0; /* Number of bh's in the readahead
+ buffer, bh_use[] */
+ size_t ra_ptr = 0; /* Current index into readahead
+ buffer */
+ ext4_lblk_t nblocks;
+ int i, namelen, retval;
+- struct ext4_filename fname;
+
+ *res_dir = NULL;
+ sb = dir->i_sb;
+- namelen = d_name->len;
++ namelen = fname->usr_fname->len;
+ if (namelen > EXT4_NAME_LEN)
+ return NULL;
+
+- retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
+- if (retval == -ENOENT)
+- return NULL;
+- if (retval)
+- return ERR_PTR(retval);
+-
+ if (ext4_has_inline_data(dir)) {
+ int has_inline_data = 1;
+- ret = ext4_find_inline_entry(dir, &fname, res_dir,
++ ret = ext4_find_inline_entry(dir, fname, res_dir,
+ &has_inline_data);
+ if (has_inline_data) {
+ if (inlined)
+@@ -1405,7 +1398,7 @@ static struct buffer_head * ext4_find_en
+ goto restart;
+ }
+ if (is_dx(dir)) {
+- ret = ext4_dx_find_entry(dir, &fname, res_dir);
++ 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
+@@ -1470,7 +1463,7 @@ restart:
+ goto cleanup_and_exit;
+ }
+ set_buffer_verified(bh);
+- i = search_dirblock(bh, dir, &fname,
++ i = search_dirblock(bh, dir, fname,
+ block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
+ if (i == 1) {
+ EXT4_I(dir)->i_dir_start_lookup = block;
+@@ -1501,10 +1494,50 @@ cleanup_and_exit:
+ /* Clean up the read-ahead blocks */
+ for (; ra_ptr < ra_max; ra_ptr++)
+ brelse(bh_use[ra_ptr]);
+- ext4_fname_free_filename(&fname);
+ return ret;
+ }
+
++static struct buffer_head *ext4_find_entry(struct inode *dir,
++ const struct qstr *d_name,
++ struct ext4_dir_entry_2 **res_dir,
++ int *inlined)
++{
++ int err;
++ struct ext4_filename fname;
++ struct buffer_head *bh;
++
++ err = ext4_fname_setup_filename(dir, d_name, 1, &fname);
++ if (err == -ENOENT)
++ return NULL;
++ if (err)
++ return ERR_PTR(err);
++
++ bh = __ext4_find_entry(dir, &fname, res_dir, inlined);
++
++ ext4_fname_free_filename(&fname);
++ return bh;
++}
++
++static struct buffer_head *ext4_lookup_entry(struct inode *dir,
++ struct dentry *dentry,
++ struct ext4_dir_entry_2 **res_dir)
++{
++ int err;
++ struct ext4_filename fname;
++ struct buffer_head *bh;
++
++ err = ext4_fname_prepare_lookup(dir, dentry, &fname);
++ if (err == -ENOENT)
++ return NULL;
++ if (err)
++ return ERR_PTR(err);
++
++ bh = __ext4_find_entry(dir, &fname, res_dir, NULL);
++
++ ext4_fname_free_filename(&fname);
++ return bh;
++}
++
+ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
+ struct ext4_filename *fname,
+ struct ext4_dir_entry_2 **res_dir)
+@@ -1563,16 +1596,11 @@ static struct dentry *ext4_lookup(struct
+ struct inode *inode;
+ struct ext4_dir_entry_2 *de;
+ struct buffer_head *bh;
+- int err;
+-
+- err = fscrypt_prepare_lookup(dir, dentry, flags);
+- if (err)
+- return ERR_PTR(err);
+
+ if (dentry->d_name.len > EXT4_NAME_LEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+- bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
++ bh = ext4_lookup_entry(dir, dentry, &de);
+ if (IS_ERR(bh))
+ return (struct dentry *) bh;
+ inode = NULL;
+--- a/fs/f2fs/namei.c
++++ b/fs/f2fs/namei.c
+@@ -432,19 +432,23 @@ static struct dentry *f2fs_lookup(struct
+ nid_t ino = -1;
+ int err = 0;
+ unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir));
++ struct fscrypt_name fname;
+
+ trace_f2fs_lookup_start(dir, dentry, flags);
+
+- err = fscrypt_prepare_lookup(dir, dentry, flags);
+- if (err)
+- goto out;
+-
+ if (dentry->d_name.len > F2FS_NAME_LEN) {
+ err = -ENAMETOOLONG;
+ goto out;
+ }
+
+- de = f2fs_find_entry(dir, &dentry->d_name, &page);
++ err = fscrypt_prepare_lookup(dir, dentry, &fname);
++ if (err == -ENOENT)
++ goto out_splice;
++ if (err)
++ goto out;
++ de = __f2fs_find_entry(dir, &fname, &page);
++ fscrypt_free_filename(&fname);
++
+ if (!de) {
+ if (IS_ERR(page)) {
+ err = PTR_ERR(page);
+@@ -484,8 +488,7 @@ static struct dentry *f2fs_lookup(struct
+ }
+ out_splice:
+ new = d_splice_alias(inode, dentry);
+- if (IS_ERR(new))
+- err = PTR_ERR(new);
++ err = PTR_ERR_OR_ZERO(new);
+ trace_f2fs_lookup_end(dir, dentry, ino, err);
+ return new;
+ out_iput:
+--- a/fs/ubifs/dir.c
++++ b/fs/ubifs/dir.c
+@@ -220,11 +220,9 @@ static struct dentry *ubifs_lookup(struc
+
+ dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino);
+
+- err = fscrypt_prepare_lookup(dir, dentry, flags);
+- if (err)
+- return ERR_PTR(err);
+-
+- err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm);
++ err = fscrypt_prepare_lookup(dir, dentry, &nm);
++ if (err == -ENOENT)
++ return d_splice_alias(NULL, dentry);
+ if (err)
+ return ERR_PTR(err);
+
+--- a/include/linux/fscrypt.h
++++ b/include/linux/fscrypt.h
+@@ -32,6 +32,7 @@ struct fscrypt_name {
+ u32 hash;
+ u32 minor_hash;
+ struct fscrypt_str crypto_buf;
++ bool is_ciphertext_name;
+ };
+
+ #define FSTR_INIT(n, l) { .name = n, .len = l }
+@@ -138,25 +139,32 @@ static inline int fscrypt_prepare_rename
+ * fscrypt_prepare_lookup - prepare to lookup a name in a possibly-encrypted directory
+ * @dir: directory being searched
+ * @dentry: filename being looked up
+- * @flags: lookup flags
++ * @fname: (output) the name to use to search the on-disk directory
+ *
+- * Prepare for ->lookup() in a directory which may be encrypted. Lookups can be
+- * done with or without the directory's encryption key; without the key,
++ * Prepare for ->lookup() in a directory which may be encrypted by determining
++ * the name that will actually be used to search the directory on-disk. Lookups
++ * can be done with or without the directory's encryption key; without the key,
+ * filenames are presented in encrypted form. Therefore, we'll try to set up
+ * the directory's encryption key, but even without it the lookup can continue.
+ *
+ * This also installs a custom ->d_revalidate() method which will invalidate the
+ * dentry if it was created without the key and the key is later added.
+ *
+- * Return: 0 on success, -errno if a problem occurred while setting up the
+- * encryption key
++ * Return: 0 on success; -ENOENT if key is unavailable but the filename isn't a
++ * correctly formed encoded ciphertext name, so a negative dentry should be
++ * created; or another -errno code.
+ */
+ static inline int fscrypt_prepare_lookup(struct inode *dir,
+ struct dentry *dentry,
+- unsigned int flags)
++ struct fscrypt_name *fname)
+ {
+ if (IS_ENCRYPTED(dir))
+- return __fscrypt_prepare_lookup(dir, dentry);
++ return __fscrypt_prepare_lookup(dir, dentry, fname);
++
++ memset(fname, 0, sizeof(*fname));
++ fname->usr_fname = &dentry->d_name;
++ fname->disk_name.name = (unsigned char *)dentry->d_name.name;
++ fname->disk_name.len = dentry->d_name.len;
+ return 0;
+ }
+
+--- a/include/linux/fscrypt_notsupp.h
++++ b/include/linux/fscrypt_notsupp.h
+@@ -112,7 +112,7 @@ static inline int fscrypt_setup_filename
+ if (IS_ENCRYPTED(dir))
+ return -EOPNOTSUPP;
+
+- memset(fname, 0, sizeof(struct fscrypt_name));
++ memset(fname, 0, sizeof(*fname));
+ fname->usr_fname = iname;
+ fname->disk_name.name = (unsigned char *)iname->name;
+ fname->disk_name.len = iname->len;
+@@ -199,7 +199,8 @@ static inline int __fscrypt_prepare_rena
+ }
+
+ static inline int __fscrypt_prepare_lookup(struct inode *dir,
+- struct dentry *dentry)
++ struct dentry *dentry,
++ struct fscrypt_name *fname)
+ {
+ return -EOPNOTSUPP;
+ }
+--- a/include/linux/fscrypt_supp.h
++++ b/include/linux/fscrypt_supp.h
+@@ -191,7 +191,8 @@ extern int __fscrypt_prepare_rename(stru
+ struct inode *new_dir,
+ struct dentry *new_dentry,
+ unsigned int flags);
+-extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry);
++extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
++ struct fscrypt_name *fname);
+ extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
+ unsigned int max_len,
+ struct fscrypt_str *disk_link);
--- /dev/null
+From foo@baz Sun Nov 1 11:35:18 AM CET 2020
+From: Eric Biggers <ebiggers@kernel.org>
+Date: Sat, 31 Oct 2020 15:05:52 -0700
+Subject: fscrypt: only set dentry_operations on ciphertext dentries
+To: stable@vger.kernel.org
+Cc: linux-fscrypt@vger.kernel.org, linux-ext4@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net, linux-mtd@lists.infradead.org, Theodore Ts'o <tytso@mit.edu>
+Message-ID: <20201031220553.1085782-5-ebiggers@kernel.org>
+
+From: Eric Biggers <ebiggers@google.com>
+
+commit d456a33f041af4b54f3ce495a86d00c246165032 upstream.
+
+Plaintext dentries are always valid, so only set fscrypt_d_ops on
+ciphertext dentries.
+
+Besides marginally improved performance, this allows overlayfs to use an
+fscrypt-encrypted upperdir, provided that all the following are true:
+
+ (1) The fscrypt encryption key is placed in the keyring before
+ mounting overlayfs, and remains while the overlayfs is mounted.
+
+ (2) The overlayfs workdir uses the same encryption policy.
+
+ (3) No dentries for the ciphertext names of subdirectories have been
+ created in the upperdir or workdir yet. (Since otherwise
+ d_splice_alias() will reuse the old dentry with ->d_op set.)
+
+One potential use case is using an ephemeral encryption key to encrypt
+all files created or changed by a container, so that they can be
+securely erased ("crypto-shredded") after the container stops.
+
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/crypto/hooks.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/fs/crypto/hooks.c
++++ b/fs/crypto/hooks.c
+@@ -115,9 +115,8 @@ int __fscrypt_prepare_lookup(struct inod
+ spin_lock(&dentry->d_lock);
+ dentry->d_flags |= DCACHE_ENCRYPTED_NAME;
+ spin_unlock(&dentry->d_lock);
++ d_set_d_op(dentry, &fscrypt_d_ops);
+ }
+-
+- d_set_d_op(dentry, &fscrypt_d_ops);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup);
--- /dev/null
+From f5e55e777cc93eae1416f0fa4908e8846b6d7825 Mon Sep 17 00:00:00 2001
+From: Eric Biggers <ebiggers@google.com>
+Date: Tue, 22 Jan 2019 16:20:21 -0800
+Subject: fscrypt: return -EXDEV for incompatible rename or link into encrypted dir
+
+From: Eric Biggers <ebiggers@google.com>
+
+commit f5e55e777cc93eae1416f0fa4908e8846b6d7825 upstream.
+
+Currently, trying to rename or link a regular file, directory, or
+symlink into an encrypted directory fails with EPERM when the source
+file is unencrypted or is encrypted with a different encryption policy,
+and is on the same mountpoint. It is correct for the operation to fail,
+but the choice of EPERM breaks tools like 'mv' that know to copy rather
+than rename if they see EXDEV, but don't know what to do with EPERM.
+
+Our original motivation for EPERM was to encourage users to securely
+handle their data. Encrypting files by "moving" them into an encrypted
+directory can be insecure because the unencrypted data may remain in
+free space on disk, where it can later be recovered by an attacker.
+It's much better to encrypt the data from the start, or at least try to
+securely delete the source data e.g. using the 'shred' program.
+
+However, the current behavior hasn't been effective at achieving its
+goal because users tend to be confused, hack around it, and complain;
+see e.g. https://github.com/google/fscrypt/issues/76. And in some cases
+it's actually inconsistent or unnecessary. For example, 'mv'-ing files
+between differently encrypted directories doesn't work even in cases
+where it can be secure, such as when in userspace the same passphrase
+protects both directories. Yet, you *can* already 'mv' unencrypted
+files into an encrypted directory if the source files are on a different
+mountpoint, even though doing so is often insecure.
+
+There are probably better ways to teach users to securely handle their
+files. For example, the 'fscrypt' userspace tool could provide a
+command that migrates unencrypted files into an encrypted directory,
+acting like 'shred' on the source files and providing appropriate
+warnings depending on the type of the source filesystem and disk.
+
+Receiving errors on unimportant files might also force some users to
+disable encryption, thus making the behavior counterproductive. It's
+desirable to make encryption as unobtrusive as possible.
+
+Therefore, change the error code from EPERM to EXDEV so that tools
+looking for EXDEV will fall back to a copy.
+
+This, of course, doesn't prevent users from still doing the right things
+to securely manage their files. Note that this also matches the
+behavior when a file is renamed between two project quota hierarchies;
+so there's precedent for using EXDEV for things other than mountpoints.
+
+xfstests generic/398 will require an update with this change.
+
+[Rewritten from an earlier patch series by Michael Halcrow.]
+
+Cc: Michael Halcrow <mhalcrow@google.com>
+Cc: Joe Richey <joerichey@google.com>
+Signed-off-by: Eric Biggers <ebiggers@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ Documentation/filesystems/fscrypt.rst | 12 ++++++++++--
+ fs/crypto/hooks.c | 6 +++---
+ fs/crypto/policy.c | 3 +--
+ include/linux/fscrypt.h | 4 ++--
+ 4 files changed, 16 insertions(+), 9 deletions(-)
+
+--- a/Documentation/filesystems/fscrypt.rst
++++ b/Documentation/filesystems/fscrypt.rst
+@@ -426,10 +426,18 @@ astute users may notice some differences
+ - Unencrypted files, or files encrypted with a different encryption
+ policy (i.e. different key, modes, or flags), cannot be renamed or
+ linked into an encrypted directory; see `Encryption policy
+- enforcement`_. Attempts to do so will fail with EPERM. However,
++ enforcement`_. Attempts to do so will fail with EXDEV. However,
+ encrypted files can be renamed within an encrypted directory, or
+ into an unencrypted directory.
+
++ Note: "moving" an unencrypted file into an encrypted directory, e.g.
++ with the `mv` program, is implemented in userspace by a copy
++ followed by a delete. Be aware that the original unencrypted data
++ may remain recoverable from free space on the disk; prefer to keep
++ all files encrypted from the very beginning. The `shred` program
++ may be used to overwrite the source files but isn't guaranteed to be
++ effective on all filesystems and storage devices.
++
+ - Direct I/O is not supported on encrypted files. Attempts to use
+ direct I/O on such files will fall back to buffered I/O.
+
+@@ -516,7 +524,7 @@ not be encrypted.
+ Except for those special files, it is forbidden to have unencrypted
+ files, or files encrypted with a different encryption policy, in an
+ encrypted directory tree. Attempts to link or rename such a file into
+-an encrypted directory will fail with EPERM. This is also enforced
++an encrypted directory will fail with EXDEV. This is also enforced
+ during ->lookup() to provide limited protection against offline
+ attacks that try to disable or downgrade encryption in known locations
+ where applications may later write sensitive data. It is recommended
+--- a/fs/crypto/hooks.c
++++ b/fs/crypto/hooks.c
+@@ -58,7 +58,7 @@ int __fscrypt_prepare_link(struct inode
+ return err;
+
+ if (!fscrypt_has_permitted_context(dir, inode))
+- return -EPERM;
++ return -EXDEV;
+
+ return 0;
+ }
+@@ -82,13 +82,13 @@ int __fscrypt_prepare_rename(struct inod
+ if (IS_ENCRYPTED(new_dir) &&
+ !fscrypt_has_permitted_context(new_dir,
+ d_inode(old_dentry)))
+- return -EPERM;
++ return -EXDEV;
+
+ if ((flags & RENAME_EXCHANGE) &&
+ IS_ENCRYPTED(old_dir) &&
+ !fscrypt_has_permitted_context(old_dir,
+ d_inode(new_dentry)))
+- return -EPERM;
++ return -EXDEV;
+ }
+ return 0;
+ }
+--- a/fs/crypto/policy.c
++++ b/fs/crypto/policy.c
+@@ -153,8 +153,7 @@ EXPORT_SYMBOL(fscrypt_ioctl_get_policy);
+ * malicious offline violations of this constraint, while the link and rename
+ * checks are needed to prevent online violations of this constraint.
+ *
+- * Return: 1 if permitted, 0 if forbidden. If forbidden, the caller must fail
+- * the filesystem operation with EPERM.
++ * Return: 1 if permitted, 0 if forbidden.
+ */
+ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child)
+ {
+--- a/include/linux/fscrypt.h
++++ b/include/linux/fscrypt.h
+@@ -89,7 +89,7 @@ static inline int fscrypt_require_key(st
+ * in an encrypted directory tree use the same encryption policy.
+ *
+ * Return: 0 on success, -ENOKEY if the directory's encryption key is missing,
+- * -EPERM if the link would result in an inconsistent encryption policy, or
++ * -EXDEV if the link would result in an inconsistent encryption policy, or
+ * another -errno code.
+ */
+ static inline int fscrypt_prepare_link(struct dentry *old_dentry,
+@@ -119,7 +119,7 @@ static inline int fscrypt_prepare_link(s
+ * We also verify that the rename will not violate the constraint that all files
+ * in an encrypted directory tree use the same encryption policy.
+ *
+- * Return: 0 on success, -ENOKEY if an encryption key is missing, -EPERM if the
++ * Return: 0 on success, -ENOKEY if an encryption key is missing, -EXDEV if the
+ * rename would cause inconsistent encryption policies, or another -errno code.
+ */
+ static inline int fscrypt_prepare_rename(struct inode *old_dir,
mtd-lpddr-fix-bad-logic-in-print_drs_error.patch
serial-pl011-fix-lockdep-splat-when-handling-magic-sysrq-interrupt.patch
ata-sata_rcar-fix-dma-boundary-mask.patch
+fscrypt-return-exdev-for-incompatible-rename-or-link-into-encrypted-dir.patch
+fscrypt-clean-up-and-improve-dentry-revalidation.patch
+fscrypt-fix-race-allowing-rename-and-link-of-ciphertext-dentries.patch
+fs-fscrypt-clear-dcache_encrypted_name-when-unaliasing-directory.patch
+fscrypt-only-set-dentry_operations-on-ciphertext-dentries.patch
+fscrypt-fix-race-where-lookup-marks-plaintext-dentry-as-ciphertext.patch