diff mbox series

[3/6] nfsd: pass nfs_vers explicitly to __fh_verify()

Message ID 20240701025802.22985-4-neilb@suse.de (mailing list archive)
State New
Headers show
Series nfsd: provide simpler interface for LOCALIO access | expand

Commit Message

NeilBrown July 1, 2024, 2:53 a.m. UTC
Rather then depending on rqstp->rq_vers to determine nfs version, pass
it in explicitly.  This removes another dependency on rqstp and ensures
the correct version is checked.  The rqstp can be for an NLM request and
while some code tests that, other code does not.

Signed-off-by: NeilBrown <neilb@suse.de>
---
 fs/nfsd/nfsfh.c | 36 +++++++++++++++++++++---------------
 1 file changed, 21 insertions(+), 15 deletions(-)

Comments

Chuck Lever III July 1, 2024, 2:57 p.m. UTC | #1
On Mon, Jul 01, 2024 at 12:53:18PM +1000, NeilBrown wrote:
> Rather then depending on rqstp->rq_vers to determine nfs version, pass
> it in explicitly.  This removes another dependency on rqstp and ensures
> the correct version is checked.  The rqstp can be for an NLM request and
> while some code tests that, other code does not.

This is my only other major quibble with this series, which
otherwise looks like it will shape up to be a nice set of clean-ups.

I'd rather avoid having program- and version-specific logic in these
utilities. It makes it a little more difficult for us to shave out
support for older NFS versions using Kconfig options, for example.

Have you thought of any alternatives to passing an "RPC version"
argument? I will also ponder.


