Merge tag 'fuse-update-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 27 Feb 2023 17:53:58 +0000 (09:53 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 27 Feb 2023 17:53:58 +0000 (09:53 -0800)
Pull fuse updates from Miklos Szeredi:

 - Fix regression in fileattr permission checking

 - Fix possible hang during PID namespace destruction

 - Add generic support for request extensions

 - Add supplementary group list extension

 - Add limited support for supplying supplementary groups in create
   requests

 - Documentation fixes

* tag 'fuse-update-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: add inode/permission checks to fileattr_get/fileattr_set
  fuse: fix all W=1 kernel-doc warnings
  fuse: in fuse_flush only wait if someone wants the return code
  fuse: optional supplementary group in create requests
  fuse: add request extension

1  2 
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/fuse/inode.c
fs/fuse/ioctl.c

diff --combined fs/fuse/dir.c
@@@ -145,7 -145,7 +145,7 @@@ static void fuse_dir_changed(struct ino
        inode_maybe_inc_iversion(dir, false);
  }
  
- /**
+ /*
   * Mark the attributes as stale due to an atime change.  Avoid the invalidate if
   * atime is not used.
   */
@@@ -466,7 -466,7 +466,7 @@@ static struct dentry *fuse_lookup(struc
  }
  
  static int get_security_context(struct dentry *entry, umode_t mode,
-                               void **security_ctx, u32 *security_ctxlen)
+                               struct fuse_in_arg *ext)
  {
        struct fuse_secctx *fctx;
        struct fuse_secctx_header *header;
  
                memcpy(ptr, ctx, ctxlen);
        }
-       *security_ctxlen = total_len;
-       *security_ctx = header;
+       ext->size = total_len;
+       ext->value = header;
        err = 0;
  out_err:
        kfree(ctx);
        return err;
  }
  
+ static void *extend_arg(struct fuse_in_arg *buf, u32 bytes)
+ {
+       void *p;
+       u32 newlen = buf->size + bytes;
+       p = krealloc(buf->value, newlen, GFP_KERNEL);
+       if (!p) {
+               kfree(buf->value);
+               buf->size = 0;
+               buf->value = NULL;
+               return NULL;
+       }
+       memset(p + buf->size, 0, bytes);
+       buf->value = p;
+       buf->size = newlen;
+       return p + newlen - bytes;
+ }
+ static u32 fuse_ext_size(size_t size)
+ {
+       return FUSE_REC_ALIGN(sizeof(struct fuse_ext_header) + size);
+ }
+ /*
+  * This adds just a single supplementary group that matches the parent's group.
+  */
+ static int get_create_supp_group(struct inode *dir, struct fuse_in_arg *ext)
+ {
+       struct fuse_conn *fc = get_fuse_conn(dir);
+       struct fuse_ext_header *xh;
+       struct fuse_supp_groups *sg;
+       kgid_t kgid = dir->i_gid;
+       gid_t parent_gid = from_kgid(fc->user_ns, kgid);
+       u32 sg_len = fuse_ext_size(sizeof(*sg) + sizeof(sg->groups[0]));
+       if (parent_gid == (gid_t) -1 || gid_eq(kgid, current_fsgid()) ||
+           !in_group_p(kgid))
+               return 0;
+       xh = extend_arg(ext, sg_len);
+       if (!xh)
+               return -ENOMEM;
+       xh->size = sg_len;
+       xh->type = FUSE_EXT_GROUPS;
+       sg = (struct fuse_supp_groups *) &xh[1];
+       sg->nr_groups = 1;
+       sg->groups[0] = parent_gid;
+       return 0;
+ }
+ static int get_create_ext(struct fuse_args *args,
+                         struct inode *dir, struct dentry *dentry,
+                         umode_t mode)
+ {
+       struct fuse_conn *fc = get_fuse_conn_super(dentry->d_sb);
+       struct fuse_in_arg ext = { .size = 0, .value = NULL };
+       int err = 0;
+       if (fc->init_security)
+               err = get_security_context(dentry, mode, &ext);
+       if (!err && fc->create_supp_group)
+               err = get_create_supp_group(dir, &ext);
+       if (!err && ext.size) {
+               WARN_ON(args->in_numargs >= ARRAY_SIZE(args->in_args));
+               args->is_ext = true;
+               args->ext_idx = args->in_numargs++;
+               args->in_args[args->ext_idx] = ext;
+       } else {
+               kfree(ext.value);
+       }
+       return err;
+ }
+ static void free_ext_value(struct fuse_args *args)
+ {
+       if (args->is_ext)
+               kfree(args->in_args[args->ext_idx].value);
+ }
  /*
   * Atomic create+open operation
   *
@@@ -541,8 -627,6 +627,6 @@@ static int fuse_create_open(struct inod
        struct fuse_entry_out outentry;
        struct fuse_inode *fi;
        struct fuse_file *ff;
-       void *security_ctx = NULL;
-       u32 security_ctxlen;
        bool trunc = flags & O_TRUNC;
  
        /* Userspace expects S_IFREG in create mode */
        args.out_args[1].size = sizeof(outopen);
        args.out_args[1].value = &outopen;
  
