linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint
@ 2018-05-22 20:38 Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 01/13] vfs: Add support for mounting with MS_CASEFOLD Gabriel Krisman Bertazi
                   ` (12 more replies)
  0 siblings, 13 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

Hi,

[[Just for recording at the ML.  i am not proposing it for merge with
the mountpoint design.]]

Just for completeness, this is the code I mentioned at LSFMM, which
implements case-insensitive lookups as an attribute of the mountpoint,
allowing support for simultaneous case-insensitive and senstive views of
the filesystem.

It supports a single ci mountpoint like this:

  mount -o ignorecase,encoding=utf8n-10.0.0 /dev/vda /mnt

It allows simultaneous views via a bind,remount, by supporting the
parameter ignorecase.  Like this:

  mount -o ignorecase,encoding=utf8n-10.0.0 /dev/vda /mnt
  mount -o bind /mnt /mnt-ci
  mount -o remount,bind,ignorecase none /mnt-ci

This patchset also enables ext4 ci support, which requires the utf8n
implementation I submitted on the other patchset.  utf8n is the only
encoding supported by this ext4 code, but ascii can be trivially
supported with a short patch to implement casefold/strcmp operations.
Attempts to mount with other encodings will succeed but will trigger
failures when attempting to access the filesystem.  This could be
improved at the fs layer to provide an error message at mount time.

The patchset for utf8n is awaiting review for merge at:
  https://marc.info/?l=linux-fsdevel&m=152692428419075&w=2

Since the vfs/ext4 support in this patchset is just for the record, and
I am not proposing to push this patchset current state anymore, feel
free to ignore review for this.

If you want an easy way to fetch all the changes required to build this
code, including the ucd source files used to generate the decodification
tree, you can fetch directly from:

  https://gitlab.collabora.com/krisman/linux.git -b vfs-ms_casefold-smart

Gabriel Krisman Bertazi (13):
  vfs: Add support for mounting with MS_CASEFOLD
  vfs: Propagate LOOKUP_CASEFOLD after doing parent lookups
  vfs: Add support for positive dentries CI lookups in the dentry cache
  vfs: Allow bind,remount with MS_CASEFOLD
  vfs: Handle case-exact lookup in d_add_ci
  vfs: Add Support for hard negative dentries in the dcache
  vfs: Handle MNT_CASEFOLD in /proc/mounts
  fscrypt: Introduce charset-based matching functions
  ext4: Include encoding information on the superblock
  ext4: Plumb LOOK_CASEFOLD up to ext4 comparison functions
  ext4: Support charset name matching
  ext4: Implement ext4 dcache hooks for custom charsets
  ext4: Notify VFS of support for casefolded mountpoints

 fs/dcache.c                     | 95 +++++++++++++++++++++++---------
 fs/ext4/dir.c                   | 48 ++++++++++++++++
 fs/ext4/ext4.h                  |  9 ++-
 fs/ext4/inline.c                |  7 ++-
 fs/ext4/namei.c                 | 93 +++++++++++++++++++++----------
 fs/ext4/super.c                 | 49 ++++++++++++++++-
 fs/namei.c                      | 98 +++++++++++++++++++++++++++++----
 fs/namespace.c                  | 34 +++++++++---
 fs/proc_namespace.c             |  1 +
 include/linux/dcache.h          | 24 ++++++--
 include/linux/fs.h              |  2 +
 include/linux/fscrypt.h         |  1 +
 include/linux/fscrypt_notsupp.h | 16 ++++++
 include/linux/fscrypt_supp.h    | 27 +++++++++
 include/linux/mount.h           |  1 +
 include/linux/namei.h           |  1 +
 include/uapi/linux/fs.h         |  1 +
 17 files changed, 423 insertions(+), 84 deletions(-)

-- 
2.17.0

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 01/13] vfs: Add support for mounting with MS_CASEFOLD
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 02/13] vfs: Propagate LOOKUP_CASEFOLD after doing parent lookups Gabriel Krisman Bertazi
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

At this point, negative dentries are ignored for LOOKUP_CASEFOLD.  This
will be addressed later in the series.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/namei.c              | 27 +++++++++++++++++++++++++--
 fs/namespace.c          | 11 ++++++++++-
 include/linux/fs.h      |  2 ++
 include/linux/mount.h   |  1 +
 include/linux/namei.h   |  1 +
 include/uapi/linux/fs.h |  1 +
 6 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 921ae32dbc80..ddd5c9e9ab3c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -761,8 +761,15 @@ static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
 {
 	if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE))
 		return dentry->d_op->d_revalidate(dentry, flags);
-	else
-		return 1;
+
+	/* Completely ignore negative dentries
+	   for LOOKUP_CASEFOLD. */
+	if (unlikely(flags & LOOKUP_CASEFOLD)) {
+		if (d_is_negative(dentry))
+			return 0;
+	}
+
+	return 1;
 }
 
 /**
@@ -1159,6 +1166,15 @@ static int follow_automount(struct path *path, struct nameidata *nd,
 
 }
 
+static void set_casefold_lookup(struct nameidata *nd, struct path *path)
+{
+	if (path && path->mnt &&
+	    path->mnt->mnt_flags & MNT_CASEFOLD)
+		nd->flags |= LOOKUP_CASEFOLD;
+	else
+		nd->flags &= ~LOOKUP_CASEFOLD;
+}
+
 /*
  * Handle a dentry that is managed in some way.
  * - Flagged for transit management (autofs)
@@ -1202,6 +1218,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
 				path->mnt = mounted;
 				path->dentry = dget(mounted->mnt_root);
 				need_mntput = true;
+				set_casefold_lookup(nd, path);
 				continue;
 			}
 
@@ -1288,6 +1305,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
 		path->mnt = &mounted->mnt;
 		path->dentry = mounted->mnt.mnt_root;
 		nd->flags |= LOOKUP_JUMPED;
+		set_casefold_lookup(nd, path);
 		*seqp = read_seqcount_begin(&path->dentry->d_seq);
 		/*
 		 * Update the inode too. We don't need to re-check the
@@ -1336,6 +1354,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
 			nd->path.mnt = &mparent->mnt;
 			inode = inode2;
 			nd->seq = seq;
+			set_casefold_lookup(nd, &nd->path);
 		}
 	}
 	while (unlikely(d_mountpoint(nd->path.dentry))) {
@@ -1350,6 +1369,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
 		inode = nd->path.dentry->d_inode;
 		nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
 	}
+	set_casefold_lookup(nd, &nd->path);
 	nd->inode = inode;
 	return 0;
 }
@@ -2143,6 +2163,7 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 		} else {
 			path_get(&nd->path);
 		}
+		set_casefold_lookup(nd, &nd->path);
 		return s;
 	}
 
@@ -2177,6 +2198,7 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 			get_fs_pwd(current->fs, &nd->path);
 			nd->inode = nd->path.dentry->d_inode;
 		}
+		set_casefold_lookup(nd, &nd->path);
 		return s;
 	} else {
 		/* Caller must check execute permissions on the starting path component */
@@ -2205,6 +2227,7 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 			nd->inode = nd->path.dentry->d_inode;
 		}
 		fdput(f);
+		set_casefold_lookup(nd, &nd->path);
 		return s;
 	}
 }
diff --git a/fs/namespace.c b/fs/namespace.c
index 9d1374ab6e06..0053e29b73cc 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2509,6 +2509,12 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
 	if (!type)
 		return -ENODEV;
 
+	if (mnt_flags & MNT_CASEFOLD &&
+	    !(type->fs_flags & FS_HAS_CASEFOLD)) {
+		put_filesystem(type);
+		return -EINVAL;
+	}
+
 	mnt = vfs_kern_mount(type, sb_flags, name, data);
 	if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
 	    !mnt->mnt_sb->s_subtype)
@@ -2809,6 +2815,8 @@ long do_mount(const char *dev_name, const char __user *dir_name,
 		mnt_flags |= MNT_NODIRATIME;
 	if (flags & MS_STRICTATIME)
 		mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
+	if (flags & MS_CASEFOLD)
+		mnt_flags |= MNT_CASEFOLD;
 	if (flags & SB_RDONLY)
 		mnt_flags |= MNT_READONLY;
 
@@ -2827,7 +2835,8 @@ long do_mount(const char *dev_name, const char __user *dir_name,
 			    SB_SILENT |
 			    SB_POSIXACL |
 			    SB_LAZYTIME |
-			    SB_I_VERSION);
+			    SB_I_VERSION |
+			    SB_CASEFOLD);
 
 	if (flags & MS_REMOUNT)
 		retval = do_remount(&path, flags, sb_flags, mnt_flags,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 2a815560fda0..743d38876a51 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1287,6 +1287,7 @@ extern int send_sigurg(struct fown_struct *fown);
 #define SB_SYNCHRONOUS	16	/* Writes are synced at once */
 #define SB_MANDLOCK	64	/* Allow mandatory locks on an FS */
 #define SB_DIRSYNC	128	/* Directory modifications are synchronous */
+#define SB_CASEFOLD	256	/* Directory lookups are case-insensitive */
 #define SB_NOATIME	1024	/* Do not update access times. */
 #define SB_NODIRATIME	2048	/* Do not update directory access times */
 #define SB_SILENT	32768
@@ -2069,6 +2070,7 @@ struct file_system_type {
 #define FS_BINARY_MOUNTDATA	2
 #define FS_HAS_SUBTYPE		4
 #define FS_USERNS_MOUNT		8	/* Can be mounted by userns root */
+#define FS_HAS_CASEFOLD	        16
 #define FS_RENAME_DOES_D_MOVE	32768	/* FS will handle d_move() during rename() internally. */
 	struct dentry *(*mount) (struct file_system_type *, int,
 		       const char *, void *);
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 45b1f56c6c2f..05f5d6f71108 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -52,6 +52,7 @@ struct mnt_namespace;
 			    MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED)
 
 #define MNT_INTERNAL	0x4000
+#define MNT_CASEFOLD	0x8000
 
 #define MNT_LOCK_ATIME		0x040000
 #define MNT_LOCK_NOEXEC		0x080000
diff --git a/include/linux/namei.h b/include/linux/namei.h
index a982bb7cd480..fb74ab4c86f3 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -46,6 +46,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_ROOT		0x2000
 #define LOOKUP_EMPTY		0x4000
 #define LOOKUP_DOWN		0x8000
+#define LOOKUP_CASEFOLD	      0x010000
 
 extern int path_pts(struct path *path);
 
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index d2a8313fabd7..7fa23e5f49d6 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -113,6 +113,7 @@ struct inodes_stat_t {
 #define MS_REMOUNT	32	/* Alter flags of a mounted FS */
 #define MS_MANDLOCK	64	/* Allow mandatory locks on an FS */
 #define MS_DIRSYNC	128	/* Directory modifications are synchronous */
+#define MS_CASEFOLD	256	/* Directory lookups are case-insensitive */
 #define MS_NOATIME	1024	/* Do not update access times. */
 #define MS_NODIRATIME	2048	/* Do not update directory access times */
 #define MS_BIND		4096
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 02/13] vfs: Propagate LOOKUP_CASEFOLD after doing parent lookups
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 01/13] vfs: Add support for mounting with MS_CASEFOLD Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 03/13] vfs: Add support for positive dentries CI lookups in the dentry cache Gabriel Krisman Bertazi
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

filename_parentat() is executed in order to quickly access a child
dentry for creation, unlinking or rename, outside the path_walk path.
Although, if the parent was accessed via a CI mount, we need to make
sure the child will also be searched in a CI manner, so the nameidata
flag from the first search needs to be propageted to the following child
__lookup_hash call.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/namei.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 54 insertions(+), 7 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index ddd5c9e9ab3c..135a9a4e676b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2369,7 +2369,7 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
 
 static struct filename *filename_parentat(int dfd, struct filename *name,
 				unsigned int flags, struct path *parent,
-				struct qstr *last, int *type)
+				struct qstr *last, int *type, bool *foldtree)
 {
 	int retval;
 	struct nameidata nd;
@@ -2385,6 +2385,7 @@ static struct filename *filename_parentat(int dfd, struct filename *name,
 	if (likely(!retval)) {
 		*last = nd.last;
 		*type = nd.last_type;
+		*foldtree = !!(nd.flags & LOOKUP_CASEFOLD);
 		audit_inode(name, parent->dentry, LOOKUP_PARENT);
 	} else {
 		putname(name);
@@ -2401,9 +2402,14 @@ struct dentry *kern_path_locked(const char *name, struct path *path)
 	struct dentry *d;
 	struct qstr last;
 	int type;
+	bool foldtree = false;
 
 	filename = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path,
-				    &last, &type);
+				     &last, &type, &foldtree);
+
+	/* kernel path don't care about foldtree.  Root cannot be folded. */
+	WARN_ON(foldtree);
+
 	if (IS_ERR(filename))
 		return ERR_CAST(filename);
 	if (unlikely(type != LAST_NORM)) {
@@ -3620,6 +3626,7 @@ static struct dentry *filename_create(int dfd, struct filename *name,
 	int err2;
 	int error;
 	bool is_dir = (lookup_flags & LOOKUP_DIRECTORY);
+	bool foldtree = false;
 
 	/*
 	 * Note that only LOOKUP_REVAL and LOOKUP_DIRECTORY matter here. Any
@@ -3627,7 +3634,8 @@ static struct dentry *filename_create(int dfd, struct filename *name,
 	 */
 	lookup_flags &= LOOKUP_REVAL;
 
-	name = filename_parentat(dfd, name, lookup_flags, path, &last, &type);
+	name = filename_parentat(dfd, name, lookup_flags, path, &last,
+				 &type, &foldtree);
 	if (IS_ERR(name))
 		return ERR_CAST(name);
 
@@ -3644,6 +3652,16 @@ static struct dentry *filename_create(int dfd, struct filename *name,
 	 * Do the final lookup.
 	 */
 	lookup_flags |= LOOKUP_CREATE | LOOKUP_EXCL;
+
+	/* If parent is a casefold mountpoint, search for an existing
+	 * dentry with LOOKUP_CASEFOLD.
+	 * LOOKUP_CASEFOLD needs to be explicitly cleared in case it is
+	 * doing retry. */
+	if (foldtree)
+		lookup_flags |= LOOKUP_CASEFOLD;
+	else
+		lookup_flags &= ~LOOKUP_CASEFOLD;
+
 	inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
 	dentry = __lookup_hash(&last, path->dentry, lookup_flags);
 	if (IS_ERR(dentry))
@@ -3904,9 +3922,10 @@ static long do_rmdir(int dfd, const char __user *pathname)
 	struct qstr last;
 	int type;
 	unsigned int lookup_flags = 0;
+	bool foldtree;
 retry:
 	name = filename_parentat(dfd, getname(pathname), lookup_flags,
-				&path, &last, &type);
+				 &path, &last, &type, &foldtree);
 	if (IS_ERR(name))
 		return PTR_ERR(name);
 
@@ -3926,6 +3945,13 @@ static long do_rmdir(int dfd, const char __user *pathname)
 	if (error)
 		goto exit1;
 
+	/* LOOKUP_CASEFOLD needs to be explicitly cleared in case it is
+	 * doing retry. */
+	if (foldtree)
+		lookup_flags |= LOOKUP_CASEFOLD;
+	else
+		lookup_flags &= ~LOOKUP_CASEFOLD;
+
 	inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
 	dentry = __lookup_hash(&last, path.dentry, lookup_flags);
 	error = PTR_ERR(dentry);
@@ -4033,8 +4059,10 @@ long do_unlinkat(int dfd, struct filename *name)
 	struct inode *inode = NULL;
 	struct inode *delegated_inode = NULL;
 	unsigned int lookup_flags = 0;
+	bool foldtree = false;
 retry:
-	name = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
+	name = filename_parentat(dfd, name, lookup_flags, &path, &last, &type,
+				 &foldtree);
 	if (IS_ERR(name))
 		return PTR_ERR(name);
 
@@ -4045,6 +4073,13 @@ long do_unlinkat(int dfd, struct filename *name)
 	error = mnt_want_write(path.mnt);
 	if (error)
 		goto exit1;
+
+	/* LOOKUP_CASEFOLD needs to be explicitly cleared in case it is
+	 * doing retry. */
+	if (foldtree)
+		lookup_flags |= LOOKUP_CASEFOLD;
+	else
+		lookup_flags &= ~LOOKUP_CASEFOLD;
 retry_deleg:
 	inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
 	dentry = __lookup_hash(&last, path.dentry, lookup_flags);
@@ -4514,6 +4549,7 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
 	struct filename *to;
 	unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
 	bool should_retry = false;
+	bool foldtree = false;
 	int error;
 
 	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
@@ -4531,14 +4567,14 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
 
 retry:
 	from = filename_parentat(olddfd, getname(oldname), lookup_flags,
-				&old_path, &old_last, &old_type);
+				 &old_path, &old_last, &old_type, &foldtree);
 	if (IS_ERR(from)) {
 		error = PTR_ERR(from);
 		goto exit;
 	}
 
 	to = filename_parentat(newdfd, getname(newname), lookup_flags,
-				&new_path, &new_last, &new_type);
+			       &new_path, &new_last, &new_type, &foldtree);
 	if (IS_ERR(to)) {
 		error = PTR_ERR(to);
 		goto exit1;
@@ -4561,6 +4597,17 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
 	if (error)
 		goto exit2;
 
+	/* Since rename(2) doesn't work across mountpoints, we are sure
+	 * that foldtree for TO and FROM are the same.
+	 *
+	 * LOOKUP_CASEFOLD needs to be explicitly cleared in case it is
+	 * doing retry. */
+	if (foldtree)
+		lookup_flags |= LOOKUP_CASEFOLD;
+	else
+		lookup_flags &= ~LOOKUP_CASEFOLD;
+
+
 retry_deleg:
 	trap = lock_rename(new_path.dentry, old_path.dentry);
 
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 03/13] vfs: Add support for positive dentries CI lookups in the dentry cache
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 01/13] vfs: Add support for mounting with MS_CASEFOLD Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 02/13] vfs: Propagate LOOKUP_CASEFOLD after doing parent lookups Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 04/13] vfs: Allow bind,remount with MS_CASEFOLD Gabriel Krisman Bertazi
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

Negative dentries are still invalidated, triggering a lookup in the
disk.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/dcache.c            | 43 +++++++++++++++++++++++++++++++++---------
 fs/namei.c             |  5 +++--
 include/linux/dcache.h |  8 ++++++--
 3 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 7c38f39958bc..4bd28d7caa53 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2049,6 +2049,16 @@ struct dentry *d_obtain_root(struct inode *inode)
 }
 EXPORT_SYMBOL(d_obtain_root);
 
+static inline bool d_same_name_ci(const struct dentry *dentry,
+				  const struct dentry *parent,
+				  const struct qstr *name)
+{
+	return parent->d_op->d_compare_ci(dentry,
+					  dentry->d_name.len,
+					  dentry->d_name.name,
+					  name) == 0;
+}
+
 /**
  * d_add_ci - lookup or allocate new dentry with case-exact name
  * @inode:  the inode case-insensitive lookup has found
@@ -2122,6 +2132,7 @@ static inline bool d_same_name(const struct dentry *dentry,
  * @parent: parent dentry
  * @name: qstr of name we wish to find
  * @seqp: returns d_seq value at the point where the dentry was found
+ * @ci_lookup: Whether d_lookup_rcu should do a CI lookup
  * Returns: dentry, or NULL
  *
  * __d_lookup_rcu is the dcache lookup function for rcu-walk name
@@ -2148,7 +2159,7 @@ static inline bool d_same_name(const struct dentry *dentry,
  */
 struct dentry *__d_lookup_rcu(const struct dentry *parent,
 				const struct qstr *name,
-				unsigned *seqp)
+				unsigned *seqp, int ci_lookup)
 {
 	u64 hashlen = name->hash_len;
 	const unsigned char *str = name->name;
@@ -2215,9 +2226,16 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
 				cpu_relax();
 				goto seqretry;
 			}
-			if (parent->d_op->d_compare(dentry,
-						    tlen, tname, name) != 0)
-				continue;
+
+			if (ci_lookup) {
+				if (parent->d_op->d_compare_ci(dentry, tlen,
+							       tname, name) != 0)
+					continue;
+			} else {
+				if (parent->d_op->d_compare(dentry, tlen,
+							    tname, name) != 0)
+					continue;
+			}
 		} else {
 			if (dentry->d_name.hash_len != hashlen)
 				continue;
@@ -2248,7 +2266,7 @@ struct dentry *d_lookup(const struct dentry *parent, const struct qstr *name)
 
 	do {
 		seq = read_seqbegin(&rename_lock);
-		dentry = __d_lookup(parent, name);
+		dentry = __d_lookup(parent, name, 0);
 		if (dentry)
 			break;
 	} while (read_seqretry(&rename_lock, seq));
@@ -2260,6 +2278,7 @@ EXPORT_SYMBOL(d_lookup);
  * __d_lookup - search for a dentry (racy)
  * @parent: parent dentry
  * @name: qstr of name we wish to find
+ * @ci_lookup: Whether d_lookup_rcu should do a CI lookup
  * Returns: dentry, or NULL
  *
  * __d_lookup is like d_lookup, however it may (rarely) return a
@@ -2271,7 +2290,8 @@ EXPORT_SYMBOL(d_lookup);
  *
  * __d_lookup callers must be commented.
  */
-struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
+struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name,
+			  int ci_lookup)
 {
 	unsigned int hash = name->hash;
 	struct hlist_bl_head *b = d_hash(hash);
@@ -2312,8 +2332,13 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
 		if (d_unhashed(dentry))
 			goto next;
 
-		if (!d_same_name(dentry, parent, name))
-			goto next;
+		if (ci_lookup) {
+			if (!d_same_name_ci(dentry, parent, name))
+				goto next;
+		} else {
+			if (!d_same_name(dentry, parent, name))
+				goto next;
+		}
 
 		dentry->d_lockref.count++;
 		found = dentry;
@@ -2476,7 +2501,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
 	rcu_read_lock();
 	seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1;
 	r_seq = read_seqbegin(&rename_lock);
-	dentry = __d_lookup_rcu(parent, name, &d_seq);
+	dentry = __d_lookup_rcu(parent, name, &d_seq, 0);
 	if (unlikely(dentry)) {
 		if (!lockref_get_not_dead(&dentry->d_lockref)) {
 			rcu_read_unlock();
diff --git a/fs/namei.c b/fs/namei.c
index 135a9a4e676b..1ebe5e775a9a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1540,6 +1540,7 @@ static int lookup_fast(struct nameidata *nd,
 	struct dentry *dentry, *parent = nd->path.dentry;
 	int status = 1;
 	int err;
+	int ci_lookup = (nd->flags & LOOKUP_CASEFOLD);
 
 	/*
 	 * Rename seqlock is not required here because in the off chance
@@ -1549,7 +1550,7 @@ static int lookup_fast(struct nameidata *nd,
 	if (nd->flags & LOOKUP_RCU) {
 		unsigned seq;
 		bool negative;
-		dentry = __d_lookup_rcu(parent, &nd->last, &seq);
+		dentry = __d_lookup_rcu(parent, &nd->last, &seq, ci_lookup);
 		if (unlikely(!dentry)) {
 			if (unlazy_walk(nd))
 				return -ECHILD;
@@ -1595,7 +1596,7 @@ static int lookup_fast(struct nameidata *nd,
 			/* we'd been told to redo it in non-rcu mode */
 			status = d_revalidate(dentry, nd->flags);
 	} else {
-		dentry = __d_lookup(parent, &nd->last);
+		dentry = __d_lookup(parent, &nd->last, ci_lookup);
 		if (unlikely(!dentry))
 			return 0;
 		status = d_revalidate(dentry, nd->flags);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 82a99d366aec..a4ce5ea207ad 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -139,6 +139,8 @@ struct dentry_operations {
 	int (*d_hash)(const struct dentry *, struct qstr *);
 	int (*d_compare)(const struct dentry *,
 			unsigned int, const char *, const struct qstr *);
+	int (*d_compare_ci)(const struct dentry *,
+			    unsigned int, const char *, const struct qstr *);
 	int (*d_delete)(const struct dentry *);
 	int (*d_init)(struct dentry *);
 	void (*d_release)(struct dentry *);
@@ -282,9 +284,11 @@ extern struct dentry *d_ancestor(struct dentry *, struct dentry *);
 /* appendix may either be NULL or be used for transname suffixes */
 extern struct dentry *d_lookup(const struct dentry *, const struct qstr *);
 extern struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
-extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *);
+extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *,
+				 int ci_lookup);
 extern struct dentry *__d_lookup_rcu(const struct dentry *parent,
-				const struct qstr *name, unsigned *seq);
+				const struct qstr *name, unsigned *seq,
+				int ci_lookup);
 
 static inline unsigned d_count(const struct dentry *dentry)
 {
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 04/13] vfs: Allow bind,remount with MS_CASEFOLD
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (2 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 03/13] vfs: Add support for positive dentries CI lookups in the dentry cache Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 05/13] vfs: Handle case-exact lookup in d_add_ci Gabriel Krisman Bertazi
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/namespace.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index 0053e29b73cc..127d3028c1e8 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2261,17 +2261,28 @@ static int do_loopback(struct path *path, const char *old_name,
 static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
 {
 	int error = 0;
-	int readonly_request = 0;
+	int readonly_request = 0, casefold_request = 0;
 
 	if (ms_flags & MS_RDONLY)
 		readonly_request = 1;
-	if (readonly_request == __mnt_is_readonly(mnt))
-		return 0;
+	if (ms_flags & MS_CASEFOLD)
+		casefold_request = 1;
+
+	if (readonly_request != __mnt_is_readonly(mnt)) {
+		if (readonly_request)
+			error = mnt_make_readonly(real_mount(mnt));
+		else
+			__mnt_unmake_readonly(real_mount(mnt));
+		if (error)
+			goto out;
+	}
 
-	if (readonly_request)
-		error = mnt_make_readonly(real_mount(mnt));
+	if (casefold_request)
+		mnt->mnt_flags |= MNT_CASEFOLD;
 	else
-		__mnt_unmake_readonly(real_mount(mnt));
+		mnt->mnt_flags &= ~MNT_CASEFOLD;
+
+out:
 	return error;
 }
 
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 05/13] vfs: Handle case-exact lookup in d_add_ci
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (3 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 04/13] vfs: Allow bind,remount with MS_CASEFOLD Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 06/13] vfs: Add Support for hard negative dentries in the dcache Gabriel Krisman Bertazi
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

This prevents a soft hang if called d_add_ci is called from the FS
layer, when doing a CI search but the result dentry is the exact match.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/dcache.c | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 4bd28d7caa53..30c5eff31e88 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2049,6 +2049,20 @@ struct dentry *d_obtain_root(struct inode *inode)
 }
 EXPORT_SYMBOL(d_obtain_root);
 
+static inline bool d_same_name(const struct dentry *dentry,
+				const struct dentry *parent,
+				const struct qstr *name)
+{
+	if (likely(!(parent->d_flags & DCACHE_OP_COMPARE))) {
+		if (dentry->d_name.len != name->len)
+			return false;
+		return dentry_cmp(dentry, name->name, name->len) == 0;
+	}
+	return parent->d_op->d_compare(dentry,
+				       dentry->d_name.len, dentry->d_name.name,
+				       name) == 0;
+}
+
 static inline bool d_same_name_ci(const struct dentry *dentry,
 				  const struct dentry *parent,
 				  const struct qstr *name)
@@ -2080,6 +2094,10 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
 {
 	struct dentry *found, *res;
 
+	/* Trivial case: CI search is exact match.  */
+	if (d_same_name(dentry, dentry->d_parent, name))
+		return d_splice_alias(inode, dentry);
+
 	/*
 	 * First check if a dentry matching the name already exists,
 	 * if not go ahead and create it now.
@@ -2112,21 +2130,6 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
 }
 EXPORT_SYMBOL(d_add_ci);
 
-
-static inline bool d_same_name(const struct dentry *dentry,
-				const struct dentry *parent,
-				const struct qstr *name)
-{
-	if (likely(!(parent->d_flags & DCACHE_OP_COMPARE))) {
-		if (dentry->d_name.len != name->len)
-			return false;
-		return dentry_cmp(dentry, name->name, name->len) == 0;
-	}
-	return parent->d_op->d_compare(dentry,
-				       dentry->d_name.len, dentry->d_name.name,
-				       name) == 0;
-}
-
 /**
  * __d_lookup_rcu - search for a dentry (racy, store-free)
  * @parent: parent dentry
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 06/13] vfs: Add Support for hard negative dentries in the dcache
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (4 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 05/13] vfs: Handle case-exact lookup in d_add_ci Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 07/13] vfs: Handle MNT_CASEFOLD in /proc/mounts Gabriel Krisman Bertazi
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

A hard negative dentry is created after an unsuccessful
case-insensitive(CI) lookup operation, to indicate that there is not any
file in that directory whose casefolded name matches the casefolded
searched name.

Hard negative dentries are the CI counterpart of the existing negative
dentries for the case-isensitive lookup case. For clarity, this patch
denotes the old negative dentry as "soft negative dentry" where
applicable.  In places where it doesn't matter whether it is a hard or
soft dentry, the wrapper d_is_negative() can still be used.

Despite being the counterpart of soft negative dentries, a hard dentry
actually express a much stronger constraint, asserting that there is no
name in the filesystem that would match the string being searched, AFTER
a casefold() operation. A hard negative dentry allows the dcache to
return a -ENOENT immediately during a dcache lookup , eliminating the
need to go to the disk for every CI operation.  The hard negative dentry
definition also encloses the soft dentry constraint, such that we can
also use a hard negative dentry to return ENOENT after a Case
sensitive(CS) lookup.

Special care must be taken to invalidate (or at least demote) Hard
dentries once a matching file is created in the disk.  The file being
created might not have the exact name as the hard dentry (but it must
have an exact casefold match), but this still means the dentry must be
invalidated.  This is handled by invalidating the dentry found in
lookup_open, only if it is a hard negative.  Unless we are doing a
creation, there is no need to invalidate it elsewhere.

Soft dentries could be promoted to hard dentries after a LOOKUP_CASEFOLD
operation, but for simplifying the code, we don't do the promotion
explicitly, instead we invalidate the soft dentry and recreate it during
the following lookup as a hard dentry.  Likewise, hard dentries could be
demoted when a inexact-case match is created.  For this case, the
current code also invalidates the dentry and creates a new one.

Filesystems who want to expose CI operations must make sure their
revalidate() functions no longer reject any type of negative dentries,
they can only reject soft negatives.  If they do reject negative
dentries, say in the transition phase, there is no problem, they will be
basically ignoring this feature and paying the cost to hit the disk
everytime.

A function called d_add_ci_negative_dentry is also added in this series,
as a helper for user filesystems, when they hit an inexisting file
during a LOOKUP_CASEFOLD operation. An example of usage comes later in
the series, with the patches that add LOOKUP_CASEFOLD support for the
EXT4 filesystem.

This patch reduces the time of doing an inexact-name lookup of 10k
inexistant but already dcached files, down to 0.007 seconds in my test
vm, which is pretty similar to issuing the same operation to a CS
filesystem without this patch series.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/dcache.c            | 29 +++++++++++++++++++++--------
 fs/namei.c             |  7 +++++--
 include/linux/dcache.h | 16 ++++++++++++++--
 3 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 30c5eff31e88..a68af63c8a96 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1784,12 +1784,17 @@ void d_set_fallthru(struct dentry *dentry)
 }
 EXPORT_SYMBOL(d_set_fallthru);
 
-static unsigned d_flags_for_inode(struct inode *inode)
+static unsigned d_flags_for_inode(struct dentry *dentry, struct inode *inode)
 {
 	unsigned add_flags = DCACHE_REGULAR_TYPE;
 
-	if (!inode)
-		return DCACHE_MISS_TYPE;
+	if (!inode) {
+		/* hard negative dentries get set beforehand. */
+		if (d_is_hard_negative(dentry))
+			return DCACHE_HARD_NEGATIVE_TYPE;
+		else
+			return DCACHE_MISS_TYPE;
+	}
 
 	if (S_ISDIR(inode->i_mode)) {
 		add_flags = DCACHE_DIRECTORY_TYPE;
@@ -1821,7 +1826,7 @@ static unsigned d_flags_for_inode(struct inode *inode)
 
 static void __d_instantiate(struct dentry *dentry, struct inode *inode)
 {
-	unsigned add_flags = d_flags_for_inode(inode);
+	unsigned add_flags = d_flags_for_inode(dentry, inode);
 	WARN_ON(d_in_lookup(dentry));
 
 	spin_lock(&dentry->d_lock);
@@ -1948,7 +1953,7 @@ static struct dentry *__d_instantiate_anon(struct dentry *dentry,
 	}
 
 	/* attach a disconnected dentry */
-	add_flags = d_flags_for_inode(inode);
+	add_flags = d_flags_for_inode(dentry, inode);
 
 	if (disconnected)
 		add_flags |= DCACHE_DISCONNECTED;
@@ -2130,6 +2135,14 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
 }
 EXPORT_SYMBOL(d_add_ci);
 
+struct dentry *d_add_ci_negative_dentry(struct dentry *dentry)
+{
+	/* Mark the dentry as hard negative. */
+	dentry->d_flags |= DCACHE_HARD_NEGATIVE_TYPE;
+
+	return d_splice_alias(NULL, dentry);
+}
+
 /**
  * __d_lookup_rcu - search for a dentry (racy, store-free)
  * @parent: parent dentry
@@ -2230,7 +2243,7 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
 				goto seqretry;
 			}
 
-			if (ci_lookup) {
+			if (ci_lookup || d_is_hard_negative(dentry)) {
 				if (parent->d_op->d_compare_ci(dentry, tlen,
 							       tname, name) != 0)
 					continue;
@@ -2335,7 +2348,7 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name,
 		if (d_unhashed(dentry))
 			goto next;
 
-		if (ci_lookup) {
+		if (ci_lookup || d_is_hard_negative(dentry)) {
 			if (!d_same_name_ci(dentry, parent, name))
 				goto next;
 		} else {
@@ -2618,7 +2631,7 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode)
 		__d_lookup_done(dentry);
 	}
 	if (inode) {
-		unsigned add_flags = d_flags_for_inode(inode);
+		unsigned add_flags = d_flags_for_inode(dentry, inode);
 		hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
 		raw_write_seqcount_begin(&dentry->d_seq);
 		__d_set_inode_and_type(dentry, inode, add_flags);
diff --git a/fs/namei.c b/fs/namei.c
index 1ebe5e775a9a..69726d2f6f81 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -765,7 +765,7 @@ static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
 	/* Completely ignore negative dentries
 	   for LOOKUP_CASEFOLD. */
 	if (unlikely(flags & LOOKUP_CASEFOLD)) {
-		if (d_is_negative(dentry))
+		if (d_is_soft_negative(dentry))
 			return 0;
 	}
 
@@ -3142,7 +3142,9 @@ static int lookup_open(struct nameidata *nd, struct path *path,
 			dentry = d_alloc_parallel(dir, &nd->last, &wq);
 			if (IS_ERR(dentry))
 				return PTR_ERR(dentry);
-		}
+		} else if (d_is_hard_negative(dentry))
+			goto invalidate;
+
 		if (d_in_lookup(dentry))
 			break;
 
@@ -3151,6 +3153,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
 			break;
 		if (error)
 			goto out_dput;
+invalidate:
 		d_invalidate(dentry);
 		dput(dentry);
 		dentry = NULL;
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index a4ce5ea207ad..f215bcc2f60b 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -206,13 +206,14 @@ struct dentry_operations {
 #define DCACHE_LRU_LIST			0x00080000
 
 #define DCACHE_ENTRY_TYPE		0x00700000
-#define DCACHE_MISS_TYPE		0x00000000 /* Negative dentry (maybe fallthru to nowhere) */
+#define DCACHE_MISS_TYPE		0x00000000 /* Soft negative dentry (maybe fallthru to nowhere) */
 #define DCACHE_WHITEOUT_TYPE		0x00100000 /* Whiteout dentry (stop pathwalk) */
 #define DCACHE_DIRECTORY_TYPE		0x00200000 /* Normal directory */
 #define DCACHE_AUTODIR_TYPE		0x00300000 /* Lookupless directory (presumed automount) */
 #define DCACHE_REGULAR_TYPE		0x00400000 /* Regular file type (or fallthru to such) */
 #define DCACHE_SPECIAL_TYPE		0x00500000 /* Other file type (or fallthru to such) */
 #define DCACHE_SYMLINK_TYPE		0x00600000 /* Symlink (or fallthru to such) */
+#define DCACHE_HARD_NEGATIVE_TYPE	0x00700000 /* Hard negative dentry */
 
 #define DCACHE_MAY_FREE			0x00800000
 #define DCACHE_FALLTHRU			0x01000000 /* Fall through to lower layer */
@@ -244,6 +245,7 @@ extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *,
 					wait_queue_head_t *);
 extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
 extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *);
+extern struct dentry * d_add_ci_negative_dentry(struct dentry *);
 extern struct dentry * d_exact_alias(struct dentry *, struct inode *);
 extern struct dentry *d_find_any_alias(struct inode *inode);
 extern struct dentry * d_obtain_alias(struct inode *);
@@ -444,12 +446,22 @@ static inline bool d_is_file(const struct dentry *dentry)
 	return d_is_reg(dentry) || d_is_special(dentry);
 }
 
-static inline bool d_is_negative(const struct dentry *dentry)
+static inline bool d_is_soft_negative(const struct dentry *dentry)
 {
 	// TODO: check d_is_whiteout(dentry) also.
 	return d_is_miss(dentry);
 }
 
+static inline bool d_is_hard_negative(const struct dentry *dentry)
+{
+	return __d_entry_type(dentry) == DCACHE_HARD_NEGATIVE_TYPE;
+}
+
+static inline bool d_is_negative(const struct dentry *dentry)
+{
+	return d_is_soft_negative(dentry) || d_is_hard_negative(dentry);
+}
+extern const struct inode_operations ext4_dir_inode_operations;
 static inline bool d_is_positive(const struct dentry *dentry)
 {
 	return !d_is_negative(dentry);
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 07/13] vfs: Handle MNT_CASEFOLD in /proc/mounts
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (5 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 06/13] vfs: Add Support for hard negative dentries in the dcache Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 08/13] fscrypt: Introduce charset-based matching functions Gabriel Krisman Bertazi
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/proc_namespace.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index e16fb8f2049e..95d5b987c8b5 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -70,6 +70,7 @@ static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
 		{ MNT_NOATIME, ",noatime" },
 		{ MNT_NODIRATIME, ",nodiratime" },
 		{ MNT_RELATIME, ",relatime" },
+		{ MNT_CASEFOLD, ",ignorecase" },
 		{ 0, NULL }
 	};
 	const struct proc_fs_info *fs_infop;
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 08/13] fscrypt: Introduce charset-based matching functions
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (6 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 07/13] vfs: Handle MNT_CASEFOLD in /proc/mounts Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 09/13] ext4: Include encoding information on the superblock Gabriel Krisman Bertazi
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

Changes since RFC v1:
  - Send length of both strings to comparison functions.
  - Change length type to size_t.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 include/linux/fscrypt.h         |  1 +
 include/linux/fscrypt_notsupp.h | 16 ++++++++++++++++
 include/linux/fscrypt_supp.h    | 27 +++++++++++++++++++++++++++
 3 files changed, 44 insertions(+)

diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 952ab97af325..96559f7bfc63 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -15,6 +15,7 @@
 #define _LINUX_FSCRYPT_H
 
 #include <linux/fs.h>
+#include <linux/nls.h>
 
 #define FS_CRYPTO_BLOCK_SIZE		16
 
diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h
index 44b50c04bae9..f38824aa3250 100644
--- a/include/linux/fscrypt_notsupp.h
+++ b/include/linux/fscrypt_notsupp.h
@@ -159,6 +159,22 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
 	return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
 }
 
+static inline bool fscrypt_charset_match_name(struct nls_table *charset,
+					      const struct fscrypt_name *fname,
+					      const u8 *de_name,
+					      u32 de_name_len, bool ignorecase)
+{
+	if (!ignorecase) {
+		return !nls_strncmp(charset, (char *) de_name, de_name_len,
+				    fname->disk_name.name,
+				    fname->disk_name.len);
+	}
+
+	return !nls_strncasecmp(charset, (char *) de_name,
+				(size_t) de_name_len, fname->disk_name.name,
+				(size_t) fname->disk_name.len);
+}
+
 /* bio.c */
 static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx,
 					     struct bio *bio)
diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h
index 477a7a6504d2..8c55678ce9ce 100644
--- a/include/linux/fscrypt_supp.h
+++ b/include/linux/fscrypt_supp.h
@@ -187,6 +187,33 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname,
 	return !memcmp(de_name, fname->disk_name.name, fname->disk_name.len);
 }
 
+static inline bool fscrypt_charset_match_name(struct nls_table *charset,
+					      const struct fscrypt_name *fname,
+					      const u8 *de_name,
+					      u32 de_name_len, bool ignorecase)
+{
+	if (unlikely(!fname->disk_name.name)) {
+		const struct fscrypt_digested_name *n =
+			(const void *)fname->crypto_buf.name;
+		if (WARN_ON_ONCE(fname->usr_fname->name[0] != '_'))
+			return false;
+		if (de_name_len <= FSCRYPT_FNAME_MAX_UNDIGESTED_SIZE)
+			return false;
+		return !memcmp(FSCRYPT_FNAME_DIGEST(de_name, de_name_len),
+			       n->digest, FSCRYPT_FNAME_DIGEST_SIZE);
+	}
+
+	if (!ignorecase) {
+		return !nls_strncmp(charset, (char *) de_name, de_name_len,
+				    fname->disk_name.name,
+				    fname->disk_name.len);
+	}
+
+	return !nls_strncasecmp(charset, (char *) de_name, de_name_len,
+				fname->disk_name.name,
+				fname->disk_name.len);
+}
+
 /* bio.c */
 extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *);
 extern void fscrypt_pullback_bio_page(struct page **, bool);
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 09/13] ext4: Include encoding information on the superblock
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (7 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 08/13] fscrypt: Introduce charset-based matching functions Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 10/13] ext4: Plumb LOOK_CASEFOLD up to ext4 comparison functions Gabriel Krisman Bertazi
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/ext4/ext4.h  |  1 +
 fs/ext4/super.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 3241475a1733..22f49d7d3efc 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1366,6 +1366,7 @@ struct ext4_sb_info {
 	struct kobject s_kobj;
 	struct completion s_kobj_unregister;
 	struct super_block *s_sb;
+	struct nls_table *encoding;
 
 	/* Journaling */
 	struct journal_s *s_journal;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 39bf464c35f1..bbf0a5c4104b 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -42,6 +42,7 @@
 #include <linux/cleancache.h>
 #include <linux/uaccess.h>
 #include <linux/iversion.h>
+#include <linux/nls.h>
 
 #include <linux/kthread.h>
 #include <linux/freezer.h>
@@ -1352,6 +1353,7 @@ enum {
 	Opt_dioread_nolock, Opt_dioread_lock,
 	Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
 	Opt_max_dir_size_kb, Opt_nojournal_checksum, Opt_nombcache,
+	Opt_encoding,
 };
 
 static const match_table_t tokens = {
@@ -1434,6 +1436,7 @@ static const match_table_t tokens = {
 	{Opt_noinit_itable, "noinit_itable"},
 	{Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
 	{Opt_test_dummy_encryption, "test_dummy_encryption"},
+	{Opt_encoding, "encoding=%s"},
 	{Opt_nombcache, "nombcache"},
 	{Opt_nombcache, "no_mbcache"},	/* for backward compatibility */
 	{Opt_removed, "check=none"},	/* mount option from ext2/3 */
@@ -1644,9 +1647,24 @@ static const struct mount_opts {
 	{Opt_max_dir_size_kb, 0, MOPT_GTE0},
 	{Opt_test_dummy_encryption, 0, MOPT_GTE0},
 	{Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET},
+	{Opt_encoding, 0, MOPT_EXT4_ONLY | MOPT_STRING},
 	{Opt_err, 0, 0}
 };
 
+static void split_charset_version(char *encoding_arg, char **charset,
+				  char **version)
+{
+	char *separator;
+	*version = NULL;
+	*charset = encoding_arg;
+
+	separator = strchr(encoding_arg, '-');
+	if (separator && *separator && *(separator+1) != '\0') {
+		*separator = '\0';
+		*version = separator + 1;
+	}
+}
+
 static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 			    substring_t *args, unsigned long *journal_devnum,
 			    unsigned int *journal_ioprio, int is_remount)
@@ -1879,6 +1897,21 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 		sbi->s_mount_opt |= m->mount_opt;
 	} else if (token == Opt_data_err_ignore) {
 		sbi->s_mount_opt &= ~m->mount_opt;
+	} else if (token == Opt_encoding) {
+		char *encoding_arg = match_strdup(&args[0]);
+		char *charset = NULL;
+		char *version = NULL;
+
+		split_charset_version(encoding_arg, &charset, &version);
+
+		sbi->encoding = load_nls_version(charset, version);
+		if (IS_ERR(sbi->encoding)) {
+			ext4_msg(sb, KERN_ERR, "Encoding %s-%s not supported",
+				 charset,version);
+			kfree(encoding_arg);
+			return -1;
+		}
+		kfree(encoding_arg);
 	} else {
 		if (!args->from)
 			arg = 1;
@@ -1965,6 +1998,12 @@ static int parse_options(char *options, struct super_block *sb,
 			return 0;
 		}
 	}
+
+	if (!sbi->encoding) {
+		ext4_msg(sb, KERN_WARNING, "Encoding not explicitly specified "
+			 "or identified on the superblock.  ASCII is assumed.");
+	}
+
 	return 1;
 }
 
@@ -3595,6 +3634,12 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 			   &journal_ioprio, 0))
 		goto failed_mount;
 
+	if (!sbi->encoding) {
+		sbi->encoding = load_nls("ascii");
+		if (!sbi->encoding)
+			goto failed_mount;
+	}
+
 	if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
 		printk_once(KERN_WARNING "EXT4-fs: Warning: mounting "
 			    "with data=journal disables delayed "
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 10/13] ext4: Plumb LOOK_CASEFOLD up to ext4 comparison functions
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (8 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 09/13] ext4: Include encoding information on the superblock Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 11/13] ext4: Support charset name matching Gabriel Krisman Bertazi
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

We also ignore the flags when creating a file, this means we allow files
to be created who only differ by case folding.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/ext4/ext4.h   |  6 ++++--
 fs/ext4/inline.c |  7 ++++---
 fs/ext4/namei.c  | 50 +++++++++++++++++++++++++++---------------------
 3 files changed, 36 insertions(+), 27 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 22f49d7d3efc..ebda06e4cb24 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2505,7 +2505,8 @@ extern int ext4_search_dir(struct buffer_head *bh,
 			   struct inode *dir,
 			   struct ext4_filename *fname,
 			   unsigned int offset,
-			   struct ext4_dir_entry_2 **res_dir);
+			   struct ext4_dir_entry_2 **res_dir,
+			   unsigned int flags);
 extern int ext4_generic_delete_entry(handle_t *handle,
 				     struct inode *dir,
 				     struct ext4_dir_entry_2 *de_del,
@@ -2991,7 +2992,8 @@ extern int htree_inlinedir_to_tree(struct file *dir_file,
 extern struct buffer_head *ext4_find_inline_entry(struct inode *dir,
 					struct ext4_filename *fname,
 					struct ext4_dir_entry_2 **res_dir,
-					int *has_inline_data);
+					int *has_inline_data,
+					unsigned int flags);
 extern int ext4_delete_inline_entry(handle_t *handle,
 				    struct inode *dir,
 				    struct ext4_dir_entry_2 *de_del,
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 70cf4c7b268a..273d28a9c0b7 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1612,7 +1612,8 @@ int ext4_try_create_inline_dir(handle_t *handle, struct inode *parent,
 struct buffer_head *ext4_find_inline_entry(struct inode *dir,
 					struct ext4_filename *fname,
 					struct ext4_dir_entry_2 **res_dir,
-					int *has_inline_data)
+					int *has_inline_data,
+					unsigned int flags)
 {
 	int ret;
 	struct ext4_iloc iloc;
@@ -1632,7 +1633,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, res_dir);
+			      dir, fname, 0, res_dir, flags);
 	if (ret == 1)
 		goto out_find;
 	if (ret < 0)
@@ -1645,7 +1646,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, res_dir);
+			      dir, fname, 0, res_dir, flags);
 	if (ret == 1)
 		goto out_find;
 
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index b1f21e3a0763..3373017e9315 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -35,6 +35,7 @@
 #include <linux/buffer_head.h>
 #include <linux/bio.h>
 #include <linux/iversion.h>
+#include <linux/namei.h>
 #include "ext4.h"
 #include "ext4_jbd2.h"
 
@@ -273,7 +274,8 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash,
 				 __u32 *start_hash);
 static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
 		struct ext4_filename *fname,
-		struct ext4_dir_entry_2 **res_dir);
+	        struct ext4_dir_entry_2 **res_dir,
+	        unsigned int flags);
 static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
 			     struct inode *dir, struct inode *inode);
 
@@ -1172,10 +1174,11 @@ static inline int search_dirblock(struct buffer_head *bh,
 				  struct inode *dir,
 				  struct ext4_filename *fname,
 				  unsigned int offset,
-				  struct ext4_dir_entry_2 **res_dir)
+				  struct ext4_dir_entry_2 **res_dir,
+				  unsigned int flags)
 {
 	return ext4_search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir,
-			       fname, offset, res_dir);
+			       fname, offset, res_dir, flags);
 }
 
 /*
@@ -1257,7 +1260,8 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
  * Return: %true if the directory entry matches, otherwise %false.
  */
 static inline bool ext4_match(const struct ext4_filename *fname,
-			      const struct ext4_dir_entry_2 *de)
+			      const struct ext4_dir_entry_2 *de,
+			      bool casefold)
 {
 	struct fscrypt_name f;
 
@@ -1277,7 +1281,8 @@ static inline bool ext4_match(const struct ext4_filename *fname,
  */
 int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
 		    struct inode *dir, struct ext4_filename *fname,
-		    unsigned int offset, struct ext4_dir_entry_2 **res_dir)
+		    unsigned int offset, struct ext4_dir_entry_2 **res_dir,
+		    unsigned int flags)
 {
 	struct ext4_dir_entry_2 * de;
 	char * dlimit;
@@ -1289,7 +1294,7 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
 		/* this code is executed quadratically often */
 		/* do minimal checking `by hand' */
 		if ((char *) de + de->name_len <= dlimit &&
-		    ext4_match(fname, de)) {
+		    ext4_match(fname, de, flags & LOOKUP_CASEFOLD)) {
 			/* found a match - just to be sure, do
 			 * a full check */
 			if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
@@ -1339,7 +1344,7 @@ static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
 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 *inlined, unsigned int flags)
 {
 	struct super_block *sb;
 	struct buffer_head *bh_use[NAMEI_RA_SIZE];
@@ -1369,7 +1374,7 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
 	if (ext4_has_inline_data(dir)) {
 		int has_inline_data = 1;
 		ret = ext4_find_inline_entry(dir, &fname, res_dir,
-					     &has_inline_data);
+					     &has_inline_data, flags);
 		if (has_inline_data) {
 			if (inlined)
 				*inlined = 1;
@@ -1388,7 +1393,7 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
 		goto restart;
 	}
 	if (is_dx(dir)) {
-		ret = ext4_dx_find_entry(dir, &fname, res_dir);
+		ret = ext4_dx_find_entry(dir, &fname, res_dir, flags);
 		/*
 		 * On success, or if the error was file not found,
 		 * return.  Otherwise, fall back to doing a search the
@@ -1452,7 +1457,7 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
 		}
 		set_buffer_verified(bh);
 		i = search_dirblock(bh, dir, &fname,
-			    block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
+			    block << EXT4_BLOCK_SIZE_BITS(sb), res_dir, flags);
 		if (i == 1) {
 			EXT4_I(dir)->i_dir_start_lookup = block;
 			ret = bh;
@@ -1488,7 +1493,8 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
 
 static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
 			struct ext4_filename *fname,
-			struct ext4_dir_entry_2 **res_dir)
+		        struct ext4_dir_entry_2 **res_dir,
+		        unsigned int flags)
 {
 	struct super_block * sb = dir->i_sb;
 	struct dx_frame frames[EXT4_HTREE_LEVEL], *frame;
@@ -1510,7 +1516,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
 
 		retval = search_dirblock(bh, dir, fname,
 					 block << EXT4_BLOCK_SIZE_BITS(sb),
-					 res_dir);
+					 res_dir, flags);
 		if (retval == 1)
 			goto success;
 		brelse(bh);
@@ -1553,7 +1559,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
 	if (dentry->d_name.len > EXT4_NAME_LEN)
 		return ERR_PTR(-ENAMETOOLONG);
 
-	bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
+	bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, flags);
 	if (IS_ERR(bh))
 		return (struct dentry *) bh;
 	inode = NULL;
@@ -1597,7 +1603,7 @@ struct dentry *ext4_get_parent(struct dentry *child)
 	struct ext4_dir_entry_2 * de;
 	struct buffer_head *bh;
 
-	bh = ext4_find_entry(d_inode(child), &dotdot, &de, NULL);
+	bh = ext4_find_entry(d_inode(child), &dotdot, &de, NULL, 0);
 	if (IS_ERR(bh))
 		return (struct dentry *) bh;
 	if (!bh)
@@ -1796,7 +1802,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
 		if (ext4_check_dir_entry(dir, NULL, de, bh,
 					 buf, buf_size, offset))
 			return -EFSCORRUPTED;
-		if (ext4_match(fname, de))
+		if (ext4_match(fname, de, false))
 			return -EEXIST;
 		nlen = EXT4_DIR_REC_LEN(de->name_len);
 		rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
@@ -2925,7 +2931,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
 		return retval;
 
 	retval = -ENOENT;
-	bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
+	bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, 0);
 	if (IS_ERR(bh))
 		return PTR_ERR(bh);
 	if (!bh)
@@ -3002,7 +3008,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
 		return retval;
 
 	retval = -ENOENT;
-	bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
+	bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL, 0);
 	if (IS_ERR(bh))
 		return PTR_ERR(bh);
 	if (!bh)
@@ -3364,7 +3370,7 @@ static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
 	struct buffer_head *bh;
 	struct ext4_dir_entry_2 *de;
 
-	bh = ext4_find_entry(dir, d_name, &de, NULL);
+	bh = ext4_find_entry(dir, d_name, &de, NULL, 0);
 	if (IS_ERR(bh))
 		return PTR_ERR(bh);
 	if (bh) {
@@ -3499,7 +3505,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 			return retval;
 	}
 
-	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
+	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL, 0);
 	if (IS_ERR(old.bh))
 		return PTR_ERR(old.bh);
 	/*
@@ -3513,7 +3519,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
 		goto end_rename;
 
 	new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
-				 &new.de, &new.inlined);
+				 &new.de, &new.inlined, 0);
 	if (IS_ERR(new.bh)) {
 		retval = PTR_ERR(new.bh);
 		new.bh = NULL;
@@ -3693,7 +3699,7 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
 		return retval;
 
 	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name,
-				 &old.de, &old.inlined);
+				 &old.de, &old.inlined, 0);
 	if (IS_ERR(old.bh))
 		return PTR_ERR(old.bh);
 	/*
@@ -3707,7 +3713,7 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
 		goto end_rename;
 
 	new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
-				 &new.de, &new.inlined);
+				 &new.de, &new.inlined, 0);
 	if (IS_ERR(new.bh)) {
 		retval = PTR_ERR(new.bh);
 		new.bh = NULL;
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 11/13] ext4: Support charset name matching
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (9 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 10/13] ext4: Plumb LOOK_CASEFOLD up to ext4 comparison functions Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 12/13] ext4: Implement ext4 dcache hooks for custom charsets Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 13/13] ext4: Notify VFS of support for casefolded mountpoints Gabriel Krisman Bertazi
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

Implement generic caseless lookup based on the mount flags using the
charset library.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/ext4/namei.c | 47 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 39 insertions(+), 8 deletions(-)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 3373017e9315..ad7330b82740 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1259,7 +1259,8 @@ static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
  *
  * Return: %true if the directory entry matches, otherwise %false.
  */
-static inline bool ext4_match(const struct ext4_filename *fname,
+static inline bool ext4_match(struct nls_table *charset,
+			      const struct ext4_filename *fname,
 			      const struct ext4_dir_entry_2 *de,
 			      bool casefold)
 {
@@ -1273,7 +1274,8 @@ static inline bool ext4_match(const struct ext4_filename *fname,
 #ifdef CONFIG_EXT4_FS_ENCRYPTION
 	f.crypto_buf = fname->crypto_buf;
 #endif
-	return fscrypt_match_name(&f, de->name, de->name_len);
+	return fscrypt_charset_match_name(charset, &f, de->name,
+					  de->name_len, casefold);
 }
 
 /*
@@ -1287,6 +1289,7 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
 	struct ext4_dir_entry_2 * de;
 	char * dlimit;
 	int de_len;
+	struct super_block *sb = dir->i_sb;
 
 	de = (struct ext4_dir_entry_2 *)search_buf;
 	dlimit = search_buf + buf_size;
@@ -1294,7 +1297,9 @@ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
 		/* this code is executed quadratically often */
 		/* do minimal checking `by hand' */
 		if ((char *) de + de->name_len <= dlimit &&
-		    ext4_match(fname, de, flags & LOOKUP_CASEFOLD)) {
+		    ext4_match(EXT4_SB(sb)->encoding, fname, de,
+			       flags & LOOKUP_CASEFOLD)) {
+
 			/* found a match - just to be sure, do
 			 * a full check */
 			if (ext4_check_dir_entry(dir, NULL, de, bh, bh->b_data,
@@ -1395,11 +1400,13 @@ static struct buffer_head * ext4_find_entry (struct inode *dir,
 	if (is_dx(dir)) {
 		ret = ext4_dx_find_entry(dir, &fname, res_dir, flags);
 		/*
-		 * On success, or if the error was file not found,
-		 * return.  Otherwise, fall back to doing a search the
-		 * old fashioned way.
+		 * On success, or if the file cannot be found, return.
+		 * If an error occurred, or if the file was not found
+		 * but we can do case-insensitive lookups, fall back to
+		 * the linear search.
 		 */
-		if (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR)
+		if ((!IS_ERR(ret) && (ret || !(flags & LOOKUP_CASEFOLD))) ||
+		    (IS_ERR(ret) && PTR_ERR(ret) != ERR_BAD_DX_DIR))
 			goto cleanup_and_exit;
 		dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, "
 			       "falling back\n"));
@@ -1592,6 +1599,29 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsi
 			return ERR_PTR(-EPERM);
 		}
 	}
+
+	if (flags & LOOKUP_CASEFOLD) {
+		struct dentry *new;
+		struct qstr ciname;
+		char *name;
+
+		if (!de)
+			return d_add_ci_negative_dentry(dentry);
+
+		name = kmalloc((sizeof(char) * de->name_len) + 1,
+			       GFP_NOFS);
+		if (!name)
+			return ERR_PTR(-ENOMEM);
+
+		memcpy(name, de->name, de->name_len);
+		name[de->name_len] = '\0';
+		ciname.len = de->name_len;
+		ciname.name = name;
+		new = d_add_ci(dentry, inode, &ciname);
+		kfree(name);
+		return new;
+	}
+
 	return d_splice_alias(inode, dentry);
 }
 
@@ -1795,6 +1825,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
 	int nlen, rlen;
 	unsigned int offset = 0;
 	char *top;
+	struct super_block *sb = dir->i_sb;
 
 	de = (struct ext4_dir_entry_2 *)buf;
 	top = buf + buf_size - reclen;
@@ -1802,7 +1833,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
 		if (ext4_check_dir_entry(dir, NULL, de, bh,
 					 buf, buf_size, offset))
 			return -EFSCORRUPTED;
-		if (ext4_match(fname, de, false))
+		if (ext4_match(EXT4_SB(sb)->encoding, fname, de, false))
 			return -EEXIST;
 		nlen = EXT4_DIR_REC_LEN(de->name_len);
 		rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 12/13] ext4: Implement ext4 dcache hooks for custom charsets
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (10 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 11/13] ext4: Support charset name matching Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  2018-05-22 20:38 ` [PATCH NOMERGE 13/13] ext4: Notify VFS of support for casefolded mountpoints Gabriel Krisman Bertazi
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

Changes from RFC v1:
  - Handle errors from charset_normalize/casefold (Olaf Weber)
  - Send length of both strings to comparison functions.
  - Cast length type to size_t

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/ext4/dir.c   | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/ext4.h  |  2 ++
 fs/ext4/super.c |  2 ++
 3 files changed, 52 insertions(+)

diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index da87cf757f7d..02911f2681ab 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -26,6 +26,7 @@
 #include <linux/buffer_head.h>
 #include <linux/slab.h>
 #include <linux/iversion.h>
+#include <linux/nls.h>
 #include "ext4.h"
 #include "xattr.h"
 
@@ -662,3 +663,50 @@ const struct file_operations ext4_dir_operations = {
 	.open		= ext4_dir_open,
 	.release	= ext4_release_dir,
 };
+
+static int ext4_d_compare(const struct dentry *dentry, unsigned int len,
+			  const char *str, const struct qstr *name)
+{
+	struct nls_table *charset = EXT4_SB(dentry->d_sb)->encoding;
+	size_t nlen = strlen(name->name);
+
+	return nls_strncmp(charset, str, len, name->name, nlen);
+}
+
+static int ext4_d_compare_ci(const struct dentry *dentry, unsigned int len,
+			     const char *str, const struct qstr *name)
+{
+	struct nls_table *charset = EXT4_SB(dentry->d_sb)->encoding;
+	size_t nlen = strlen(name->name);
+
+	return nls_strncasecmp(charset, str, len, name->name, nlen);
+}
+
+static int ext4_d_ci_hash(const struct dentry *dentry, struct qstr *q)
+{
+	unsigned long hash;
+	int i, len;
+	unsigned char *folded = NULL;
+	const struct nls_table *charset = EXT4_SB(dentry->d_sb)->encoding;
+
+	len = nls_casefold(charset, q->name, q->len, &folded);
+
+	if (len < 0) {
+		kfree(folded);
+		return -EINVAL;
+	}
+
+	hash = init_name_hash(dentry);
+	for (i = 0; i < len; i++)
+		hash = partial_name_hash(folded[i], hash);
+	q->hash = end_name_hash(hash);
+
+	kfree(folded);
+	return 0;
+}
+
+const struct dentry_operations ext4_dentry_ops = {
+	.d_hash = ext4_d_ci_hash,
+	.d_compare = ext4_d_compare,
+	.d_compare_ci = ext4_d_compare_ci,
+};
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index ebda06e4cb24..eb193d66565a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2939,6 +2939,8 @@ static inline void ext4_unlock_group(struct super_block *sb,
 
 /* dir.c */
 extern const struct file_operations ext4_dir_operations;
+extern const struct dentry_operations ext4_dentry_ops;
+extern const struct dentry_operations ext4_ci_dentry_ops;
 
 /* file.c */
 extern const struct inode_operations ext4_file_inode_operations;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index bbf0a5c4104b..e73976986dcb 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4241,6 +4241,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		iput(root);
 		goto failed_mount4;
 	}
+
+	sb->s_d_op = &ext4_dentry_ops;
 	sb->s_root = d_make_root(root);
 	if (!sb->s_root) {
 		ext4_msg(sb, KERN_ERR, "get root dentry failed");
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH NOMERGE 13/13] ext4: Notify VFS of support for casefolded mountpoints
  2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
                   ` (11 preceding siblings ...)
  2018-05-22 20:38 ` [PATCH NOMERGE 12/13] ext4: Implement ext4 dcache hooks for custom charsets Gabriel Krisman Bertazi
@ 2018-05-22 20:38 ` Gabriel Krisman Bertazi
  12 siblings, 0 replies; 14+ messages in thread
From: Gabriel Krisman Bertazi @ 2018-05-22 20:38 UTC (permalink / raw)
  To: viro; +Cc: linux-fsdevel, Gabriel Krisman Bertazi

This effectively enables case-insensitive lookups for ext4.

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
---
 fs/ext4/super.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index e73976986dcb..dda63227c48e 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -5827,7 +5827,7 @@ static struct file_system_type ext4_fs_type = {
 	.name		= "ext4",
 	.mount		= ext4_mount,
 	.kill_sb	= kill_block_super,
-	.fs_flags	= FS_REQUIRES_DEV,
+	.fs_flags	= FS_REQUIRES_DEV | FS_HAS_CASEFOLD,
 };
 MODULE_ALIAS_FS("ext4");
 
-- 
2.17.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2018-05-22 20:38 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-22 20:38 [PATCH NOMERGE 00/13] Case insensitiveness as a mountpoint Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 01/13] vfs: Add support for mounting with MS_CASEFOLD Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 02/13] vfs: Propagate LOOKUP_CASEFOLD after doing parent lookups Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 03/13] vfs: Add support for positive dentries CI lookups in the dentry cache Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 04/13] vfs: Allow bind,remount with MS_CASEFOLD Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 05/13] vfs: Handle case-exact lookup in d_add_ci Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 06/13] vfs: Add Support for hard negative dentries in the dcache Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 07/13] vfs: Handle MNT_CASEFOLD in /proc/mounts Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 08/13] fscrypt: Introduce charset-based matching functions Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 09/13] ext4: Include encoding information on the superblock Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 10/13] ext4: Plumb LOOK_CASEFOLD up to ext4 comparison functions Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 11/13] ext4: Support charset name matching Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 12/13] ext4: Implement ext4 dcache hooks for custom charsets Gabriel Krisman Bertazi
2018-05-22 20:38 ` [PATCH NOMERGE 13/13] ext4: Notify VFS of support for casefolded mountpoints Gabriel Krisman Bertazi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).