Merge tag 'fuse-update-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 8 Aug 2022 18:10:02 +0000 (11:10 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 8 Aug 2022 18:10:02 +0000 (11:10 -0700)
Pull fuse updates from Miklos Szeredi:

 - Fix an issue with reusing the bdi in case of block based filesystems

 - Allow root (in init namespace) to access fuse filesystems in user
   namespaces if expicitly enabled with a module param

 - Misc fixes

* tag 'fuse-update-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: retire block-device-based superblock on force unmount
  vfs: function to prevent re-use of block-device-based superblocks
  virtio_fs: Modify format for virtio_fs_direct_access
  virtiofs: delete unused parameter for virtio_fs_cleanup_vqs
  fuse: Add module param for CAP_SYS_ADMIN access bypassing allow_other
  fuse: Remove the control interface for virtio-fs
  fuse: ioctl: translate ENOSYS
  fuse: limit nsec
  fuse: avoid unnecessary spinlock bump
  fuse: fix deadlock between atomic O_TRUNC and page invalidation
  fuse: write inode in fuse_release()

1  2 
fs/fuse/file.c
fs/super.c
include/linux/fs.h

diff --combined fs/fuse/file.c
@@@ -210,13 -210,9 +210,9 @@@ void fuse_finish_open(struct inode *ino
                fi->attr_version = atomic64_inc_return(&fc->attr_version);
                i_size_write(inode, 0);
                spin_unlock(&fi->lock);
-               truncate_pagecache(inode, 0);
                file_update_time(file);
                fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
-       } else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) {
-               invalidate_inode_pages2(inode->i_mapping);
        }
        if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
                fuse_link_write_file(file);
  }
@@@ -239,30 -235,38 +235,38 @@@ int fuse_open_common(struct inode *inod
        if (err)
                return err;
  
-       if (is_wb_truncate || dax_truncate) {
+       if (is_wb_truncate || dax_truncate)
                inode_lock(inode);
-               fuse_set_nowrite(inode);
-       }
  
        if (dax_truncate) {
                filemap_invalidate_lock(inode->i_mapping);
                err = fuse_dax_break_layouts(inode, 0, 0);
                if (err)
-                       goto out;
+                       goto out_inode_unlock;
        }
  
+       if (is_wb_truncate || dax_truncate)
+               fuse_set_nowrite(inode);
        err = fuse_do_open(fm, get_node_id(inode), file, isdir);
        if (!err)
                fuse_finish_open(inode, file);
  
- out:
+       if (is_wb_truncate || dax_truncate)
+               fuse_release_nowrite(inode);
+       if (!err) {
+               struct fuse_file *ff = file->private_data;
+               if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC))
+                       truncate_pagecache(inode, 0);
+               else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
+                       invalidate_inode_pages2(inode->i_mapping);
+       }
        if (dax_truncate)
                filemap_invalidate_unlock(inode->i_mapping);
-       if (is_wb_truncate | dax_truncate) {
-               fuse_release_nowrite(inode);
+ out_inode_unlock:
+       if (is_wb_truncate || dax_truncate)
                inode_unlock(inode);
-       }
  
        return err;
  }