> Signed-off-by: NeilBrown <neilb@suse.de>
> ---
>  fs/nfsd/nfsfh.c | 36 +++++++++++++++++++++---------------
>  1 file changed, 21 insertions(+), 15 deletions(-)
> 
> diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
> index 760684fa4b50..adc731bb171e 100644
> --- a/fs/nfsd/nfsfh.c
> +++ b/fs/nfsd/nfsfh.c
> @@ -62,7 +62,7 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
>   * the write call).
>   */
>  static inline __be32
> -nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
> +nfsd_mode_check(int nfs_vers, struct dentry *dentry,
>  		umode_t requested)
>  {
>  	umode_t mode = d_inode(dentry)->i_mode & S_IFMT;
> @@ -80,7 +80,7 @@ nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
>  	 * v4 has an error more specific than err_notdir which we should
>  	 * return in preference to err_notdir:
>  	 */
> -	if (rqstp->rq_vers == 4 && mode == S_IFLNK)
> +	if (nfs_vers == 4 && mode == S_IFLNK)
>  		return nfserr_symlink;
>  	if (requested == S_IFDIR)
>  		return nfserr_notdir;
> @@ -117,8 +117,9 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
>  	return nfserrno(nfsd_setuser(cred, exp));
>  }
>  
> -static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
> -	struct dentry *dentry, struct svc_export *exp)
> +static inline __be32 check_pseudo_root(int nfs_vers,
> +				       struct dentry *dentry,
> +				       struct svc_export *exp)
>  {
>  	if (!(exp->ex_flags & NFSEXP_V4ROOT))
>  		return nfs_ok;
> @@ -128,7 +129,7 @@ static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
>  	 * in v4-specific code, in which case v2/v3 clients could bypass
>  	 * them.
>  	 */
> -	if (!nfsd_v4client(rqstp))
> +	if (nfs_vers != 4)
>  		return nfserr_stale;
>  	/*
>  	 * We're exposing only the directories and symlinks that have to be
> @@ -153,7 +154,7 @@ static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
>   * fh_dentry.
>   */
>  static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
> -				 struct svc_cred *cred,
> +				 struct svc_cred *cred, int nfs_vers,
>  				 struct svc_fh *fhp)
>  {
>  	struct knfsd_fh	*fh = &fhp->fh_handle;
> @@ -166,9 +167,9 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
>  	__be32 error;
>  
>  	error = nfserr_stale;
> -	if (rqstp->rq_vers > 2)
> +	if (nfs_vers > 2)
>  		error = nfserr_badhandle;
> -	if (rqstp->rq_vers == 4 && fh->fh_size == 0)
> +	if (nfs_vers == 4 && fh->fh_size == 0)
>  		return nfserr_nofilehandle;
>  
>  	if (fh->fh_version != 1)
> @@ -241,7 +242,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
>  	 * Look up the dentry using the NFS file handle.
>  	 */
>  	error = nfserr_stale;
> -	if (rqstp->rq_vers > 2)
> +	if (nfs_vers > 2)
>  		error = nfserr_badhandle;
>  
>  	fileid_type = fh->fh_fileid_type;
> @@ -281,7 +282,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
>  	fhp->fh_dentry = dentry;
>  	fhp->fh_export = exp;
>  
> -	switch (rqstp->rq_vers) {
> +	switch (nfs_vers) {
>  	case 4:
>  		if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR)
>  			fhp->fh_no_atomic_attr = true;
> @@ -330,6 +331,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
>  static __be32
>  __fh_verify(struct svc_rqst *rqstp,
>  	    struct nfsd_net *nn, struct svc_cred *cred,
> +	    int nfs_vers,
>  	    struct svc_fh *fhp, umode_t type, int access)
>  {
>  	struct svc_export *exp = NULL;
> @@ -337,7 +339,7 @@ __fh_verify(struct svc_rqst *rqstp,
>  	__be32		error;
>  
>  	if (!fhp->fh_dentry) {
> -		error = nfsd_set_fh_dentry(rqstp, nn, cred, fhp);
> +		error = nfsd_set_fh_dentry(rqstp, nn, cred, nfs_vers, fhp);
>  		if (error)
>  			goto out;
>  	}
> @@ -362,7 +364,7 @@ __fh_verify(struct svc_rqst *rqstp,
>  	 *	  (for example, if different id-squashing options are in
>  	 *	  effect on the new filesystem).
>  	 */
> -	error = check_pseudo_root(rqstp, dentry, exp);
> +	error = check_pseudo_root(nfs_vers, dentry, exp);
>  	if (error)
>  		goto out;
>  
> @@ -370,7 +372,7 @@ __fh_verify(struct svc_rqst *rqstp,
>  	if (error)
>  		goto out;
>  
> -	error = nfsd_mode_check(rqstp, dentry, type);
> +	error = nfsd_mode_check(nfs_vers, dentry, type);
>  	if (error)
>  		goto out;
>  
> @@ -407,12 +409,16 @@ __fh_verify(struct svc_rqst *rqstp,
>  __be32
>  fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
>  {
> +	int nfs_vers;
> +	if (rqstp->rq_prog == NFS_PROGRAM)
> +		nfs_vers = rqstp->rq_vers;
> +	else /* must be NLM */
> +		nfs_vers = rqstp->rq_vers == 4 ? 3 : 2;
>  	return __fh_verify(rqstp, net_generic(SVC_NET(rqstp), nfsd_net_id),
> -			   &rqstp->rq_cred,
> +			   &rqstp->rq_cred, nfs_vers,
>  			   fhp, type, access);
>  }
>  
> -
>  /*
>   * Compose a file handle for an NFS reply.
>   *
> -- 
> 2.44.0
>
kernel test robot July 1, 2024, 7:16 p.m. UTC | #2
Hi NeilBrown,

kernel test robot noticed the following build warnings:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.10-rc6 next-20240701]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/NeilBrown/nfsd-introduce-__fh_verify-which-takes-explicit-nfsd_net-arg/20240701-122856
base:   linus/master
patch link:    https://lore.kernel.org/r/20240701025802.22985-4-neilb%40suse.de
patch subject: [PATCH 3/6] nfsd: pass nfs_vers explicitly to __fh_verify()
config: parisc-defconfig
compiler: hppa-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build):

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202407020259.Zq0qWwiK-lkp@intel.com/

All warnings (new ones prefixed by >>):

   fs/nfsd/nfsfh.c:336: warning: Function parameter or struct member 'nn' not described in '__fh_verify'
   fs/nfsd/nfsfh.c:336: warning: Function parameter or struct member 'cred' not described in '__fh_verify'
>> fs/nfsd/nfsfh.c:336: warning: Function parameter or struct member 'nfs_vers' not described in '__fh_verify'
   fs/nfsd/nfsfh.c:336: warning: expecting prototype for fh_verify(). Prototype was for __fh_verify() instead


vim +336 fs/nfsd/nfsfh.c

03550fac06c4f0 J. Bruce Fields 2008-03-14  303  
b3d47676d474ec J. Bruce Fields 2008-10-20  304  /**
b3d47676d474ec J. Bruce Fields 2008-10-20  305   * fh_verify - filehandle lookup and access checking
b3d47676d474ec J. Bruce Fields 2008-10-20  306   * @rqstp: pointer to current rpc request
b3d47676d474ec J. Bruce Fields 2008-10-20  307   * @fhp: filehandle to be verified
b3d47676d474ec J. Bruce Fields 2008-10-20  308   * @type: expected type of object pointed to by filehandle
b3d47676d474ec J. Bruce Fields 2008-10-20  309   * @access: type of access needed to object
b3d47676d474ec J. Bruce Fields 2008-10-20  310   *
b3d47676d474ec J. Bruce Fields 2008-10-20  311   * Look up a dentry from the on-the-wire filehandle, check the client's
b3d47676d474ec J. Bruce Fields 2008-10-20  312   * access to the export, and set the current task's credentials.
b3d47676d474ec J. Bruce Fields 2008-10-20  313   *
b3d47676d474ec J. Bruce Fields 2008-10-20  314   * Regardless of success or failure of fh_verify(), fh_put() should be
b3d47676d474ec J. Bruce Fields 2008-10-20  315   * called on @fhp when the caller is finished with the filehandle.
b3d47676d474ec J. Bruce Fields 2008-10-20  316   *
b3d47676d474ec J. Bruce Fields 2008-10-20  317   * fh_verify() may be called multiple times on a given filehandle, for
b3d47676d474ec J. Bruce Fields 2008-10-20  318   * example, when processing an NFSv4 compound.  The first call will look
b3d47676d474ec J. Bruce Fields 2008-10-20  319   * up a dentry using the on-the-wire filehandle.  Subsequent calls will
b3d47676d474ec J. Bruce Fields 2008-10-20  320   * skip the lookup and just perform the other checks and possibly change
b3d47676d474ec J. Bruce Fields 2008-10-20  321   * the current task's credentials.
03550fac06c4f0 J. Bruce Fields 2008-03-14  322   *
b3d47676d474ec J. Bruce Fields 2008-10-20  323   * @type specifies the type of object expected using one of the S_IF*
b3d47676d474ec J. Bruce Fields 2008-10-20  324   * constants defined in include/linux/stat.h.  The caller may use zero
b3d47676d474ec J. Bruce Fields 2008-10-20  325   * to indicate that it doesn't care, or a negative integer to indicate
b3d47676d474ec J. Bruce Fields 2008-10-20  326   * that it expects something not of the given type.
03550fac06c4f0 J. Bruce Fields 2008-03-14  327   *
b3d47676d474ec J. Bruce Fields 2008-10-20  328   * @access is formed from the NFSD_MAY_* constants defined in
93f580a9a2413d Oleg Drokin     2016-07-07  329   * fs/nfsd/vfs.h.
03550fac06c4f0 J. Bruce Fields 2008-03-14  330   */
63e675e35f79ed NeilBrown       2024-07-01  331  static __be32
72924991777f5b NeilBrown       2024-07-01  332  __fh_verify(struct svc_rqst *rqstp,
72924991777f5b NeilBrown       2024-07-01  333  	    struct nfsd_net *nn, struct svc_cred *cred,
6d6d7e8ca01d36 NeilBrown       2024-07-01  334  	    int nfs_vers,
63e675e35f79ed NeilBrown       2024-07-01  335  	    struct svc_fh *fhp, umode_t type, int access)
03550fac06c4f0 J. Bruce Fields 2008-03-14 @336  {
20ad856e47323e Amir Goldstein  2021-01-06  337  	struct svc_export *exp = NULL;
03550fac06c4f0 J. Bruce Fields 2008-03-14  338  	struct dentry	*dentry;
03550fac06c4f0 J. Bruce Fields 2008-03-14  339  	__be32		error;
03550fac06c4f0 J. Bruce Fields 2008-03-14  340  
03550fac06c4f0 J. Bruce Fields 2008-03-14  341  	if (!fhp->fh_dentry) {
6d6d7e8ca01d36 NeilBrown       2024-07-01  342  		error = nfsd_set_fh_dentry(rqstp, nn, cred, nfs_vers, fhp);
03550fac06c4f0 J. Bruce Fields 2008-03-14  343  		if (error)
03550fac06c4f0 J. Bruce Fields 2008-03-14  344  			goto out;
864f0f61f829ba J. Bruce Fields 2009-11-25  345  	}
^1da177e4c3f41 Linus Torvalds  2005-04-16  346  	dentry = fhp->fh_dentry;
^1da177e4c3f41 Linus Torvalds  2005-04-16  347  	exp = fhp->fh_export;
051382885552e1 Chuck Lever     2022-06-21  348  
051382885552e1 Chuck Lever     2022-06-21  349  	trace_nfsd_fh_verify(rqstp, fhp, type, access);
051382885552e1 Chuck Lever     2022-06-21  350  
6fa02839bf9412 J. Bruce Fields 2007-11-12  351  	/*
864f0f61f829ba J. Bruce Fields 2009-11-25  352  	 * We still have to do all these permission checks, even when
864f0f61f829ba J. Bruce Fields 2009-11-25  353  	 * fh_dentry is already set:
864f0f61f829ba J. Bruce Fields 2009-11-25  354  	 * 	- fh_verify may be called multiple times with different
864f0f61f829ba J. Bruce Fields 2009-11-25  355  	 * 	  "access" arguments (e.g. nfsd_proc_create calls
864f0f61f829ba J. Bruce Fields 2009-11-25  356  	 * 	  fh_verify(...,NFSD_MAY_EXEC) first, then later (in
864f0f61f829ba J. Bruce Fields 2009-11-25  357  	 * 	  nfsd_create) calls fh_verify(...,NFSD_MAY_CREATE).
864f0f61f829ba J. Bruce Fields 2009-11-25  358  	 *	- in the NFSv4 case, the filehandle may have been filled
864f0f61f829ba J. Bruce Fields 2009-11-25  359  	 *	  in by fh_compose, and given a dentry, but further
864f0f61f829ba J. Bruce Fields 2009-11-25  360  	 *	  compound operations performed with that filehandle
864f0f61f829ba J. Bruce Fields 2009-11-25  361  	 *	  still need permissions checks.  In the worst case, a
864f0f61f829ba J. Bruce Fields 2009-11-25  362  	 *	  mountpoint crossing may have changed the export
864f0f61f829ba J. Bruce Fields 2009-11-25  363  	 *	  options, and we may now need to use a different uid
864f0f61f829ba J. Bruce Fields 2009-11-25  364  	 *	  (for example, if different id-squashing options are in
864f0f61f829ba J. Bruce Fields 2009-11-25  365  	 *	  effect on the new filesystem).
6fa02839bf9412 J. Bruce Fields 2007-11-12  366  	 */
6d6d7e8ca01d36 NeilBrown       2024-07-01  367  	error = check_pseudo_root(nfs_vers, dentry, exp);
03a816b46d7eba Steve Dickson   2009-09-09  368  	if (error)
03a816b46d7eba Steve Dickson   2009-09-09  369  		goto out;
03a816b46d7eba Steve Dickson   2009-09-09  370  
72924991777f5b NeilBrown       2024-07-01  371  	error = nfsd_setuser_and_check_port(rqstp, cred, exp);
7fc90ec93a5eb7 J. Bruce Fields 2006-06-30  372  	if (error)
7fc90ec93a5eb7 J. Bruce Fields 2006-06-30  373  		goto out;
7fc90ec93a5eb7 J. Bruce Fields 2006-06-30  374  
6d6d7e8ca01d36 NeilBrown       2024-07-01  375  	error = nfsd_mode_check(nfs_vers, dentry, type);
^1da177e4c3f41 Linus Torvalds  2005-04-16  376  	if (error)
^1da177e4c3f41 Linus Torvalds  2005-04-16  377  		goto out;
^1da177e4c3f41 Linus Torvalds  2005-04-16  378  
9091224f3cff47 J. Bruce Fields 2007-07-17  379  	/*
9091224f3cff47 J. Bruce Fields 2007-07-17  380  	 * pseudoflavor restrictions are not enforced on NLM,
9091224f3cff47 J. Bruce Fields 2007-07-17  381  	 * which clients virtually always use auth_sys for,
9091224f3cff47 J. Bruce Fields 2007-07-17  382  	 * even while using RPCSEC_GSS for NFS.
9091224f3cff47 J. Bruce Fields 2007-07-17  383  	 */
204f4ce75434c3 J. Bruce Fields 2011-04-08  384  	if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
04716e6621ff4a J. Bruce Fields 2008-08-07  385  		goto skip_pseudoflavor_check;
04716e6621ff4a J. Bruce Fields 2008-08-07  386  	/*
04716e6621ff4a J. Bruce Fields 2008-08-07  387  	 * Clients may expect to be able to use auth_sys during mount,
04716e6621ff4a J. Bruce Fields 2008-08-07  388  	 * even if they use gss for everything else; see section 2.3.2
04716e6621ff4a J. Bruce Fields 2008-08-07  389  	 * of rfc 2623.
04716e6621ff4a J. Bruce Fields 2008-08-07  390  	 */
04716e6621ff4a J. Bruce Fields 2008-08-07  391  	if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
04716e6621ff4a J. Bruce Fields 2008-08-07  392  			&& exp->ex_path.dentry == dentry)
04716e6621ff4a J. Bruce Fields 2008-08-07  393  		goto skip_pseudoflavor_check;
04716e6621ff4a J. Bruce Fields 2008-08-07  394  
32c1eb0cd7ee00 Andy Adamson    2007-07-17  395  	error = check_nfsd_access(exp, rqstp);
32c1eb0cd7ee00 Andy Adamson    2007-07-17  396  	if (error)
32c1eb0cd7ee00 Andy Adamson    2007-07-17  397  		goto out;
32c1eb0cd7ee00 Andy Adamson    2007-07-17  398  
04716e6621ff4a J. Bruce Fields 2008-08-07  399  skip_pseudoflavor_check:
^1da177e4c3f41 Linus Torvalds  2005-04-16  400  	/* Finally, check access permissions. */
72924991777f5b NeilBrown       2024-07-01  401  	error = nfsd_permission(cred, exp, dentry, access);
^1da177e4c3f41 Linus Torvalds  2005-04-16  402  out:
93c128e709aec2 Jeff Layton     2022-10-12  403  	trace_nfsd_fh_verify_err(rqstp, fhp, type, access, error);
^1da177e4c3f41 Linus Torvalds  2005-04-16  404  	if (error == nfserr_stale)
4b14885411f74b Josef Bacik     2024-01-26  405  		nfsd_stats_fh_stale_inc(nn, exp);
^1da177e4c3f41 Linus Torvalds  2005-04-16  406  	return error;
^1da177e4c3f41 Linus Torvalds  2005-04-16  407  }
^1da177e4c3f41 Linus Torvalds  2005-04-16  408
diff mbox series

Patch

diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 760684fa4b50..adc731bb171e 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -62,7 +62,7 @@  static int nfsd_acceptable(void *expv, struct dentry *dentry)
  * the write call).
  */
 static inline __be32
-nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
+nfsd_mode_check(int nfs_vers, struct dentry *dentry,
 		umode_t requested)
 {
 	umode_t mode = d_inode(dentry)->i_mode & S_IFMT;
@@ -80,7 +80,7 @@  nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
 	 * v4 has an error more specific than err_notdir which we should
 	 * return in preference to err_notdir:
 	 */
-	if (rqstp->rq_vers == 4 && mode == S_IFLNK)
+	if (nfs_vers == 4 && mode == S_IFLNK)
 		return nfserr_symlink;
 	if (requested == S_IFDIR)
 		return nfserr_notdir;
@@ -117,8 +117,9 @@  static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
 	return nfserrno(nfsd_setuser(cred, exp));
 }
 
-static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
-	struct dentry *dentry, struct svc_export *exp)
+static inline __be32 check_pseudo_root(int nfs_vers,
+				       struct dentry *dentry,
+				       struct svc_export *exp)
 {
 	if (!(exp->ex_flags & NFSEXP_V4ROOT))
 		return nfs_ok;
@@ -128,7 +129,7 @@  static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
 	 * in v4-specific code, in which case v2/v3 clients could bypass
 	 * them.
 	 */
-	if (!nfsd_v4client(rqstp))
+	if (nfs_vers != 4)
 		return nfserr_stale;
 	/*
 	 * We're exposing only the directories and symlinks that have to be
@@ -153,7 +154,7 @@  static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
  * fh_dentry.
  */
 static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
-				 struct svc_cred *cred,
+				 struct svc_cred *cred, int nfs_vers,
 				 struct svc_fh *fhp)
 {
 	struct knfsd_fh	*fh = &fhp->fh_handle;
@@ -166,9 +167,9 @@  static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
 	__be32 error;
 
 	error = nfserr_stale;
-	if (rqstp->rq_vers > 2)
+	if (nfs_vers > 2)
 		error = nfserr_badhandle;
-	if (rqstp->rq_vers == 4 && fh->fh_size == 0)
+	if (nfs_vers == 4 && fh->fh_size == 0)
 		return nfserr_nofilehandle;
 
 	if (fh->fh_version != 1)
@@ -241,7 +242,7 @@  static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
 	 * Look up the dentry using the NFS file handle.
 	 */
 	error = nfserr_stale;
-	if (rqstp->rq_vers > 2)
+	if (nfs_vers > 2)
 		error = nfserr_badhandle;
 
 	fileid_type = fh->fh_fileid_type;
@@ -281,7 +282,7 @@  static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
 	fhp->fh_dentry = dentry;
 	fhp->fh_export = exp;
 
-	switch (rqstp->rq_vers) {
+	switch (nfs_vers) {
 	case 4:
 		if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR)
 			fhp->fh_no_atomic_attr = true;
@@ -330,6 +331,7 @@  static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct nfsd_net *nn,
 static __be32
 __fh_verify(struct svc_rqst *rqstp,
 	    struct nfsd_net *nn, struct svc_cred *cred,
+	    int nfs_vers,
 	    struct svc_fh *fhp, umode_t type, int access)
 {
 	struct svc_export *exp = NULL;
@@ -337,7 +339,7 @@  __fh_verify(struct svc_rqst *rqstp,
 	__be32		error;
 
 	if (!fhp->fh_dentry) {
-		error = nfsd_set_fh_dentry(rqstp, nn, cred, fhp);
+		error = nfsd_set_fh_dentry(rqstp, nn, cred, nfs_vers, fhp);
 		if (error)
 			goto out;
 	}
@@ -362,7 +364,7 @@  __fh_verify(struct svc_rqst *rqstp,
 	 *	  (for example, if different id-squashing options are in
 	 *	  effect on the new filesystem).
 	 */
-	error = check_pseudo_root(rqstp, dentry, exp);
+	error = check_pseudo_root(nfs_vers, dentry, exp);
 	if (error)
 		goto out;
 
@@ -370,7 +372,7 @@  __fh_verify(struct svc_rqst *rqstp,
 	if (error)
 		goto out;
 
-	error = nfsd_mode_check(rqstp, dentry, type);
+	error = nfsd_mode_check(nfs_vers, dentry, type);
 	if (error)
 		goto out;
 
@@ -407,12 +409,16 @@  __fh_verify(struct svc_rqst *rqstp,
 __be32
 fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
 {
+	int nfs_vers;
+	if (rqstp->rq_prog == NFS_PROGRAM)
+		nfs_vers = rqstp->rq_vers;
+	else /* must be NLM */
+		nfs_vers = rqstp->rq_vers == 4 ? 3 : 2;
 	return __fh_verify(rqstp, net_generic(SVC_NET(rqstp), nfsd_net_id),
-			   &rqstp->rq_cred,
+			   &rqstp->rq_cred, nfs_vers,
 			   fhp, type, access);
 }
 
-
 /*
  * Compose a file handle for an NFS reply.
  *