int err;
        bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode);
  
 -      if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
 +      if (!fc->default_permissions)
                attr->ia_valid |= ATTR_FORCE;
  
-       err = inode_change_ok(inode, attr);
+       err = setattr_prepare(dentry, attr);
        if (err)
                return err;
  
  {
        struct inode *inode = d_inode(entry);
        struct fuse_conn *fc = get_fuse_conn(inode);
 +      struct file *file = (attr->ia_valid & ATTR_FILE) ? attr->ia_file : NULL;
 +      int ret;
  
 -      if (!fuse_allow_current_process(fc))
 +      if (!fuse_allow_current_process(get_fuse_conn(inode)))
                return -EACCES;
  
 -      return fuse_update_attributes(inode, stat, NULL, NULL);
 -}
 -
 -static int fuse_setxattr(struct dentry *unused, struct inode *inode,
 -                       const char *name, const void *value,
 -                       size_t size, int flags)
 -{
 -      struct fuse_conn *fc = get_fuse_conn(inode);
 -      FUSE_ARGS(args);
 -      struct fuse_setxattr_in inarg;
 -      int err;
 -
 -      if (fc->no_setxattr)
 -              return -EOPNOTSUPP;
 +      if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) {
 +              attr->ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID |
 +                                  ATTR_MODE);
  
 -      memset(&inarg, 0, sizeof(inarg));
 -      inarg.size = size;
 -      inarg.flags = flags;
 -      args.in.h.opcode = FUSE_SETXATTR;
 -      args.in.h.nodeid = get_node_id(inode);
 -      args.in.numargs = 3;
 -      args.in.args[0].size = sizeof(inarg);
 -      args.in.args[0].value = &inarg;
 -      args.in.args[1].size = strlen(name) + 1;
 -      args.in.args[1].value = name;
 -      args.in.args[2].size = size;
 -      args.in.args[2].value = value;
 -      err = fuse_simple_request(fc, &args);
 -      if (err == -ENOSYS) {
 -              fc->no_setxattr = 1;
 -              err = -EOPNOTSUPP;
 -      }
 -      if (!err) {
 -              fuse_invalidate_attr(inode);
 -              fuse_update_ctime(inode);
 +              /*
 +               * The only sane way to reliably kill suid/sgid is to do it in
 +               * the userspace filesystem
 +               *
 +               * This should be done on write(), truncate() and chown().
 +               */
 +              if (!fc->handle_killpriv) {
 +                      int kill;
 +
 +                      /*
 +                       * ia_mode calculation may have used stale i_mode.
 +                       * Refresh and recalculate.
 +                       */
 +                      ret = fuse_do_getattr(inode, NULL, file);
 +                      if (ret)
 +                              return ret;
 +
 +                      attr->ia_mode = inode->i_mode;
 +                      kill = should_remove_suid(entry);
 +                      if (kill & ATTR_KILL_SUID) {
 +                              attr->ia_valid |= ATTR_MODE;
 +                              attr->ia_mode &= ~S_ISUID;
 +                      }
 +                      if (kill & ATTR_KILL_SGID) {
 +                              attr->ia_valid |= ATTR_MODE;
 +                              attr->ia_mode &= ~S_ISGID;
 +                      }
 +              }
        }
 -      return err;
 -}
 -
 -static ssize_t fuse_getxattr(struct dentry *entry, struct inode *inode,
 -                           const char *name, void *value, size_t size)
 -{
 -      struct fuse_conn *fc = get_fuse_conn(inode);
 -      FUSE_ARGS(args);
 -      struct fuse_getxattr_in inarg;
 -      struct fuse_getxattr_out outarg;
 -      ssize_t ret;
 +      if (!attr->ia_valid)
 +              return 0;
  
-       ret = fuse_do_setattr(inode, attr, file);
 -      if (fc->no_getxattr)
 -              return -EOPNOTSUPP;
++      ret = fuse_do_setattr(entry, attr, file);
 +      if (!ret) {
 +              /*
 +               * If filesystem supports acls it may have updated acl xattrs in
 +               * the filesystem, so forget cached acls for the inode.
 +               */
 +              if (fc->posix_acl)
 +                      forget_all_cached_acls(inode);
  
 -      memset(&inarg, 0, sizeof(inarg));
 -      inarg.size = size;
 -      args.in.h.opcode = FUSE_GETXATTR;
 -      args.in.h.nodeid = get_node_id(inode);
 -      args.in.numargs = 2;
 -      args.in.args[0].size = sizeof(inarg);
 -      args.in.args[0].value = &inarg;
 -      args.in.args[1].size = strlen(name) + 1;
 -      args.in.args[1].value = name;
 -      /* This is really two different operations rolled into one */
 -      args.out.numargs = 1;
 -      if (size) {
 -              args.out.argvar = 1;
 -              args.out.args[0].size = size;
 -              args.out.args[0].value = value;
 -      } else {
 -              args.out.args[0].size = sizeof(outarg);
 -              args.out.args[0].value = &outarg;
 -      }
 -      ret = fuse_simple_request(fc, &args);
 -      if (!ret && !size)
 -              ret = outarg.size;
 -      if (ret == -ENOSYS) {
 -              fc->no_getxattr = 1;
 -              ret = -EOPNOTSUPP;
 +              /* Directory mode changed, may need to revalidate access */
 +              if (d_is_dir(entry) && (attr->ia_valid & ATTR_MODE))
 +                      fuse_invalidate_entry_cache(entry);
        }
        return ret;
  }