linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs
@ 2024-10-21 16:37 André Almeida
  2024-10-21 16:37 ` [PATCH v8 1/9] libfs: Create the helper function generic_ci_validate_strict_name() André Almeida
                   ` (9 more replies)
  0 siblings, 10 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, Gabriel Krisman Bertazi, Gabriel Krisman Bertazi,
	André Almeida, Randy Dunlap

Hi,

This patchset adds support for case-insensitive file names lookups in
tmpfs. The main difference from other casefold filesystems is that tmpfs
has no information on disk, just on RAM, so we can't use mkfs to create a
case-insensitive tmpfs.  For this implementation, I opted to have a mount
option for casefolding. The rest of the patchset follows a similar approach
as ext4 and f2fs.

* Use case (from the original cover letter)

The use case for this feature is similar to the use case for ext4, to
better support compatibility layers (like Wine), particularly in
combination with sandboxing/container tools (like Flatpak). Those
containerization tools can share a subset of the host filesystem with an
application. In the container, the root directory and any parent
directories required for a shared directory are on tmpfs, with the
shared directories bind-mounted into the container's view of the
filesystem.

If the host filesystem is using case-insensitive directories, then the
application can do lookups inside those directories in a
case-insensitive way, without this needing to be implemented in
user-space. However, if the host is only sharing a subset of a
case-insensitive directory with the application, then the parent
directories of the mount point will be part of the container's root
tmpfs. When the application tries to do case-insensitive lookups of
those parent directories on a case-sensitive tmpfs, the lookup will
fail.

For example, if /srv/games is a case-insensitive directory on the host,
then applications will expect /srv/games/Steam/Half-Life and
/srv/games/steam/half-life to be interchangeable; but if the
container framework is only sharing /srv/games/Steam/Half-Life and
/srv/games/Steam/Portal (and not the rest of /srv/games) with the
container, with /srv, /srv/games and /srv/games/Steam as part of the
container's tmpfs root, then making /srv/games a case-insensitive
directory inside the container would be necessary to meet that
expectation.

* Testing

I send a patch for xfstests to enable the casefold test (generic/556) for
tmpfs.[1] The test succeed.

You can test this patchset using:

  sudo mount -t tmpfs -o casefold tmpfs mnt/

And making a dir case-insensitive:

  mkdir mnt/dir
  chattr +F mnt/dir

[1] https://lore.kernel.org/fstests/20240823173008.280917-1-andrealmeid@igalia.com/

Changes in v8:
 - Fix docs typo (Randy)
 - Consistently guard `encoding` and `strict_encoding` fields from struct
   shmem_options, so those fields only exists with CONFIG_UNICODE (Brauner)
 v7: https://lore.kernel.org/r/20241017-tonyk-tmpfs-v7-0-a9c056f8391f@igalia.com

Changes in v7:
 - Fixed generic_ci_validate_strict_name()
 - Dropped patch "tmpfs: Always set simple_dentry_operations as dentry ops"
 - Re-place generic_ci_validate_strict_name() before inode creation
 v6: https://lore.kernel.org/r/20241010-tonyk-tmpfs-v6-0-79f0ae02e4c8@igalia.com

Changes in v6:
 - Fixed kernel bot warning 'shmem_ci_dentry_ops' defined but not used
 v5: https://lore.kernel.org/lkml/20241002234444.398367-1-andrealmeid@igalia.com/

Changes in v5:
 - New patch "Always set simple_dentry_operations as dentry ops"
 - "Squashed libfs: Check for casefold dirs on simple_lookup()" into "tmpfs: Add
    casefold lookup support"
 - Fail to mount if strict_encoding is used without encoding
 - Inlined generic_ci_validate_strict_name()
 - Added IS_ENABLED(UNICODE) guards to public generic_ci_ funcs
 - Dropped .d_revalidate = fscrypt_d_revalidate, tmpfs doesn't support it
 v4: https://lore.kernel.org/lkml/20240911144502.115260-1-andrealmeid@igalia.com/

Changes in v4:
 - Got rid of shmem_lookup() and changed simple_lookup() to cover casefold use
   case
 - Simplified shmem_parse_opt_casefold() and how it handle the lastest_version
   option
 - Simplified utf8_parse_version() to return the version in one variable instead
   of three
 - Rewrote part of the documentation patch
 - Make sure that d_sb->s_d_op is set during mount time
 - Moved `generic_ci_always_del_dentry_ops` to mm/shmem.c as `shmem_ci_dentry_ops`
v3: https://lore.kernel.org/lkml/20240905190252.461639-1-andrealmeid@igalia.com/

Changes in v3:
 - Renamed utf8_check_strict_name() to generic_ci_validate_strict_name(), and
 reworked the big if(...) to be more clear
 - Expose the latest UTF-8 version in include/linux/unicode.h
 - shmem_lookup() now sets d_ops
 - reworked shmem_parse_opt_casefold()
 - if `mount -o casefold` has no param, load latest UTF-8 version
 - using (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir) when possible
 - Fixed bug when adding a non-casefold flag in a non-empty dir
v2: https://lore.kernel.org/lkml/20240902225511.757831-1-andrealmeid@igalia.com/

Changes in v2:
 - Found and fixed a bug in utf8_load()
 - Created a helper for checking strict file names (Krisman)
 - Merged patch 1/ and 3/ together (Krisman)
 - Reworded the explanation about d_compare (Krisman)
 - Removed bool casefold from shmem_sb_info (Krisman)
 - Reworked d_add(dentry, NULL) to be called as d_add(dentry, inode) (Krisman)
 - Moved utf8_parse_version to common unicode code
 - Fixed some smatch/sparse warnings (kernel test bot/Dan Carpenter)
v1: https://lore.kernel.org/linux-fsdevel/20240823173332.281211-1-andrealmeid@igalia.com/

---
André Almeida (9):
      libfs: Create the helper function generic_ci_validate_strict_name()
      ext4: Use generic_ci_validate_strict_name helper
      unicode: Export latest available UTF-8 version number
      unicode: Recreate utf8_parse_version()
      libfs: Export generic_ci_ dentry functions
      tmpfs: Add casefold lookup support
      tmpfs: Add flag FS_CASEFOLD_FL support for tmpfs dirs
      tmpfs: Expose filesystem features via sysfs
      docs: tmpfs: Add casefold options

 Documentation/filesystems/tmpfs.rst |  24 ++++
 fs/ext4/namei.c                     |   5 +-
 fs/libfs.c                          |  12 +-
 fs/unicode/utf8-core.c              |  26 ++++
 fs/unicode/utf8-selftest.c          |   3 -
 include/linux/fs.h                  |  49 ++++++++
 include/linux/shmem_fs.h            |   6 +-
 include/linux/unicode.h             |   4 +
 mm/shmem.c                          | 234 ++++++++++++++++++++++++++++++++++--
 9 files changed, 340 insertions(+), 23 deletions(-)
---
base-commit: 42f7652d3eb527d03665b09edac47f85fb600924
change-id: 20241010-tonyk-tmpfs-fd4e181913d2

Best regards,
-- 
André Almeida <andrealmeid@igalia.com>



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

* [PATCH v8 1/9] libfs: Create the helper function generic_ci_validate_strict_name()
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-21 16:37 ` [PATCH v8 2/9] ext4: Use generic_ci_validate_strict_name helper André Almeida
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, Gabriel Krisman Bertazi, Gabriel Krisman Bertazi,
	André Almeida

Create a helper function for filesystems do the checks required for
casefold directories and strict encoding.

Suggested-by: Gabriel Krisman Bertazi <krisman@suse.de>
Reviewed-by: Gabriel Krisman Bertazi <gabriel@krisman.be>
Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
Changes from v6:
- Correctly negate utf8_validate() return

Changes from v4:
- Inline this function

Changes from v2:
- Moved function to libfs and adpated its name
- Wrapped at 72 chars column
- Decomposed the big if (...) to be more clear
---
 include/linux/fs.h | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3559446279c152c70b7f1f3e0154f6e66a5aba33..403ee5d54c60a0a97e2eba9ef80d8fb4bbd2288f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -45,6 +45,7 @@
 #include <linux/slab.h>
 #include <linux/maple_tree.h>
 #include <linux/rw_hint.h>
+#include <linux/unicode.h>
 
 #include <asm/byteorder.h>
 #include <uapi/linux/fs.h>
@@ -3456,6 +3457,50 @@ extern int generic_ci_match(const struct inode *parent,
 			    const struct qstr *folded_name,
 			    const u8 *de_name, u32 de_name_len);
 
+#if IS_ENABLED(CONFIG_UNICODE)
+/**
+ * generic_ci_validate_strict_name - Check if a given name is suitable
+ * for a directory
+ *
+ * This functions checks if the proposed filename is valid for the
+ * parent directory. That means that only valid UTF-8 filenames will be
+ * accepted for casefold directories from filesystems created with the
+ * strict encoding flag.  That also means that any name will be
+ * accepted for directories that doesn't have casefold enabled, or
+ * aren't being strict with the encoding.
+ *
+ * @dir: inode of the directory where the new file will be created
+ * @name: name of the new file
+ *
+ * Return:
+ * * True if the filename is suitable for this directory. It can be
+ * true if a given name is not suitable for a strict encoding
+ * directory, but the directory being used isn't strict
+ * * False if the filename isn't suitable for this directory. This only
+ * happens when a directory is casefolded and the filesystem is strict
+ * about its encoding.
+ */
+static inline bool generic_ci_validate_strict_name(struct inode *dir, struct qstr *name)
+{
+	if (!IS_CASEFOLDED(dir) || !sb_has_strict_encoding(dir->i_sb))
+		return true;
+
+	/*
+	 * A casefold dir must have a encoding set, unless the filesystem
+	 * is corrupted
+	 */
+	if (WARN_ON_ONCE(!dir->i_sb->s_encoding))
+		return true;
+
+	return !utf8_validate(dir->i_sb->s_encoding, name);
+}
+#else
+static inline bool generic_ci_validate_strict_name(struct inode *dir, struct qstr *name)
+{
+	return true;
+}
+#endif
+
 static inline bool sb_has_encoding(const struct super_block *sb)
 {
 #if IS_ENABLED(CONFIG_UNICODE)

-- 
2.47.0



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

* [PATCH v8 2/9] ext4: Use generic_ci_validate_strict_name helper
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
  2024-10-21 16:37 ` [PATCH v8 1/9] libfs: Create the helper function generic_ci_validate_strict_name() André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-21 16:37 ` [PATCH v8 3/9] unicode: Export latest available UTF-8 version number André Almeida
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, Gabriel Krisman Bertazi, André Almeida

Use the helper function to check the requirements for casefold
directories using strict encoding.

Suggested-by: Gabriel Krisman Bertazi <krisman@suse.de>
Signed-off-by: André Almeida <andrealmeid@igalia.com>
Acked-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
---
Changes from v4:
- Now we can drop the if IS_ENABLED() guard
---
 fs/ext4/namei.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 790db7eac6c2ad5e1790e363e4ac273162e35013..612ccbeb493b8d901c123221ef6573457193dd16 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2395,11 +2395,8 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 	if (fscrypt_is_nokey_name(dentry))
 		return -ENOKEY;
 
-#if IS_ENABLED(CONFIG_UNICODE)
-	if (sb_has_strict_encoding(sb) && IS_CASEFOLDED(dir) &&
-	    utf8_validate(sb->s_encoding, &dentry->d_name))
+	if (!generic_ci_validate_strict_name(dir, &dentry->d_name))
 		return -EINVAL;
-#endif
 
 	retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname);
 	if (retval)

-- 
2.47.0



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

* [PATCH v8 3/9] unicode: Export latest available UTF-8 version number
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
  2024-10-21 16:37 ` [PATCH v8 1/9] libfs: Create the helper function generic_ci_validate_strict_name() André Almeida
  2024-10-21 16:37 ` [PATCH v8 2/9] ext4: Use generic_ci_validate_strict_name helper André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-21 16:37 ` [PATCH v8 4/9] unicode: Recreate utf8_parse_version() André Almeida
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, André Almeida, Gabriel Krisman Bertazi

