]> Git Repo - J-linux.git/commitdiff
Merge tag 'nfsd-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
authorLinus Torvalds <[email protected]>
Wed, 22 Feb 2023 22:21:40 +0000 (14:21 -0800)
committerLinus Torvalds <[email protected]>
Wed, 22 Feb 2023 22:21:40 +0000 (14:21 -0800)
Pull nfsd updates from Chuck Lever:
 "Two significant security enhancements are part of this release:

   - NFSD's RPC header encoding and decoding, including RPCSEC GSS and
     gssproxy header parsing, has been overhauled to make it more
     memory-safe.

   - Support for Kerberos AES-SHA2-based encryption types has been added
     for both the NFS client and server. This provides a clean path for
     deprecating and removing insecure encryption types based on DES and
     SHA-1. AES-SHA2 is also FIPS-140 compliant, so that NFS with
     Kerberos may now be used on systems with fips enabled.

  In addition to these, NFSD is now able to handle crossing into an
  auto-mounted mount point on an exported NFS mount. A number of fixes
  have been made to NFSD's server-side copy implementation.

  RPC metrics have been converted to per-CPU variables. This helps
  reduce unnecessary cross-CPU and cross-node memory bus traffic, and
  significantly reduces noise when KCSAN is enabled"

* tag 'nfsd-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (121 commits)
  NFSD: Clean up nfsd_symlink()
  NFSD: copy the whole verifier in nfsd_copy_write_verifier
  nfsd: don't fsync nfsd_files on last close
  SUNRPC: Fix occasional warning when destroying gss_krb5_enctypes
  nfsd: fix courtesy client with deny mode handling in nfs4_upgrade_open
  NFSD: fix problems with cleanup on errors in nfsd4_copy
  nfsd: fix race to check ls_layouts
  nfsd: don't hand out delegation on setuid files being opened for write
  SUNRPC: Remove ->xpo_secure_port()
  SUNRPC: Clean up the svc_xprt_flags() macro
  nfsd: remove fs/nfsd/fault_inject.c
  NFSD: fix leaked reference count of nfsd4_ssc_umount_item
  nfsd: clean up potential nfsd_file refcount leaks in COPY codepath
  nfsd: zero out pointers after putting nfsd_files on COPY setup error
  SUNRPC: Fix whitespace damage in svcauth_unix.c
  nfsd: eliminate __nfs4_get_fd
  nfsd: add some kerneldoc comments for stateid preprocessing functions
  nfsd: eliminate find_deleg_file_locked
  nfsd: don't take nfsd4_copy ref for OP_OFFLOAD_STATUS
  SUNRPC: Add encryption self-tests
  ...

12 files changed:
1  2 
fs/namei.c
fs/nfs/export.c
fs/nfsd/nfs2acl.c
fs/nfsd/nfs3acl.c
fs/nfsd/nfs3proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfsproc.c
fs/nfsd/vfs.c
include/linux/lockd/lockd.h
include/linux/namei.h
net/sunrpc/svcsock.c
net/sunrpc/xdr.c

diff --combined fs/namei.c
index 5855dc6edbd5b60d831cdb62eec91f4974cdbc78,c06f4706606302c319a736e29e50fe30a35a7d79..edfedfbccaef471e9eb6fe02a20df58069d9b6dc
@@@ -20,7 -20,6 +20,7 @@@
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/fs.h>
 +#include <linux/filelock.h>
  #include <linux/namei.h>
  #include <linux/pagemap.h>
  #include <linux/sched/mm.h>
