]> git.ipfire.org Git - people/ms/linux.git/commitdiff
mm/shmem: fix chattr fsflags support in tmpfs
authorHugh Dickins <hughd@google.com>
Thu, 11 Aug 2022 04:51:09 +0000 (21:51 -0700)
committerAndrew Morton <akpm@linux-foundation.org>
Sat, 20 Aug 2022 22:17:45 +0000 (15:17 -0700)
ext[234] have always allowed unimplemented chattr flags to be set, but
other filesystems have tended to be stricter.  Follow the stricter
approach for tmpfs: I don't want to have to explain why csu attributes
don't actually work, and we won't need to update the chattr(1) manpage;
and it's never wrong to start off strict, relaxing later if persuaded.
Allow only a (append only) i (immutable) A (no atime) and d (no dump).

Although lsattr showed 'A' inherited, the NOATIME behavior was not being
inherited: because nothing sync'ed FS_NOATIME_FL to S_NOATIME.  Add
shmem_set_inode_flags() to sync the flags, using inode_set_flags() to
avoid that instant of lost immutablility during fileattr_set().

But that change switched generic/079 from passing to failing: because
FS_IMMUTABLE_FL and FS_APPEND_FL had been unconventionally included in the
INHERITED fsflags: remove them and generic/079 is back to passing.

Link: https://lkml.kernel.org/r/2961dcb0-ddf3-b9f0-3268-12a4ff996856@google.com
Fixes: e408e695f5f1 ("mm/shmem: support FS_IOC_[SG]ETFLAGS in tmpfs")
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: "Theodore Ts'o" <tytso@mit.edu>
Cc: Radoslaw Burny <rburny@google.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/shmem_fs.h
mm/shmem.c

index 1b6c4013f691b0ee64eade2718e33b9c6bd01c1b..ff0b990de83d457c162d1789745e2040e0df9efb 100644 (file)
@@ -29,15 +29,10 @@ struct shmem_inode_info {
        struct inode            vfs_inode;
 };
 
-#define SHMEM_FL_USER_VISIBLE FS_FL_USER_VISIBLE
-#define SHMEM_FL_USER_MODIFIABLE FS_FL_USER_MODIFIABLE
-#define SHMEM_FL_INHERITED FS_FL_USER_MODIFIABLE
-
-/* Flags that are appropriate for regular files (all but dir-specific ones). */
-#define SHMEM_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL))
-
-/* Flags that are appropriate for non-directories/regular files. */
-#define SHMEM_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL)
+#define SHMEM_FL_USER_VISIBLE          FS_FL_USER_VISIBLE
+#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)
 
 struct shmem_sb_info {
        unsigned long max_blocks;   /* How many blocks are allowed */
index 5783f11351bb0806c3828a4ada7690b8d5343552..170b4078420f551c3c4002b2bb4a7b71ce3b7edc 100644 (file)
@@ -2281,16 +2281,34 @@ static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
        return 0;
 }
 
-/* Mask out flags that are inappropriate for the given type of inode. */
-static unsigned shmem_mask_flags(umode_t mode, __u32 flags)
+#ifdef CONFIG_TMPFS_XATTR
+static int shmem_initxattrs(struct inode *, const struct xattr *, void *);
+
+/*
+ * 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)
+{
+       unsigned int i_flags = 0;
+
+       if (fsflags & FS_NOATIME_FL)
+               i_flags |= S_NOATIME;
+       if (fsflags & FS_APPEND_FL)
+               i_flags |= S_APPEND;
+       if (fsflags & FS_IMMUTABLE_FL)
+               i_flags |= S_IMMUTABLE;
+       /*
+        * But FS_NODUMP_FL does not require any action in i_flags.
+        */
+       inode_set_flags(inode, i_flags, S_NOATIME | S_APPEND | S_IMMUTABLE);
+}
+#else
+static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
 {
-       if (S_ISDIR(mode))
-               return flags;
-       else if (S_ISREG(mode))
-               return flags & SHMEM_REG_FLMASK;
-       else
-               return flags & SHMEM_OTHER_FLMASK;
 }
+#define shmem_initxattrs NULL
+#endif
 
 static struct inode *shmem_get_inode(struct super_block *sb, struct inode *dir,
                                     umode_t mode, dev_t dev, unsigned long flags)
@@ -2319,7 +2337,8 @@ static struct inode *shmem_get_inode(struct super_block *sb, struct inode *dir,
                info->i_crtime = inode->i_mtime;
                info->fsflags = (dir == NULL) ? 0 :
                        SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED;
-               info->fsflags = shmem_mask_flags(mode, info->fsflags);
+               if (info->fsflags)
+                       shmem_set_inode_flags(inode, info->fsflags);
                INIT_LIST_HEAD(&info->shrinklist);
                INIT_LIST_HEAD(&info->swaplist);
                simple_xattrs_init(&info->xattrs);
@@ -2468,12 +2487,6 @@ out_unacct_blocks:
 static const struct inode_operations shmem_symlink_inode_operations;
 static const struct inode_operations shmem_short_symlink_operations;
 
-#ifdef CONFIG_TMPFS_XATTR
-static int shmem_initxattrs(struct inode *, const struct xattr *, void *);
-#else
-#define shmem_initxattrs NULL
-#endif
-
 static int
 shmem_write_begin(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len,
@@ -3179,18 +3192,13 @@ static int shmem_fileattr_set(struct user_namespace *mnt_userns,
 
        if (fileattr_has_fsx(fa))
                return -EOPNOTSUPP;
+       if (fa->flags & ~SHMEM_FL_USER_MODIFIABLE)
+               return -EOPNOTSUPP;
 
        info->fsflags = (info->fsflags & ~SHMEM_FL_USER_MODIFIABLE) |
                (fa->flags & SHMEM_FL_USER_MODIFIABLE);
 
-       inode->i_flags &= ~(S_APPEND | S_IMMUTABLE | S_NOATIME);
-       if (info->fsflags & FS_APPEND_FL)
-               inode->i_flags |= S_APPEND;
-       if (info->fsflags & FS_IMMUTABLE_FL)
-               inode->i_flags |= S_IMMUTABLE;
-       if (info->fsflags & FS_NOATIME_FL)
-               inode->i_flags |= S_NOATIME;
-
+       shmem_set_inode_flags(inode, info->fsflags);
        inode->i_ctime = current_time(inode);
        return 0;
 }