Hi,
This is the v5 of this patchset. Thanks Christian and Eric for you
review.
In this version, the patchset grew a bit because it adds:
- a preparation patch to merge d_revalidate_name and d_revalidate,
attending Christian's request. The original patch is now touching
several filesystems to update the hook signature, so it has grown
quite a bit. But, all of that was autogenerated with coccinelle and
tested with allyesconfig only.
- A new patch to expose a helper from libfs that I use in ecryptfs.
- Code to prevent ecryptfs from mounting on top of casefolded
directories. Also following on Christian's review.
Other than these, there are some minor fixes, listed in each patch
changelog, and more clarifications on the locking, thanks to the
excellent feedback from Eric.
Eric, I believe I have covered the cases where instantiation can happen
and I don't think it is possible for a dentry to become positive amidst
the d_revalidation, since we are holding the inode parent lock to
synchronize with creations. Please let me know if you find anything
else.
Finally, I've dropped the r-b tags from patch "fs: Expose name under
lookup to d_revalidate hooks", because it changed too much since
the time of review.
Regarding testing, I verified it doesn't regress fstests for f2fs and
ext4, verified building all filesystems work fine with allyesconfig,
even with variation of CONFIG_FS_ENCRYPTION and CONFIG_UNICODE, and,
finally, I checked I wasn't able to mount or lookup casefolded
directories with ecryptfs and overlayfs.
Thanks,
Gabriel Krisman Bertazi (10):
fs: Expose helper to check if a directory needs casefolding
ecryptfs: Reject casefold directory inodes
9p: Split ->weak_revalidate from ->revalidate
fs: Expose name under lookup to d_revalidate hooks
fs: Add DCACHE_CASEFOLDED_NAME flag
libfs: Validate negative dentries in case-insensitive directories
libfs: Chain encryption checks after case-insensitive revalidation
libfs: Merge encrypted_ci_dentry_ops and ci_dentry_ops
ext4: Enable negative dentries on case-insensitive lookup
f2fs: Enable negative dentries on case-insensitive lookup
Documentation/filesystems/locking.rst | 3 +-
Documentation/filesystems/vfs.rst | 11 ++-
fs/9p/vfs_dentry.c | 11 ++-
fs/afs/dir.c | 6 +-
fs/afs/dynroot.c | 4 +-
fs/ceph/dir.c | 3 +-
fs/coda/dir.c | 3 +-
fs/crypto/fname.c | 3 +-
fs/dcache.c | 8 ++
fs/ecryptfs/dentry.c | 5 +-
fs/ecryptfs/inode.c | 8 ++
fs/exfat/namei.c | 3 +-
fs/ext4/namei.c | 35 +-------
fs/f2fs/namei.c | 25 +-----
fs/fat/namei_vfat.c | 6 +-
fs/fuse/dir.c | 3 +-
fs/gfs2/dentry.c | 3 +-
fs/hfs/sysdep.c | 3 +-
fs/jfs/namei.c | 3 +-
fs/kernfs/dir.c | 3 +-
fs/libfs.c | 124 +++++++++++++++++---------
fs/namei.c | 18 ++--
fs/nfs/dir.c | 9 +-
fs/ocfs2/dcache.c | 4 +-
fs/orangefs/dcache.c | 3 +-
fs/overlayfs/super.c | 20 +++--
fs/proc/base.c | 6 +-
fs/proc/fd.c | 3 +-
fs/proc/generic.c | 6 +-
fs/proc/proc_sysctl.c | 3 +-
fs/reiserfs/xattr.c | 3 +-
fs/smb/client/dir.c | 3 +-
fs/vboxsf/dir.c | 4 +-
include/linux/dcache.h | 10 ++-
include/linux/fs.h | 21 +++++
include/linux/fscrypt.h | 4 +-
36 files changed, 242 insertions(+), 148 deletions(-)
--
2.41.0
From: Gabriel Krisman Bertazi <[email protected]>
Now that casefold needs d_revalidate and calls fscrypt_d_revalidate
itself, generic_encrypt_ci_dentry_ops and generic_ci_dentry_ops are now
equivalent. Merge them together and simplify the setup code.
Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
---
changes since v2:
- reword comment for clarity (Eric)
---
fs/libfs.c | 45 +++++++++++++--------------------------------
1 file changed, 13 insertions(+), 32 deletions(-)
diff --git a/fs/libfs.c b/fs/libfs.c
index efb245118d10..6b15a4f0312f 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1516,7 +1516,7 @@ static int generic_ci_d_revalidate(struct dentry *dentry,
return fscrypt_d_revalidate(dentry, name, flags);
}
-static const struct dentry_operations generic_ci_dentry_ops = {
+static const struct dentry_operations generic_encrypted_ci_dentry_ops = {
.d_hash = generic_ci_d_hash,
.d_compare = generic_ci_d_compare,
.d_revalidate = generic_ci_d_revalidate,
@@ -1529,26 +1529,19 @@ static const struct dentry_operations generic_encrypted_dentry_ops = {
};
#endif
-#if defined(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_UNICODE)
-static const struct dentry_operations generic_encrypted_ci_dentry_ops = {
- .d_hash = generic_ci_d_hash,
- .d_compare = generic_ci_d_compare,
- .d_revalidate = generic_ci_d_revalidate,
-};
-#endif
-
/**
* generic_set_encrypted_ci_d_ops - helper for setting d_ops for given dentry
* @dentry: dentry to set ops on
*
- * Casefolded directories need d_hash and d_compare set, so that the dentries
- * contained in them are handled case-insensitively. Note that these operations
- * are needed on the parent directory rather than on the dentries in it, and
- * while the casefolding flag can be toggled on and off on an empty directory,
- * dentry_operations can't be changed later. As a result, if the filesystem has
- * casefolding support enabled at all, we have to give all dentries the
- * casefolding operations even if their inode doesn't have the casefolding flag
- * currently (and thus the casefolding ops would be no-ops for now).
+ * Casefolded directories need some dentry_operations set, so that the dentries
+ * contained in them are handled case-insensitively. Note that d_hash and
+ * d_compare are needed on the parent directory rather than on the dentries in
+ * it, and while the casefolding flag can be toggled on and off on an empty
+ * directory, dentry_operations can't be changed later. As a result, if the
+ * filesystem has casefolding support enabled at all, we have to give all
+ * dentries the casefolding operations even if their inode doesn't have the
+ * casefolding flag currently (and thus the casefolding ops would be no-ops for
+ * now).
*
* Encryption works differently in that the only dentry operation it needs is
* d_revalidate, which it only needs on dentries that have the no-key name flag.
@@ -1557,34 +1550,22 @@ static const struct dentry_operations generic_encrypted_ci_dentry_ops = {
* Finally, to maximize compatibility with overlayfs (which isn't compatible
* with certain dentry operations) and to avoid taking an unnecessary
* performance hit, we use custom dentry_operations for each possible
- * combination rather than always installing all operations.
+ * combination of operations rather than always installing them.
*/
void generic_set_encrypted_ci_d_ops(struct dentry *dentry)
{
-#ifdef CONFIG_FS_ENCRYPTION
- bool needs_encrypt_ops = dentry->d_flags & DCACHE_NOKEY_NAME;
-#endif
#if IS_ENABLED(CONFIG_UNICODE)
- bool needs_ci_ops = dentry->d_sb->s_encoding;
-#endif
-#if defined(CONFIG_FS_ENCRYPTION) && IS_ENABLED(CONFIG_UNICODE)
- if (needs_encrypt_ops && needs_ci_ops) {
+ if (dentry->d_sb->s_encoding) {
d_set_d_op(dentry, &generic_encrypted_ci_dentry_ops);
return;
}
#endif
#ifdef CONFIG_FS_ENCRYPTION
- if (needs_encrypt_ops) {
+ if (dentry->d_flags & DCACHE_NOKEY_NAME) {
d_set_d_op(dentry, &generic_encrypted_dentry_ops);
return;
}
#endif
-#if IS_ENABLED(CONFIG_UNICODE)
- if (needs_ci_ops) {
- d_set_d_op(dentry, &generic_ci_dentry_ops);
- return;
- }
-#endif
}
EXPORT_SYMBOL(generic_set_encrypted_ci_d_ops);
--
2.41.0
Even though it seems to be able to resolve some names of
case-insensitive directories, the lack of d_hash and d_compare means we
end up with a broken state in the d_cache. Considering it was never a
goal to support these two together, and we are preparing to use
d_revalidate in case-insensitive filesystems, which would make the
combination even more broken, reject any attempt to get a casefolded
inode from ecryptfs.
Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
---
fs/ecryptfs/inode.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 83274915ba6d..1305dc49df78 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -78,6 +78,14 @@ static struct inode *__ecryptfs_get_inode(struct inode *lower_inode,
if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb))
return ERR_PTR(-EXDEV);
+
+ /* Reject dealing with casefold directories. */
+ if (S_ISDIR(lower_inode->i_mode) && dir_is_casefolded(lower_inode)) {
+ pr_err_ratelimited("%s: Can't handle casefolded directory.\n",
+ __func__);
+ return ERR_PTR(-EREMOTE);
+ }
+
if (!igrab(lower_inode))
return ERR_PTR(-ESTALE);
inode = iget5_locked(sb, (unsigned long)lower_inode,
--
2.41.0
From: Gabriel Krisman Bertazi <[email protected]>
Instead of invalidating negative dentries during case-insensitive
lookups, mark them as such and let them be added to the dcache.
d_ci_revalidate is able to properly filter them out if necessary based
on the dentry casefold flag.
Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
---
Changes since v4:
- Use helper to decide if should set dentry flag.
Changes since v2:
- Move dentry flag set closer to fscrypt code (Eric)
---
fs/f2fs/namei.c | 25 ++++---------------------
1 file changed, 4 insertions(+), 21 deletions(-)
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index bee0568888da..8b8fd4cdf62d 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -533,6 +533,10 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
err = f2fs_prepare_lookup(dir, dentry, &fname);
generic_set_encrypted_ci_d_ops(dentry);
+
+ if (dir_is_casefolded(dir))
+ d_set_casefolded_name(dentry);
+
if (err == -ENOENT)
goto out_splice;
if (err)
@@ -578,17 +582,6 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
goto out_iput;
}
out_splice:
-#if IS_ENABLED(CONFIG_UNICODE)
- if (!inode && IS_CASEFOLDED(dir)) {
- /* Eventually we want to call d_add_ci(dentry, NULL)
- * for negative dentries in the encoding case as
- * well. For now, prevent the negative dentry
- * from being cached.
- */
- trace_f2fs_lookup_end(dir, dentry, ino, err);
- return NULL;
- }
-#endif
new = d_splice_alias(inode, dentry);
trace_f2fs_lookup_end(dir, !IS_ERR_OR_NULL(new) ? new : dentry,
ino, IS_ERR(new) ? PTR_ERR(new) : err);
@@ -641,16 +634,6 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
f2fs_delete_entry(de, page, dir, inode);
f2fs_unlock_op(sbi);
-#if IS_ENABLED(CONFIG_UNICODE)
- /* VFS negative dentries are incompatible with Encoding and
- * Case-insensitiveness. Eventually we'll want avoid
- * invalidating the dentries here, alongside with returning the
- * negative dentries at f2fs_lookup(), when it is better
- * supported by the VFS for the CI case.
- */
- if (IS_CASEFOLDED(dir))
- d_invalidate(dentry);
-#endif
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
fail:
--
2.41.0
From: Gabriel Krisman Bertazi <[email protected]>
Support encrypted dentries in generic_ci_d_revalidate by chaining
fscrypt_d_revalidate at the tail of the d_revalidate. This allows
filesystem to just call generic_ci_d_revalidate and let it handle any
case-insensitive dentry (encrypted or not).
Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
---
Changes since v2:
- Enable negative dentries of encrypted filesystems (Eric)
---
fs/libfs.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/fs/libfs.c b/fs/libfs.c
index cb98c4721327..efb245118d10 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1452,9 +1452,8 @@ static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
return 0;
}
-static int generic_ci_d_revalidate(struct dentry *dentry,
- const struct qstr *name,
- unsigned int flags)
+static int ci_d_revalidate(struct dentry *dentry, const struct qstr *name,
+ unsigned int flags)
{
const struct dentry *parent;
const struct inode *dir;
@@ -1508,6 +1507,15 @@ static int generic_ci_d_revalidate(struct dentry *dentry,
return 1;
}
+static int generic_ci_d_revalidate(struct dentry *dentry,
+ const struct qstr *name,
+ unsigned int flags)
+{
+ if (!ci_d_revalidate(dentry, name, flags))
+ return 0;
+ return fscrypt_d_revalidate(dentry, name, flags);
+}
+
static const struct dentry_operations generic_ci_dentry_ops = {
.d_hash = generic_ci_d_hash,
.d_compare = generic_ci_d_compare,
@@ -1525,7 +1533,7 @@ static const struct dentry_operations generic_encrypted_dentry_ops = {
static const struct dentry_operations generic_encrypted_ci_dentry_ops = {
.d_hash = generic_ci_d_hash,
.d_compare = generic_ci_d_compare,
- .d_revalidate = fscrypt_d_revalidate,
+ .d_revalidate = generic_ci_d_revalidate,
};
#endif
--
2.41.0