Export latest available UTF-8 version number so filesystems can easily
load the newest one.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
Acked-by: Gabriel Krisman Bertazi <krisman@suse.de>
---
 fs/unicode/utf8-selftest.c | 3 ---
 include/linux/unicode.h    | 2 ++
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/fs/unicode/utf8-selftest.c b/fs/unicode/utf8-selftest.c
index 600e15efe9edfdf6d04cecd162e84f1f5a59c5e1..5ddaf27b21a6543770917d5a837e86e12eee0b81 100644
--- a/fs/unicode/utf8-selftest.c
+++ b/fs/unicode/utf8-selftest.c
@@ -17,9 +17,6 @@
 static unsigned int failed_tests;
 static unsigned int total_tests;
 
-/* Tests will be based on this version. */
-#define UTF8_LATEST	UNICODE_AGE(12, 1, 0)
-
 #define _test(cond, func, line, fmt, ...) do {				\
 		total_tests++;						\
 		if (!cond) {						\
diff --git a/include/linux/unicode.h b/include/linux/unicode.h
index 4d39e6e11a950c76f78d775fd6f351296f3d7d53..0c0ab04e84ee80227f9390ad0498f21a7ab7d34b 100644
--- a/include/linux/unicode.h
+++ b/include/linux/unicode.h
@@ -16,6 +16,8 @@ struct utf8data_table;
 	 ((unsigned int)(MIN) << UNICODE_MIN_SHIFT) |	\
 	 ((unsigned int)(REV)))
 
+#define UTF8_LATEST        UNICODE_AGE(12, 1, 0)
+
 static inline u8 unicode_major(unsigned int age)
 {
 	return (age >> UNICODE_MAJ_SHIFT) & 0xff;

-- 
2.47.0



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

* [PATCH v8 4/9] unicode: Recreate utf8_parse_version()
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
                   ` (2 preceding siblings ...)
  2024-10-21 16:37 ` [PATCH v8 3/9] unicode: Export latest available UTF-8 version number André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-21 16:37 ` [PATCH v8 5/9] libfs: Export generic_ci_ dentry functions André Almeida
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, André Almeida, Gabriel Krisman Bertazi

All filesystems that currently support UTF-8 casefold can fetch the
UTF-8 version from the filesystem metadata stored on disk. They can get
the data stored and directly match it to a integer, so they can skip the
string parsing step, which motivated the removal of this function in the
first place.

However, for tmpfs, the only way to tell the kernel which UTF-8 version
we are about to use is via mount options, using a string. Re-introduce
utf8_parse_version() to be used by tmpfs.

This version differs from the original by skipping the intermediate step
of copying the version string to an auxiliary string before calling
match_token(). This versions calls match_token() in the argument string.
The paramenters are simpler now as well.

utf8_parse_version() was created by 9d53690f0d4 ("unicode: implement
higher level API for string handling") and later removed by 49bd03cc7e9
("unicode: pass a UNICODE_AGE() tripple to utf8_load").

Signed-off-by: André Almeida <andrealmeid@igalia.com>
Reviewed-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
---
Changes from v3:
- Return version on the return value, instead of returning version at
  function args.
---
 fs/unicode/utf8-core.c  | 26 ++++++++++++++++++++++++++
 include/linux/unicode.h |  2 ++
 2 files changed, 28 insertions(+)

diff --git a/fs/unicode/utf8-core.c b/fs/unicode/utf8-core.c
index 8395066341a437d0c20d6ab49b0a022eac7eec5c..7f7cb14e01ce8aa87d14dffdd767f63a90cf11f7 100644
--- a/fs/unicode/utf8-core.c
+++ b/fs/unicode/utf8-core.c
@@ -214,3 +214,29 @@ void utf8_unload(struct unicode_map *um)
 }
 EXPORT_SYMBOL(utf8_unload);
 
+/**
+ * utf8_parse_version - Parse a UTF-8 version number from a string
+ *
+ * @version: input string
+ *
+ * Returns the parsed version on success, negative code on error
+ */
+int utf8_parse_version(char *version)
+{
+	substring_t args[3];
+	unsigned int maj, min, rev;
+	static const struct match_token token[] = {
+		{1, "%d.%d.%d"},
+		{0, NULL}
+	};
+
+	if (match_token(version, token, args) != 1)
+		return -EINVAL;
+
+	if (match_int(&args[0], &maj) || match_int(&args[1], &min) ||
+	    match_int(&args[2], &rev))
+		return -EINVAL;
+
+	return UNICODE_AGE(maj, min, rev);
+}
+EXPORT_SYMBOL(utf8_parse_version);
diff --git a/include/linux/unicode.h b/include/linux/unicode.h
index 0c0ab04e84ee80227f9390ad0498f21a7ab7d34b..5e6b212a2aedab7ebf4363083339f4c5e9b82f8f 100644
--- a/include/linux/unicode.h
+++ b/include/linux/unicode.h
@@ -78,4 +78,6 @@ int utf8_casefold_hash(const struct unicode_map *um, const void *salt,
 struct unicode_map *utf8_load(unsigned int version);
 void utf8_unload(struct unicode_map *um);
 
+int utf8_parse_version(char *version);
+
 #endif /* _LINUX_UNICODE_H */

-- 
2.47.0



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

* [PATCH v8 5/9] libfs: Export generic_ci_ dentry functions
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
                   ` (3 preceding siblings ...)
  2024-10-21 16:37 ` [PATCH v8 4/9] unicode: Recreate utf8_parse_version() André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-21 16:37 ` [PATCH v8 6/9] tmpfs: Add casefold lookup support André Almeida
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, Gabriel Krisman Bertazi, André Almeida

Export generic_ci_ dentry functions so they can be used by
case-insensitive filesystems that need something more custom than the
default one set by `struct generic_ci_dentry_ops`.

Reviewed-by: Gabriel Krisman Bertazi <gabriel@krisman.be>
Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
Changes from v4:
- Guard func signatures inside IS_ENABLED(CONFIG_UNICODE)
Changes from v3:
- New patch
---
 fs/libfs.c         | 8 +++++---
 include/linux/fs.h | 4 ++++
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/fs/libfs.c b/fs/libfs.c
index 46966fd8bcf9f042e85d0b66134e59fbef83abfd..7b290404c5f9901010ada2f921a214dbc94eb5fa 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1791,8 +1791,8 @@ bool is_empty_dir_inode(struct inode *inode)
  *
  * Return: 0 if names match, 1 if mismatch, or -ERRNO
  */
-static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
-				const char *str, const struct qstr *name)
+int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
+			 const char *str, const struct qstr *name)
 {
 	const struct dentry *parent;
 	const struct inode *dir;
@@ -1835,6 +1835,7 @@ static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
 
 	return utf8_strncasecmp(dentry->d_sb->s_encoding, name, &qstr);
 }
+EXPORT_SYMBOL(generic_ci_d_compare);
 
 /**
  * generic_ci_d_hash - generic d_hash implementation for casefolding filesystems
@@ -1843,7 +1844,7 @@ static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
  *
  * Return: 0 if hash was successful or unchanged, and -EINVAL on error
  */
-static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
+int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
 {
 	const struct inode *dir = READ_ONCE(dentry->d_inode);
 	struct super_block *sb = dentry->d_sb;
@@ -1858,6 +1859,7 @@ static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
 		return -EINVAL;
 	return 0;
 }
