xref: /netbsd-src/sys/nfs/nfs_srvsubs.c (revision 9b69ffe4d3917d83154c707e9a2256b14cf42f76)
1*9b69ffe4Sriastradh /*	$NetBSD: nfs_srvsubs.c,v 1.17 2023/03/23 19:52:42 riastradh Exp $	*/
292ce8c6aSad 
392ce8c6aSad /*
492ce8c6aSad  * Copyright (c) 1989, 1993
592ce8c6aSad  *	The Regents of the University of California.  All rights reserved.
692ce8c6aSad  *
792ce8c6aSad  * This code is derived from software contributed to Berkeley by
892ce8c6aSad  * Rick Macklem at The University of Guelph.
992ce8c6aSad  *
1092ce8c6aSad  * Redistribution and use in source and binary forms, with or without
1192ce8c6aSad  * modification, are permitted provided that the following conditions
1292ce8c6aSad  * are met:
1392ce8c6aSad  * 1. Redistributions of source code must retain the above copyright
1492ce8c6aSad  *    notice, this list of conditions and the following disclaimer.
1592ce8c6aSad  * 2. Redistributions in binary form must reproduce the above copyright
1692ce8c6aSad  *    notice, this list of conditions and the following disclaimer in the
1792ce8c6aSad  *    documentation and/or other materials provided with the distribution.
1892ce8c6aSad  * 3. Neither the name of the University nor the names of its contributors
1992ce8c6aSad  *    may be used to endorse or promote products derived from this software
2092ce8c6aSad  *    without specific prior written permission.
2192ce8c6aSad  *
2292ce8c6aSad  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2392ce8c6aSad  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2492ce8c6aSad  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2592ce8c6aSad  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2692ce8c6aSad  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2792ce8c6aSad  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2892ce8c6aSad  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2992ce8c6aSad  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3092ce8c6aSad  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3192ce8c6aSad  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3292ce8c6aSad  * SUCH DAMAGE.
3392ce8c6aSad  *
3492ce8c6aSad  *	@(#)nfs_subs.c	8.8 (Berkeley) 5/22/95
3592ce8c6aSad  */
3692ce8c6aSad 
3792ce8c6aSad /*
3892ce8c6aSad  * Copyright 2000 Wasabi Systems, Inc.
3992ce8c6aSad  * All rights reserved.
4092ce8c6aSad  *
4192ce8c6aSad  * Written by Frank van der Linden for Wasabi Systems, Inc.
4292ce8c6aSad  *
4392ce8c6aSad  * Redistribution and use in source and binary forms, with or without
4492ce8c6aSad  * modification, are permitted provided that the following conditions
4592ce8c6aSad  * are met:
4692ce8c6aSad  * 1. Redistributions of source code must retain the above copyright
4792ce8c6aSad  *    notice, this list of conditions and the following disclaimer.
4892ce8c6aSad  * 2. Redistributions in binary form must reproduce the above copyright
4992ce8c6aSad  *    notice, this list of conditions and the following disclaimer in the
5092ce8c6aSad  *    documentation and/or other materials provided with the distribution.
5192ce8c6aSad  * 3. All advertising materials mentioning features or use of this software
5292ce8c6aSad  *    must display the following acknowledgement:
5392ce8c6aSad  *      This product includes software developed for the NetBSD Project by
5492ce8c6aSad  *      Wasabi Systems, Inc.
5592ce8c6aSad  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
5692ce8c6aSad  *    or promote products derived from this software without specific prior
5792ce8c6aSad  *    written permission.
5892ce8c6aSad  *
5992ce8c6aSad  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
6092ce8c6aSad  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
6192ce8c6aSad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
6292ce8c6aSad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
6392ce8c6aSad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
6492ce8c6aSad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
6592ce8c6aSad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
6692ce8c6aSad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
6792ce8c6aSad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
6892ce8c6aSad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
6992ce8c6aSad  * POSSIBILITY OF SUCH DAMAGE.
7092ce8c6aSad  */
7192ce8c6aSad 
7292ce8c6aSad #include <sys/cdefs.h>
73*9b69ffe4Sriastradh __KERNEL_RCSID(0, "$NetBSD: nfs_srvsubs.c,v 1.17 2023/03/23 19:52:42 riastradh Exp $");
7492ce8c6aSad 
7592ce8c6aSad #include <sys/param.h>
7692ce8c6aSad #include <sys/proc.h>
7792ce8c6aSad #include <sys/systm.h>
7892ce8c6aSad #include <sys/kernel.h>
7992ce8c6aSad #include <sys/kmem.h>
8092ce8c6aSad #include <sys/mount.h>
8192ce8c6aSad #include <sys/vnode.h>
8292ce8c6aSad #include <sys/namei.h>
8392ce8c6aSad #include <sys/mbuf.h>
8492ce8c6aSad #include <sys/socket.h>
8592ce8c6aSad #include <sys/stat.h>
8692ce8c6aSad #include <sys/filedesc.h>
8792ce8c6aSad #include <sys/time.h>
8892ce8c6aSad #include <sys/dirent.h>
8992ce8c6aSad #include <sys/once.h>
9092ce8c6aSad #include <sys/kauth.h>
9192ce8c6aSad #include <sys/atomic.h>
9292ce8c6aSad 
9392ce8c6aSad #include <uvm/uvm_extern.h>
9492ce8c6aSad 
9592ce8c6aSad #include <nfs/rpcv2.h>
9692ce8c6aSad #include <nfs/nfsproto.h>
9792ce8c6aSad #include <nfs/nfsnode.h>
9892ce8c6aSad #include <nfs/nfs.h>
9992ce8c6aSad #include <nfs/xdr_subs.h>
10092ce8c6aSad #include <nfs/nfsm_subs.h>
10192ce8c6aSad #include <nfs/nfsmount.h>
10292ce8c6aSad #include <nfs/nfsrtt.h>
10392ce8c6aSad #include <nfs/nfs_var.h>
10492ce8c6aSad 
10592ce8c6aSad #include <miscfs/specfs/specdev.h>
10692ce8c6aSad 
10792ce8c6aSad #include <netinet/in.h>
10892ce8c6aSad 
10992ce8c6aSad /*
11092ce8c6aSad  * Set up nameidata for a lookup() call and do it.
11192ce8c6aSad  *
11292ce8c6aSad  * If pubflag is set, this call is done for a lookup operation on the
11392ce8c6aSad  * public filehandle. In that case we allow crossing mountpoints and
11492ce8c6aSad  * absolute pathnames. However, the caller is expected to check that
11592ce8c6aSad  * the lookup result is within the public fs, and deny access if
11692ce8c6aSad  * it is not.
11792ce8c6aSad  */
11892ce8c6aSad int
nfs_namei(struct nameidata * ndp,nfsrvfh_t * nsfh,uint32_t len,struct nfssvc_sock * slp,struct mbuf * nam,struct mbuf ** mdp,char ** dposp,struct vnode ** retdirp,int * dirattr_retp,struct vattr * dirattrp,struct lwp * l,int kerbflag,int pubflag)1195f179135Shannken nfs_namei(struct nameidata *ndp, nfsrvfh_t *nsfh, uint32_t len, struct nfssvc_sock *slp, struct mbuf *nam, struct mbuf **mdp, char **dposp, struct vnode **retdirp, int *dirattr_retp, struct vattr *dirattrp, struct lwp *l, int kerbflag, int pubflag)
12092ce8c6aSad {
12192ce8c6aSad 	int i, rem;
12292ce8c6aSad 	struct mbuf *md;
123d4eb0539Sdholland 	char *fromcp, *tocp, *cp, *path;
12492ce8c6aSad 	struct vnode *dp;
1258d360572Sdholland 	int error, rdonly;
1268d360572Sdholland 	int neverfollow;
12792ce8c6aSad 	struct componentname *cnp = &ndp->ni_cnd;
12892ce8c6aSad 
12992ce8c6aSad 	*retdirp = NULL;
130d4eb0539Sdholland 	ndp->ni_pathbuf = NULL;
13192ce8c6aSad 
132*9b69ffe4Sriastradh 	if (len > NFS_MAXPATHLEN - 1)
13392ce8c6aSad 		return (ENAMETOOLONG);
13492ce8c6aSad 	if (len == 0)
13592ce8c6aSad 		return (EACCES);
13692ce8c6aSad 
13792ce8c6aSad 	/*
138d4eb0539Sdholland 	 * Copy the name from the mbuf list to ndp->ni_pathbuf
13992ce8c6aSad 	 * and set the various ndp fields appropriately.
14092ce8c6aSad 	 */
141d4eb0539Sdholland 	path = PNBUF_GET();
14292ce8c6aSad 	fromcp = *dposp;
143d4eb0539Sdholland 	tocp = path;
14492ce8c6aSad 	md = *mdp;
14592ce8c6aSad 	rem = mtod(md, char *) + md->m_len - fromcp;
14692ce8c6aSad 	for (i = 0; i < len; i++) {
14792ce8c6aSad 		while (rem == 0) {
14892ce8c6aSad 			md = md->m_next;
14992ce8c6aSad 			if (md == NULL) {
15092ce8c6aSad 				error = EBADRPC;
15192ce8c6aSad 				goto out;
15292ce8c6aSad 			}
15392ce8c6aSad 			fromcp = mtod(md, void *);
15492ce8c6aSad 			rem = md->m_len;
15592ce8c6aSad 		}
15692ce8c6aSad 		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
15792ce8c6aSad 			error = EACCES;
15892ce8c6aSad 			goto out;
15992ce8c6aSad 		}
16092ce8c6aSad 		*tocp++ = *fromcp++;
16192ce8c6aSad 		rem--;
16292ce8c6aSad 	}
16392ce8c6aSad 	*tocp = '\0';
16492ce8c6aSad 	*mdp = md;
16592ce8c6aSad 	*dposp = fromcp;
16692ce8c6aSad 	len = nfsm_rndup(len)-len;
16792ce8c6aSad 	if (len > 0) {
16892ce8c6aSad 		if (rem >= len)
16992ce8c6aSad 			*dposp += len;
17092ce8c6aSad 		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
17192ce8c6aSad 			goto out;
17292ce8c6aSad 	}
17392ce8c6aSad 
17492ce8c6aSad 	/*
17592ce8c6aSad 	 * Extract and set starting directory.
17692ce8c6aSad 	 */
17792ce8c6aSad 	error = nfsrv_fhtovp(nsfh, false, &dp, ndp->ni_cnd.cn_cred, slp,
17892ce8c6aSad 	    nam, &rdonly, kerbflag, pubflag);
17992ce8c6aSad 	if (error)
18092ce8c6aSad 		goto out;
18192ce8c6aSad 	if (dp->v_type != VDIR) {
18292ce8c6aSad 		vrele(dp);
18392ce8c6aSad 		error = ENOTDIR;
18492ce8c6aSad 		goto out;
18592ce8c6aSad 	}
18692ce8c6aSad 
18792ce8c6aSad 	if (rdonly)
18892ce8c6aSad 		cnp->cn_flags |= RDONLY;
18992ce8c6aSad 
19092ce8c6aSad 	*retdirp = dp;
1915f179135Shannken 	if (dirattr_retp != NULL) {
1925f179135Shannken 		vn_lock(dp, LK_SHARED | LK_RETRY);
1935f179135Shannken 		*dirattr_retp = VOP_GETATTR(dp, dirattrp, ndp->ni_cnd.cn_cred);
1945f179135Shannken 		VOP_UNLOCK(dp);
1955f179135Shannken 	}
19692ce8c6aSad 
19792ce8c6aSad 	if (pubflag) {
19892ce8c6aSad 		/*
19992ce8c6aSad 		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
20092ce8c6aSad 		 * and the 'native path' indicator.
20192ce8c6aSad 		 */
20292ce8c6aSad 		cp = PNBUF_GET();
203d4eb0539Sdholland 		fromcp = path;
20492ce8c6aSad 		tocp = cp;
20592ce8c6aSad 		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
20692ce8c6aSad 			switch ((unsigned char)*fromcp) {
20792ce8c6aSad 			case WEBNFS_NATIVE_CHAR:
20892ce8c6aSad 				/*
20992ce8c6aSad 				 * 'Native' path for us is the same
21092ce8c6aSad 				 * as a path according to the NFS spec,
21192ce8c6aSad 				 * just skip the escape char.
21292ce8c6aSad 				 */
21392ce8c6aSad 				fromcp++;
21492ce8c6aSad 				break;
21592ce8c6aSad 			/*
21692ce8c6aSad 			 * More may be added in the future, range 0x80-0xff
21792ce8c6aSad 			 */
21892ce8c6aSad 			default:
21992ce8c6aSad 				error = EIO;
22092ce8c6aSad 				vrele(dp);
22192ce8c6aSad 				PNBUF_PUT(cp);
22292ce8c6aSad 				goto out;
22392ce8c6aSad 			}
22492ce8c6aSad 		}
22592ce8c6aSad 		/*
22692ce8c6aSad 		 * Translate the '%' escapes, URL-style.
22792ce8c6aSad 		 */
22892ce8c6aSad 		while (*fromcp != '\0') {
22992ce8c6aSad 			if (*fromcp == WEBNFS_ESC_CHAR) {
23092ce8c6aSad 				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
23192ce8c6aSad 					fromcp++;
23292ce8c6aSad 					*tocp++ = HEXSTRTOI(fromcp);
23392ce8c6aSad 					fromcp += 2;
23492ce8c6aSad 					continue;
23592ce8c6aSad 				} else {
23692ce8c6aSad 					error = ENOENT;
23792ce8c6aSad 					vrele(dp);
23892ce8c6aSad 					PNBUF_PUT(cp);
23992ce8c6aSad 					goto out;
24092ce8c6aSad 				}
24192ce8c6aSad 			} else
24292ce8c6aSad 				*tocp++ = *fromcp++;
24392ce8c6aSad 		}
24492ce8c6aSad 		*tocp = '\0';
245d4eb0539Sdholland 		PNBUF_PUT(path);
246d4eb0539Sdholland 		path = cp;
24792ce8c6aSad 	}
24892ce8c6aSad 
249b496a9dcSdholland 	ndp->ni_atdir = NULL;
250d4eb0539Sdholland 	ndp->ni_pathbuf = pathbuf_assimilate(path);
251d4eb0539Sdholland 	if (ndp->ni_pathbuf == NULL) {
252d4eb0539Sdholland 		error = ENOMEM;
253d4eb0539Sdholland 		goto out;
254d4eb0539Sdholland 	}
25592ce8c6aSad 
25692ce8c6aSad 	if (pubflag) {
257d4eb0539Sdholland 		if (path[0] == '/')
25892ce8c6aSad 			dp = rootvnode;
25992ce8c6aSad 	} else {
26092ce8c6aSad 		cnp->cn_flags |= NOCROSSMOUNT;
26192ce8c6aSad 	}
26292ce8c6aSad 
2638d360572Sdholland 	neverfollow = !pubflag;
26492ce8c6aSad 
26592ce8c6aSad 	/*
26692ce8c6aSad 	 * And call lookup() to do the real work
267d4eb0539Sdholland 	 *
268ce8c87efSdholland 	 * Note: ndp->ni_pathbuf is left undestroyed on success;
269ce8c87efSdholland 	 * caller must clean it up.
27092ce8c6aSad 	 */
2718d360572Sdholland 	error = lookup_for_nfsd(ndp, dp, neverfollow);
27292ce8c6aSad 	if (error) {
273ce8c87efSdholland 		goto out;
27492ce8c6aSad 	}
2758d360572Sdholland 	return 0;
27692ce8c6aSad 
27792ce8c6aSad out:
278d4eb0539Sdholland 	if (ndp->ni_pathbuf != NULL) {
279d4eb0539Sdholland 		pathbuf_destroy(ndp->ni_pathbuf);
2804a414f41Sdholland 		ndp->ni_pathbuf = NULL;
281d4eb0539Sdholland 	} else {
282d4eb0539Sdholland 		PNBUF_PUT(path);
283d4eb0539Sdholland 	}
28492ce8c6aSad 	return (error);
28592ce8c6aSad }
28692ce8c6aSad 
28792ce8c6aSad /*
28892ce8c6aSad  * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
28992ce8c6aSad  * 	- look up fsid in mount list (if not found ret error)
29092ce8c6aSad  *	- get vp and export rights by calling VFS_FHTOVP()
29192ce8c6aSad  *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
29292ce8c6aSad  *	- if not lockflag unlock it with VOP_UNLOCK()
29392ce8c6aSad  */
29492ce8c6aSad int
nfsrv_fhtovp(nfsrvfh_t * nsfh,int lockflag,struct vnode ** vpp,kauth_cred_t cred,struct nfssvc_sock * slp,struct mbuf * nam,int * rdonlyp,int kerbflag,int pubflag)29592ce8c6aSad nfsrv_fhtovp(nfsrvfh_t *nsfh, int lockflag, struct vnode **vpp,
29692ce8c6aSad     kauth_cred_t cred, struct nfssvc_sock *slp, struct mbuf *nam, int *rdonlyp,
29792ce8c6aSad     int kerbflag, int pubflag)
29892ce8c6aSad {
29992ce8c6aSad 	struct mount *mp;
30092ce8c6aSad 	kauth_cred_t credanon;
30192ce8c6aSad 	int error, exflags;
30292ce8c6aSad 	struct sockaddr_in *saddr;
30392ce8c6aSad 	fhandle_t *fhp;
30492ce8c6aSad 
30592ce8c6aSad 	fhp = NFSRVFH_FHANDLE(nsfh);
30692ce8c6aSad 	*vpp = (struct vnode *)0;
30792ce8c6aSad 
30892ce8c6aSad 	if (nfs_ispublicfh(nsfh)) {
30992ce8c6aSad 		if (!pubflag || !nfs_pub.np_valid)
31092ce8c6aSad 			return (ESTALE);
31192ce8c6aSad 		fhp = nfs_pub.np_handle;
31292ce8c6aSad 	}
31392ce8c6aSad 
31492ce8c6aSad 	error = netexport_check(&fhp->fh_fsid, nam, &mp, &exflags, &credanon);
31592ce8c6aSad 	if (error) {
31692ce8c6aSad 		return error;
31792ce8c6aSad 	}
31892ce8c6aSad 
319c2e9cb94Sad 	error = VFS_FHTOVP(mp, &fhp->fh_fid, LK_EXCLUSIVE, vpp);
32092ce8c6aSad 	if (error)
32192ce8c6aSad 		return (error);
32292ce8c6aSad 
32392ce8c6aSad 	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
32492ce8c6aSad 		saddr = mtod(nam, struct sockaddr_in *);
32592ce8c6aSad 		if ((saddr->sin_family == AF_INET) &&
32692ce8c6aSad 		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
32792ce8c6aSad 			vput(*vpp);
32892ce8c6aSad 			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
32992ce8c6aSad 		}
33092ce8c6aSad 		if ((saddr->sin_family == AF_INET6) &&
33192ce8c6aSad 		    ntohs(saddr->sin_port) >= IPV6PORT_RESERVED) {
33292ce8c6aSad 			vput(*vpp);
33392ce8c6aSad 			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
33492ce8c6aSad 		}
33592ce8c6aSad 	}
33692ce8c6aSad 	/*
33792ce8c6aSad 	 * Check/setup credentials.
33892ce8c6aSad 	 */
33992ce8c6aSad 	if (exflags & MNT_EXKERB) {
34092ce8c6aSad 		if (!kerbflag) {
34192ce8c6aSad 			vput(*vpp);
34292ce8c6aSad 			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
34392ce8c6aSad 		}
34492ce8c6aSad 	} else if (kerbflag) {
34592ce8c6aSad 		vput(*vpp);
34692ce8c6aSad 		return (NFSERR_AUTHERR | AUTH_TOOWEAK);
34792ce8c6aSad 	} else if (kauth_cred_geteuid(cred) == 0 || /* NFS maproot, see below */
34892ce8c6aSad 	    (exflags & MNT_EXPORTANON)) {
34992ce8c6aSad 		/*
35092ce8c6aSad 		 * This is used by the NFS maproot option. While we can change
35192ce8c6aSad 		 * the secmodel on our own host, we can't change it on the
35292ce8c6aSad 		 * clients. As means of least surprise, we're doing the
35392ce8c6aSad 		 * traditional thing here.
35492ce8c6aSad 		 * Should look into adding a "mapprivileged" or similar where
35592ce8c6aSad 		 * the users can be explicitly specified...
35692ce8c6aSad 		 * [elad, yamt 2008-03-05]
35792ce8c6aSad 		 */
35892ce8c6aSad 		kauth_cred_clone(credanon, cred);
35992ce8c6aSad 	}
36092ce8c6aSad 	if (exflags & MNT_EXRDONLY)
36192ce8c6aSad 		*rdonlyp = 1;
36292ce8c6aSad 	else
36392ce8c6aSad 		*rdonlyp = 0;
36492ce8c6aSad 	if (!lockflag)
3651423e65bShannken 		VOP_UNLOCK(*vpp);
36692ce8c6aSad 	return (0);
36792ce8c6aSad }
36892ce8c6aSad 
36992ce8c6aSad /*
37092ce8c6aSad  * WebNFS: check if a filehandle is a public filehandle. For v3, this
37192ce8c6aSad  * means a length of 0, for v2 it means all zeroes.
37292ce8c6aSad  */
37392ce8c6aSad int
nfs_ispublicfh(const nfsrvfh_t * nsfh)37492ce8c6aSad nfs_ispublicfh(const nfsrvfh_t *nsfh)
37592ce8c6aSad {
37692ce8c6aSad 	const char *cp = (const void *)(NFSRVFH_DATA(nsfh));
37792ce8c6aSad 	int i;
37892ce8c6aSad 
37992ce8c6aSad 	if (NFSRVFH_SIZE(nsfh) == 0) {
38092ce8c6aSad 		return true;
38192ce8c6aSad 	}
38292ce8c6aSad 	if (NFSRVFH_SIZE(nsfh) != NFSX_V2FH) {
38392ce8c6aSad 		return false;
38492ce8c6aSad 	}
38592ce8c6aSad 	for (i = 0; i < NFSX_V2FH; i++)
38692ce8c6aSad 		if (*cp++ != 0)
38792ce8c6aSad 			return false;
38892ce8c6aSad 	return true;
38992ce8c6aSad }
39092ce8c6aSad 
39192ce8c6aSad int
nfsrv_composefh(struct vnode * vp,nfsrvfh_t * nsfh,bool v3)39292ce8c6aSad nfsrv_composefh(struct vnode *vp, nfsrvfh_t *nsfh, bool v3)
39392ce8c6aSad {
39492ce8c6aSad 	int error;
39592ce8c6aSad 	size_t fhsize;
39692ce8c6aSad 
39792ce8c6aSad 	fhsize = NFSD_MAXFHSIZE;
39892ce8c6aSad 	error = vfs_composefh(vp, (void *)NFSRVFH_DATA(nsfh), &fhsize);
39992ce8c6aSad 	if (NFSX_FHTOOBIG_P(fhsize, v3)) {
40092ce8c6aSad 		error = EOPNOTSUPP;
40192ce8c6aSad 	}
40292ce8c6aSad 	if (error != 0) {
40392ce8c6aSad 		return error;
40492ce8c6aSad 	}
40592ce8c6aSad 	if (!v3 && fhsize < NFSX_V2FH) {
40692ce8c6aSad 		memset((char *)NFSRVFH_DATA(nsfh) + fhsize, 0,
40792ce8c6aSad 		    NFSX_V2FH - fhsize);
40892ce8c6aSad 		fhsize = NFSX_V2FH;
40992ce8c6aSad 	}
41092ce8c6aSad 	if ((fhsize % NFSX_UNSIGNED) != 0) {
41192ce8c6aSad 		return EOPNOTSUPP;
41292ce8c6aSad 	}
41392ce8c6aSad 	nsfh->nsfh_size = fhsize;
41492ce8c6aSad 	return 0;
41592ce8c6aSad }
41692ce8c6aSad 
41792ce8c6aSad int
nfsrv_comparefh(const nfsrvfh_t * fh1,const nfsrvfh_t * fh2)41892ce8c6aSad nfsrv_comparefh(const nfsrvfh_t *fh1, const nfsrvfh_t *fh2)
41992ce8c6aSad {
42092ce8c6aSad 
42192ce8c6aSad 	if (NFSRVFH_SIZE(fh1) != NFSRVFH_SIZE(fh2)) {
42292ce8c6aSad 		return NFSRVFH_SIZE(fh2) - NFSRVFH_SIZE(fh1);
42392ce8c6aSad 	}
42492ce8c6aSad 	return memcmp(NFSRVFH_DATA(fh1), NFSRVFH_DATA(fh2), NFSRVFH_SIZE(fh1));
42592ce8c6aSad }
42692ce8c6aSad 
42792ce8c6aSad void
nfsrv_copyfh(nfsrvfh_t * fh1,const nfsrvfh_t * fh2)42892ce8c6aSad nfsrv_copyfh(nfsrvfh_t *fh1, const nfsrvfh_t *fh2)
42992ce8c6aSad {
43092ce8c6aSad 	size_t size;
43192ce8c6aSad 
43292ce8c6aSad 	fh1->nsfh_size = size = NFSRVFH_SIZE(fh2);
43392ce8c6aSad 	memcpy(NFSRVFH_DATA(fh1), NFSRVFH_DATA(fh2), size);
43492ce8c6aSad }
435