@@@ -338,6 -342,15 +342,15 @@@ static int fuse_open(struct inode *inod
  
  static int fuse_release(struct inode *inode, struct file *file)
  {
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       /*
+        * Dirty pages might remain despite write_inode_now() call from
+        * fuse_flush() due to writes racing with the close.
+        */
+       if (fc->writeback_cache)
+               write_inode_now(inode, 1);
        fuse_release_common(file, false);
  
        /* return value is ignored by VFS */
@@@ -1042,7 -1055,7 +1055,7 @@@ static unsigned int fuse_write_flags(st
  {
        unsigned int flags = iocb->ki_filp->f_flags;
  
 -      if (iocb->ki_flags & IOCB_DSYNC)
 +      if (iocb_is_dsync(iocb))
                flags |= O_DSYNC;
        if (iocb->ki_flags & IOCB_SYNC)
                flags |= O_SYNC;
diff --combined fs/super.c
@@@ -265,7 -265,7 +265,7 @@@ static struct super_block *alloc_super(
        s->s_shrink.count_objects = super_cache_count;
        s->s_shrink.batch = 1024;
        s->s_shrink.flags = SHRINKER_NUMA_AWARE | SHRINKER_MEMCG_AWARE;
 -      if (prealloc_shrinker(&s->s_shrink))
 +      if (prealloc_shrinker(&s->s_shrink, "sb-%s", type->name))
                goto fail;
        if (list_lru_init_memcg(&s->s_dentry_lru, &s->s_shrink))
                goto fail;
@@@ -422,6 -422,35 +422,35 @@@ bool trylock_super(struct super_block *
        return false;
  }
  
+ /**
+  *    retire_super    -       prevents superblock from being reused
+  *    @sb: superblock to retire
+  *
+  *    The function marks superblock to be ignored in superblock test, which
+  *    prevents it from being reused for any new mounts.  If the superblock has
+  *    a private bdi, it also unregisters it, but doesn't reduce the refcount
+  *    of the superblock to prevent potential races.  The refcount is reduced
+  *    by generic_shutdown_super().  The function can not be called
+  *    concurrently with generic_shutdown_super().  It is safe to call the
+  *    function multiple times, subsequent calls have no effect.
+  *
+  *    The marker will affect the re-use only for block-device-based
+  *    superblocks.  Other superblocks will still get marked if this function
+  *    is used, but that will not affect their reusability.
+  */
+ void retire_super(struct super_block *sb)
+ {
+       WARN_ON(!sb->s_bdev);
+       down_write(&sb->s_umount);
+       if (sb->s_iflags & SB_I_PERSB_BDI) {
+               bdi_unregister(sb->s_bdi);
+               sb->s_iflags &= ~SB_I_PERSB_BDI;
+       }
+       sb->s_iflags |= SB_I_RETIRED;
+       up_write(&sb->s_umount);
+ }
+ EXPORT_SYMBOL(retire_super);
  /**
   *    generic_shutdown_super  -       common helper for ->kill_sb()
   *    @sb: superblock to kill
@@@ -1216,7 -1245,7 +1245,7 @@@ static int set_bdev_super_fc(struct sup
  
  static int test_bdev_super_fc(struct super_block *s, struct fs_context *fc)
  {
-       return s->s_bdev == fc->sget_key;
+       return !(s->s_iflags & SB_I_RETIRED) && s->s_bdev == fc->sget_key;
  }
  
  /**
@@@ -1288,8 -1317,6 +1317,8 @@@ int get_tree_bdev(struct fs_context *fc
        } else {
                s->s_mode = mode;
                snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
 +              shrinker_debugfs_rename(&s->s_shrink, "sb-%s:%s",
 +                                      fc->fs_type->name, s->s_id);
                sb_set_blocksize(s, block_size(bdev));
                error = fill_super(s, fc);
                if (error) {
@@@ -1309,7 -1336,7 +1338,7 @@@ EXPORT_SYMBOL(get_tree_bdev)
  
  static int test_bdev_super(struct super_block *s, void *data)
  {
-       return (void *)s->s_bdev == data;
+       return !(s->s_iflags & SB_I_RETIRED) && (void *)s->s_bdev == data;
  }
  
  struct dentry *mount_bdev(struct file_system_type *fs_type,
        } else {
                s->s_mode = mode;
                snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
 +              shrinker_debugfs_rename(&s->s_shrink, "sb-%s:%s",
 +                                      fs_type->name, s->s_id);
                sb_set_blocksize(s, block_size(bdev));
                error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
                if (error) {
diff --combined include/linux/fs.h
@@@ -74,7 -74,6 +74,7 @@@ struct fsverity_operations
  struct fs_context;
  struct fs_parameter_spec;
  struct fileattr;
 +struct iomap_ops;
  
  extern void __init inode_init(void);
  extern void __init inode_init_early(void);
@@@ -181,9 -180,6 +181,9 @@@ typedef int (dio_iodone_t)(struct kioc
  /* File supports async buffered reads */
  #define FMODE_BUF_RASYNC      ((__force fmode_t)0x40000000)
  
 +/* File supports async nowait buffered writes */
 +#define FMODE_BUF_WASYNC      ((__force fmode_t)0x80000000)
 +
  /*
   * Attribute flags.  These should be or-ed together to figure out what
   * has been changed!
  struct iattr {
        unsigned int    ia_valid;
        umode_t         ia_mode;
 -      kuid_t          ia_uid;
 -      kgid_t          ia_gid;
 +      /*
 +       * The two anonymous unions wrap structures with the same member.
 +       *
 +       * Filesystems raising FS_ALLOW_IDMAP need to use ia_vfs{g,u}id which
 +       * are a dedicated type requiring the filesystem to use the dedicated
 +       * helpers. Other filesystem can continue to use ia_{g,u}id until they
 +       * have been ported.
 +       *
 +       * They always contain the same value. In other words FS_ALLOW_IDMAP
 +       * pass down the same value on idmapped mounts as they would on regular
 +       * mounts.
 +       */
 +      union {
 +              kuid_t          ia_uid;
 +              vfsuid_t        ia_vfsuid;
 +      };
 +      union {
 +              kgid_t          ia_gid;
 +              vfsgid_t        ia_vfsgid;
 +      };
        loff_t          ia_size;
        struct timespec64 ia_atime;
        struct timespec64 ia_mtime;
@@@ -384,11 -362,13 +384,11 @@@ struct address_space_operations 
        void (*free_folio)(struct folio *folio);
        ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter);
        /*
 -       * migrate the contents of a page to the specified target. If
 +       * migrate the contents of a folio to the specified target. If
         * migrate_mode is MIGRATE_ASYNC, it must not block.
         */
 -      int (*migratepage) (struct address_space *,
 -                      struct page *, struct page *, enum migrate_mode);
 -      bool (*isolate_page)(struct page *, isolate_mode_t);
 -      void (*putback_page)(struct page *);
 +      int (*migrate_folio)(struct address_space *, struct folio *dst,
 +                      struct folio *src, enum migrate_mode);
        int (*launder_folio)(struct folio *);
        bool (*is_partially_uptodate) (struct folio *, size_t from,
                        size_t count);
@@@ -944,10 -924,9 +944,10 @@@ static inline int ra_has_index(struct f
  
  struct file {
        union {
 -              struct llist_node       fu_llist;
 -              struct rcu_head         fu_rcuhead;
 -      } f_u;
 +              struct llist_node       f_llist;
 +              struct rcu_head         f_rcuhead;
 +              unsigned int            f_iocb_flags;
 +      };
        struct path             f_path;
        struct inode            *f_inode;       /* cached value */
        const struct file_operations    *f_op;
@@@ -1433,6 -1412,7 +1433,7 @@@ extern int send_sigurg(struct fown_stru
  #define SB_I_SKIP_SYNC        0x00000100      /* Skip superblock at global sync */
  #define SB_I_PERSB_BDI        0x00000200      /* has a per-sb bdi */
  #define SB_I_TS_EXPIRY_WARNED 0x00000400 /* warned about timestamp range expiry */
+ #define SB_I_RETIRED  0x00000800      /* superblock shouldn't be reused */
  
  /* Possible states of 'frozen' field */
  enum {
@@@ -1621,68 -1601,13 +1622,68 @@@ static inline void i_gid_write(struct i
   * @mnt_userns: user namespace of the mount the inode was found from
   * @inode: inode to map
   *
 + * Note, this will eventually be removed completely in favor of the type-safe
 + * i_uid_into_vfsuid().
 + *
   * Return: the inode's i_uid mapped down according to @mnt_userns.
   * If the inode's i_uid has no mapping INVALID_UID is returned.
   */
  static inline kuid_t i_uid_into_mnt(struct user_namespace *mnt_userns,
                                    const struct inode *inode)
  {
 -      return mapped_kuid_fs(mnt_userns, i_user_ns(inode), inode->i_uid);
 +      return AS_KUIDT(make_vfsuid(mnt_userns, i_user_ns(inode), inode->i_uid));
 +}
 +
 +/**
 + * i_uid_into_vfsuid - map an inode's i_uid down into a mnt_userns
 + * @mnt_userns: user namespace of the mount the inode was found from
 + * @inode: inode to map
 + *
 + * Return: whe inode's i_uid mapped down according to @mnt_userns.
 + * If the inode's i_uid has no mapping INVALID_VFSUID is returned.
 + */
 +static inline vfsuid_t i_uid_into_vfsuid(struct user_namespace *mnt_userns,
 +                                       const struct inode *inode)
 +{
 +      return make_vfsuid(mnt_userns, i_user_ns(inode), inode->i_uid);
 +}
 +
 +/**
 + * i_uid_needs_update - check whether inode's i_uid needs to be updated
 + * @mnt_userns: user namespace of the mount the inode was found from
 + * @attr: the new attributes of @inode
 + * @inode: the inode to update
 + *
 + * Check whether the $inode's i_uid field needs to be updated taking idmapped
 + * mounts into account if the filesystem supports it.
 + *
 + * Return: true if @inode's i_uid field needs to be updated, false if not.
 + */
 +static inline bool i_uid_needs_update(struct user_namespace *mnt_userns,
 +                                    const struct iattr *attr,
 +                                    const struct inode *inode)
 +{
 +      return ((attr->ia_valid & ATTR_UID) &&
 +              !vfsuid_eq(attr->ia_vfsuid,
 +                         i_uid_into_vfsuid(mnt_userns, inode)));
 +}
 +
 +/**
 + * i_uid_update - update @inode's i_uid field
 + * @mnt_userns: user namespace of the mount the inode was found from
 + * @attr: the new attributes of @inode
 + * @inode: the inode to update
 + *
 + * Safely update @inode's i_uid field translating the vfsuid of any idmapped
 + * mount into the filesystem kuid.
 + */
 +static inline void i_uid_update(struct user_namespace *mnt_userns,
 +                              const struct iattr *attr,
 +                              struct inode *inode)
 +{
 +      if (attr->ia_valid & ATTR_UID)
 +              inode->i_uid = from_vfsuid(mnt_userns, i_user_ns(inode),
 +                                         attr->ia_vfsuid);
  }
  
  /**
   * @mnt_userns: user namespace of the mount the inode was found from
   * @inode: inode to map
   *
 + * Note, this will eventually be removed completely in favor of the type-safe
 + * i_gid_into_vfsgid().
 + *
   * Return: the inode's i_gid mapped down according to @mnt_userns.
   * If the inode's i_gid has no mapping INVALID_GID is returned.
   */
  static inline kgid_t i_gid_into_mnt(struct user_namespace *mnt_userns,
                                    const struct inode *inode)
  {
 -      return mapped_kgid_fs(mnt_userns, i_user_ns(inode), inode->i_gid);
 +      return AS_KGIDT(make_vfsgid(mnt_userns, i_user_ns(inode), inode->i_gid));
 +}
 +
 +/**
 + * i_gid_into_vfsgid - map an inode's i_gid down into a mnt_userns
 + * @mnt_userns: user namespace of the mount the inode was found from
 + * @inode: inode to map
 + *
 + * Return: the inode's i_gid mapped down according to @mnt_userns.
 + * If the inode's i_gid has no mapping INVALID_VFSGID is returned.
 + */
 +static inline vfsgid_t i_gid_into_vfsgid(struct user_namespace *mnt_userns,
 +                                       const struct inode *inode)
 +{
 +      return make_vfsgid(mnt_userns, i_user_ns(inode), inode->i_gid);
 +}
 +
 +/**
 + * i_gid_needs_update - check whether inode's i_gid needs to be updated
 + * @mnt_userns: user namespace of the mount the inode was found from
 + * @attr: the new attributes of @inode
 + * @inode: the inode to update
 + *
 + * Check whether the $inode's i_gid field needs to be updated taking idmapped
 + * mounts into account if the filesystem supports it.
 + *
 + * Return: true if @inode's i_gid field needs to be updated, false if not.
 + */
 +static inline bool i_gid_needs_update(struct user_namespace *mnt_userns,
 +                                    const struct iattr *attr,
 +                                    const struct inode *inode)
 +{
 +      return ((attr->ia_valid & ATTR_GID) &&
 +              !vfsgid_eq(attr->ia_vfsgid,
 +                         i_gid_into_vfsgid(mnt_userns, inode)));
 +}
 +
 +/**
 + * i_gid_update - update @inode's i_gid field
 + * @mnt_userns: user namespace of the mount the inode was found from
 + * @attr: the new attributes of @inode
 + * @inode: the inode to update
 + *
 + * Safely update @inode's i_gid field translating the vfsgid of any idmapped
 + * mount into the filesystem kgid.
 + */
 +static inline void i_gid_update(struct user_namespace *mnt_userns,
 +                              const struct iattr *attr,
 +                              struct inode *inode)
 +{
 +      if (attr->ia_valid & ATTR_GID)
 +              inode->i_gid = from_vfsgid(mnt_userns, i_user_ns(inode),
 +                                         attr->ia_vfsgid);
  }
  
  /**
@@@ -2201,13 -2071,10 +2202,13 @@@ extern ssize_t vfs_copy_file_range(stru
  extern ssize_t generic_copy_file_range(struct file *file_in, loff_t pos_in,
                                       struct file *file_out, loff_t pos_out,
                                       size_t len, unsigned int flags);
 -extern int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
 -                                       struct file *file_out, loff_t pos_out,
 -                                       loff_t *count,
 -                                       unsigned int remap_flags);
 +int __generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
 +                                  struct file *file_out, loff_t pos_out,
 +                                  loff_t *len, unsigned int remap_flags,
 +                                  const struct iomap_ops *dax_read_ops);
 +int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
 +                                struct file *file_out, loff_t pos_out,
 +                                loff_t *count, unsigned int remap_flags);
  extern loff_t do_clone_file_range(struct file *file_in, loff_t pos_in,
                                  struct file *file_out, loff_t pos_out,
                                  loff_t len, unsigned int remap_flags);
@@@ -2329,15 -2196,17 +2330,15 @@@ static inline bool sb_rdonly(const stru
  static inline bool HAS_UNMAPPED_ID(struct user_namespace *mnt_userns,
                                   struct inode *inode)
  {
 -      return !uid_valid(i_uid_into_mnt(mnt_userns, inode)) ||
 -             !gid_valid(i_gid_into_mnt(mnt_userns, inode));
 +      return !vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)) ||
 +             !vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode));
  }
  
 -static inline int iocb_flags(struct file *file);
 -
  static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp)
  {
        *kiocb = (struct kiocb) {
                .ki_filp = filp,
 -              .ki_flags = iocb_flags(filp),
 +              .ki_flags = filp->f_iocb_flags,
                .ki_ioprio = get_current_ioprio(),
        };
  }
@@@ -2519,7 -2388,6 +2520,7 @@@ static inline void file_accessed(struc
  }
  
  extern int file_modified(struct file *file);
 +int kiocb_modified(struct kiocb *iocb);
  
  int sync_inode_metadata(struct inode *inode, int wait);
  
@@@ -2565,6 -2433,7 +2566,7 @@@ extern struct dentry *mount_nodev(struc
        int flags, void *data,
        int (*fill_super)(struct super_block *, void *, int));
  extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path);
+ void retire_super(struct super_block *sb);
  void generic_shutdown_super(struct super_block *sb);
  void kill_block_super(struct super_block *sb);
  void kill_anon_super(struct super_block *sb);
@@@ -2853,12 -2722,6 +2855,12 @@@ extern int vfs_fsync(struct file *file
  extern int sync_file_range(struct file *file, loff_t offset, loff_t nbytes,
                                unsigned int flags);
  
 +static inline bool iocb_is_dsync(const struct kiocb *iocb)
 +{
 +      return (iocb->ki_flags & IOCB_DSYNC) ||
 +              IS_SYNC(iocb->ki_filp->f_mapping->host);
 +}
 +
  /*
   * Sync the bytes written if this was a synchronous write.  Expect ki_pos
   * to already be updated for the write, and will return either the amount
   */
  static inline ssize_t generic_write_sync(struct kiocb *iocb, ssize_t count)
  {
 -      if (iocb->ki_flags & IOCB_DSYNC) {
 +      if (iocb_is_dsync(iocb)) {
                int ret = vfs_fsync_range(iocb->ki_filp,
                                iocb->ki_pos - count, iocb->ki_pos - 1,
                                (iocb->ki_flags & IOCB_SYNC) ? 0 : 1);
@@@ -3161,7 -3024,7 +3163,7 @@@ extern long do_splice_direct(struct fil
  extern void
  file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping);
  extern loff_t noop_llseek(struct file *file, loff_t offset, int whence);
 -extern loff_t no_llseek(struct file *file, loff_t offset, int whence);
 +#define no_llseek NULL
  extern loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize);
  extern loff_t generic_file_llseek(struct file *file, loff_t offset, int whence);
  extern loff_t generic_file_llseek_size(struct file *file, loff_t offset,
@@@ -3354,6 -3217,18 +3356,6 @@@ extern int generic_check_addressable(un
  
  extern void generic_set_encrypted_ci_d_ops(struct dentry *dentry);
  
 -#ifdef CONFIG_MIGRATION
 -extern int buffer_migrate_page(struct address_space *,
 -                              struct page *, struct page *,
 -                              enum migrate_mode);
 -extern int buffer_migrate_page_norefs(struct address_space *,
 -                              struct page *, struct page *,
 -                              enum migrate_mode);
 -#else
 -#define buffer_migrate_page NULL
 -#define buffer_migrate_page_norefs NULL
 -#endif
 -
  int may_setattr(struct user_namespace *mnt_userns, struct inode *inode,
                unsigned int ia_valid);
  int setattr_prepare(struct user_namespace *, struct dentry *, struct iattr *);
@@@ -3389,7 -3264,7 +3391,7 @@@ static inline int iocb_flags(struct fil
                res |= IOCB_APPEND;
        if (file->f_flags & O_DIRECT)
                res |= IOCB_DIRECT;
 -      if ((file->f_flags & O_DSYNC) || IS_SYNC(file->f_mapping->host))
 +      if (file->f_flags & O_DSYNC)
                res |= IOCB_DSYNC;
        if (file->f_flags & __O_SYNC)
                res |= IOCB_SYNC;