+EXPORT_SYMBOL(generic_ci_d_hash);
 
 static const struct dentry_operations generic_ci_dentry_ops = {
 	.d_hash = generic_ci_d_hash,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 403ee5d54c60a0a97e2eba9ef80d8fb4bbd2288f..b277369672a140eba13dce7fa7b0883359c643f8 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3458,6 +3458,10 @@ extern int generic_ci_match(const struct inode *parent,
 			    const u8 *de_name, u32 de_name_len);
 
 #if IS_ENABLED(CONFIG_UNICODE)
+int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str);
+int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
+			 const char *str, const struct qstr *name);
+
 /**
  * generic_ci_validate_strict_name - Check if a given name is suitable
  * for a directory

-- 
2.47.0



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

* [PATCH v8 6/9] tmpfs: Add casefold lookup support
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
                   ` (4 preceding siblings ...)
  2024-10-21 16:37 ` [PATCH v8 5/9] libfs: Export generic_ci_ dentry functions André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-21 16:37 ` [PATCH v8 7/9] tmpfs: Add flag FS_CASEFOLD_FL support for tmpfs dirs André Almeida
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, Gabriel Krisman Bertazi, Gabriel Krisman Bertazi,
	André Almeida

Enable casefold lookup in tmpfs, based on the encoding defined by
userspace. That means that instead of comparing byte per byte a file
name, it compares to a case-insensitive equivalent of the Unicode
string.

* Dcache handling

There's a special need when dealing with case-insensitive dentries.
First of all, we currently invalidated every negative casefold dentries.
That happens because currently VFS code has no proper support to deal
with that, giving that it could incorrectly reuse a previous filename
for a new file that has a casefold match. For instance, this could
happen:

$ mkdir DIR
$ rm -r DIR
$ mkdir dir
$ ls
DIR/

And would be perceived as inconsistency from userspace point of view,
because even that we match files in a case-insensitive manner, we still
honor whatever is the initial filename.

Along with that, tmpfs stores only the first equivalent name dentry used
in the dcache, preventing duplications of dentries in the dcache. The
d_compare() version for casefold files uses a normalized string, so the
filename under lookup will be compared to another normalized string for
the existing file, achieving a casefolded lookup.

* Enabling casefold via mount options

Most filesystems have their data stored in disk, so casefold option need
to be enabled when building a filesystem on a device (via mkfs).
However, as tmpfs is a RAM backed filesystem, there's no disk
information and thus no mkfs to store information about casefold.

For tmpfs, create casefold options for mounting. Userspace can then
enable casefold support for a mount point using:

$ mount -t tmpfs -o casefold=utf8-12.1.0 fs_name mount_dir/

Userspace must set what Unicode standard is aiming to. The available
options depends on what the kernel Unicode subsystem supports.

And for strict encoding:

$ mount -t tmpfs -o casefold=utf8-12.1.0,strict_encoding fs_name mount_dir/

Strict encoding means that tmpfs will refuse to create invalid UTF-8
sequences. When this option is not enabled, any invalid sequence will be
treated as an opaque byte sequence, ignoring the encoding thus not being
able to be looked up in a case-insensitive way.

* Check for casefold dirs on simple_lookup()

On simple_lookup(), do not create dentries for casefold directories.
Currently, VFS does not support case-insensitive negative dentries and
can create inconsistencies in the filesystem. Prevent such dentries to
being created in the first place.

Reviewed-by: Gabriel Krisman Bertazi <gabriel@krisman.be>
Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
Changes from v7:
- Consistently guard `encoding` and `strict_encoding` fields from struct
  shmem_options, so those fields only exists with CONFIG_UNICODE.

Changes from v6:
- Dropped patch "tmpfs: Always set simple_dentry_operations as dentry ops"
- Re-place generic_ci_validate_strict_name() before inode creation

Changes from v4:
- Squash commit Check for casefold dirs on simple_lookup() here
- Fails to mount if strict_encoding is used without encoding
- tmpfs doesn't support fscrypt, so I dropped d_revalidate line

Changes from v3:
- Simplified shmem_parse_opt_casefold()
- sb->s_d_op is set to shmem_ci_dentry_ops during mount time
- got rid of shmem_lookup(), modified simple_lookup()

Changes from v2:
- simple_lookup() now sets d_ops
- reworked shmem_parse_opt_casefold()
- if `mount -o casefold` has no param, load latest UTF-8 version
- using (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir) when possible
---
 fs/libfs.c |   4 ++
 mm/shmem.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 127 insertions(+), 4 deletions(-)

diff --git a/fs/libfs.c b/fs/libfs.c
index 7b290404c5f9901010ada2f921a214dbc94eb5fa..a168ece5cc61b74114f537f5b7b8a07f2d48b2aa 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -77,6 +77,10 @@ struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned
 		return ERR_PTR(-ENAMETOOLONG);
 	if (!dentry->d_sb->s_d_op)
 		d_set_d_op(dentry, &simple_dentry_operations);
+
+	if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
+		return NULL;
+
 	d_add(dentry, NULL);
 	return NULL;
 }
diff --git a/mm/shmem.c b/mm/shmem.c
index c5adb987b23cf9ba5b8117ece2b467a434f7c0a3..f26488ff3d6ae1abb9b63d55ca74909249dbf4eb 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -40,6 +40,7 @@
 #include <linux/fs_parser.h>
 #include <linux/swapfile.h>
 #include <linux/iversion.h>
+#include <linux/unicode.h>
 #include "swap.h"
 
 static struct vfsmount *shm_mnt __ro_after_init;
@@ -123,6 +124,10 @@ struct shmem_options {
 	bool noswap;
 	unsigned short quota_types;
 	struct shmem_quota_limits qlimits;
+#if IS_ENABLED(CONFIG_UNICODE)
+	struct unicode_map *encoding;
+	bool strict_encoding;
+#endif
 #define SHMEM_SEEN_BLOCKS 1
 #define SHMEM_SEEN_INODES 2
 #define SHMEM_SEEN_HUGE 4
@@ -3565,6 +3570,9 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir,
 	struct inode *inode;
 	int error;
 
+	if (!generic_ci_validate_strict_name(dir, &dentry->d_name))
+		return -EINVAL;
+
 	inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, dev, VM_NORESERVE);
 	if (IS_ERR(inode))
 		return PTR_ERR(inode);
@@ -3584,7 +3592,12 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir,
 	dir->i_size += BOGO_DIRENT_SIZE;
 	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
 	inode_inc_iversion(dir);
-	d_instantiate(dentry, inode);
+
+	if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
+		d_add(dentry, inode);
+	else
+		d_instantiate(dentry, inode);
+
 	dget(dentry); /* Extra count - pin the dentry in core */
 	return error;
 
@@ -3675,7 +3688,10 @@ static int shmem_link(struct dentry *old_dentry, struct inode *dir,
 	inc_nlink(inode);
 	ihold(inode);	/* New dentry reference */
 	dget(dentry);	/* Extra pinning count for the created dentry */
-	d_instantiate(dentry, inode);
+	if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
+		d_add(dentry, inode);
+	else
+		d_instantiate(dentry, inode);
 out:
 	return ret;
 }
@@ -3695,6 +3711,14 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry)
 	inode_inc_iversion(dir);
 	drop_nlink(inode);
 	dput(dentry);	/* Undo the count from "create" - does all the work */
+
+	/*
+	 * For now, VFS can't deal with case-insensitive negative dentries, so
+	 * we invalidate them
+	 */
+	if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
+		d_invalidate(dentry);
+
 	return 0;
 }
 
@@ -3839,7 +3863,10 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir,
 	dir->i_size += BOGO_DIRENT_SIZE;
 	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
 	inode_inc_iversion(dir);
-	d_instantiate(dentry, inode);
+	if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
+		d_add(dentry, inode);
+	else
+		d_instantiate(dentry, inode);
 	dget(dentry);
 	return 0;
 
@@ -4192,6 +4219,9 @@ enum shmem_param {
 	Opt_usrquota_inode_hardlimit,
 	Opt_grpquota_block_hardlimit,
 	Opt_grpquota_inode_hardlimit,
+	Opt_casefold_version,
+	Opt_casefold,
+	Opt_strict_encoding,
 };
 
 static const struct constant_table shmem_param_enums_huge[] = {
@@ -4223,9 +4253,54 @@ const struct fs_parameter_spec shmem_fs_parameters[] = {
 	fsparam_string("grpquota_block_hardlimit", Opt_grpquota_block_hardlimit),
 	fsparam_string("grpquota_inode_hardlimit", Opt_grpquota_inode_hardlimit),
 #endif
+	fsparam_string("casefold",	Opt_casefold_version),
+	fsparam_flag  ("casefold",	Opt_casefold),
+	fsparam_flag  ("strict_encoding", Opt_strict_encoding),
 	{}
 };
 
+#if IS_ENABLED(CONFIG_UNICODE)
+static int shmem_parse_opt_casefold(struct fs_context *fc, struct fs_parameter *param,
+				    bool latest_version)
+{
+	struct shmem_options *ctx = fc->fs_private;
+	unsigned int version = UTF8_LATEST;
+	struct unicode_map *encoding;
+	char *version_str = param->string + 5;
+
+	if (!latest_version) {
+		if (strncmp(param->string, "utf8-", 5))
+			return invalfc(fc, "Only UTF-8 encodings are supported "
+				       "in the format: utf8-<version number>");
+
+		version = utf8_parse_version(version_str);
+		if (version < 0)
+			return invalfc(fc, "Invalid UTF-8 version: %s", version_str);
+	}
+
+	encoding = utf8_load(version);
+
+	if (IS_ERR(encoding)) {
+		return invalfc(fc, "Failed loading UTF-8 version: utf8-%u.%u.%u\n",
+			       unicode_major(version), unicode_minor(version),
+			       unicode_rev(version));
+	}
+
+	pr_info("tmpfs: Using encoding : utf8-%u.%u.%u\n",
+		unicode_major(version), unicode_minor(version), unicode_rev(version));
+
+	ctx->encoding = encoding;
+
+	return 0;
+}
+#else
+static int shmem_parse_opt_casefold(struct fs_context *fc, struct fs_parameter *param,
+				    bool latest_version)
+{
+	return invalfc(fc, "tmpfs: Kernel not built with CONFIG_UNICODE\n");
+}
+#endif
+
 static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
 {
 	struct shmem_options *ctx = fc->fs_private;
@@ -4384,6 +4459,17 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
 				       "Group quota inode hardlimit too large.");
 		ctx->qlimits.grpquota_ihardlimit = size;
 		break;
+	case Opt_casefold_version:
+		return shmem_parse_opt_casefold(fc, param, false);
+	case Opt_casefold:
+		return shmem_parse_opt_casefold(fc, param, true);
+	case Opt_strict_encoding:
+#if IS_ENABLED(CONFIG_UNICODE)
+		ctx->strict_encoding = true;
+		break;
+#else
+		return invalfc(fc, "tmpfs: Kernel not built with CONFIG_UNICODE\n");
+#endif
 	}
 	return 0;
 