-       if (fm->fc->init_security) {
-               err = get_security_context(entry, mode, &security_ctx,
-                                          &security_ctxlen);
-               if (err)
-                       goto out_put_forget_req;
-               args.in_numargs = 3;
-               args.in_args[2].size = security_ctxlen;
-               args.in_args[2].value = security_ctx;
-       }
+       err = get_create_ext(&args, dir, entry, mode);
+       if (err)
+               goto out_put_forget_req;
  
        err = fuse_simple_request(fm, &args);
-       kfree(security_ctx);
+       free_ext_value(&args);
        if (err)
                goto out_free_ff;
  
@@@ -645,7 -722,7 +722,7 @@@ out_err
        return err;
  }
  
 -static int fuse_mknod(struct user_namespace *, struct inode *, struct dentry *,
 +static int fuse_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
                      umode_t, dev_t);
  static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
                            struct file *file, unsigned flags,
@@@ -686,7 -763,7 +763,7 @@@ out_dput
        return err;
  
  mknod:
 -      err = fuse_mknod(&init_user_ns, dir, entry, mode, 0);
 +      err = fuse_mknod(&nop_mnt_idmap, dir, entry, mode, 0);
        if (err)
                goto out_dput;
  no_open:
@@@ -705,8 -782,6 +782,6 @@@ static int create_new_entry(struct fuse
        struct dentry *d;
        int err;
        struct fuse_forget_link *forget;
-       void *security_ctx = NULL;
-       u32 security_ctxlen;
  
        if (fuse_is_bad(dir))
                return -EIO;
        args->out_args[0].size = sizeof(outarg);
        args->out_args[0].value = &outarg;
  
-       if (fm->fc->init_security && args->opcode != FUSE_LINK) {
-               err = get_security_context(entry, mode, &security_ctx,
-                                          &security_ctxlen);
+       if (args->opcode != FUSE_LINK) {
+               err = get_create_ext(args, dir, entry, mode);
                if (err)
                        goto out_put_forget_req;
-               BUG_ON(args->in_numargs != 2);
-               args->in_numargs = 3;
-               args->in_args[2].size = security_ctxlen;
-               args->in_args[2].value = security_ctx;
        }
  
        err = fuse_simple_request(fm, args);
-       kfree(security_ctx);
+       free_ext_value(args);
        if (err)
                goto out_put_forget_req;
  
        return err;
  }
  
 -static int fuse_mknod(struct user_namespace *mnt_userns, struct inode *dir,
 +static int fuse_mknod(struct mnt_idmap *idmap, struct inode *dir,
                      struct dentry *entry, umode_t mode, dev_t rdev)
  {
        struct fuse_mknod_in inarg;
        return create_new_entry(fm, &args, dir, entry, mode);
  }
  
 -static int fuse_create(struct user_namespace *mnt_userns, struct inode *dir,
 +static int fuse_create(struct mnt_idmap *idmap, struct inode *dir,
                       struct dentry *entry, umode_t mode, bool excl)
  {
 -      return fuse_mknod(&init_user_ns, dir, entry, mode, 0);
 +      return fuse_mknod(&nop_mnt_idmap, dir, entry, mode, 0);
  }
  
 -static int fuse_tmpfile(struct user_namespace *mnt_userns, struct inode *dir,
 +static int fuse_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
                        struct file *file, umode_t mode)
  {
        struct fuse_conn *fc = get_fuse_conn(dir);
        return err;
  }
  
 -static int fuse_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
 +static int fuse_mkdir(struct mnt_idmap *idmap, struct inode *dir,
                      struct dentry *entry, umode_t mode)
  {
        struct fuse_mkdir_in inarg;
        return create_new_entry(fm, &args, dir, entry, S_IFDIR);
  }
  
 -static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir,
 +static int fuse_symlink(struct mnt_idmap *idmap, struct inode *dir,
                        struct dentry *entry, const char *link)
  {
        struct fuse_mount *fm = get_fuse_mount(dir);
@@@ -998,7 -1066,7 +1066,7 @@@ static int fuse_rename_common(struct in
        return err;
  }
  
 -static int fuse_rename2(struct user_namespace *mnt_userns, struct inode *olddir,
 +static int fuse_rename2(struct mnt_idmap *idmap, struct inode *olddir,
                        struct dentry *oldent, struct inode *newdir,
                        struct dentry *newent, unsigned int flags)
  {
@@@ -1156,7 -1224,7 +1224,7 @@@ static int fuse_update_get_attr(struct 
                forget_all_cached_acls(inode);
                err = fuse_do_getattr(inode, stat, file);
        } else if (stat) {
 -              generic_fillattr(&init_user_ns, inode, stat);
 +              generic_fillattr(&nop_mnt_idmap, inode, stat);
                stat->mode = fi->orig_i_mode;
                stat->ino = fi->orig_ino;
        }
@@@ -1326,7 -1394,7 +1394,7 @@@ static int fuse_perm_getattr(struct ino
   * access request is sent.  Execute permission is still checked
   * locally based on file mode.
   */
 -static int fuse_permission(struct user_namespace *mnt_userns,
 +static int fuse_permission(struct mnt_idmap *idmap,
                           struct inode *inode, int mask)
  {
        struct fuse_conn *fc = get_fuse_conn(inode);
        }
  
        if (fc->default_permissions) {
 -              err = generic_permission(&init_user_ns, inode, mask);
 +              err = generic_permission(&nop_mnt_idmap, inode, mask);
  
                /* If permission is denied, try to refresh file
                   attributes.  This is also needed, because the root
                if (err == -EACCES && !refreshed) {
                        err = fuse_perm_getattr(inode, mask);
                        if (!err)
 -                              err = generic_permission(&init_user_ns,
 +                              err = generic_permission(&nop_mnt_idmap,
                                                         inode, mask);
                }
  
@@@ -1690,7 -1758,7 +1758,7 @@@ int fuse_do_setattr(struct dentry *dent
        if (!fc->default_permissions)
                attr->ia_valid |= ATTR_FORCE;
  
 -      err = setattr_prepare(&init_user_ns, dentry, attr);
 +      err = setattr_prepare(&nop_mnt_idmap, dentry, attr);
        if (err)
                return err;
  
@@@ -1837,7 -1905,7 +1905,7 @@@ error
        return err;
  }
  
 -static int fuse_setattr(struct user_namespace *mnt_userns, struct dentry *entry,
 +static int fuse_setattr(struct mnt_idmap *idmap, struct dentry *entry,
                        struct iattr *attr)
  {
        struct inode *inode = d_inode(entry);
        return ret;
  }
  
 -static int fuse_getattr(struct user_namespace *mnt_userns,
 +static int fuse_getattr(struct mnt_idmap *idmap,
                        const struct path *path, struct kstat *stat,
                        u32 request_mask, unsigned int flags)
  {
@@@ -1942,8 -2010,7 +2010,8 @@@ static const struct inode_operations fu
        .permission     = fuse_permission,
        .getattr        = fuse_getattr,
        .listxattr      = fuse_listxattr,
 -      .get_inode_acl  = fuse_get_acl,
 +      .get_inode_acl  = fuse_get_inode_acl,
 +      .get_acl        = fuse_get_acl,
        .set_acl        = fuse_set_acl,
        .fileattr_get   = fuse_fileattr_get,
        .fileattr_set   = fuse_fileattr_set,
@@@ -1965,8 -2032,7 +2033,8 @@@ static const struct inode_operations fu
        .permission     = fuse_permission,
        .getattr        = fuse_getattr,
        .listxattr      = fuse_listxattr,
 -      .get_inode_acl  = fuse_get_acl,
 +      .get_inode_acl  = fuse_get_inode_acl,
 +      .get_acl        = fuse_get_acl,
        .set_acl        = fuse_set_acl,
        .fileattr_get   = fuse_fileattr_get,
        .fileattr_set   = fuse_fileattr_set,
diff --combined fs/fuse/file.c
@@@ -18,7 -18,7 +18,8 @@@
  #include <linux/falloc.h>
  #include <linux/uio.h>
  #include <linux/fs.h>
 +#include <linux/filelock.h>
+ #include <linux/file.h>
  
  static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
                          unsigned int open_flags, int opcode,
@@@ -478,48 -478,36 +479,36 @@@ static void fuse_sync_writes(struct ino
        fuse_release_nowrite(inode);
  }
  
- static int fuse_flush(struct file *file, fl_owner_t id)
- {
-       struct inode *inode = file_inode(file);
-       struct fuse_mount *fm = get_fuse_mount(inode);
-       struct fuse_file *ff = file->private_data;
+ struct fuse_flush_args {
+       struct fuse_args args;
        struct fuse_flush_in inarg;
-       FUSE_ARGS(args);
-       int err;
-       if (fuse_is_bad(inode))
-               return -EIO;
+       struct work_struct work;
+       struct file *file;
+ };
  
-       if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache)
-               return 0;
+ static int fuse_do_flush(struct fuse_flush_args *fa)
+ {
+       int err;
+       struct inode *inode = file_inode(fa->file);
+       struct fuse_mount *fm = get_fuse_mount(inode);
  
        err = write_inode_now(inode, 1);
        if (err)
-               return err;
+               goto out;
  
        inode_lock(inode);
        fuse_sync_writes(inode);
        inode_unlock(inode);
  
-       err = filemap_check_errors(file->f_mapping);
+       err = filemap_check_errors(fa->file->f_mapping);
        if (err)
-               return err;
+               goto out;
  
        err = 0;
        if (fm->fc->no_flush)
                goto inval_attr_out;
  
-       memset(&inarg, 0, sizeof(inarg));
-       inarg.fh = ff->fh;
-       inarg.lock_owner = fuse_lock_owner_id(fm->fc, id);
-       args.opcode = FUSE_FLUSH;
-       args.nodeid = get_node_id(inode);
-       args.in_numargs = 1;
-       args.in_args[0].size = sizeof(inarg);
-       args.in_args[0].value = &inarg;
-       args.force = true;
-       err = fuse_simple_request(fm, &args);
+       err = fuse_simple_request(fm, &fa->args);
        if (err == -ENOSYS) {
                fm->fc->no_flush = 1;
                err = 0;
@@@ -532,9 -520,57 +521,57 @@@ inval_attr_out
         */
        if (!err && fm->fc->writeback_cache)
                fuse_invalidate_attr_mask(inode, STATX_BLOCKS);
+ out:
+       fput(fa->file);
+       kfree(fa);
        return err;
  }
  
+ static void fuse_flush_async(struct work_struct *work)
+ {
+       struct fuse_flush_args *fa = container_of(work, typeof(*fa), work);
+       fuse_do_flush(fa);
+ }
+ static int fuse_flush(struct file *file, fl_owner_t id)
+ {
+       struct fuse_flush_args *fa;
+       struct inode *inode = file_inode(file);
+       struct fuse_mount *fm = get_fuse_mount(inode);
+       struct fuse_file *ff = file->private_data;
+       if (fuse_is_bad(inode))
+               return -EIO;
+       if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache)
+               return 0;
+       fa = kzalloc(sizeof(*fa), GFP_KERNEL);
+       if (!fa)
+               return -ENOMEM;
+       fa->inarg.fh = ff->fh;
+       fa->inarg.lock_owner = fuse_lock_owner_id(fm->fc, id);
+       fa->args.opcode = FUSE_FLUSH;
+       fa->args.nodeid = get_node_id(inode);
+       fa->args.in_numargs = 1;
+       fa->args.in_args[0].size = sizeof(fa->inarg);
+       fa->args.in_args[0].value = &fa->inarg;
+       fa->args.force = true;
+       fa->file = get_file(file);
+       /* Don't wait if the task is exiting */
+       if (current->flags & PF_EXITING) {
+               INIT_WORK(&fa->work, fuse_flush_async);
+               schedule_work(&fa->work);
+               return 0;
+       }
+       return fuse_do_flush(fa);
+ }
  int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
                      int datasync, int opcode)
  {
@@@ -653,7 -689,7 +690,7 @@@ static ssize_t fuse_get_res_by_io(struc
        return io->bytes < 0 ? io->size : io->bytes;
  }
  
- /**
+ /*
   * In case of short read, the caller sets 'pos' to the position of
   * actual end of fuse request in IO request. Otherwise, if bytes_requested
   * == bytes_transferred or rw == WRITE, the caller sets 'pos' to -1.
@@@ -1314,8 -1350,7 +1351,8 @@@ static ssize_t fuse_cache_write_iter(st
                        return err;
  
                if (fc->handle_killpriv_v2 &&
 -                  setattr_should_drop_suidgid(&init_user_ns, file_inode(file))) {
 +                  setattr_should_drop_suidgid(&nop_mnt_idmap,
 +                                              file_inode(file))) {
                        goto writethrough;
                }
  
@@@ -2186,7 -2221,7 +2223,7 @@@ static bool fuse_writepage_need_send(st
        return false;
  }
  
 -static int fuse_writepages_fill(struct page *page,
 +static int fuse_writepages_fill(struct folio *folio,
                struct writeback_control *wbc, void *_data)
  {
        struct fuse_fill_wb_data *data = _data;
                        goto out_unlock;
        }
  
 -      if (wpa && fuse_writepage_need_send(fc, page, ap, data)) {
 +      if (wpa && fuse_writepage_need_send(fc, &folio->page, ap, data)) {
                fuse_writepages_send(data);
                data->wpa = NULL;
        }
                data->max_pages = 1;
  
                ap = &wpa->ia.ap;
 -              fuse_write_args_fill(&wpa->ia, data->ff, page_offset(page), 0);
 +              fuse_write_args_fill(&wpa->ia, data->ff, folio_pos(folio), 0);
                wpa->ia.write.in.write_flags |= FUSE_WRITE_CACHE;
                wpa->next = NULL;
                ap->args.in_pages = true;
                ap->num_pages = 0;
                wpa->inode = inode;
        }
 -      set_page_writeback(page);
 +      folio_start_writeback(folio);
  
 -      copy_highpage(tmp_page, page);
 +      copy_highpage(tmp_page, &folio->page);
        ap->pages[ap->num_pages] = tmp_page;
        ap->descs[ap->num_pages].offset = 0;
        ap->descs[ap->num_pages].length = PAGE_SIZE;
 -      data->orig_pages[ap->num_pages] = page;
 +      data->orig_pages[ap->num_pages] = &folio->page;
  
        inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
        inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP);
                spin_lock(&fi->lock);
                ap->num_pages++;
                spin_unlock(&fi->lock);
 -      } else if (fuse_writepage_add(wpa, page)) {
 +      } else if (fuse_writepage_add(wpa, &folio->page)) {
                data->wpa = wpa;
        } else {
 -              end_page_writeback(page);
 +              folio_end_writeback(folio);
        }
  out_unlock:
 -      unlock_page(page);
 +      folio_unlock(folio);
  
        return err;
  }
diff --combined fs/fuse/fuse_i.h
@@@ -249,8 -249,9 +249,9 @@@ struct fuse_page_desc 
  struct fuse_args {
        uint64_t nodeid;
        uint32_t opcode;
-       unsigned short in_numargs;
-       unsigned short out_numargs;
+       uint8_t in_numargs;
+       uint8_t out_numargs;
+       uint8_t ext_idx;
        bool force:1;
        bool noreply:1;
        bool nocreds:1;
        bool page_zeroing:1;
        bool page_replace:1;
        bool may_block:1;
+       bool is_ext:1;
        struct fuse_in_arg in_args[3];
        struct fuse_arg out_args[2];
        void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
@@@ -781,6 -783,9 +783,9 @@@ struct fuse_conn 
        /* Initialize security xattrs when creating a new inode */
        unsigned int init_security:1;
  
+       /* Add supplementary group info when creating a new inode */
+       unsigned int create_supp_group:1;
        /* Does the filesystem support per inode DAX? */
        unsigned int inode_dax:1;
  
@@@ -1264,12 -1269,12 +1269,12 @@@ ssize_t fuse_getxattr(struct inode *ino
  ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
  int fuse_removexattr(struct inode *inode, const char *name);
  extern const struct xattr_handler *fuse_xattr_handlers[];
 -extern const struct xattr_handler *fuse_acl_xattr_handlers[];
 -extern const struct xattr_handler *fuse_no_acl_xattr_handlers[];
  
  struct posix_acl;
 -struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu);
 -int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
 +struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu);
 +struct posix_acl *fuse_get_acl(struct mnt_idmap *idmap,
 +                             struct dentry *dentry, int type);
 +int fuse_set_acl(struct mnt_idmap *, struct dentry *dentry,
                 struct posix_acl *acl, int type);
  
  /* readdir.c */
@@@ -1309,7 -1314,7 +1314,7 @@@ long fuse_file_ioctl(struct file *file
  long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
                            unsigned long arg);
  int fuse_fileattr_get(struct dentry *dentry, struct fileattr *fa);
 -int fuse_fileattr_set(struct user_namespace *mnt_userns,
 +int fuse_fileattr_set(struct mnt_idmap *idmap,
                      struct dentry *dentry, struct fileattr *fa);
  
  /* file.c */
diff --combined fs/fuse/inode.c
@@@ -311,8 -311,7 +311,8 @@@ void fuse_change_attributes(struct inod
                fuse_dax_dontcache(inode, attr->flags);
  }
  
 -static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
 +static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
 +                          struct fuse_conn *fc)
  {
        inode->i_mode = attr->mode & S_IFMT;
        inode->i_size = attr->size;
                                   new_decode_dev(attr->rdev));
        } else
                BUG();
 +      /*
 +       * Ensure that we don't cache acls for daemons without FUSE_POSIX_ACL
 +       * so they see the exact same behavior as before.
 +       */
 +      if (!fc->posix_acl)
 +              inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
  }
  
  static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
@@@ -379,7 -372,7 +379,7 @@@ struct inode *fuse_iget(struct super_bl
                if (!inode)
                        return NULL;
  
 -              fuse_init_inode(inode, attr);
 +              fuse_init_inode(inode, attr, fc);
                get_fuse_inode(inode)->nodeid = nodeid;
                inode->i_flags |= S_AUTOMOUNT;
                goto done;
@@@ -395,7 -388,7 +395,7 @@@ retry
                if (!fc->writeback_cache || !S_ISREG(attr->mode))
                        inode->i_flags |= S_NOCMTIME;
                inode->i_generation = generation;
 -              fuse_init_inode(inode, attr);
 +              fuse_init_inode(inode, attr, fc);
                unlock_new_inode(inode);
        } else if (fuse_stale_inode(inode, generation, attr)) {
                /* nodeid was reused, any I/O on the old inode should fail */
@@@ -1181,6 -1174,7 +1181,6 @@@ static void process_init_reply(struct f
                        if ((flags & FUSE_POSIX_ACL)) {
                                fc->default_permissions = 1;
                                fc->posix_acl = 1;
 -                              fm->sb->s_xattr = fuse_acl_xattr_handlers;
                        }
                        if (flags & FUSE_CACHE_SYMLINKS)
                                fc->cache_symlinks = 1;
                                fc->setxattr_ext = 1;
                        if (flags & FUSE_SECURITY_CTX)
                                fc->init_security = 1;
+                       if (flags & FUSE_CREATE_SUPP_GROUP)
+                               fc->create_supp_group = 1;
                } else {
                        ra_pages = fc->max_read / PAGE_SIZE;
                        fc->no_lock = 1;
@@@ -1252,7 -1248,7 +1254,7 @@@ void fuse_send_init(struct fuse_mount *
                FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
                FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
                FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
-               FUSE_SECURITY_CTX;
+               FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP;
  #ifdef CONFIG_FUSE_DAX
        if (fm->fc->dax)
                flags |= FUSE_MAP_ALIGNMENT;
@@@ -1426,6 -1422,13 +1428,6 @@@ static void fuse_sb_defaults(struct sup
        if (sb->s_user_ns != &init_user_ns)
                sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
        sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
 -
 -      /*
 -       * If we are not in the initial user namespace posix
 -       * acls must be translated.
 -       */
 -      if (sb->s_user_ns != &init_user_ns)
 -              sb->s_xattr = fuse_no_acl_xattr_handlers;
  }
  
  static int fuse_fill_super_submount(struct super_block *sb,
diff --combined fs/fuse/ioctl.c
@@@ -419,6 -419,12 +419,12 @@@ static struct fuse_file *fuse_priv_ioct
        struct fuse_mount *fm = get_fuse_mount(inode);
        bool isdir = S_ISDIR(inode->i_mode);
  
+       if (!fuse_allow_current_process(fm->fc))
+               return ERR_PTR(-EACCES);
+       if (fuse_is_bad(inode))
+               return ERR_PTR(-EIO);
        if (!S_ISREG(inode->i_mode) && !isdir)
                return ERR_PTR(-ENOTTY);
  
@@@ -467,7 -473,7 +473,7 @@@ cleanup
        return err;
  }
  
 -int fuse_fileattr_set(struct user_namespace *mnt_userns,
 +int fuse_fileattr_set(struct mnt_idmap *idmap,
                      struct dentry *dentry, struct fileattr *fa)
  {
        struct inode *inode = d_inode(dentry);