]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tmpfs: Add flag FS_CASEFOLD_FL support for tmpfs dirs
authorAndré Almeida <andrealmeid@igalia.com>
Mon, 21 Oct 2024 16:37:23 +0000 (13:37 -0300)
committerChristian Brauner <brauner@kernel.org>
Mon, 28 Oct 2024 12:36:55 +0000 (13:36 +0100)
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>
Link: https://lore.kernel.org/r/20241021-tonyk-tmpfs-v8-7-f443d5814194@igalia.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
include/linux/shmem_fs.h
mm/shmem.c

index 515a9a6a3c6f82c55952d821887514217a6a00d1..018da28c01e7d71b8fb00bfb23c000248c8a83f4 100644 (file)
@@ -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 */
index f26488ff3d6ae1abb9b63d55ca74909249dbf4eb..ea01628e443423d82d44277e085b867ab9bf4b28 100644 (file)
@@ -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;