@@ -4613,6 +4699,11 @@ static void shmem_put_super(struct super_block *sb)
 {
 	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 
+#if IS_ENABLED(CONFIG_UNICODE)
+	if (sb->s_encoding)
+		utf8_unload(sb->s_encoding);
+#endif
+
 #ifdef CONFIG_TMPFS_QUOTA
 	shmem_disable_quotas(sb);
 #endif
@@ -4623,6 +4714,14 @@ static void shmem_put_super(struct super_block *sb)
 	sb->s_fs_info = NULL;
 }
 
+#if IS_ENABLED(CONFIG_UNICODE) && defined(CONFIG_TMPFS)
+static const struct dentry_operations shmem_ci_dentry_ops = {
+	.d_hash = generic_ci_d_hash,
+	.d_compare = generic_ci_d_compare,
+	.d_delete = always_delete_dentry,
+};
+#endif
+
 static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct shmem_options *ctx = fc->fs_private;
@@ -4657,9 +4756,25 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
 	}
 	sb->s_export_op = &shmem_export_ops;
 	sb->s_flags |= SB_NOSEC | SB_I_VERSION;
+
+#if IS_ENABLED(CONFIG_UNICODE)
+	if (!ctx->encoding && ctx->strict_encoding) {
+		pr_err("tmpfs: strict_encoding option without encoding is forbidden\n");
+		error = -EINVAL;
+		goto failed;
+	}
+
+	if (ctx->encoding) {
+		sb->s_encoding = ctx->encoding;
+		sb->s_d_op = &shmem_ci_dentry_ops;
+		if (ctx->strict_encoding)
+			sb->s_encoding_flags = SB_ENC_STRICT_MODE_FL;
+	}
+#endif
+
 #else
 	sb->s_flags |= SB_NOUSER;
-#endif
+#endif /* CONFIG_TMPFS */
 	sbinfo->max_blocks = ctx->blocks;
 	sbinfo->max_inodes = ctx->inodes;
 	sbinfo->free_ispace = sbinfo->max_inodes * BOGO_INODE_SIZE;
@@ -4933,6 +5048,10 @@ int shmem_init_fs_context(struct fs_context *fc)
 	ctx->uid = current_fsuid();
 	ctx->gid = current_fsgid();
 
+#if IS_ENABLED(CONFIG_UNICODE)
+	ctx->encoding = NULL;
+#endif
+
 	fc->fs_private = ctx;
 	fc->ops = &shmem_fs_context_ops;
 	return 0;

-- 
2.47.0



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

* [PATCH v8 7/9] tmpfs: Add flag FS_CASEFOLD_FL support for tmpfs dirs
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
                   ` (5 preceding siblings ...)
  2024-10-21 16:37 ` [PATCH v8 6/9] tmpfs: Add casefold lookup support André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-21 16:37 ` [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs André Almeida
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, Gabriel Krisman Bertazi, Gabriel Krisman Bertazi,
	André Almeida

Enable setting flag FS_CASEFOLD_FL for tmpfs directories, when tmpfs is
mounted with casefold support. A special check is need for this flag,
since it can't be set for non-empty directories.

Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
Reviewed-by: Gabriel Krisman Bertazi <gabriel@krisman.be>
Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
Changes from v2:
- Fixed bug when adding a non-casefold flag in a non-empty dir
---
 include/linux/shmem_fs.h |  6 ++---
 mm/shmem.c               | 70 +++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 67 insertions(+), 9 deletions(-)

diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index 515a9a6a3c6f82c55952d821887514217a6a00d1..018da28c01e7d71b8fb00bfb23c000248c8a83f4 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -42,10 +42,10 @@ struct shmem_inode_info {
 	struct inode		vfs_inode;
 };
 
-#define SHMEM_FL_USER_VISIBLE		FS_FL_USER_VISIBLE
+#define SHMEM_FL_USER_VISIBLE		(FS_FL_USER_VISIBLE | FS_CASEFOLD_FL)
 #define SHMEM_FL_USER_MODIFIABLE \
-	(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL)
-#define SHMEM_FL_INHERITED		(FS_NODUMP_FL | FS_NOATIME_FL)
+	(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL | FS_CASEFOLD_FL)
+#define SHMEM_FL_INHERITED		(FS_NODUMP_FL | FS_NOATIME_FL | FS_CASEFOLD_FL)
 
 struct shmem_quota_limits {
 	qsize_t usrquota_bhardlimit; /* Default user quota block hard limit */
diff --git a/mm/shmem.c b/mm/shmem.c
index f26488ff3d6ae1abb9b63d55ca74909249dbf4eb..ea01628e443423d82d44277e085b867ab9bf4b28 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2757,13 +2757,62 @@ static int shmem_file_open(struct inode *inode, struct file *file)
 #ifdef CONFIG_TMPFS_XATTR
 static int shmem_initxattrs(struct inode *, const struct xattr *, void *);
 
+#if IS_ENABLED(CONFIG_UNICODE)
+/*
+ * shmem_inode_casefold_flags - Deal with casefold file attribute flag
+ *
+ * The casefold file attribute needs some special checks. I can just be added to
+ * an empty dir, and can't be removed from a non-empty dir.
+ */
+static int shmem_inode_casefold_flags(struct inode *inode, unsigned int fsflags,
+				      struct dentry *dentry, unsigned int *i_flags)
+{
+	unsigned int old = inode->i_flags;
+	struct super_block *sb = inode->i_sb;
+
+	if (fsflags & FS_CASEFOLD_FL) {
+		if (!(old & S_CASEFOLD)) {
+			if (!sb->s_encoding)
+				return -EOPNOTSUPP;
+
+			if (!S_ISDIR(inode->i_mode))
+				return -ENOTDIR;
+
+			if (dentry && !simple_empty(dentry))
+				return -ENOTEMPTY;
+		}
+
+		*i_flags = *i_flags | S_CASEFOLD;
+	} else if (old & S_CASEFOLD) {
+		if (dentry && !simple_empty(dentry))
+			return -ENOTEMPTY;
+	}
+
+	return 0;
+}
+#else
+static int shmem_inode_casefold_flags(struct inode *inode, unsigned int fsflags,
+				      struct dentry *dentry, unsigned int *i_flags)
+{
+	if (fsflags & FS_CASEFOLD_FL)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+#endif
+
 /*
  * chattr's fsflags are unrelated to extended attributes,
  * but tmpfs has chosen to enable them under the same config option.
  */
-static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
+static int shmem_set_inode_flags(struct inode *inode, unsigned int fsflags, struct dentry *dentry)
 {
 	unsigned int i_flags = 0;
+	int ret;
+
+	ret = shmem_inode_casefold_flags(inode, fsflags, dentry, &i_flags);
+	if (ret)
+		return ret;
 
 	if (fsflags & FS_NOATIME_FL)
 		i_flags |= S_NOATIME;
@@ -2774,10 +2823,12 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
 	/*
 	 * But FS_NODUMP_FL does not require any action in i_flags.
 	 */
-	inode_set_flags(inode, i_flags, S_NOATIME | S_APPEND | S_IMMUTABLE);
+	inode_set_flags(inode, i_flags, S_NOATIME | S_APPEND | S_IMMUTABLE | S_CASEFOLD);
+
+	return 0;
 }
 #else
-static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
+static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags, struct dentry *dentry)
 {
 }
 #define shmem_initxattrs NULL
@@ -2824,7 +2875,7 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
 	info->fsflags = (dir == NULL) ? 0 :
 		SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED;
 	if (info->fsflags)
-		shmem_set_inode_flags(inode, info->fsflags);
+		shmem_set_inode_flags(inode, info->fsflags, NULL);
 	INIT_LIST_HEAD(&info->shrinklist);
 	INIT_LIST_HEAD(&info->swaplist);
 	simple_xattrs_init(&info->xattrs);
@@ -3931,16 +3982,23 @@ static int shmem_fileattr_set(struct mnt_idmap *idmap,
 {
 	struct inode *inode = d_inode(dentry);
 	struct shmem_inode_info *info = SHMEM_I(inode);
+	int ret, flags;
 
 	if (fileattr_has_fsx(fa))
 		return -EOPNOTSUPP;
 	if (fa->flags & ~SHMEM_FL_USER_MODIFIABLE)
 		return -EOPNOTSUPP;
 
-	info->fsflags = (info->fsflags & ~SHMEM_FL_USER_MODIFIABLE) |
+	flags = (info->fsflags & ~SHMEM_FL_USER_MODIFIABLE) |
 		(fa->flags & SHMEM_FL_USER_MODIFIABLE);
 
-	shmem_set_inode_flags(inode, info->fsflags);
+	ret = shmem_set_inode_flags(inode, flags, dentry);
+
+	if (ret)
+		return ret;
+
+	info->fsflags = flags;
+
 	inode_set_ctime_current(inode);
 	inode_inc_iversion(inode);
 	return 0;

-- 
2.47.0



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

* [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
                   ` (6 preceding siblings ...)
  2024-10-21 16:37 ` [PATCH v8 7/9] tmpfs: Add flag FS_CASEFOLD_FL support for tmpfs dirs André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-31  5:18   ` Nathan Chancellor
  2024-10-21 16:37 ` [PATCH v8 9/9] docs: tmpfs: Add casefold options André Almeida
  2024-10-28 12:37 ` [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs Christian Brauner
  9 siblings, 1 reply; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, André Almeida, Gabriel Krisman Bertazi

Expose filesystem features through sysfs, so userspace can query if
tmpfs support casefold.

This follows the same setup as defined by ext4 and f2fs to expose
casefold support to userspace.

Signed-off-by: André Almeida <andrealmeid@igalia.com>
Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
---
 mm/shmem.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/mm/shmem.c b/mm/shmem.c
index ea01628e443423d82d44277e085b867ab9bf4b28..0739143d1419c732359d3a3c3457c3acb90c5b22 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -5546,3 +5546,40 @@ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
 	return page;
 }
 EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);