@@@ -274,7 -273,7 +274,7 @@@ void putname(struct filename *name
  
  /**
   * check_acl - perform ACL permission checking
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @inode:    inode to check permissions on
   * @mask:     right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
   *
   * retrieve POSIX acls it needs to know whether it is called from a blocking or
   * non-blocking context and thus cares about the MAY_NOT_BLOCK bit.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -static int check_acl(struct user_namespace *mnt_userns,
 +static int check_acl(struct mnt_idmap *idmap,
                     struct inode *inode, int mask)
  {
  #ifdef CONFIG_FS_POSIX_ACL
                /* no ->get_inode_acl() calls in RCU mode... */
                if (is_uncached_acl(acl))
                        return -ECHILD;
 -              return posix_acl_permission(mnt_userns, inode, acl, mask);
 +              return posix_acl_permission(idmap, inode, acl, mask);
        }
  
        acl = get_inode_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
 -              int error = posix_acl_permission(mnt_userns, inode, acl, mask);
 +              int error = posix_acl_permission(idmap, inode, acl, mask);
                posix_acl_release(acl);
                return error;
        }
  
  /**
   * acl_permission_check - perform basic UNIX permission checking
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @inode:    inode to check permissions on
   * @mask:     right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
   *
   * function may retrieve POSIX acls it needs to know whether it is called from a
   * blocking or non-blocking context and thus cares about the MAY_NOT_BLOCK bit.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -static int acl_permission_check(struct user_namespace *mnt_userns,
 +static int acl_permission_check(struct mnt_idmap *idmap,
                                struct inode *inode, int mask)
  {
        unsigned int mode = inode->i_mode;
        vfsuid_t vfsuid;
  
        /* Are we the owner? If so, ACL's don't matter */
 -      vfsuid = i_uid_into_vfsuid(mnt_userns, inode);
 +      vfsuid = i_uid_into_vfsuid(idmap, inode);
        if (likely(vfsuid_eq_kuid(vfsuid, current_fsuid()))) {
                mask &= 7;
                mode >>= 6;
  
        /* Do we have ACL's? */
        if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
 -              int error = check_acl(mnt_userns, inode, mask);
 +              int error = check_acl(idmap, inode, mask);
                if (error != -EAGAIN)
                        return error;
        }
         * about? Need to check group ownership if so.
         */
        if (mask & (mode ^ (mode >> 3))) {
 -              vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode);
 +              vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);
                if (vfsgid_in_group_p(vfsgid))
                        mode >>= 3;
        }
  
  /**
   * generic_permission -  check for access rights on a Posix-like filesystem
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @inode:    inode to check access rights for
   * @mask:     right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC,
   *            %MAY_NOT_BLOCK ...)
   * request cannot be satisfied (eg. requires blocking or too much complexity).
   * It would then be called again in ref-walk mode.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -int generic_permission(struct user_namespace *mnt_userns, struct inode *inode,
 +int generic_permission(struct mnt_idmap *idmap, struct inode *inode,
                       int mask)
  {
        int ret;
        /*
         * Do the basic permission checks.
         */
 -      ret = acl_permission_check(mnt_userns, inode, mask);
 +      ret = acl_permission_check(idmap, inode, mask);
        if (ret != -EACCES)
                return ret;
  
        if (S_ISDIR(inode->i_mode)) {
                /* DACs are overridable for directories */
                if (!(mask & MAY_WRITE))
 -                      if (capable_wrt_inode_uidgid(mnt_userns, inode,
 +                      if (capable_wrt_inode_uidgid(idmap, inode,
                                                     CAP_DAC_READ_SEARCH))
                                return 0;
 -              if (capable_wrt_inode_uidgid(mnt_userns, inode,
 +              if (capable_wrt_inode_uidgid(idmap, inode,
                                             CAP_DAC_OVERRIDE))
                        return 0;
                return -EACCES;
         */
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
        if (mask == MAY_READ)
 -              if (capable_wrt_inode_uidgid(mnt_userns, inode,
 +              if (capable_wrt_inode_uidgid(idmap, inode,
                                             CAP_DAC_READ_SEARCH))
                        return 0;
        /*
         * at least one exec bit set.
         */
        if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
 -              if (capable_wrt_inode_uidgid(mnt_userns, inode,
 +              if (capable_wrt_inode_uidgid(idmap, inode,
                                             CAP_DAC_OVERRIDE))
                        return 0;
  
@@@ -442,7 -441,7 +442,7 @@@ EXPORT_SYMBOL(generic_permission)
  
  /**
   * do_inode_permission - UNIX permission checking
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @inode:    inode to check permissions on
   * @mask:     right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC ...)
   *
   * flag in inode->i_opflags, that says "this has not special
   * permission function, use the fast case".
   */
 -static inline int do_inode_permission(struct user_namespace *mnt_userns,
 +static inline int do_inode_permission(struct mnt_idmap *idmap,
                                      struct inode *inode, int mask)
  {
        if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
                if (likely(inode->i_op->permission))
 -                      return inode->i_op->permission(mnt_userns, inode, mask);
 +                      return inode->i_op->permission(idmap, inode, mask);
  
                /* This gets set once for the inode lifetime */
                spin_lock(&inode->i_lock);
                inode->i_opflags |= IOP_FASTPERM;
                spin_unlock(&inode->i_lock);
        }
 -      return generic_permission(mnt_userns, inode, mask);
 +      return generic_permission(idmap, inode, mask);
  }
  
  /**
@@@ -488,7 -487,7 +488,7 @@@ static int sb_permission(struct super_b
  
  /**
   * inode_permission - Check for access rights to a given inode
 - * @mnt_userns:       User namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @inode:    Inode to check permission on
   * @mask:     Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
   *
   *
   * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
   */
 -int inode_permission(struct user_namespace *mnt_userns,
 +int inode_permission(struct mnt_idmap *idmap,
                     struct inode *inode, int mask)
  {
        int retval;
                 * written back improperly if their true value is unknown
                 * to the vfs.
                 */
 -              if (HAS_UNMAPPED_ID(mnt_userns, inode))
 +              if (HAS_UNMAPPED_ID(idmap, inode))
                        return -EACCES;
        }
  
 -      retval = do_inode_permission(mnt_userns, inode, mask);
 +      retval = do_inode_permission(idmap, inode, mask);
        if (retval)
                return retval;
  
@@@ -1095,14 -1094,14 +1095,14 @@@ fs_initcall(init_fs_namei_sysctls)
   */
  static inline int may_follow_link(struct nameidata *nd, const struct inode *inode)
  {
 -      struct user_namespace *mnt_userns;
 +      struct mnt_idmap *idmap;
        vfsuid_t vfsuid;
  
        if (!sysctl_protected_symlinks)
                return 0;
  
 -      mnt_userns = mnt_user_ns(nd->path.mnt);
 -      vfsuid = i_uid_into_vfsuid(mnt_userns, inode);
 +      idmap = mnt_idmap(nd->path.mnt);
 +      vfsuid = i_uid_into_vfsuid(idmap, inode);
        /* Allowed if owner and follower match. */
        if (vfsuid_eq_kuid(vfsuid, current_fsuid()))
                return 0;
  
  /**
   * safe_hardlink_source - Check for safe hardlink conditions
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap: idmap of the mount the inode was found from
   * @inode: the source inode to hardlink from
   *
   * Return false if at least one of the following conditions:
   *
   * Otherwise returns true.
   */
 -static bool safe_hardlink_source(struct user_namespace *mnt_userns,
 +static bool safe_hardlink_source(struct mnt_idmap *idmap,
                                 struct inode *inode)
  {
        umode_t mode = inode->i_mode;
                return false;
  
        /* Hardlinking to unreadable or unwritable sources is dangerous. */
 -      if (inode_permission(mnt_userns, inode, MAY_READ | MAY_WRITE))
 +      if (inode_permission(idmap, inode, MAY_READ | MAY_WRITE))
                return false;
  
        return true;
  
  /**
   * may_linkat - Check permissions for creating a hardlink
 - * @mnt_userns:       user namespace of the mount the inode was found from
 - * @link: the source to hardlink from
 + * @idmap: idmap of the mount the inode was found from
 + * @link:  the source to hardlink from
   *
   * Block hardlink when all of:
   *  - sysctl_protected_hardlinks enabled
   *  - hardlink source is unsafe (see safe_hardlink_source() above)
   *  - not CAP_FOWNER in a namespace with the inode owner uid mapped
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply pass @nop_mnt_idmap.
   *
   * Returns 0 if successful, -ve on error.
   */
 -int may_linkat(struct user_namespace *mnt_userns, const struct path *link)
 +int may_linkat(struct mnt_idmap *idmap, const struct path *link)
  {
        struct inode *inode = link->dentry->d_inode;
  
        /* Inode writeback is not safe when the uid or gid are invalid. */
 -      if (!vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)) ||
 -          !vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode)))
 +      if (!vfsuid_valid(i_uid_into_vfsuid(idmap, inode)) ||
 +          !vfsgid_valid(i_gid_into_vfsgid(idmap, inode)))
                return -EOVERFLOW;
  
        if (!sysctl_protected_hardlinks)
        /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
         * otherwise, it must be a safe source.
         */
 -      if (safe_hardlink_source(mnt_userns, inode) ||
 -          inode_owner_or_capable(mnt_userns, inode))
 +      if (safe_hardlink_source(idmap, inode) ||
 +          inode_owner_or_capable(idmap, inode))
                return 0;
  
        audit_log_path_denied(AUDIT_ANOM_LINK, "linkat");
   * may_create_in_sticky - Check whether an O_CREAT open in a sticky directory
   *                      should be allowed, or not, on files that already
   *                      exist.
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap: idmap of the mount the inode was found from
   * @nd: nameidata pathwalk data
   * @inode: the inode of the file to open
   *
   * the directory doesn't have to be world writable: being group writable will
   * be enough.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply pass @nop_mnt_idmap.
   *
   * Returns 0 if the open is allowed, -ve on error.
   */
 -static int may_create_in_sticky(struct user_namespace *mnt_userns,
 +static int may_create_in_sticky(struct mnt_idmap *idmap,
                                struct nameidata *nd, struct inode *const inode)
  {
        umode_t dir_mode = nd->dir_mode;
        if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) ||
            (!sysctl_protected_regular && S_ISREG(inode->i_mode)) ||
            likely(!(dir_mode & S_ISVTX)) ||
 -          vfsuid_eq(i_uid_into_vfsuid(mnt_userns, inode), dir_vfsuid) ||
 -          vfsuid_eq_kuid(i_uid_into_vfsuid(mnt_userns, inode), current_fsuid()))
 +          vfsuid_eq(i_uid_into_vfsuid(idmap, inode), dir_vfsuid) ||
 +          vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid()))
                return 0;
  
        if (likely(dir_mode & 0002) ||
@@@ -1459,11 -1458,11 +1459,11 @@@ EXPORT_SYMBOL(follow_down_one)
   * point, the filesystem owning that dentry may be queried as to whether the
   * caller is permitted to proceed or not.
   */
- int follow_down(struct path *path)
+ int follow_down(struct path *path, unsigned int flags)
  {
        struct vfsmount *mnt = path->mnt;
        bool jumped;
-       int ret = traverse_mounts(path, &jumped, NULL, 0);
+       int ret = traverse_mounts(path, &jumped, NULL, flags);
  
        if (path->mnt != mnt)
                mntput(mnt);
@@@ -1705,15 -1704,15 +1705,15 @@@ static struct dentry *lookup_slow(cons
        return res;
  }
  
 -static inline int may_lookup(struct user_namespace *mnt_userns,
 +static inline int may_lookup(struct mnt_idmap *idmap,
                             struct nameidata *nd)
  {
        if (nd->flags & LOOKUP_RCU) {
 -              int err = inode_permission(mnt_userns, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
 +              int err = inode_permission(idmap, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
                if (err != -ECHILD || !try_to_unlazy(nd))
                        return err;
        }
 -      return inode_permission(mnt_userns, nd->inode, MAY_EXEC);
 +      return inode_permission(idmap, nd->inode, MAY_EXEC);
  }
  
  static int reserve_stack(struct nameidata *nd, struct path *link)
@@@ -2254,13 -2253,13 +2254,13 @@@ static int link_path_walk(const char *n
  
        /* At this point we know we have a real path component. */
        for(;;) {
 -              struct user_namespace *mnt_userns;
 +              struct mnt_idmap *idmap;
                const char *link;
                u64 hash_len;
                int type;
  
 -              mnt_userns = mnt_user_ns(nd->path.mnt);
 -              err = may_lookup(mnt_userns, nd);
 +              idmap = mnt_idmap(nd->path.mnt);
 +              err = may_lookup(idmap, nd);
                if (err)
                        return err;
  
  OK:
                        /* pathname or trailing symlink, done */
                        if (!depth) {
 -                              nd->dir_vfsuid = i_uid_into_vfsuid(mnt_userns, nd->inode);
 +                              nd->dir_vfsuid = i_uid_into_vfsuid(idmap, nd->inode);
                                nd->dir_mode = nd->inode->i_mode;
                                nd->flags &= ~LOOKUP_PARENT;
                                return 0;
@@@ -2623,7 -2622,7 +2623,7 @@@ int vfs_path_lookup(struct dentry *dent
  }
  EXPORT_SYMBOL(vfs_path_lookup);
  
 -static int lookup_one_common(struct user_namespace *mnt_userns,
 +static int lookup_one_common(struct mnt_idmap *idmap,
                             const char *name, struct dentry *base, int len,
                             struct qstr *this)
  {
                        return err;
        }
  
 -      return inode_permission(mnt_userns, base->d_inode, MAY_EXEC);
 +      return inode_permission(idmap, base->d_inode, MAY_EXEC);
  }
  
  /**
@@@ -2677,7 -2676,7 +2677,7 @@@ struct dentry *try_lookup_one_len(cons
  
        WARN_ON_ONCE(!inode_is_locked(base->d_inode));
  
 -      err = lookup_one_common(&init_user_ns, name, base, len, &this);
 +      err = lookup_one_common(&nop_mnt_idmap, name, base, len, &this);
        if (err)
                return ERR_PTR(err);
  
@@@ -2704,7 -2703,7 +2704,7 @@@ struct dentry *lookup_one_len(const cha
  
        WARN_ON_ONCE(!inode_is_locked(base->d_inode));
  
 -      err = lookup_one_common(&init_user_ns, name, base, len, &this);
 +      err = lookup_one_common(&nop_mnt_idmap, name, base, len, &this);
        if (err)
                return ERR_PTR(err);
  
@@@ -2715,7 -2714,7 +2715,7 @@@ EXPORT_SYMBOL(lookup_one_len)
  
  /**
   * lookup_one - filesystem helper to lookup single pathname component
 - * @mnt_userns:       user namespace of the mount the lookup is performed from
 + * @idmap:    idmap of the mount the lookup is performed from
   * @name:     pathname component to lookup
   * @base:     base directory to lookup from
   * @len:      maximum length @len should be interpreted to
   *
   * The caller must hold base->i_mutex.
   */
 -struct dentry *lookup_one(struct user_namespace *mnt_userns, const char *name,
 +struct dentry *lookup_one(struct mnt_idmap *idmap, const char *name,
                          struct dentry *base, int len)
  {
        struct dentry *dentry;
  
        WARN_ON_ONCE(!inode_is_locked(base->d_inode));
  
 -      err = lookup_one_common(mnt_userns, name, base, len, &this);
 +      err = lookup_one_common(idmap, name, base, len, &this);
        if (err)
                return ERR_PTR(err);
  
@@@ -2745,7 -2744,7 +2745,7 @@@ EXPORT_SYMBOL(lookup_one)
  
  /**
   * lookup_one_unlocked - filesystem helper to lookup single pathname component
 - * @mnt_userns:       idmapping of the mount the lookup is performed from
 + * @idmap:    idmap of the mount the lookup is performed from
   * @name:     pathname component to lookup
   * @base:     base directory to lookup from
   * @len:      maximum length @len should be interpreted to
   * Unlike lookup_one_len, it should be called without the parent
   * i_mutex held, and will take the i_mutex itself if necessary.
   */
 -struct dentry *lookup_one_unlocked(struct user_namespace *mnt_userns,
 +struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap,
                                   const char *name, struct dentry *base,
                                   int len)
  {
        int err;
        struct dentry *ret;
  
 -      err = lookup_one_common(mnt_userns, name, base, len, &this);
 +      err = lookup_one_common(idmap, name, base, len, &this);
        if (err)
                return ERR_PTR(err);
  
@@@ -2778,7 -2777,7 +2778,7 @@@ EXPORT_SYMBOL(lookup_one_unlocked)
  /**
   * lookup_one_positive_unlocked - filesystem helper to lookup single
   *                              pathname component
 - * @mnt_userns:       idmapping of the mount the lookup is performed from
 + * @idmap:    idmap of the mount the lookup is performed from
   * @name:     pathname component to lookup
   * @base:     base directory to lookup from
   * @len:      maximum length @len should be interpreted to
   *
   * The helper should be called without i_mutex held.
   */
 -struct dentry *lookup_one_positive_unlocked(struct user_namespace *mnt_userns,
 +struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap,
                                            const char *name,
                                            struct dentry *base, int len)
  {
 -      struct dentry *ret = lookup_one_unlocked(mnt_userns, name, base, len);
 +      struct dentry *ret = lookup_one_unlocked(idmap, name, base, len);
  
        if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
                dput(ret);
@@@ -2824,7 -2823,7 +2824,7 @@@ EXPORT_SYMBOL(lookup_one_positive_unloc
  struct dentry *lookup_one_len_unlocked(const char *name,
                                       struct dentry *base, int len)
  {
 -      return lookup_one_unlocked(&init_user_ns, name, base, len);
 +      return lookup_one_unlocked(&nop_mnt_idmap, name, base, len);
  }
  EXPORT_SYMBOL(lookup_one_len_unlocked);
  
  struct dentry *lookup_positive_unlocked(const char *name,
                                       struct dentry *base, int len)
  {
 -      return lookup_one_positive_unlocked(&init_user_ns, name, base, len);
 +      return lookup_one_positive_unlocked(&nop_mnt_idmap, name, base, len);
  }
  EXPORT_SYMBOL(lookup_positive_unlocked);
  
@@@ -2865,7 -2864,7 +2865,7 @@@ int path_pts(struct path *path
  
        path->dentry = child;
        dput(parent);
-       follow_down(path);
+       follow_down(path, 0);
        return 0;
  }
  #endif
@@@ -2881,16 -2880,16 +2881,16 @@@ int user_path_at_empty(int dfd, const c
  }
  EXPORT_SYMBOL(user_path_at_empty);
  
 -int __check_sticky(struct user_namespace *mnt_userns, struct inode *dir,
 +int __check_sticky(struct mnt_idmap *idmap, struct inode *dir,
                   struct inode *inode)
  {
        kuid_t fsuid = current_fsuid();
  
 -      if (vfsuid_eq_kuid(i_uid_into_vfsuid(mnt_userns, inode), fsuid))
 +      if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), fsuid))
                return 0;
 -      if (vfsuid_eq_kuid(i_uid_into_vfsuid(mnt_userns, dir), fsuid))
 +      if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, dir), fsuid))
                return 0;
 -      return !capable_wrt_inode_uidgid(mnt_userns, inode, CAP_FOWNER);
 +      return !capable_wrt_inode_uidgid(idmap, inode, CAP_FOWNER);
  }
  EXPORT_SYMBOL(__check_sticky);
  
   * 11. We don't allow removal of NFS sillyrenamed files; it's handled by
   *     nfs_async_unlink().
   */
 -static int may_delete(struct user_namespace *mnt_userns, struct inode *dir,
 +static int may_delete(struct mnt_idmap *idmap, struct inode *dir,
                      struct dentry *victim, bool isdir)
  {
        struct inode *inode = d_backing_inode(victim);
        BUG_ON(victim->d_parent->d_inode != dir);
  
        /* Inode writeback is not safe when the uid or gid are invalid. */
 -      if (!vfsuid_valid(i_uid_into_vfsuid(mnt_userns, inode)) ||
 -          !vfsgid_valid(i_gid_into_vfsgid(mnt_userns, inode)))
 +      if (!vfsuid_valid(i_uid_into_vfsuid(idmap, inode)) ||
 +          !vfsgid_valid(i_gid_into_vfsgid(idmap, inode)))
                return -EOVERFLOW;
  
        audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
  
 -      error = inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);
 +      error = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
        if (error)
                return error;
        if (IS_APPEND(dir))
                return -EPERM;
  
 -      if (check_sticky(mnt_userns, dir, inode) || IS_APPEND(inode) ||
 +      if (check_sticky(idmap, dir, inode) || IS_APPEND(inode) ||
            IS_IMMUTABLE(inode) || IS_SWAPFILE(inode) ||
 -          HAS_UNMAPPED_ID(mnt_userns, inode))
 +          HAS_UNMAPPED_ID(idmap, inode))
                return -EPERM;
        if (isdir) {
                if (!d_is_dir(victim))
   *  4. We should have write and exec permissions on dir
   *  5. We can't do it if dir is immutable (done in permission())
   */
 -static inline int may_create(struct user_namespace *mnt_userns,
 +static inline int may_create(struct mnt_idmap *idmap,
                             struct inode *dir, struct dentry *child)
  {
        audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
                return -EEXIST;
        if (IS_DEADDIR(dir))
                return -ENOENT;
 -      if (!fsuidgid_has_mapping(dir->i_sb, mnt_userns))
 +      if (!fsuidgid_has_mapping(dir->i_sb, idmap))
                return -EOVERFLOW;
  
 -      return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);
 +      return inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
  }
  
  /*
@@@ -3045,7 -3044,7 +3045,7 @@@ static inline umode_t mode_strip_umask(
  
  /**
   * vfs_prepare_mode - prepare the mode to be used for a new inode
 - * @mnt_userns:               user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @dir:      parent directory of the new inode
   * @mode:     mode of the new inode
   * @mask_perms:       allowed permission by the vfs
   *
   * Returns: mode to be passed to the filesystem
   */
 -static inline umode_t vfs_prepare_mode(struct user_namespace *mnt_userns,
 +static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap,
                                       const struct inode *dir, umode_t mode,
                                       umode_t mask_perms, umode_t type)
  {
 -      mode = mode_strip_sgid(mnt_userns, dir, mode);
 +      mode = mode_strip_sgid(idmap, dir, mode);
        mode = mode_strip_umask(dir, mode);
  
        /*
  
  /**
   * vfs_create - create new file
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @dir:      inode of @dentry
   * @dentry:   pointer to dentry of the base directory
   * @mode:     mode of the new file
   *
   * Create a new file.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -int vfs_create(struct user_namespace *mnt_userns, struct inode *dir,
 +int vfs_create(struct mnt_idmap *idmap, struct inode *dir,
               struct dentry *dentry, umode_t mode, bool want_excl)
  {
 -      int error = may_create(mnt_userns, dir, dentry);
 +      int error;
 +
 +      error = may_create(idmap, dir, dentry);
        if (error)
                return error;
  
        if (!dir->i_op->create)
                return -EACCES; /* shouldn't it be ENOSYS? */
  
 -      mode = vfs_prepare_mode(mnt_userns, dir, mode, S_IALLUGO, S_IFREG);
 +      mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
        error = security_inode_create(dir, dentry, mode);
        if (error)
                return error;
 -      error = dir->i_op->create(mnt_userns, dir, dentry, mode, want_excl);
 +      error = dir->i_op->create(idmap, dir, dentry, mode, want_excl);
        if (!error)
                fsnotify_create(dir, dentry);
        return error;
@@@ -3127,7 -3124,7 +3127,7 @@@ int vfs_mkobj(struct dentry *dentry, um
                void *arg)
  {
        struct inode *dir = dentry->d_parent->d_inode;
 -      int error = may_create(&init_user_ns, dir, dentry);
 +      int error = may_create(&nop_mnt_idmap, dir, dentry);
        if (error)
                return error;
  
@@@ -3149,7 -3146,7 +3149,7 @@@ bool may_open_dev(const struct path *pa
                !(path->mnt->mnt_sb->s_iflags & SB_I_NODEV);
  }
  
 -static int may_open(struct user_namespace *mnt_userns, const struct path *path,
 +static int may_open(struct mnt_idmap *idmap, const struct path *path,
                    int acc_mode, int flag)
  {
        struct dentry *dentry = path->dentry;
                break;
        }
  
 -      error = inode_permission(mnt_userns, inode, MAY_OPEN | acc_mode);
 +      error = inode_permission(idmap, inode, MAY_OPEN | acc_mode);
        if (error)
                return error;
  
        }
  
        /* O_NOATIME can only be set by the owner or superuser */
 -      if (flag & O_NOATIME && !inode_owner_or_capable(mnt_userns, inode))
 +      if (flag & O_NOATIME && !inode_owner_or_capable(idmap, inode))
                return -EPERM;
  
        return 0;
  }
  
 -static int handle_truncate(struct user_namespace *mnt_userns, struct file *filp)
 +static int handle_truncate(struct mnt_idmap *idmap, struct file *filp)
  {
        const struct path *path = &filp->f_path;
        struct inode *inode = path->dentry->d_inode;
  
        error = security_file_truncate(filp);
        if (!error) {
 -              error = do_truncate(mnt_userns, path->dentry, 0,
 +              error = do_truncate(idmap, path->dentry, 0,
                                    ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
                                    filp);
        }
@@@ -3231,7 -3228,7 +3231,7 @@@ static inline int open_to_namei_flags(i
        return flag;
  }
  
 -static int may_o_create(struct user_namespace *mnt_userns,
 +static int may_o_create(struct mnt_idmap *idmap,
                        const struct path *dir, struct dentry *dentry,
                        umode_t mode)
  {
        if (error)
                return error;
  
 -      if (!fsuidgid_has_mapping(dir->dentry->d_sb, mnt_userns))
 +      if (!fsuidgid_has_mapping(dir->dentry->d_sb, idmap))
                return -EOVERFLOW;
  
 -      error = inode_permission(mnt_userns, dir->dentry->d_inode,
 +      error = inode_permission(idmap, dir->dentry->d_inode,
                                 MAY_WRITE | MAY_EXEC);
        if (error)
                return error;
@@@ -3322,7 -3319,7 +3322,7 @@@ static struct dentry *lookup_open(struc
                                  const struct open_flags *op,
                                  bool got_write)
  {
 -      struct user_namespace *mnt_userns;
 +      struct mnt_idmap *idmap;
        struct dentry *dir = nd->path.dentry;
        struct inode *dir_inode = dir->d_inode;
        int open_flag = op->open_flag;
         */
        if (unlikely(!got_write))
                open_flag &= ~O_TRUNC;
 -      mnt_userns = mnt_user_ns(nd->path.mnt);
 +      idmap = mnt_idmap(nd->path.mnt);
        if (open_flag & O_CREAT) {
                if (open_flag & O_EXCL)
                        open_flag &= ~O_TRUNC;
 -              mode = vfs_prepare_mode(mnt_userns, dir->d_inode, mode, mode, mode);
 +              mode = vfs_prepare_mode(idmap, dir->d_inode, mode, mode, mode);
                if (likely(got_write))
 -                      create_error = may_o_create(mnt_userns, &nd->path,
 +                      create_error = may_o_create(idmap, &nd->path,
                                                    dentry, mode);
                else
                        create_error = -EROFS;
                        goto out_dput;
                }
  
 -              error = dir_inode->i_op->create(mnt_userns, dir_inode, dentry,
 +              error = dir_inode->i_op->create(idmap, dir_inode, dentry,
                                                mode, open_flag & O_EXCL);
                if (error)
                        goto out_dput;
@@@ -3516,7 -3513,7 +3516,7 @@@ finish_lookup
  static int do_open(struct nameidata *nd,
                   struct file *file, const struct open_flags *op)
  {
 -      struct user_namespace *mnt_userns;
 +      struct mnt_idmap *idmap;
        int open_flag = op->open_flag;
        bool do_truncate;
        int acc_mode;
        }
        if (!(file->f_mode & FMODE_CREATED))
                audit_inode(nd->name, nd->path.dentry, 0);
 -      mnt_userns = mnt_user_ns(nd->path.mnt);
 +      idmap = mnt_idmap(nd->path.mnt);
        if (open_flag & O_CREAT) {
                if ((open_flag & O_EXCL) && !(file->f_mode & FMODE_CREATED))
                        return -EEXIST;
                if (d_is_dir(nd->path.dentry))
                        return -EISDIR;
 -              error = may_create_in_sticky(mnt_userns, nd,
 +              error = may_create_in_sticky(idmap, nd,
                                             d_backing_inode(nd->path.dentry));
                if (unlikely(error))
                        return error;
                        return error;
                do_truncate = true;
        }
 -      error = may_open(mnt_userns, &nd->path, acc_mode, open_flag);
 +      error = may_open(idmap, &nd->path, acc_mode, open_flag);
        if (!error && !(file->f_mode & FMODE_OPENED))
                error = vfs_open(&nd->path, file);
        if (!error)
                error = ima_file_check(file, op->acc_mode);
        if (!error && do_truncate)
 -              error = handle_truncate(mnt_userns, file);
 +              error = handle_truncate(idmap, file);
        if (unlikely(error > 0)) {
                WARN_ON(1);
                error = -EINVAL;
  
  /**
   * vfs_tmpfile - create tmpfile
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @dentry:   pointer to dentry of the base directory
   * @mode:     mode of the new tmpfile
   * @open_flag:        flags
   *
   * Create a temporary file.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -static int vfs_tmpfile(struct user_namespace *mnt_userns,
 +static int vfs_tmpfile(struct mnt_idmap *idmap,
                       const struct path *parentpath,
                       struct file *file, umode_t mode)
  {
        int open_flag = file->f_flags;
  
        /* we want directory to be writable */
 -      error = inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);
 +      error = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
        if (error)
                return error;
        if (!dir->i_op->tmpfile)
                return -ENOMEM;
        file->f_path.mnt = parentpath->mnt;
        file->f_path.dentry = child;
 -      mode = vfs_prepare_mode(mnt_userns, dir, mode, mode, mode);
 -      error = dir->i_op->tmpfile(mnt_userns, dir, file, mode);
 +      mode = vfs_prepare_mode(idmap, dir, mode, mode, mode);
 +      error = dir->i_op->tmpfile(idmap, dir, file, mode);
        dput(child);
        if (error)
                return error;
        /* Don't check for other permissions, the inode was just created */
 -      error = may_open(mnt_userns, &file->f_path, 0, file->f_flags);
 +      error = may_open(idmap, &file->f_path, 0, file->f_flags);
        if (error)
                return error;
        inode = file_inode(file);
                inode->i_state |= I_LINKABLE;
                spin_unlock(&inode->i_lock);
        }
 -      ima_post_create_tmpfile(mnt_userns, inode);
 +      ima_post_create_tmpfile(idmap, inode);
        return 0;
  }
  
  /**
   * vfs_tmpfile_open - open a tmpfile for kernel internal use
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @parentpath:       path of the base directory
   * @mode:     mode of the new tmpfile
   * @open_flag:        flags
   * hence this is only for kernel internal use, and must not be installed into
   * file tables or such.
   */
 -struct file *vfs_tmpfile_open(struct user_namespace *mnt_userns,
 +struct file *vfs_tmpfile_open(struct mnt_idmap *idmap,
                          const struct path *parentpath,
                          umode_t mode, int open_flag, const struct cred *cred)
  {
  
        file = alloc_empty_file_noaccount(open_flag, cred);
        if (!IS_ERR(file)) {
 -              error = vfs_tmpfile(mnt_userns, parentpath, file, mode);
 +              error = vfs_tmpfile(idmap, parentpath, file, mode);
                if (error) {
                        fput(file);
                        file = ERR_PTR(error);
@@@ -3661,6 -3658,7 +3661,6 @@@ static int do_tmpfile(struct nameidata 
                const struct open_flags *op,
                struct file *file)
  {
 -      struct user_namespace *mnt_userns;
        struct path path;
        int error = path_lookupat(nd, flags | LOOKUP_DIRECTORY, &path);
  
        error = mnt_want_write(path.mnt);
        if (unlikely(error))
                goto out;
 -      mnt_userns = mnt_user_ns(path.mnt);
 -      error = vfs_tmpfile(mnt_userns, &path, file, op->mode);
 +      error = vfs_tmpfile(mnt_idmap(path.mnt), &path, file, op->mode);
        if (error)
                goto out2;
        audit_inode(nd->name, file->f_path.dentry, 0);
@@@ -3874,7 -3873,7 +3874,7 @@@ EXPORT_SYMBOL(user_path_create)
  
  /**
   * vfs_mknod - create device node or file
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @dir:      inode of @dentry
   * @dentry:   pointer to dentry of the base directory
   * @mode:     mode of the new device node or file
   *
   * Create a device node or file.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -int vfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
 +int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
              struct dentry *dentry, umode_t mode, dev_t dev)
  {
        bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
 -      int error = may_create(mnt_userns, dir, dentry);
 +      int error = may_create(idmap, dir, dentry);
  
        if (error)
                return error;
        if (!dir->i_op->mknod)
                return -EPERM;
  
 -      mode = vfs_prepare_mode(mnt_userns, dir, mode, mode, mode);
 +      mode = vfs_prepare_mode(idmap, dir, mode, mode, mode);
        error = devcgroup_inode_mknod(mode, dev);
        if (error)
                return error;
        if (error)
                return error;
  
 -      error = dir->i_op->mknod(mnt_userns, dir, dentry, mode, dev);
 +      error = dir->i_op->mknod(idmap, dir, dentry, mode, dev);
        if (!error)
                fsnotify_create(dir, dentry);
        return error;
@@@ -3940,7 -3939,7 +3940,7 @@@ static int may_mknod(umode_t mode
  static int do_mknodat(int dfd, struct filename *name, umode_t mode,
                unsigned int dev)
  {
 -      struct user_namespace *mnt_userns;
 +      struct mnt_idmap *idmap;
        struct dentry *dentry;
        struct path path;
        int error;
@@@ -3960,20 -3959,20 +3960,20 @@@ retry
        if (error)
                goto out2;
  
 -      mnt_userns = mnt_user_ns(path.mnt);
 +      idmap = mnt_idmap(path.mnt);
        switch (mode & S_IFMT) {
                case 0: case S_IFREG:
 -                      error = vfs_create(mnt_userns, path.dentry->d_inode,
 +                      error = vfs_create(idmap, path.dentry->d_inode,
                                           dentry, mode, true);
                        if (!error)
 -                              ima_post_path_mknod(mnt_userns, dentry);
 +                              ima_post_path_mknod(idmap, dentry);
                        break;
                case S_IFCHR: case S_IFBLK:
 -                      error = vfs_mknod(mnt_userns, path.dentry->d_inode,
 +                      error = vfs_mknod(idmap, path.dentry->d_inode,
                                          dentry, mode, new_decode_dev(dev));
                        break;
                case S_IFIFO: case S_IFSOCK:
 -                      error = vfs_mknod(mnt_userns, path.dentry->d_inode,
 +                      error = vfs_mknod(idmap, path.dentry->d_inode,
                                          dentry, mode, 0);
                        break;
        }
@@@ -4001,33 -4000,32 +4001,33 @@@ SYSCALL_DEFINE3(mknod, const char __use
  
  /**
   * vfs_mkdir - create directory
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @dir:      inode of @dentry
   * @dentry:   pointer to dentry of the base directory
   * @mode:     mode of the new directory
   *
   * Create a directory.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -int vfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
 +int vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
              struct dentry *dentry, umode_t mode)
  {
 -      int error = may_create(mnt_userns, dir, dentry);
 +      int error;
        unsigned max_links = dir->i_sb->s_max_links;
  
 +      error = may_create(idmap, dir, dentry);
        if (error)
                return error;
  
        if (!dir->i_op->mkdir)
                return -EPERM;
  
 -      mode = vfs_prepare_mode(mnt_userns, dir, mode, S_IRWXUGO | S_ISVTX, 0);
 +      mode = vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, 0);
        error = security_inode_mkdir(dir, dentry, mode);
        if (error)
                return error;
        if (max_links && dir->i_nlink >= max_links)
                return -EMLINK;
  
 -      error = dir->i_op->mkdir(mnt_userns, dir, dentry, mode);
 +      error = dir->i_op->mkdir(idmap, dir, dentry, mode);
        if (!error)
                fsnotify_mkdir(dir, dentry);
        return error;
@@@ -4058,8 -4056,10 +4058,8 @@@ retry
        error = security_path_mkdir(&path, dentry,
                        mode_strip_umask(path.dentry->d_inode, mode));
        if (!error) {
 -              struct user_namespace *mnt_userns;
 -              mnt_userns = mnt_user_ns(path.mnt);
 -              error = vfs_mkdir(mnt_userns, path.dentry->d_inode, dentry,
 -                                mode);
 +              error = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
 +                                dentry, mode);
        }
        done_path_create(&path, dentry);
        if (retry_estale(error, lookup_flags)) {
@@@ -4083,22 -4083,22 +4083,22 @@@ SYSCALL_DEFINE2(mkdir, const char __use
  
  /**
   * vfs_rmdir - remove directory
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @dir:      inode of @dentry
   * @dentry:   pointer to dentry of the base directory
   *
   * Remove a directory.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -int vfs_rmdir(struct user_namespace *mnt_userns, struct inode *dir,
 +int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
                     struct dentry *dentry)
  {
 -      int error = may_delete(mnt_userns, dir, dentry, 1);
 +      int error = may_delete(idmap, dir, dentry, 1);
  
        if (error)
                return error;
@@@ -4138,6 -4138,7 +4138,6 @@@ EXPORT_SYMBOL(vfs_rmdir)
  
  int do_rmdir(int dfd, struct filename *name)
  {
 -      struct user_namespace *mnt_userns;
        int error;
        struct dentry *dentry;
        struct path path;
@@@ -4177,7 -4178,8 +4177,7 @@@ retry
        error = security_path_rmdir(&path, dentry);
        if (error)
                goto exit4;
 -      mnt_userns = mnt_user_ns(path.mnt);
 -      error = vfs_rmdir(mnt_userns, path.dentry->d_inode, dentry);
 +      error = vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode, dentry);
  exit4:
        dput(dentry);
  exit3:
@@@ -4201,7 -4203,7 +4201,7 @@@ SYSCALL_DEFINE1(rmdir, const char __use
  
  /**
   * vfs_unlink - unlink a filesystem object
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @dir:      parent directory
   * @dentry:   victim
   * @delegated_inode: returns victim inode, if the inode is delegated.
   * be appropriate for callers that expect the underlying filesystem not
   * to be NFS exported.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -int vfs_unlink(struct user_namespace *mnt_userns, struct inode *dir,
 +int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
               struct dentry *dentry, struct inode **delegated_inode)
  {
        struct inode *target = dentry->d_inode;
 -      int error = may_delete(mnt_userns, dir, dentry, 0);
 +      int error = may_delete(idmap, dir, dentry, 0);
  
        if (error)
                return error;
@@@ -4302,6 -4304,7 +4302,6 @@@ retry_deleg
        dentry = __lookup_hash(&last, path.dentry, lookup_flags);
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
 -              struct user_namespace *mnt_userns;
  
                /* Why not before? Because we want correct error value */
                if (last.name[last.len])
                error = security_path_unlink(&path, dentry);
                if (error)
                        goto exit3;
 -              mnt_userns = mnt_user_ns(path.mnt);
 -              error = vfs_unlink(mnt_userns, path.dentry->d_inode, dentry,
 -                                 &delegated_inode);
 +              error = vfs_unlink(mnt_idmap(path.mnt), path.dentry->d_inode,
 +                                 dentry, &delegated_inode);
  exit3:
                dput(dentry);
        }
@@@ -4366,25 -4370,24 +4366,25 @@@ SYSCALL_DEFINE1(unlink, const char __us
  
  /**
   * vfs_symlink - create symlink
 - * @mnt_userns:       user namespace of the mount the inode was found from
 + * @idmap:    idmap of the mount the inode was found from
   * @dir:      inode of @dentry
   * @dentry:   pointer to dentry of the base directory
   * @oldname:  name of the file to link to
   *
   * Create a symlink.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -int vfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
 +int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
                struct dentry *dentry, const char *oldname)
  {
 -      int error = may_create(mnt_userns, dir, dentry);
 +      int error;
  
 +      error = may_create(idmap, dir, dentry);
        if (error)
                return error;
  
        if (error)
                return error;
  
 -      error = dir->i_op->symlink(mnt_userns, dir, dentry, oldname);
 +      error = dir->i_op->symlink(idmap, dir, dentry, oldname);
        if (!error)
                fsnotify_create(dir, dentry);
        return error;
@@@ -4420,9 -4423,13 +4420,9 @@@ retry
                goto out_putnames;
  
        error = security_path_symlink(&path, dentry, from->name);
 -      if (!error) {
 -              struct user_namespace *mnt_userns;
 -
 -              mnt_userns = mnt_user_ns(path.mnt);
 -              error = vfs_symlink(mnt_userns, path.dentry->d_inode, dentry,
 -                                  from->name);
 -      }
 +      if (!error)
 +              error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode,
 +                                  dentry, from->name);
        done_path_create(&path, dentry);
        if (retry_estale(error, lookup_flags)) {
                lookup_flags |= LOOKUP_REVAL;
@@@ -4448,7 -4455,7 +4448,7 @@@ SYSCALL_DEFINE2(symlink, const char __u
  /**
   * vfs_link - create a new link
   * @old_dentry:       object to be linked
 - * @mnt_userns:       the user namespace of the mount
 + * @idmap:    idmap of the mount
   * @dir:      new parent
   * @new_dentry:       where to create the new link
   * @delegated_inode: returns inode needing a delegation break
   * be appropriate for callers that expect the underlying filesystem not
   * to be NFS exported.
   *
 - * If the inode has been found through an idmapped mount the user namespace of
 - * the vfsmount must be passed through @mnt_userns. This function will then take
 - * care to map the inode according to @mnt_userns before checking permissions.
 + * If the inode has been found through an idmapped mount the idmap of
 + * the vfsmount must be passed through @idmap. This function will then take
 + * care to map the inode according to @idmap before checking permissions.
   * On non-idmapped mounts or if permission checking is to be performed on the
 - * raw inode simply passs init_user_ns.
 + * raw inode simply passs @nop_mnt_idmap.
   */
 -int vfs_link(struct dentry *old_dentry, struct user_namespace *mnt_userns,
 +int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
             struct inode *dir, struct dentry *new_dentry,
             struct inode **delegated_inode)
  {
        if (!inode)
                return -ENOENT;
  
 -      error = may_create(mnt_userns, dir, new_dentry);
 +      error = may_create(idmap, dir, new_dentry);
        if (error)
                return error;
  
         * be writen back improperly if their true value is unknown to
         * the vfs.
         */
 -      if (HAS_UNMAPPED_ID(mnt_userns, inode))
 +      if (HAS_UNMAPPED_ID(idmap, inode))
                return -EPERM;
        if (!dir->i_op->link)
                return -EPERM;
@@@ -4546,7 -4553,7 +4546,7 @@@ EXPORT_SYMBOL(vfs_link)
  int do_linkat(int olddfd, struct filename *old, int newdfd,
              struct filename *new, int flags)
  {
 -      struct user_namespace *mnt_userns;
 +      struct mnt_idmap *idmap;
        struct dentry *new_dentry;
        struct path old_path, new_path;
        struct inode *delegated_inode = NULL;
@@@ -4583,14 -4590,14 +4583,14 @@@ retry
        error = -EXDEV;
        if (old_path.mnt != new_path.mnt)
                goto out_dput;
 -      mnt_userns = mnt_user_ns(new_path.mnt);
 -      error = may_linkat(mnt_userns, &old_path);
 +      idmap = mnt_idmap(new_path.mnt);
 +      error = may_linkat(idmap, &old_path);
        if (unlikely(error))
                goto out_dput;
        error = security_path_link(old_path.dentry, &new_path, new_dentry);
        if (error)
                goto out_dput;
 -      error = vfs_link(old_path.dentry, mnt_userns, new_path.dentry->d_inode,
 +      error = vfs_link(old_path.dentry, idmap, new_path.dentry->d_inode,
                         new_dentry, &delegated_inode);
  out_dput:
        done_path_create(&new_path, new_dentry);
@@@ -4690,20 -4697,20 +4690,20 @@@ int vfs_rename(struct renamedata *rd
        if (source == target)
                return 0;
  
 -      error = may_delete(rd->old_mnt_userns, old_dir, old_dentry, is_dir);
 +      error = may_delete(rd->old_mnt_idmap, old_dir, old_dentry, is_dir);
        if (error)
                return error;
  
        if (!target) {
 -              error = may_create(rd->new_mnt_userns, new_dir, new_dentry);
 +              error = may_create(rd->new_mnt_idmap, new_dir, new_dentry);
        } else {
                new_is_dir = d_is_dir(new_dentry);
  
                if (!(flags & RENAME_EXCHANGE))
 -                      error = may_delete(rd->new_mnt_userns, new_dir,
 +                      error = may_delete(rd->new_mnt_idmap, new_dir,
                                           new_dentry, is_dir);
                else
 -                      error = may_delete(rd->new_mnt_userns, new_dir,
 +                      error = may_delete(rd->new_mnt_idmap, new_dir,
                                           new_dentry, new_is_dir);
        }
        if (error)
         */
        if (new_dir != old_dir) {
                if (is_dir) {
 -                      error = inode_permission(rd->old_mnt_userns, source,
 +                      error = inode_permission(rd->old_mnt_idmap, source,
                                                 MAY_WRITE);
                        if (error)
                                return error;
                }
                if ((flags & RENAME_EXCHANGE) && new_is_dir) {
 -                      error = inode_permission(rd->new_mnt_userns, target,
 +                      error = inode_permission(rd->new_mnt_idmap, target,
                                                 MAY_WRITE);
                        if (error)
                                return error;
                if (error)
                        goto out;
        }
 -      error = old_dir->i_op->rename(rd->new_mnt_userns, old_dir, old_dentry,
 +      error = old_dir->i_op->rename(rd->new_mnt_idmap, old_dir, old_dentry,
                                      new_dir, new_dentry, flags);
        if (error)
                goto out;
@@@ -4914,10 -4921,10 +4914,10 @@@ retry_deleg
  
        rd.old_dir         = old_path.dentry->d_inode;
        rd.old_dentry      = old_dentry;
 -      rd.old_mnt_userns  = mnt_user_ns(old_path.mnt);
 +      rd.old_mnt_idmap   = mnt_idmap(old_path.mnt);
        rd.new_dir         = new_path.dentry->d_inode;
        rd.new_dentry      = new_dentry;
 -      rd.new_mnt_userns  = mnt_user_ns(new_path.mnt);
 +      rd.new_mnt_idmap   = mnt_idmap(new_path.mnt);
        rd.delegated_inode = &delegated_inode;
        rd.flags           = flags;
        error = vfs_rename(&rd);
diff --combined fs/nfs/export.c
index 1a9d5aa51dfb72c0f683e632dad4e3397d21c7cc,0a5ee1754d50dd31cf07d5171599daedb0c877f9..d6a6d1ebb8fd0b1081a7509631de2a19591c0e85
@@@ -42,7 -42,7 +42,7 @@@ nfs_encode_fh(struct inode *inode, __u3
        dprintk("%s: max fh len %d inode %p parent %p",
                __func__, *max_len, inode, parent);
  
-       if (*max_len < len || IS_AUTOMOUNT(inode)) {
+       if (*max_len < len) {
                dprintk("%s: fh len %d too small, required %d\n",
                        __func__, *max_len, len);
                *max_len = len;
@@@ -145,10 -145,17 +145,10 @@@ out
        return parent;
  }
  
 -static u64 nfs_fetch_iversion(struct inode *inode)
 -{
 -      nfs_revalidate_inode(inode, NFS_INO_INVALID_CHANGE);
 -      return inode_peek_iversion_raw(inode);
 -}
 -
  const struct export_operations nfs_export_ops = {
        .encode_fh = nfs_encode_fh,
        .fh_to_dentry = nfs_fh_to_dentry,
        .get_parent = nfs_get_parent,
 -      .fetch_iversion = nfs_fetch_iversion,
        .flags = EXPORT_OP_NOWCC|EXPORT_OP_NOSUBTREECHK|
                EXPORT_OP_CLOSE_BEFORE_UNLINK|EXPORT_OP_REMOTE_FS|
                EXPORT_OP_NOATOMIC_ATTR,
diff --combined fs/nfsd/nfs2acl.c
index 995cb2c90b1a0d5628253516a2508d73b4d47684,dea55883e0996f4bf6570eab84a906227d967380..12b2b9bc07bfe3da346fc34bae0f92f4928f77c2
@@@ -113,11 -113,11 +113,11 @@@ static __be32 nfsacld_proc_setacl(struc
  
        inode_lock(inode);
  
 -      error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_ACCESS,
 +      error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS,
                              argp->acl_access);
        if (error)
                goto out_drop_lock;
 -      error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_DEFAULT,
 +      error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT,
                              argp->acl_default);
        if (error)
                goto out_drop_lock;
@@@ -377,10 -377,11 +377,11 @@@ static const struct svc_procedure nfsd_
        },
  };
  
- static unsigned int nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)];
+ static DEFINE_PER_CPU_ALIGNED(unsigned long,
+                             nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)]);
  const struct svc_version nfsd_acl_version2 = {
        .vs_vers        = 2,
-       .vs_nproc       = 5,
+       .vs_nproc       = ARRAY_SIZE(nfsd_acl_procedures2),
        .vs_proc        = nfsd_acl_procedures2,
        .vs_count       = nfsd_acl_count2,
        .vs_dispatch    = nfsd_dispatch,
diff --combined fs/nfsd/nfs3acl.c
index 887803735e2ae6cfb56895ad8fa0dd064b4169e3,bf1c52c1bdab54e02af561df2a6332a105a80574..73adca47d37398961347f602996399275e3c025e
@@@ -103,11 -103,11 +103,11 @@@ static __be32 nfsd3_proc_setacl(struct 
  
        inode_lock(inode);
  
 -      error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_ACCESS,
 +      error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS,
                              argp->acl_access);
        if (error)
                goto out_drop_lock;
 -      error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_DEFAULT,
 +      error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT,
                              argp->acl_default);
  
  out_drop_lock:
@@@ -266,10 -266,11 +266,11 @@@ static const struct svc_procedure nfsd_
        },
  };
  
- static unsigned int nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)];
+ static DEFINE_PER_CPU_ALIGNED(unsigned long,
+                             nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)]);
  const struct svc_version nfsd_acl_version3 = {
        .vs_vers        = 3,
-       .vs_nproc       = 3,
+       .vs_nproc       = ARRAY_SIZE(nfsd_acl_procedures3),
        .vs_proc        = nfsd_acl_procedures3,
        .vs_count       = nfsd_acl_count3,
        .vs_dispatch    = nfsd_dispatch,
diff --combined fs/nfsd/nfs3proc.c
index f41992ecd0d791b1bb03ad667fb4d70f5e4b2912,1c73921c02e086225dd081023c032eb2ce43ab7b..e6bb8eeb5bc2890bcf09852fa655b1a03486f82e
@@@ -320,7 -320,7 +320,7 @@@ nfsd3_create_file(struct svc_rqst *rqst
                iap->ia_mode &= ~current_umask();
  
        fh_fill_pre_attrs(fhp);
 -      host_err = vfs_create(&init_user_ns, inode, child, iap->ia_mode, true);
 +      host_err = vfs_create(&nop_mnt_idmap, inode, child, iap->ia_mode, true);
        if (host_err < 0) {
                status = nfserrno(host_err);
                goto out;
@@@ -1064,10 -1064,11 +1064,11 @@@ static const struct svc_procedure nfsd_
        },
  };
  
- static unsigned int nfsd_count3[ARRAY_SIZE(nfsd_procedures3)];
+ static DEFINE_PER_CPU_ALIGNED(unsigned long,
+                             nfsd_count3[ARRAY_SIZE(nfsd_procedures3)]);
  const struct svc_version nfsd_version3 = {
        .vs_vers        = 3,
-       .vs_nproc       = 22,
+       .vs_nproc       = ARRAY_SIZE(nfsd_procedures3),
        .vs_proc        = nfsd_procedures3,
        .vs_dispatch    = nfsd_dispatch,
        .vs_count       = nfsd_count3,
diff --combined fs/nfsd/nfs4state.c
index c1684da6c01f92a557e583f019b7e9ceb1038c76,a202be19f26f5cfa4b3bf4eec475f2b94363fa41..6e61fa3acaf119b489bbb74439d43412521492e6
@@@ -599,14 -599,6 +599,6 @@@ put_nfs4_file(struct nfs4_file *fi
        }
  }
  
- static struct nfsd_file *
- __nfs4_get_fd(struct nfs4_file *f, int oflag)
- {
-       if (f->fi_fds[oflag])
-               return nfsd_file_get(f->fi_fds[oflag]);
-       return NULL;
- }
  static struct nfsd_file *
  find_writeable_file_locked(struct nfs4_file *f)
  {
  
        lockdep_assert_held(&f->fi_lock);
  
-       ret = __nfs4_get_fd(f, O_WRONLY);
+       ret = nfsd_file_get(f->fi_fds[O_WRONLY]);
        if (!ret)
-               ret = __nfs4_get_fd(f, O_RDWR);
+               ret = nfsd_file_get(f->fi_fds[O_RDWR]);
        return ret;
  }
  
@@@ -639,9 -631,9 +631,9 @@@ find_readable_file_locked(struct nfs4_f
  
        lockdep_assert_held(&f->fi_lock);
  
-       ret = __nfs4_get_fd(f, O_RDONLY);
+       ret = nfsd_file_get(f->fi_fds[O_RDONLY]);
        if (!ret)
-               ret = __nfs4_get_fd(f, O_RDWR);
+               ret = nfsd_file_get(f->fi_fds[O_RDWR]);
        return ret;
  }
  
@@@ -665,11 -657,11 +657,11 @@@ find_any_file(struct nfs4_file *f
        if (!f)
                return NULL;
        spin_lock(&f->fi_lock);
-       ret = __nfs4_get_fd(f, O_RDWR);
+       ret = nfsd_file_get(f->fi_fds[O_RDWR]);
        if (!ret) {
-               ret = __nfs4_get_fd(f, O_WRONLY);
+               ret = nfsd_file_get(f->fi_fds[O_WRONLY]);
                if (!ret)
-                       ret = __nfs4_get_fd(f, O_RDONLY);
+                       ret = nfsd_file_get(f->fi_fds[O_RDONLY]);
        }
        spin_unlock(&f->fi_lock);
        return ret;
@@@ -688,15 -680,6 +680,6 @@@ static struct nfsd_file *find_any_file_
        return NULL;
  }
  
- static struct nfsd_file *find_deleg_file_locked(struct nfs4_file *f)
- {
-       lockdep_assert_held(&f->fi_lock);
-       if (f->fi_deleg_file)
-               return f->fi_deleg_file;
-       return NULL;
- }
  static atomic_long_t num_delegations;
  unsigned long max_delegations;
  
@@@ -992,7 -975,6 +975,6 @@@ static int nfs4_init_cp_state(struct nf
  
        stid->cs_stid.si_opaque.so_clid.cl_boot = (u32)nn->boot_time;
        stid->cs_stid.si_opaque.so_clid.cl_id = nn->s2s_cp_cl_id;
-       stid->cs_type = cs_type;
  
        idr_preload(GFP_KERNEL);
        spin_lock(&nn->s2s_cp_lock);
        idr_preload_end();
        if (new_id < 0)
                return 0;
+       stid->cs_type = cs_type;
        return 1;
  }
  
@@@ -1036,7 -1019,8 +1019,8 @@@ void nfs4_free_copy_state(struct nfsd4_
  {
        struct nfsd_net *nn;
  
-       WARN_ON_ONCE(copy->cp_stateid.cs_type != NFS4_COPY_STID);
+       if (copy->cp_stateid.cs_type != NFS4_COPY_STID)
+               return;
        nn = net_generic(copy->cp_clp->net, nfsd_net_id);
        spin_lock(&nn->s2s_cp_lock);
        idr_remove(&nn->s2s_cp_stateids,
@@@ -2705,7 -2689,7 +2689,7 @@@ static int nfs4_show_deleg(struct seq_f
        ds = delegstateid(st);
        nf = st->sc_file;
        spin_lock(&nf->fi_lock);
-       file = find_deleg_file_locked(nf);
+       file = nf->fi_deleg_file;
        if (!file)
                goto out;
  
@@@ -5298,16 -5282,17 +5282,17 @@@ nfs4_upgrade_open(struct svc_rqst *rqst
        /* test and set deny mode */
        spin_lock(&fp->fi_lock);
        status = nfs4_file_check_deny(fp, open->op_share_deny);
-       if (status == nfs_ok) {
-               if (status != nfserr_share_denied) {
-                       set_deny(open->op_share_deny, stp);
-                       fp->fi_share_deny |=
-                               (open->op_share_deny & NFS4_SHARE_DENY_BOTH);
-               } else {
-                       if (nfs4_resolve_deny_conflicts_locked(fp, false,
-                                       stp, open->op_share_deny, false))
-                               status = nfserr_jukebox;
-               }
+       switch (status) {
+       case nfs_ok:
+               set_deny(open->op_share_deny, stp);
+               fp->fi_share_deny |=
+                       (open->op_share_deny & NFS4_SHARE_DENY_BOTH);
+               break;
+       case nfserr_share_denied:
+               if (nfs4_resolve_deny_conflicts_locked(fp, false,
+                               stp, open->op_share_deny, false))
+                       status = nfserr_jukebox;
+               break;
        }
        spin_unlock(&fp->fi_lock);
  
@@@ -5356,7 -5341,7 +5341,7 @@@ static int nfsd4_check_conflicting_open
  {
        struct nfs4_ol_stateid *st;
        struct file *f = fp->fi_deleg_file->nf_file;
 -      struct inode *ino = locks_inode(f);
 +      struct inode *ino = file_inode(f);
        int writes;
  
        writes = atomic_read(&ino->i_writecount);
@@@ -5438,6 -5423,23 +5423,23 @@@ nfsd4_verify_deleg_dentry(struct nfsd4_
        return 0;
  }
  
+ /*
+  * We avoid breaking delegations held by a client due to its own activity, but
+  * clearing setuid/setgid bits on a write is an implicit activity and the client
+  * may not notice and continue using the old mode. Avoid giving out a delegation
+  * on setuid/setgid files when the client is requesting an open for write.
+  */
+ static int
+ nfsd4_verify_setuid_write(struct nfsd4_open *open, struct nfsd_file *nf)
+ {
+       struct inode *inode = file_inode(nf->nf_file);
+       if ((open->op_share_access & NFS4_SHARE_ACCESS_WRITE) &&
+           (inode->i_mode & (S_ISUID|S_ISGID)))
+               return -EAGAIN;
+       return 0;
+ }
  static struct nfs4_delegation *
  nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
                    struct svc_fh *parent)
        spin_lock(&fp->fi_lock);
        if (nfs4_delegation_exists(clp, fp))
                status = -EAGAIN;
+       else if (nfsd4_verify_setuid_write(open, nf))
+               status = -EAGAIN;
        else if (!fp->fi_deleg_file) {
                fp->fi_deleg_file = nf;
                /* increment early to prevent fi_deleg_file from being
        if (status)
                goto out_unlock;
  
+       /*
+        * Now that the deleg is set, check again to ensure that nothing
+        * raced in and changed the mode while we weren't lookng.
+        */
+       status = nfsd4_verify_setuid_write(open, fp->fi_deleg_file);
+       if (status)
+               goto out_unlock;
        spin_lock(&state_lock);
        spin_lock(&fp->fi_lock);
        if (fp->fi_had_conflict)
@@@ -6406,23 -6418,26 +6418,26 @@@ nfsd4_lookup_stateid(struct nfsd4_compo
  static struct nfsd_file *
  nfs4_find_file(struct nfs4_stid *s, int flags)
  {
+       struct nfsd_file *ret = NULL;
        if (!s)
                return NULL;
  
        switch (s->sc_type) {
        case NFS4_DELEG_STID:
-               if (WARN_ON_ONCE(!s->sc_file->fi_deleg_file))
-                       return NULL;
-               return nfsd_file_get(s->sc_file->fi_deleg_file);
+               spin_lock(&s->sc_file->fi_lock);
+               ret = nfsd_file_get(s->sc_file->fi_deleg_file);
+               spin_unlock(&s->sc_file->fi_lock);
+               break;
        case NFS4_OPEN_STID:
        case NFS4_LOCK_STID:
                if (flags & RD_STATE)
-                       return find_readable_file(s->sc_file);
+                       ret = find_readable_file(s->sc_file);
                else
-                       return find_writeable_file(s->sc_file);
+                       ret = find_writeable_file(s->sc_file);
        }
  
-       return NULL;
+       return ret;
  }
  
  static __be32
@@@ -6547,8 -6562,19 +6562,19 @@@ void nfs4_put_cpntf_state(struct nfsd_n
        spin_unlock(&nn->s2s_cp_lock);
  }
  
- /*
-  * Checks for stateid operations
+ /**
+  * nfs4_preprocess_stateid_op - find and prep stateid for an operation
+  * @rqstp: incoming request from client
+  * @cstate: current compound state
+  * @fhp: filehandle associated with requested stateid
+  * @stateid: stateid (provided by client)
+  * @flags: flags describing type of operation to be done
+  * @nfp: optional nfsd_file return pointer (may be NULL)
+  * @cstid: optional returned nfs4_stid pointer (may be NULL)
+  *
+  * Given info from the client, look up a nfs4_stid for the operation. On
+  * success, it returns a reference to the nfs4_stid and/or the nfsd_file
+  * associated with it.
   */
  __be32
  nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
@@@ -6737,8 -6763,18 +6763,18 @@@ static __be32 nfs4_seqid_op_checks(stru
        return status;
  }
  
- /* 
-  * Checks for sequence id mutating operations. 
+ /**
+  * nfs4_preprocess_seqid_op - find and prep an ol_stateid for a seqid-morphing op
+  * @cstate: compund state
+  * @seqid: seqid (provided by client)
+  * @stateid: stateid (provided by client)
+  * @typemask: mask of allowable types for this operation
+  * @stpp: return pointer for the stateid found
+  * @nn: net namespace for request
+  *
+  * Given a stateid+seqid from a client, look up an nfs4_ol_stateid and
+  * return it in @stpp. On a nfs_ok return, the returned stateid will
+  * have its st_mutex locked.
   */
  static __be32
  nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
@@@ -7809,7 -7845,7 +7845,7 @@@ check_for_locks(struct nfs4_file *fp, s
                return status;
        }
  
 -      inode = locks_inode(nf->nf_file);
 +      inode = file_inode(nf->nf_file);
        flctx = locks_inode_context(inode);
  
        if (flctx && !list_empty_careful(&flctx->flc_posix)) {
diff --combined fs/nfsd/nfsproc.c
index a82d91afdc9ca686f853a2048df1ab1c6b60ae98,9e6069e9c9f1d7a0c77712c69d2b0d99e9b07037..c37195572fd0dc963eb387899b280b6b737f1221
@@@ -93,7 -93,7 +93,7 @@@ nfsd_proc_setattr(struct svc_rqst *rqst
                if (delta < 0)
                        delta = -delta;
                if (delta < MAX_TOUCH_TIME_ERROR &&
 -                  setattr_prepare(&init_user_ns, fhp->fh_dentry, iap) != 0) {
 +                  setattr_prepare(&nop_mnt_idmap, fhp->fh_dentry, iap) != 0) {
                        /*
                         * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
                         * This will cause notify_change to set these times
@@@ -838,11 -838,11 +838,11 @@@ static const struct svc_procedure nfsd_
        },
  };
  
static unsigned int nfsd_count2[ARRAY_SIZE(nfsd_procedures2)];
+ static DEFINE_PER_CPU_ALIGNED(unsigned long,
                            nfsd_count2[ARRAY_SIZE(nfsd_procedures2)]);
  const struct svc_version nfsd_version2 = {
        .vs_vers        = 2,
-       .vs_nproc       = 18,
+       .vs_nproc       = ARRAY_SIZE(nfsd_procedures2),
        .vs_proc        = nfsd_procedures2,
        .vs_count       = nfsd_count2,
        .vs_dispatch    = nfsd_dispatch,
diff --combined fs/nfsd/vfs.c
index ab4ee3509ce397b30875de2df2fba0370e6ab155,21d5209f6e041b7c1a53c776c7662c853bb54396..e7462b5e5f1e2bacd845896c9f7a5635ccf74be0
@@@ -126,9 -126,13 +126,13 @@@ nfsd_cross_mnt(struct svc_rqst *rqstp, 
        struct dentry *dentry = *dpp;
        struct path path = {.mnt = mntget(exp->ex_path.mnt),
                            .dentry = dget(dentry)};
+       unsigned int follow_flags = 0;
        int err = 0;
  
-       err = follow_down(&path);
+       if (exp->ex_flags & NFSEXP_CROSSMOUNT)
+               follow_flags = LOOKUP_AUTOMOUNT;
+       err = follow_down(&path, follow_flags);
        if (err < 0)
                goto out;
        if (path.mnt == exp->ex_path.mnt && path.dentry == dentry &&
@@@ -223,7 -227,7 +227,7 @@@ int nfsd_mountpoint(struct dentry *dent
                return 1;
        if (nfsd4_is_junction(dentry))
                return 1;
-       if (d_mountpoint(dentry))
+       if (d_managed(dentry))
                /*
                 * Might only be a mountpoint in a different namespace,
                 * but we need to check.
@@@ -426,7 -430,7 +430,7 @@@ static int __nfsd_setattr(struct dentr
                if (iap->ia_size < 0)
                        return -EFBIG;
  
 -              host_err = notify_change(&init_user_ns, dentry, &size_attr, NULL);
 +              host_err = notify_change(&nop_mnt_idmap, dentry, &size_attr, NULL);
                if (host_err)
                        return host_err;
                iap->ia_valid &= ~ATTR_SIZE;
                return 0;
  
        iap->ia_valid |= ATTR_CTIME;
 -      return notify_change(&init_user_ns, dentry, iap, NULL);
 +      return notify_change(&nop_mnt_idmap, dentry, iap, NULL);
  }
  
  /**
@@@ -542,12 -546,12 +546,12 @@@ nfsd_setattr(struct svc_rqst *rqstp, st
                attr->na_labelerr = security_inode_setsecctx(dentry,
                        attr->na_seclabel->data, attr->na_seclabel->len);
        if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl)
 -              attr->na_aclerr = set_posix_acl(&init_user_ns,
 +              attr->na_aclerr = set_posix_acl(&nop_mnt_idmap,
                                                dentry, ACL_TYPE_ACCESS,
                                                attr->na_pacl);
        if (IS_ENABLED(CONFIG_FS_POSIX_ACL) &&
            !attr->na_aclerr && attr->na_dpacl && S_ISDIR(inode->i_mode))
 -              attr->na_aclerr = set_posix_acl(&init_user_ns,
 +              attr->na_aclerr = set_posix_acl(&nop_mnt_idmap,
                                                dentry, ACL_TYPE_DEFAULT,
                                                attr->na_dpacl);
        inode_unlock(inode);
@@@ -583,7 -587,7 +587,7 @@@ int nfsd4_is_junction(struct dentry *de
                return 0;
        if (!(inode->i_mode & S_ISVTX))
                return 0;
 -      if (vfs_getxattr(&init_user_ns, dentry, NFSD_JUNCTION_XATTR_NAME,
 +      if (vfs_getxattr(&nop_mnt_idmap, dentry, NFSD_JUNCTION_XATTR_NAME,
                         NULL, 0) <= 0)
                return 0;
        return 1;
@@@ -1363,13 -1367,12 +1367,13 @@@ nfsd_create_locked(struct svc_rqst *rqs
        err = 0;
        switch (type) {
        case S_IFREG:
 -              host_err = vfs_create(&init_user_ns, dirp, dchild, iap->ia_mode, true);
 +              host_err = vfs_create(&nop_mnt_idmap, dirp, dchild,
 +                                    iap->ia_mode, true);
                if (!host_err)
                        nfsd_check_ignore_resizing(iap);
                break;
        case S_IFDIR:
 -              host_err = vfs_mkdir(&init_user_ns, dirp, dchild, iap->ia_mode);
 +              host_err = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode);
                if (!host_err && unlikely(d_unhashed(dchild))) {
                        struct dentry *d;
                        d = lookup_one_len(dchild->d_name.name,
        case S_IFBLK:
        case S_IFIFO:
        case S_IFSOCK:
 -              host_err = vfs_mknod(&init_user_ns, dirp, dchild,
 +              host_err = vfs_mknod(&nop_mnt_idmap, dirp, dchild,
                                     iap->ia_mode, rdev);
                break;
        default:
@@@ -1558,7 -1561,7 +1562,7 @@@ nfsd_symlink(struct svc_rqst *rqstp, st
                goto out_drop_write;
        }
        fh_fill_pre_attrs(fhp);
 -      host_err = vfs_symlink(&init_user_ns, d_inode(dentry), dnew, path);
 +      host_err = vfs_symlink(&nop_mnt_idmap, d_inode(dentry), dnew, path);
        err = nfserrno(host_err);
        cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
        if (!err)
@@@ -1626,7 -1629,7 +1630,7 @@@ nfsd_link(struct svc_rqst *rqstp, struc
        if (d_really_is_negative(dold))
                goto out_dput;
        fh_fill_pre_attrs(ffhp);
 -      host_err = vfs_link(dold, &init_user_ns, dirp, dnew, NULL);
 +      host_err = vfs_link(dold, &nop_mnt_idmap, dirp, dnew, NULL);
        fh_fill_post_attrs(ffhp);
        inode_unlock(dirp);
        if (!host_err) {
@@@ -1746,10 -1749,10 +1750,10 @@@ retry
                goto out_dput_old;
        } else {
                struct renamedata rd = {
 -                      .old_mnt_userns = &init_user_ns,
 +                      .old_mnt_idmap  = &nop_mnt_idmap,
                        .old_dir        = fdir,
                        .old_dentry     = odentry,
 -                      .new_mnt_userns = &init_user_ns,
 +                      .new_mnt_idmap  = &nop_mnt_idmap,
                        .new_dir        = tdir,
                        .new_dentry     = ndentry,
                };
@@@ -1851,14 -1854,14 +1855,14 @@@ nfsd_unlink(struct svc_rqst *rqstp, str
                        nfsd_close_cached_files(rdentry);
  
                for (retries = 1;;) {
 -                      host_err = vfs_unlink(&init_user_ns, dirp, rdentry, NULL);
 +                      host_err = vfs_unlink(&nop_mnt_idmap, dirp, rdentry, NULL);
                        if (host_err != -EAGAIN || !retries--)
                                break;
                        if (!nfsd_wait_for_delegreturn(rqstp, rinode))
                                break;
                }
        } else {
 -              host_err = vfs_rmdir(&init_user_ns, dirp, rdentry);
 +              host_err = vfs_rmdir(&nop_mnt_idmap, dirp, rdentry);
        }
        fh_fill_post_attrs(fhp);
  
@@@ -2130,7 -2133,7 +2134,7 @@@ nfsd_getxattr(struct svc_rqst *rqstp, s
  
        inode_lock_shared(inode);
  
 -      len = vfs_getxattr(&init_user_ns, dentry, name, NULL, 0);
 +      len = vfs_getxattr(&nop_mnt_idmap, dentry, name, NULL, 0);
  
        /*
         * Zero-length attribute, just return.
                goto out;
        }
  
 -      len = vfs_getxattr(&init_user_ns, dentry, name, buf, len);
 +      len = vfs_getxattr(&nop_mnt_idmap, dentry, name, buf, len);
        if (len <= 0) {
                kvfree(buf);
                buf = NULL;
@@@ -2268,7 -2271,7 +2272,7 @@@ nfsd_removexattr(struct svc_rqst *rqstp
        inode_lock(fhp->fh_dentry->d_inode);
        fh_fill_pre_attrs(fhp);
  
 -      ret = __vfs_removexattr_locked(&init_user_ns, fhp->fh_dentry,
 +      ret = __vfs_removexattr_locked(&nop_mnt_idmap, fhp->fh_dentry,
                                       name, NULL);
  
        fh_fill_post_attrs(fhp);
@@@ -2295,7 -2298,7 +2299,7 @@@ nfsd_setxattr(struct svc_rqst *rqstp, s
        inode_lock(fhp->fh_dentry->d_inode);
        fh_fill_pre_attrs(fhp);
  
 -      ret = __vfs_setxattr_locked(&init_user_ns, fhp->fh_dentry, name, buf,
 +      ret = __vfs_setxattr_locked(&nop_mnt_idmap, fhp->fh_dentry, name, buf,
                                    len, flags, NULL);
        fh_fill_post_attrs(fhp);
        inode_unlock(fhp->fh_dentry->d_inode);
@@@ -2379,14 -2382,14 +2383,14 @@@ nfsd_permission(struct svc_rqst *rqstp
                return 0;
  
        /* This assumes  NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
 -      err = inode_permission(&init_user_ns, inode,
 +      err = inode_permission(&nop_mnt_idmap, inode,
                               acc & (MAY_READ | MAY_WRITE | MAY_EXEC));
  
        /* Allow read access to binaries even when mode 111 */
        if (err == -EACCES && S_ISREG(inode->i_mode) &&
             (acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE) ||
              acc == (NFSD_MAY_READ | NFSD_MAY_READ_IF_EXEC)))
 -              err = inode_permission(&init_user_ns, inode, MAY_EXEC);
 +              err = inode_permission(&nop_mnt_idmap, inode, MAY_EXEC);
  
        return err? nfserrno(err) : 0;
  }
index 2b7f067af3c48a06bfcdc0bf13975d7dd0c5dfb5,84de6b7c994885cddd13bfa57a47bbc787e50c71..0168ac9fdda80c29157ec710b9167f4ae6175143
@@@ -196,9 -196,9 +196,9 @@@ struct nlm_block 
   * Global variables
   */
  extern const struct rpc_program       nlm_program;
- extern const struct svc_procedure nlmsvc_procedures[];
+ extern const struct svc_procedure nlmsvc_procedures[24];
  #ifdef CONFIG_LOCKD_V4
- extern const struct svc_procedure nlmsvc_procedures4[];
+ extern const struct svc_procedure nlmsvc_procedures4[24];
  #endif
  extern int                    nlmsvc_grace_period;
  extern unsigned long          nlmsvc_timeout;
@@@ -312,7 -312,7 +312,7 @@@ static inline struct file *nlmsvc_file_
  
  static inline struct inode *nlmsvc_file_inode(struct nlm_file *file)
  {
 -      return locks_inode(nlmsvc_file_file(file));
 +      return file_inode(nlmsvc_file_file(file));
  }
  
  static inline int __nlm_privileged_request4(const struct sockaddr *sap)
@@@ -372,7 -372,7 +372,7 @@@ static inline int nlm_privileged_reques
  static inline int nlm_compare_locks(const struct file_lock *fl1,
                                    const struct file_lock *fl2)
  {
 -      return locks_inode(fl1->fl_file) == locks_inode(fl2->fl_file)
 +      return file_inode(fl1->fl_file) == file_inode(fl2->fl_file)
             && fl1->fl_pid   == fl2->fl_pid
             && fl1->fl_owner == fl2->fl_owner
             && fl1->fl_start == fl2->fl_start
diff --combined include/linux/namei.h
index 0d4531fd46e79a2027ef1530caf64d476d1db5ab,c82e2ac65846401251916c5209e25f56919897e2..0d797f3367cadc890bfc0cb606d5d7dde40ea0c4
@@@ -68,16 -68,16 +68,16 @@@ extern struct dentry *try_lookup_one_le
  extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
  extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
  extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int);
 -struct dentry *lookup_one(struct user_namespace *, const char *, struct dentry *, int);
 -struct dentry *lookup_one_unlocked(struct user_namespace *mnt_userns,
 +struct dentry *lookup_one(struct mnt_idmap *, const char *, struct dentry *, int);
 +struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap,
                                   const char *name, struct dentry *base,
                                   int len);
 -struct dentry *lookup_one_positive_unlocked(struct user_namespace *mnt_userns,
 +struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap,
                                            const char *name,
                                            struct dentry *base, int len);
  
  extern int follow_down_one(struct path *);
- extern int follow_down(struct path *);
+ extern int follow_down(struct path *path, unsigned int flags);
  extern int follow_up(struct path *);
  
  extern struct dentry *lock_rename(struct dentry *, struct dentry *);
diff --combined net/sunrpc/svcsock.c
index 8bcc8c3ffbfe59c3e43dddc0db374c0d370dfd6b,81bdcb6f73bb2ea501ce4ffd35db677875a9e15e..03a4f56150865e706f2419149567220ee555af6d
@@@ -55,7 -55,6 +55,7 @@@
  #include <linux/sunrpc/stats.h>
  #include <linux/sunrpc/xprt.h>
  
 +#include <trace/events/sock.h>
  #include <trace/events/sunrpc.h>
  
  #include "socklib.h"
@@@ -253,8 -252,11 +253,8 @@@ static ssize_t svc_tcp_read_msg(struct 
  
        clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
  
 -      for (i = 0, t = 0; t < buflen; i++, t += PAGE_SIZE) {
 -              bvec[i].bv_page = rqstp->rq_pages[i];
 -              bvec[i].bv_len = PAGE_SIZE;
 -              bvec[i].bv_offset = 0;
 -      }
 +      for (i = 0, t = 0; t < buflen; i++, t += PAGE_SIZE)
 +              bvec_set_page(&bvec[i], rqstp->rq_pages[i], PAGE_SIZE, 0);
        rqstp->rq_respages = &rqstp->rq_pages[i];
        rqstp->rq_next_page = rqstp->rq_respages + 1;
  
@@@ -308,8 -310,6 +308,8 @@@ static void svc_data_ready(struct sock 
  {
        struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data;
  
 +      trace_sk_data_ready(sk);
 +
        if (svsk) {
                /* Refer to svc_setup_socket() for details. */
                rmb();
@@@ -508,6 -508,7 +508,7 @@@ static int svc_udp_recvfrom(struct svc_
        if (serv->sv_stats)
                serv->sv_stats->netudpcnt++;
  
+       svc_sock_secure_port(rqstp);
        svc_xprt_received(rqstp->rq_xprt);
        return len;
  
@@@ -636,7 -637,6 +637,6 @@@ static const struct svc_xprt_ops svc_ud
        .xpo_free = svc_sock_free,
        .xpo_has_wspace = svc_udp_has_wspace,
        .xpo_accept = svc_udp_accept,
-       .xpo_secure_port = svc_sock_secure_port,
        .xpo_kill_temp_xprt = svc_udp_kill_temp_xprt,
  };
  
@@@ -687,8 -687,6 +687,8 @@@ static void svc_tcp_listen_data_ready(s
  {
        struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data;
  
 +      trace_sk_data_ready(sk);
 +
        if (svsk) {
                /* Refer to svc_setup_socket() for details. */
                rmb();
@@@ -1030,6 -1028,7 +1030,7 @@@ static int svc_tcp_recvfrom(struct svc_
        if (serv->sv_stats)
                serv->sv_stats->nettcpcnt++;
  
+       svc_sock_secure_port(rqstp);
        svc_xprt_received(rqstp->rq_xprt);
        return rqstp->rq_arg.len;
  
@@@ -1211,7 -1210,6 +1212,6 @@@ static const struct svc_xprt_ops svc_tc
        .xpo_free = svc_sock_free,
        .xpo_has_wspace = svc_tcp_has_wspace,
        .xpo_accept = svc_tcp_accept,
-       .xpo_secure_port = svc_sock_secure_port,
        .xpo_kill_temp_xprt = svc_tcp_kill_temp_xprt,
  };
  
diff --combined net/sunrpc/xdr.c
index afe7ec02d2322991cf8d85400d1879b9e52b44ba,6b2ec24ec62d842fe9d6f88b41d78be43eccbd4f..36835b2f5446595281f869905ad1f10f52b750ac
@@@ -150,8 -150,9 +150,8 @@@ xdr_alloc_bvec(struct xdr_buf *buf, gfp
                if (!buf->bvec)
                        return -ENOMEM;
                for (i = 0; i < n; i++) {
 -                      buf->bvec[i].bv_page = buf->pages[i];
 -                      buf->bvec[i].bv_len = PAGE_SIZE;
 -                      buf->bvec[i].bv_offset = 0;
 +                      bvec_set_page(&buf->bvec[i], buf->pages[i], PAGE_SIZE,
 +                                    0);
                }
        }
        return 0;
@@@ -862,13 -863,6 +862,6 @@@ static unsigned int xdr_shrink_pagelen(
        return shift;
  }
  
- void
- xdr_shift_buf(struct xdr_buf *buf, size_t len)
- {
-       xdr_shrink_bufhead(buf, buf->head->iov_len - len);
- }
- EXPORT_SYMBOL_GPL(xdr_shift_buf);
  /**
   * xdr_stream_pos - Return the current offset from the start of the xdr_stream
   * @xdr: pointer to struct xdr_stream
@@@ -1191,6 -1185,21 +1184,21 @@@ void xdr_truncate_encode(struct xdr_str
  }
  EXPORT_SYMBOL(xdr_truncate_encode);
  
+ /**
+  * xdr_truncate_decode - Truncate a decoding stream
+  * @xdr: pointer to struct xdr_stream
+  * @len: Number of bytes to remove
+  *
+  */
+ void xdr_truncate_decode(struct xdr_stream *xdr, size_t len)
+ {
+       unsigned int nbytes = xdr_align_size(len);
+       xdr->buf->len -= nbytes;
+       xdr->nwords -= XDR_QUADLEN(nbytes);
+ }
+ EXPORT_SYMBOL_GPL(xdr_truncate_decode);
  /**
   * xdr_restrict_buflen - decrease available buffer space
   * @xdr: pointer to xdr_stream
@@@ -2273,3 -2282,60 +2281,60 @@@ ssize_t xdr_stream_decode_string_dup(st
        return ret;
  }
  EXPORT_SYMBOL_GPL(xdr_stream_decode_string_dup);
+ /**
+  * xdr_stream_decode_opaque_auth - Decode struct opaque_auth (RFC5531 S8.2)
+  * @xdr: pointer to xdr_stream
+  * @flavor: location to store decoded flavor
+  * @body: location to store decode body
+  * @body_len: location to store length of decoded body
+  *
+  * Return values:
+  *   On success, returns the number of buffer bytes consumed
+  *   %-EBADMSG on XDR buffer overflow
+  *   %-EMSGSIZE if the decoded size of the body field exceeds 400 octets
+  */
+ ssize_t xdr_stream_decode_opaque_auth(struct xdr_stream *xdr, u32 *flavor,
+                                     void **body, unsigned int *body_len)
+ {
+       ssize_t ret, len;
+       len = xdr_stream_decode_u32(xdr, flavor);
+       if (unlikely(len < 0))
+               return len;
+       ret = xdr_stream_decode_opaque_inline(xdr, body, RPC_MAX_AUTH_SIZE);
+       if (unlikely(ret < 0))
+               return ret;
+       *body_len = ret;
+       return len + ret;
+ }
+ EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_auth);
+ /**
+  * xdr_stream_encode_opaque_auth - Encode struct opaque_auth (RFC5531 S8.2)
+  * @xdr: pointer to xdr_stream
+  * @flavor: verifier flavor to encode
+  * @body: content of body to encode
+  * @body_len: length of body to encode
+  *
+  * Return values:
+  *   On success, returns length in bytes of XDR buffer consumed
+  *   %-EBADMSG on XDR buffer overflow
+  *   %-EMSGSIZE if the size of @body exceeds 400 octets
+  */
+ ssize_t xdr_stream_encode_opaque_auth(struct xdr_stream *xdr, u32 flavor,
+                                     void *body, unsigned int body_len)
+ {
+       ssize_t ret, len;
+       if (unlikely(body_len > RPC_MAX_AUTH_SIZE))
+               return -EMSGSIZE;
+       len = xdr_stream_encode_u32(xdr, flavor);
+       if (unlikely(len < 0))
+               return len;
+       ret = xdr_stream_encode_opaque(xdr, body, body_len);
+       if (unlikely(ret < 0))
+               return ret;
+       return len + ret;
+ }
+ EXPORT_SYMBOL_GPL(xdr_stream_encode_opaque_auth);
This page took 0.226677 seconds and 4 git commands to generate.