]> git.itanic.dy.fi Git - linux-stable/commitdiff
mm/shmem: support FS_IOC_[SG]ETFLAGS in tmpfs
authorTheodore Ts'o <tytso@mit.edu>
Fri, 15 Jul 2022 01:59:12 +0000 (21:59 -0400)
committerakpm <akpm@linux-foundation.org>
Sat, 30 Jul 2022 01:07:15 +0000 (18:07 -0700)
This allows userspace to set flags like FS_APPEND_FL, FS_IMMUTABLE_FL,
FS_NODUMP_FL, etc., like all other standard Linux file systems.

[akpm@linux-foundation.org: fix CONFIG_TMPFS_XATTR=n warnings]
Link: https://lkml.kernel.org/r/20220715015912.2560575-1-tytso@mit.edu
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/shmem_fs.h
mm/shmem.c

index a68f982f22d16995e19271d20e8c42fe72ceff55..1b6c4013f691b0ee64eade2718e33b9c6bd01c1b 100644 (file)
@@ -25,9 +25,20 @@ struct shmem_inode_info {
        struct simple_xattrs    xattrs;         /* list of xattrs */
        atomic_t                stop_eviction;  /* hold when working on inode */
        struct timespec64       i_crtime;       /* file creation time */
+       unsigned int            fsflags;        /* flags for FS_IOC_[SG]ETFLAGS */
        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)
+
 struct shmem_sb_info {
        unsigned long max_blocks;   /* How many blocks are allowed */
        struct percpu_counter used_blocks;  /* How many are allocated */
index 12ac67dc831fae5ee09069fd25d22c680e85d56c..06871a913b49b127ac5c59ce0c7ac3ddd043a688 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/ramfs.h>
 #include <linux/pagemap.h>
 #include <linux/file.h>
+#include <linux/fileattr.h>
 #include <linux/mm.h>
 #include <linux/random.h>
 #include <linux/sched/signal.h>
@@ -1058,6 +1059,15 @@ static int shmem_getattr(struct user_namespace *mnt_userns,
                shmem_recalc_inode(inode);
                spin_unlock_irq(&info->lock);
        }
+       if (info->fsflags & FS_APPEND_FL)
+               stat->attributes |= STATX_ATTR_APPEND;
+       if (info->fsflags & FS_IMMUTABLE_FL)
+               stat->attributes |= STATX_ATTR_IMMUTABLE;
+       if (info->fsflags & FS_NODUMP_FL)
+               stat->attributes |= STATX_ATTR_NODUMP;
+       stat->attributes_mask |= (STATX_ATTR_APPEND |
+                       STATX_ATTR_IMMUTABLE |
+                       STATX_ATTR_NODUMP);
        generic_fillattr(&init_user_ns, inode, stat);
 
        if (shmem_is_huge(NULL, inode, 0))
@@ -2272,7 +2282,18 @@ static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
        return 0;
 }
 
-static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir,
+/* Mask out flags that are inappropriate for the given type of inode. */
+static unsigned shmem_mask_flags(umode_t mode, __u32 flags)
+{
+       if (S_ISDIR(mode))
+               return flags;
+       else if (S_ISREG(mode))
+               return flags & SHMEM_REG_FLMASK;
+       else
+               return flags & SHMEM_OTHER_FLMASK;
+}
+
+static struct inode *shmem_get_inode(struct super_block *sb, struct inode *dir,
                                     umode_t mode, dev_t dev, unsigned long flags)
 {
        struct inode *inode;
@@ -2297,6 +2318,9 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
                info->seals = F_SEAL_SEAL;
                info->flags = flags & VM_NORESERVE;
                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);
                INIT_LIST_HEAD(&info->shrinklist);
                INIT_LIST_HEAD(&info->swaplist);
                simple_xattrs_init(&info->xattrs);
@@ -3138,6 +3162,40 @@ static const char *shmem_get_link(struct dentry *dentry,
 }
 
 #ifdef CONFIG_TMPFS_XATTR
+
+static int shmem_fileattr_get(struct dentry *dentry, struct fileattr *fa)
+{
+       struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
+
+       fileattr_fill_flags(fa, info->fsflags & SHMEM_FL_USER_VISIBLE);
+
+       return 0;
+}
+
+static int shmem_fileattr_set(struct user_namespace *mnt_userns,
+                             struct dentry *dentry, struct fileattr *fa)
+{
+       struct inode *inode = d_inode(dentry);
+       struct shmem_inode_info *info = SHMEM_I(inode);
+
+       if (fileattr_has_fsx(fa))
+               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;
+
+       inode->i_ctime = current_time(inode);
+       return 0;
+}
+
 /*
  * Superblocks without xattr inode operations may get some security.* xattr
  * support from the LSM "for free". As soon as we have any other xattrs
@@ -3828,6 +3886,8 @@ static const struct inode_operations shmem_inode_operations = {
 #ifdef CONFIG_TMPFS_XATTR
        .listxattr      = shmem_listxattr,
        .set_acl        = simple_set_acl,
+       .fileattr_get   = shmem_fileattr_get,
+       .fileattr_set   = shmem_fileattr_set,
 #endif
 };
 
@@ -3847,6 +3907,8 @@ static const struct inode_operations shmem_dir_inode_operations = {
 #endif
 #ifdef CONFIG_TMPFS_XATTR
        .listxattr      = shmem_listxattr,
+       .fileattr_get   = shmem_fileattr_get,
+       .fileattr_set   = shmem_fileattr_set,
 #endif
 #ifdef CONFIG_TMPFS_POSIX_ACL
        .setattr        = shmem_setattr,