+
+#if defined(CONFIG_SYSFS) && defined(CONFIG_TMPFS)
+#if IS_ENABLED(CONFIG_UNICODE)
+static DEVICE_STRING_ATTR_RO(casefold, 0444, "supported");
+#endif
+
+static struct attribute *tmpfs_attributes[] = {
+#if IS_ENABLED(CONFIG_UNICODE)
+	&dev_attr_casefold.attr.attr,
+#endif
+	NULL
+};
+
+static const struct attribute_group tmpfs_attribute_group = {
+	.attrs = tmpfs_attributes,
+	.name = "features"
+};
+
+static struct kobject *tmpfs_kobj;
+
+static int __init tmpfs_sysfs_init(void)
+{
+	int ret;
+
+	tmpfs_kobj = kobject_create_and_add("tmpfs", fs_kobj);
+	if (!tmpfs_kobj)
+		return -ENOMEM;
+
+	ret = sysfs_create_group(tmpfs_kobj, &tmpfs_attribute_group);
+	if (ret)
+		kobject_put(tmpfs_kobj);
+
+	return ret;
+}
+
+fs_initcall(tmpfs_sysfs_init);
+#endif /* CONFIG_SYSFS && CONFIG_TMPFS */

-- 
2.47.0



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

* [PATCH v8 9/9] docs: tmpfs: Add casefold options
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
                   ` (7 preceding siblings ...)
  2024-10-21 16:37 ` [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs André Almeida
@ 2024-10-21 16:37 ` André Almeida
  2024-10-28 12:37 ` [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs Christian Brauner
  9 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-21 16:37 UTC (permalink / raw)
  To: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, Gabriel Krisman Bertazi, Randy Dunlap,
	André Almeida

Document mounting options for casefold support in tmpfs.

Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
Changes from v3:
- Rewrote note about "this doesn't enable casefold by default" (Krisman)
---
 Documentation/filesystems/tmpfs.rst | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/Documentation/filesystems/tmpfs.rst b/Documentation/filesystems/tmpfs.rst
index 56a26c843dbe964086503dda9b4e8066a1242d72..d677e0428c3f68148a3761bb232bbed5b9a41f76 100644
--- a/Documentation/filesystems/tmpfs.rst
+++ b/Documentation/filesystems/tmpfs.rst
@@ -241,6 +241,28 @@ So 'mount -t tmpfs -o size=10G,nr_inodes=10k,mode=700 tmpfs /mytmpfs'
 will give you tmpfs instance on /mytmpfs which can allocate 10GB
 RAM/SWAP in 10240 inodes and it is only accessible by root.
 
+tmpfs has the following mounting options for case-insensitive lookup support:
+
+================= ==============================================================
+casefold          Enable casefold support at this mount point using the given
+                  argument as the encoding standard. Currently only UTF-8
+                  encodings are supported. If no argument is used, it will load
+                  the latest UTF-8 encoding available.
+strict_encoding   Enable strict encoding at this mount point (disabled by
+                  default). In this mode, the filesystem refuses to create file
+                  and directory with names containing invalid UTF-8 characters.
+================= ==============================================================
+
+This option doesn't render the entire filesystem case-insensitive. One needs to
+still set the casefold flag per directory, by flipping the +F attribute in an
+empty directory. Nevertheless, new directories will inherit the attribute. The
+mountpoint itself cannot be made case-insensitive.
+
+Example::
+
+    $ mount -t tmpfs -o casefold=utf8-12.1.0,strict_encoding fs_name /mytmpfs
+    $ mount -t tmpfs -o casefold fs_name /mytmpfs
+
 
 :Author:
    Christoph Rohland <cr@sap.com>, 1.12.01
@@ -250,3 +272,5 @@ RAM/SWAP in 10240 inodes and it is only accessible by root.
    KOSAKI Motohiro, 16 Mar 2010
 :Updated:
    Chris Down, 13 July 2020
+:Updated:
+   André Almeida, 23 Aug 2024

-- 
2.47.0



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

* Re: [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs
  2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
                   ` (8 preceding siblings ...)
  2024-10-21 16:37 ` [PATCH v8 9/9] docs: tmpfs: Add casefold options André Almeida
@ 2024-10-28 12:37 ` Christian Brauner
  2024-10-28 13:58   ` André Almeida
  9 siblings, 1 reply; 16+ messages in thread
From: Christian Brauner @ 2024-10-28 12:37 UTC (permalink / raw)
  To: André Almeida
  Cc: Christian Brauner, kernel-dev, linux-fsdevel, linux-kernel,
	linux-ext4, linux-mm, linux-doc, Gabriel Krisman Bertazi,
	Gabriel Krisman Bertazi, Randy Dunlap, Gabriel Krisman Bertazi,
	Alexander Viro, Jan Kara, Theodore Ts'o, Andreas Dilger,
	Hugh Dickins, Andrew Morton, Jonathan Corbet, smcv

On Mon, 21 Oct 2024 13:37:16 -0300, André Almeida wrote:
> This patchset adds support for case-insensitive file names lookups in
> tmpfs. The main difference from other casefold filesystems is that tmpfs
> has no information on disk, just on RAM, so we can't use mkfs to create a
> case-insensitive tmpfs.  For this implementation, I opted to have a mount
> option for casefolding. The rest of the patchset follows a similar approach
> as ext4 and f2fs.
> 
> [...]

Applied to the vfs.tmpfs branch of the vfs/vfs.git tree.
Patches in the vfs.tmpfs branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs.tmpfs

[1/9] libfs: Create the helper function generic_ci_validate_strict_name()
      https://git.kernel.org/vfs/vfs/c/0e152beb5aa1
[2/9] ext4: Use generic_ci_validate_strict_name helper
      https://git.kernel.org/vfs/vfs/c/3f5ad0d21db8
[3/9] unicode: Export latest available UTF-8 version number
      https://git.kernel.org/vfs/vfs/c/04dad6c6d37d
[4/9] unicode: Recreate utf8_parse_version()
      https://git.kernel.org/vfs/vfs/c/142fa60f61f9
[5/9] libfs: Export generic_ci_ dentry functions
      https://git.kernel.org/vfs/vfs/c/458532c8dfeb
[6/9] tmpfs: Add casefold lookup support
      https://git.kernel.org/vfs/vfs/c/58e55efd6c72
[7/9] tmpfs: Add flag FS_CASEFOLD_FL support for tmpfs dirs
      https://git.kernel.org/vfs/vfs/c/5cd9aecbc72c
[8/9] tmpfs: Expose filesystem features via sysfs
      https://git.kernel.org/vfs/vfs/c/5132f08bd332
[9/9] docs: tmpfs: Add casefold options
      https://git.kernel.org/vfs/vfs/c/a713f830c903


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

* Re: [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs
  2024-10-28 12:37 ` [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs Christian Brauner
@ 2024-10-28 13:58   ` André Almeida
  0 siblings, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-28 13:58 UTC (permalink / raw)
  To: Christian Brauner
  Cc: kernel-dev, linux-fsdevel, linux-kernel, linux-ext4, linux-mm,
	linux-doc, Gabriel Krisman Bertazi, Gabriel Krisman Bertazi,
	Randy Dunlap, Gabriel Krisman Bertazi, Alexander Viro, Jan Kara,
	Theodore Ts'o, Andreas Dilger, Hugh Dickins, Andrew Morton,
	Jonathan Corbet, smcv

Em 28/10/2024 09:37, Christian Brauner escreveu:
> On Mon, 21 Oct 2024 13:37:16 -0300, André Almeida wrote:
>> This patchset adds support for case-insensitive file names lookups in
>> tmpfs. The main difference from other casefold filesystems is that tmpfs
>> has no information on disk, just on RAM, so we can't use mkfs to create a
>> case-insensitive tmpfs.  For this implementation, I opted to have a mount
>> option for casefolding. The rest of the patchset follows a similar approach
>> as ext4 and f2fs.
>>
>> [...]
> 
> Applied to the vfs.tmpfs branch of the vfs/vfs.git tree.
> Patches in the vfs.tmpfs branch should appear in linux-next soon.
> 

Thanks!


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

* Re: [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs
  2024-10-21 16:37 ` [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs André Almeida
@ 2024-10-31  5:18   ` Nathan Chancellor
  2024-10-31 17:31     ` André Almeida
  0 siblings, 1 reply; 16+ messages in thread
From: Nathan Chancellor @ 2024-10-31  5:18 UTC (permalink / raw)
  To: André Almeida
  Cc: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv, kernel-dev, linux-fsdevel,
	linux-kernel, linux-ext4, linux-mm, linux-doc,
	Gabriel Krisman Bertazi, llvm

Hi André,

On Mon, Oct 21, 2024 at 01:37:24PM -0300, André Almeida wrote:
> Expose filesystem features through sysfs, so userspace can query if
> tmpfs support casefold.
> 
> This follows the same setup as defined by ext4 and f2fs to expose
> casefold support to userspace.
> 
> Signed-off-by: André Almeida <andrealmeid@igalia.com>
> Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
> ---
>  mm/shmem.c | 37 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 37 insertions(+)
> 
> diff --git a/mm/shmem.c b/mm/shmem.c
> index ea01628e443423d82d44277e085b867ab9bf4b28..0739143d1419c732359d3a3c3457c3acb90c5b22 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -5546,3 +5546,40 @@ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
>  	return page;
>  }
>  EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);
> +
> +#if defined(CONFIG_SYSFS) && defined(CONFIG_TMPFS)
> +#if IS_ENABLED(CONFIG_UNICODE)
> +static DEVICE_STRING_ATTR_RO(casefold, 0444, "supported");
> +#endif
> +
> +static struct attribute *tmpfs_attributes[] = {
> +#if IS_ENABLED(CONFIG_UNICODE)
> +	&dev_attr_casefold.attr.attr,
> +#endif
> +	NULL
> +};
> +
> +static const struct attribute_group tmpfs_attribute_group = {
> +	.attrs = tmpfs_attributes,
> +	.name = "features"
> +};
> +
> +static struct kobject *tmpfs_kobj;
> +
> +static int __init tmpfs_sysfs_init(void)
> +{
> +	int ret;
> +
> +	tmpfs_kobj = kobject_create_and_add("tmpfs", fs_kobj);
> +	if (!tmpfs_kobj)
> +		return -ENOMEM;
> +
> +	ret = sysfs_create_group(tmpfs_kobj, &tmpfs_attribute_group);
> +	if (ret)
> +		kobject_put(tmpfs_kobj);
> +
> +	return ret;
> +}
> +
> +fs_initcall(tmpfs_sysfs_init);
> +#endif /* CONFIG_SYSFS && CONFIG_TMPFS */
> 
> -- 
> 2.47.0
> 

This change as commit 5132f08bd332 ("tmpfs: Expose filesystem features
via sysfs") in -next introduces a kCFI violation when accessing
/sys/fs/tmpfs/features/casefold. An attribute group created with
sysfs_create_group() has ->sysfs_ops() set to kobj_sysfs_ops, which has
a ->show() value of kobj_attr_show(). When kobj_attr_show() goes to call
the attribute's ->show() value after container_of(), there will be a
type mismatch in the case of the casefold attr, as it was defined with a
->show() value of device_show_string() but that does not match the type
of ->show() in 'struct kobj_attribute'.

I can easily reproduce this with the following commands:

  $ printf 'CONFIG_%s=y\n' CFI_CLANG UNICODE >kernel/configs/repro.config

  $ make -skj"$(nproc)" ARCH=arm64 LLVM=1 mrproper virtconfig repro.config Image.gz
  ...

  $ curl -LSs https://github.com/ClangBuiltLinux/boot-utils/releases/download/20230707-182910/arm64-rootfs.cpio.zst | zstd -d >rootfs.cpio

  $ qemu-system-aarch64 \
      -display none \
      -nodefaults \
      -cpu max,pauth-impdef=true \
      -machine virt,gic-version=max,virtualization=true \
      -append 'console=ttyAMA0 earlycon rdinit=/bin/sh' \
      -kernel arch/arm64/boot/Image.gz \
      -initrd rootfs.cpio \
      -m 512m \
      -serial mon:stdio
  ...
  # mount -t sysfs sys /sys
  # cat /sys/fs/tmpfs/features/casefold
  [   70.558496] CFI failure at kobj_attr_show+0x2c/0x4c (target: device_show_string+0x0/0x38; expected type: 0xc527b809)
  [   70.560018] Internal error: Oops - CFI: 00000000f2008228 [#1] PREEMPT SMP
  [   70.560647] Modules linked in:
  [   70.561770] CPU: 0 UID: 0 PID: 46 Comm: cat Not tainted 6.12.0-rc4-00008-g5132f08bd332 #1
  [   70.562429] Hardware name: linux,dummy-virt (DT)
  [   70.562897] pstate: 21402009 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
  [   70.563377] pc : kobj_attr_show+0x2c/0x4c
  [   70.563674] lr : sysfs_kf_seq_show+0xb4/0x130
  [   70.563987] sp : ffff80008043bac0
  [   70.564236] x29: ffff80008043bac0 x28: 000000007ffff001 x27: 0000000000000000
  [   70.564877] x26: 0000000001000000 x25: 000000007ffff001 x24: 0000000000000001
  [   70.565339] x23: fff000000238a000 x22: ffff9fa31a3996f8 x21: fff00000023fc000
  [   70.565806] x20: fff000000201df80 x19: fff000000238b000 x18: 0000000000000000
  [   70.566273] x17: 00000000c527b809 x16: 00000000df43c25c x15: fff000001fef8200
  [   70.566727] x14: 0000000000000000 x13: fff00000022450f0 x12: 0000000000001000
  [   70.567177] x11: fff00000023fc000 x10: 0000000000000000 x9 : ffff9fa31a18fac4
  [   70.567682] x8 : ffff9fa319badde4 x7 : 0000000000000000 x6 : 000000000000003f
  [   70.568138] x5 : 0000000000000040 x4 : 0000000000000000 x3 : 0000000000000004
  [   70.568585] x2 : fff00000023fc000 x1 : ffff9fa31a881f90 x0 : fff000000201df80
  [   70.569169] Call trace:
  [   70.569389]  kobj_attr_show+0x2c/0x4c
  [   70.569706]  sysfs_kf_seq_show+0xb4/0x130
  [   70.570020]  kernfs_seq_show+0x44/0x54
  [   70.570280]  seq_read_iter+0x14c/0x4b0
  [   70.570543]  kernfs_fop_read_iter+0x60/0x198
  [   70.570820]  copy_splice_read+0x1f0/0x2f4
  [   70.571092]  splice_direct_to_actor+0xf4/0x2e0
  [   70.571376]  do_splice_direct+0x68/0xb8
  [   70.571626]  do_sendfile+0x1e8/0x488
  [   70.571874]  __arm64_sys_sendfile64+0xe0/0x12c
  [   70.572161]  invoke_syscall+0x58/0x114
  [   70.572424]  el0_svc_common+0xa8/0xdc
  [   70.572676]  do_el0_svc+0x1c/0x28
  [   70.572910]  el0_svc+0x38/0x68
  [   70.573132]  el0t_64_sync_handler+0x90/0xfc
  [   70.573394]  el0t_64_sync+0x190/0x19
  [   70.574001] Code: 72970131 72b8a4f1 6b11021f 54000040 (d4304500)
  [   70.574635] ---[ end trace 0000000000000000 ]---

I am not sure if there is a better API exists or if a local copy should
be rolled but I think the current scheme is definitely wrong because
there is no 'struct device' here.

If there is any patch I can test or further information I can provide, I
am more than happy to do so.

Cheers,
Nathan


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

* Re: [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs
  2024-10-31  5:18   ` Nathan Chancellor
@ 2024-10-31 17:31     ` André Almeida
  2024-10-31 18:13       ` Nathan Chancellor
  2024-10-31 19:11       ` André Almeida
  0 siblings, 2 replies; 16+ messages in thread
From: André Almeida @ 2024-10-31 17:31 UTC (permalink / raw)
  To: Nathan Chancellor
  Cc: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv, kernel-dev, linux-fsdevel,
	linux-kernel, linux-ext4, linux-mm, linux-doc,
	Gabriel Krisman Bertazi, llvm, linux-btrfs, Chris Mason

Hi Nathan,

Em 31/10/2024 02:18, Nathan Chancellor escreveu:
> Hi André,
> 
> On Mon, Oct 21, 2024 at 01:37:24PM -0300, André Almeida wrote:
>> Expose filesystem features through sysfs, so userspace can query if
>> tmpfs support casefold.
>>
>> This follows the same setup as defined by ext4 and f2fs to expose
>> casefold support to userspace.
>>
>> Signed-off-by: André Almeida <andrealmeid@igalia.com>
>> Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
>> ---
>>   mm/shmem.c | 37 +++++++++++++++++++++++++++++++++++++
>>   1 file changed, 37 insertions(+)
>>
>> diff --git a/mm/shmem.c b/mm/shmem.c
>> index ea01628e443423d82d44277e085b867ab9bf4b28..0739143d1419c732359d3a3c3457c3acb90c5b22 100644
>> --- a/mm/shmem.c
>> +++ b/mm/shmem.c
>> @@ -5546,3 +5546,40 @@ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
>>   	return page;
>>   }
>>   EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);
>> +
>> +#if defined(CONFIG_SYSFS) && defined(CONFIG_TMPFS)
>> +#if IS_ENABLED(CONFIG_UNICODE)
>> +static DEVICE_STRING_ATTR_RO(casefold, 0444, "supported");
>> +#endif
>> +
>> +static struct attribute *tmpfs_attributes[] = {
>> +#if IS_ENABLED(CONFIG_UNICODE)
>> +	&dev_attr_casefold.attr.attr,
>> +#endif
>> +	NULL
>> +};
>> +
>> +static const struct attribute_group tmpfs_attribute_group = {
>> +	.attrs = tmpfs_attributes,
>> +	.name = "features"
>> +};
>> +
>> +static struct kobject *tmpfs_kobj;
>> +
>> +static int __init tmpfs_sysfs_init(void)
>> +{
>> +	int ret;
>> +
>> +	tmpfs_kobj = kobject_create_and_add("tmpfs", fs_kobj);
>> +	if (!tmpfs_kobj)
>> +		return -ENOMEM;
>> +
>> +	ret = sysfs_create_group(tmpfs_kobj, &tmpfs_attribute_group);
>> +	if (ret)
>> +		kobject_put(tmpfs_kobj);
>> +
>> +	return ret;
>> +}
>> +
>> +fs_initcall(tmpfs_sysfs_init);
>> +#endif /* CONFIG_SYSFS && CONFIG_TMPFS */
>>
>> -- 
>> 2.47.0
>>
> 
> This change as commit 5132f08bd332 ("tmpfs: Expose filesystem features
> via sysfs") in -next introduces a kCFI violation when accessing
> /sys/fs/tmpfs/features/casefold. An attribute group created with
> sysfs_create_group() has ->sysfs_ops() set to kobj_sysfs_ops, which has
> a ->show() value of kobj_attr_show(). When kobj_attr_show() goes to call
> the attribute's ->show() value after container_of(), there will be a
> type mismatch in the case of the casefold attr, as it was defined with a
> ->show() value of device_show_string() but that does not match the type
> of ->show() in 'struct kobj_attribute'.
> 
> I can easily reproduce this with the following commands:
> 
>    $ printf 'CONFIG_%s=y\n' CFI_CLANG UNICODE >kernel/configs/repro.config
> 
>    $ make -skj"$(nproc)" ARCH=arm64 LLVM=1 mrproper virtconfig repro.config Image.gz
>    ...
> 
>    $ curl -LSs https://github.com/ClangBuiltLinux/boot-utils/releases/download/20230707-182910/arm64-rootfs.cpio.zst | zstd -d >rootfs.cpio
> 
>    $ qemu-system-aarch64 \
>        -display none \
>        -nodefaults \
>        -cpu max,pauth-impdef=true \
>        -machine virt,gic-version=max,virtualization=true \
>        -append 'console=ttyAMA0 earlycon rdinit=/bin/sh' \
>        -kernel arch/arm64/boot/Image.gz \
>        -initrd rootfs.cpio \
>        -m 512m \
>        -serial mon:stdio
>    ...
>    # mount -t sysfs sys /sys
>    # cat /sys/fs/tmpfs/features/casefold
>    [   70.558496] CFI failure at kobj_attr_show+0x2c/0x4c (target: device_show_string+0x0/0x38; expected type: 0xc527b809)
>    [   70.560018] Internal error: Oops - CFI: 00000000f2008228 [#1] PREEMPT SMP
>    [   70.560647] Modules linked in:
>    [   70.561770] CPU: 0 UID: 0 PID: 46 Comm: cat Not tainted 6.12.0-rc4-00008-g5132f08bd332 #1
>    [   70.562429] Hardware name: linux,dummy-virt (DT)
>    [   70.562897] pstate: 21402009 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
>    [   70.563377] pc : kobj_attr_show+0x2c/0x4c
>    [   70.563674] lr : sysfs_kf_seq_show+0xb4/0x130
>    [   70.563987] sp : ffff80008043bac0
>    [   70.564236] x29: ffff80008043bac0 x28: 000000007ffff001 x27: 0000000000000000
>    [   70.564877] x26: 0000000001000000 x25: 000000007ffff001 x24: 0000000000000001
>    [   70.565339] x23: fff000000238a000 x22: ffff9fa31a3996f8 x21: fff00000023fc000
>    [   70.565806] x20: fff000000201df80 x19: fff000000238b000 x18: 0000000000000000
>    [   70.566273] x17: 00000000c527b809 x16: 00000000df43c25c x15: fff000001fef8200
>    [   70.566727] x14: 0000000000000000 x13: fff00000022450f0 x12: 0000000000001000
>    [   70.567177] x11: fff00000023fc000 x10: 0000000000000000 x9 : ffff9fa31a18fac4
>    [   70.567682] x8 : ffff9fa319badde4 x7 : 0000000000000000 x6 : 000000000000003f
>    [   70.568138] x5 : 0000000000000040 x4 : 0000000000000000 x3 : 0000000000000004
>    [   70.568585] x2 : fff00000023fc000 x1 : ffff9fa31a881f90 x0 : fff000000201df80
>    [   70.569169] Call trace:
>    [   70.569389]  kobj_attr_show+0x2c/0x4c
>    [   70.569706]  sysfs_kf_seq_show+0xb4/0x130
>    [   70.570020]  kernfs_seq_show+0x44/0x54
>    [   70.570280]  seq_read_iter+0x14c/0x4b0
>    [   70.570543]  kernfs_fop_read_iter+0x60/0x198
>    [   70.570820]  copy_splice_read+0x1f0/0x2f4
>    [   70.571092]  splice_direct_to_actor+0xf4/0x2e0
>    [   70.571376]  do_splice_direct+0x68/0xb8
>    [   70.571626]  do_sendfile+0x1e8/0x488
>    [   70.571874]  __arm64_sys_sendfile64+0xe0/0x12c
>    [   70.572161]  invoke_syscall+0x58/0x114
>    [   70.572424]  el0_svc_common+0xa8/0xdc
>    [   70.572676]  do_el0_svc+0x1c/0x28
>    [   70.572910]  el0_svc+0x38/0x68
>    [   70.573132]  el0t_64_sync_handler+0x90/0xfc
>    [   70.573394]  el0t_64_sync+0x190/0x19
>    [   70.574001] Code: 72970131 72b8a4f1 6b11021f 54000040 (d4304500)
>    [   70.574635] ---[ end trace 0000000000000000 ]---
> 
> I am not sure if there is a better API exists or if a local copy should
> be rolled but I think the current scheme is definitely wrong because
> there is no 'struct device' here.
> 

Thank you for the report, I'm trying to fix it, it seems I have used 
something for device drivers in a filesystem. I wonder how btrfs doesn't 
get this error, since tmpfs_sysfs_init() is very similar to 
btrfs_init_sysfs().

> If there is any patch I can test or further information I can provide, I
> am more than happy to do so.
> 
> Cheers,
> Nathan



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

* Re: [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs
  2024-10-31 17:31     ` André Almeida
@ 2024-10-31 18:13       ` Nathan Chancellor
  2024-10-31 19:11       ` André Almeida
  1 sibling, 0 replies; 16+ messages in thread
From: Nathan Chancellor @ 2024-10-31 18:13 UTC (permalink / raw)
  To: André Almeida
  Cc: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv, kernel-dev, linux-fsdevel,
	linux-kernel, linux-ext4, linux-mm, linux-doc,
	Gabriel Krisman Bertazi, llvm, linux-btrfs, Chris Mason

On Thu, Oct 31, 2024 at 02:31:21PM -0300, André Almeida wrote:
> Hi Nathan,
> 
> Em 31/10/2024 02:18, Nathan Chancellor escreveu:
> > Hi André,
> > 
> > On Mon, Oct 21, 2024 at 01:37:24PM -0300, André Almeida wrote:
> > > Expose filesystem features through sysfs, so userspace can query if
> > > tmpfs support casefold.
> > > 
> > > This follows the same setup as defined by ext4 and f2fs to expose
> > > casefold support to userspace.
> > > 
> > > Signed-off-by: André Almeida <andrealmeid@igalia.com>
> > > Reviewed-by: Gabriel Krisman Bertazi <krisman@suse.de>
> > > ---
> > >   mm/shmem.c | 37 +++++++++++++++++++++++++++++++++++++
> > >   1 file changed, 37 insertions(+)
> > > 
> > > diff --git a/mm/shmem.c b/mm/shmem.c
> > > index ea01628e443423d82d44277e085b867ab9bf4b28..0739143d1419c732359d3a3c3457c3acb90c5b22 100644
> > > --- a/mm/shmem.c
> > > +++ b/mm/shmem.c
> > > @@ -5546,3 +5546,40 @@ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
> > >   	return page;
> > >   }
> > >   EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);
> > > +
> > > +#if defined(CONFIG_SYSFS) && defined(CONFIG_TMPFS)
> > > +#if IS_ENABLED(CONFIG_UNICODE)
> > > +static DEVICE_STRING_ATTR_RO(casefold, 0444, "supported");
> > > +#endif
> > > +
> > > +static struct attribute *tmpfs_attributes[] = {
> > > +#if IS_ENABLED(CONFIG_UNICODE)
> > > +	&dev_attr_casefold.attr.attr,
> > > +#endif
> > > +	NULL
> > > +};
> > > +
> > > +static const struct attribute_group tmpfs_attribute_group = {
> > > +	.attrs = tmpfs_attributes,
> > > +	.name = "features"
> > > +};
> > > +
> > > +static struct kobject *tmpfs_kobj;
> > > +
> > > +static int __init tmpfs_sysfs_init(void)
> > > +{
> > > +	int ret;
> > > +
> > > +	tmpfs_kobj = kobject_create_and_add("tmpfs", fs_kobj);
> > > +	if (!tmpfs_kobj)
> > > +		return -ENOMEM;
> > > +
> > > +	ret = sysfs_create_group(tmpfs_kobj, &tmpfs_attribute_group);
> > > +	if (ret)
> > > +		kobject_put(tmpfs_kobj);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +fs_initcall(tmpfs_sysfs_init);
> > > +#endif /* CONFIG_SYSFS && CONFIG_TMPFS */
> > > 
> > > -- 
> > > 2.47.0
> > > 
> > 
> > This change as commit 5132f08bd332 ("tmpfs: Expose filesystem features
> > via sysfs") in -next introduces a kCFI violation when accessing
> > /sys/fs/tmpfs/features/casefold. An attribute group created with
> > sysfs_create_group() has ->sysfs_ops() set to kobj_sysfs_ops, which has
> > a ->show() value of kobj_attr_show(). When kobj_attr_show() goes to call
> > the attribute's ->show() value after container_of(), there will be a
> > type mismatch in the case of the casefold attr, as it was defined with a
> > ->show() value of device_show_string() but that does not match the type
> > of ->show() in 'struct kobj_attribute'.
> > 
> > I can easily reproduce this with the following commands:
> > 
> >    $ printf 'CONFIG_%s=y\n' CFI_CLANG UNICODE >kernel/configs/repro.config
> > 
> >    $ make -skj"$(nproc)" ARCH=arm64 LLVM=1 mrproper virtconfig repro.config Image.gz
> >    ...
> > 
> >    $ curl -LSs https://github.com/ClangBuiltLinux/boot-utils/releases/download/20230707-182910/arm64-rootfs.cpio.zst | zstd -d >rootfs.cpio
> > 
> >    $ qemu-system-aarch64 \
> >        -display none \
> >        -nodefaults \
> >        -cpu max,pauth-impdef=true \
> >        -machine virt,gic-version=max,virtualization=true \
> >        -append 'console=ttyAMA0 earlycon rdinit=/bin/sh' \
> >        -kernel arch/arm64/boot/Image.gz \
> >        -initrd rootfs.cpio \
> >        -m 512m \
> >        -serial mon:stdio
> >    ...
> >    # mount -t sysfs sys /sys
> >    # cat /sys/fs/tmpfs/features/casefold
> >    [   70.558496] CFI failure at kobj_attr_show+0x2c/0x4c (target: device_show_string+0x0/0x38; expected type: 0xc527b809)
> >    [   70.560018] Internal error: Oops - CFI: 00000000f2008228 [#1] PREEMPT SMP
> >    [   70.560647] Modules linked in:
> >    [   70.561770] CPU: 0 UID: 0 PID: 46 Comm: cat Not tainted 6.12.0-rc4-00008-g5132f08bd332 #1
> >    [   70.562429] Hardware name: linux,dummy-virt (DT)
> >    [   70.562897] pstate: 21402009 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
> >    [   70.563377] pc : kobj_attr_show+0x2c/0x4c
> >    [   70.563674] lr : sysfs_kf_seq_show+0xb4/0x130
> >    [   70.563987] sp : ffff80008043bac0
> >    [   70.564236] x29: ffff80008043bac0 x28: 000000007ffff001 x27: 0000000000000000
> >    [   70.564877] x26: 0000000001000000 x25: 000000007ffff001 x24: 0000000000000001
> >    [   70.565339] x23: fff000000238a000 x22: ffff9fa31a3996f8 x21: fff00000023fc000
> >    [   70.565806] x20: fff000000201df80 x19: fff000000238b000 x18: 0000000000000000
> >    [   70.566273] x17: 00000000c527b809 x16: 00000000df43c25c x15: fff000001fef8200
> >    [   70.566727] x14: 0000000000000000 x13: fff00000022450f0 x12: 0000000000001000
> >    [   70.567177] x11: fff00000023fc000 x10: 0000000000000000 x9 : ffff9fa31a18fac4
> >    [   70.567682] x8 : ffff9fa319badde4 x7 : 0000000000000000 x6 : 000000000000003f
> >    [   70.568138] x5 : 0000000000000040 x4 : 0000000000000000 x3 : 0000000000000004
> >    [   70.568585] x2 : fff00000023fc000 x1 : ffff9fa31a881f90 x0 : fff000000201df80
> >    [   70.569169] Call trace:
> >    [   70.569389]  kobj_attr_show+0x2c/0x4c
> >    [   70.569706]  sysfs_kf_seq_show+0xb4/0x130
> >    [   70.570020]  kernfs_seq_show+0x44/0x54
> >    [   70.570280]  seq_read_iter+0x14c/0x4b0
> >    [   70.570543]  kernfs_fop_read_iter+0x60/0x198
> >    [   70.570820]  copy_splice_read+0x1f0/0x2f4
> >    [   70.571092]  splice_direct_to_actor+0xf4/0x2e0
> >    [   70.571376]  do_splice_direct+0x68/0xb8
> >    [   70.571626]  do_sendfile+0x1e8/0x488
> >    [   70.571874]  __arm64_sys_sendfile64+0xe0/0x12c
> >    [   70.572161]  invoke_syscall+0x58/0x114
> >    [   70.572424]  el0_svc_common+0xa8/0xdc
> >    [   70.572676]  do_el0_svc+0x1c/0x28
> >    [   70.572910]  el0_svc+0x38/0x68
> >    [   70.573132]  el0t_64_sync_handler+0x90/0xfc
> >    [   70.573394]  el0t_64_sync+0x190/0x19
> >    [   70.574001] Code: 72970131 72b8a4f1 6b11021f 54000040 (d4304500)
> >    [   70.574635] ---[ end trace 0000000000000000 ]---
> > 
> > I am not sure if there is a better API exists or if a local copy should
> > be rolled but I think the current scheme is definitely wrong because
> > there is no 'struct device' here.
> > 
> 
> Thank you for the report, I'm trying to fix it, it seems I have used
> something for device drivers in a filesystem. I wonder how btrfs doesn't get
> this error, since tmpfs_sysfs_init() is very similar to btrfs_init_sysfs().

Upon a quick glance, it looks like all of the btrfs sysfs functions
properly match the ->show() and ->store() type in kobj_attribute, so
there is no type mismatch when they are called indirectly.

Cheers,
Nathan


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

* Re: [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs
  2024-10-31 17:31     ` André Almeida
  2024-10-31 18:13       ` Nathan Chancellor
@ 2024-10-31 19:11       ` André Almeida
  1 sibling, 0 replies; 16+ messages in thread
From: André Almeida @ 2024-10-31 19:11 UTC (permalink / raw)
  To: Nathan Chancellor
  Cc: Gabriel Krisman Bertazi, Alexander Viro, Christian Brauner,
	Jan Kara, Theodore Ts'o, Andreas Dilger, Hugh Dickins,
	Andrew Morton, Jonathan Corbet, smcv, kernel-dev, linux-fsdevel,
	linux-kernel, linux-ext4, linux-mm, linux-doc,
	Gabriel Krisman Bertazi, llvm, linux-btrfs, Chris Mason

Em 31/10/2024 14:31, André Almeida escreveu:
> Hi Nathan,
> 
> Em 31/10/2024 02:18, Nathan Chancellor escreveu:
>> Hi André,
>>

[...]

>> If there is any patch I can test or further information I can provide, I
>> am more than happy to do so.
>>

I found the issue, was just the DEVICE_ macro that I wasn't supposed to 
use inside of tmpfs, this diff solved the error for me, I will send a 
proper patch in a moment:

-- >8 --

diff --git a/mm/shmem.c b/mm/shmem.c
index 971e6f1184a5..db52c34d5020 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -5551,12 +5551,21 @@ EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);

  #if defined(CONFIG_SYSFS) && defined(CONFIG_TMPFS)
  #if IS_ENABLED(CONFIG_UNICODE)
-static DEVICE_STRING_ATTR_RO(casefold, 0444, "supported");
+static ssize_t casefold_show(struct kobject *kobj, struct 
kobj_attribute *a,
+                       char *buf)
+{
+               return sysfs_emit(buf, "supported\n");
+}
+static struct kobj_attribute tmpfs_attr_casefold = {
+               .attr = { .name = "casefold", .mode = 0444 },
+               .show = casefold_show,
+               .store = NULL,
+};
  #endif

  static struct attribute *tmpfs_attributes[] = {
  #if IS_ENABLED(CONFIG_UNICODE)
-       &dev_attr_casefold.attr.attr,
+       &tmpfs_attr_casefold.attr,
  #endif
         NULL
  };


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

end of thread, other threads:[~2024-11-01 13:54 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-10-21 16:37 [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs André Almeida
2024-10-21 16:37 ` [PATCH v8 1/9] libfs: Create the helper function generic_ci_validate_strict_name() André Almeida
2024-10-21 16:37 ` [PATCH v8 2/9] ext4: Use generic_ci_validate_strict_name helper André Almeida
2024-10-21 16:37 ` [PATCH v8 3/9] unicode: Export latest available UTF-8 version number André Almeida
2024-10-21 16:37 ` [PATCH v8 4/9] unicode: Recreate utf8_parse_version() André Almeida
2024-10-21 16:37 ` [PATCH v8 5/9] libfs: Export generic_ci_ dentry functions André Almeida
2024-10-21 16:37 ` [PATCH v8 6/9] tmpfs: Add casefold lookup support André Almeida
2024-10-21 16:37 ` [PATCH v8 7/9] tmpfs: Add flag FS_CASEFOLD_FL support for tmpfs dirs André Almeida
2024-10-21 16:37 ` [PATCH v8 8/9] tmpfs: Expose filesystem features via sysfs André Almeida
2024-10-31  5:18   ` Nathan Chancellor
2024-10-31 17:31     ` André Almeida
2024-10-31 18:13       ` Nathan Chancellor
2024-10-31 19:11       ` André Almeida
2024-10-21 16:37 ` [PATCH v8 9/9] docs: tmpfs: Add casefold options André Almeida
2024-10-28 12:37 ` [PATCH v8 0/9] tmpfs: Add case-insensitive support for tmpfs Christian Brauner
2024-10-28 13:58   ` André Almeida

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox