1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1993
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem */
33191783Srmacklem
34191783Srmacklem#include <sys/cdefs.h>
35191783Srmacklem__FBSDID("$FreeBSD$");
36191783Srmacklem
37229802Srmacklem#include "opt_inet6.h"
38223280Srmacklem#include "opt_kdtrace.h"
39223280Srmacklem
40224778Srwatson#include <sys/capability.h>
41224778Srwatson
42191783Srmacklem/*
43191783Srmacklem * generally, I don't like #includes inside .h files, but it seems to
44191783Srmacklem * be the easiest way to handle the port.
45191783Srmacklem */
46220610Srmacklem#include <sys/hash.h>
47191783Srmacklem#include <fs/nfs/nfsport.h>
48191783Srmacklem#include <netinet/if_ether.h>
49191783Srmacklem#include <net/if_types.h>
50191783Srmacklem
51223280Srmacklem#include <fs/nfsclient/nfs_kdtrace.h>
52223280Srmacklem
53223280Srmacklem#ifdef KDTRACE_HOOKS
54223280Srmacklemdtrace_nfsclient_attrcache_flush_probe_func_t
55223280Srmacklem		dtrace_nfscl_attrcache_flush_done_probe;
56223280Srmacklemuint32_t	nfscl_attrcache_flush_done_id;
57223280Srmacklem
58223280Srmacklemdtrace_nfsclient_attrcache_get_hit_probe_func_t
59223280Srmacklem		dtrace_nfscl_attrcache_get_hit_probe;
60223280Srmacklemuint32_t	nfscl_attrcache_get_hit_id;
61223280Srmacklem
62223280Srmacklemdtrace_nfsclient_attrcache_get_miss_probe_func_t
63223280Srmacklem		dtrace_nfscl_attrcache_get_miss_probe;
64223280Srmacklemuint32_t	nfscl_attrcache_get_miss_id;
65223280Srmacklem
66223280Srmacklemdtrace_nfsclient_attrcache_load_probe_func_t
67223280Srmacklem		dtrace_nfscl_attrcache_load_done_probe;
68223280Srmacklemuint32_t	nfscl_attrcache_load_done_id;
69223280Srmacklem#endif /* !KDTRACE_HOOKS */
70223280Srmacklem
71191783Srmacklemextern u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
72191783Srmacklemextern struct vop_vector newnfs_vnodeops;
73191783Srmacklemextern struct vop_vector newnfs_fifoops;
74191783Srmacklemextern uma_zone_t newnfsnode_zone;
75191783Srmacklemextern struct buf_ops buf_ops_newnfs;
76191783Srmacklemextern int ncl_pbuf_freecnt;
77191783Srmacklemextern short nfsv4_cbport;
78191783Srmacklemextern int nfscl_enablecallb;
79191783Srmacklemextern int nfs_numnfscbd;
80191783Srmacklemextern int nfscl_inited;
81191783Srmacklemstruct mtx nfs_clstate_mutex;
82191783Srmacklemstruct mtx ncl_iod_mutex;
83191783SrmacklemNFSDLOCKMUTEX;
84191783Srmacklem
85191783Srmacklemextern void (*ncl_call_invalcaches)(struct vnode *);
86191783Srmacklem
87191783Srmacklem/*
88191783Srmacklem * Comparison function for vfs_hash functions.
89191783Srmacklem */
90191783Srmacklemint
91191783Srmacklemnewnfs_vncmpf(struct vnode *vp, void *arg)
92191783Srmacklem{
93191783Srmacklem	struct nfsfh *nfhp = (struct nfsfh *)arg;
94191783Srmacklem	struct nfsnode *np = VTONFS(vp);
95191783Srmacklem
96191783Srmacklem	if (np->n_fhp->nfh_len != nfhp->nfh_len ||
97191783Srmacklem	    NFSBCMP(np->n_fhp->nfh_fh, nfhp->nfh_fh, nfhp->nfh_len))
98191783Srmacklem		return (1);
99191783Srmacklem	return (0);
100191783Srmacklem}
101191783Srmacklem
102191783Srmacklem/*
103191783Srmacklem * Look up a vnode/nfsnode by file handle.
104191783Srmacklem * Callers must check for mount points!!
105191783Srmacklem * In all cases, a pointer to a
106191783Srmacklem * nfsnode structure is returned.
107191783Srmacklem * This variant takes a "struct nfsfh *" as second argument and uses
108191783Srmacklem * that structure up, either by hanging off the nfsnode or FREEing it.
109191783Srmacklem */
110191783Srmacklemint
111191783Srmacklemnfscl_nget(struct mount *mntp, struct vnode *dvp, struct nfsfh *nfhp,
112191783Srmacklem    struct componentname *cnp, struct thread *td, struct nfsnode **npp,
113220732Srmacklem    void *stuff, int lkflags)
114191783Srmacklem{
115191783Srmacklem	struct nfsnode *np, *dnp;
116191783Srmacklem	struct vnode *vp, *nvp;
117191783Srmacklem	struct nfsv4node *newd, *oldd;
118191783Srmacklem	int error;
119191783Srmacklem	u_int hash;
120191783Srmacklem	struct nfsmount *nmp;
121191783Srmacklem
122191783Srmacklem	nmp = VFSTONFS(mntp);
123191783Srmacklem	dnp = VTONFS(dvp);
124191783Srmacklem	*npp = NULL;
125191783Srmacklem
126191783Srmacklem	hash = fnv_32_buf(nfhp->nfh_fh, nfhp->nfh_len, FNV1_32_INIT);
127191783Srmacklem
128220732Srmacklem	error = vfs_hash_get(mntp, hash, lkflags,
129191783Srmacklem	    td, &nvp, newnfs_vncmpf, nfhp);
130191783Srmacklem	if (error == 0 && nvp != NULL) {
131191783Srmacklem		/*
132191783Srmacklem		 * I believe there is a slight chance that vgonel() could
133224081Szack		 * get called on this vnode between when NFSVOPLOCK() drops
134191783Srmacklem		 * the VI_LOCK() and vget() acquires it again, so that it
135191783Srmacklem		 * hasn't yet had v_usecount incremented. If this were to
136191783Srmacklem		 * happen, the VI_DOOMED flag would be set, so check for
137191783Srmacklem		 * that here. Since we now have the v_usecount incremented,
138191783Srmacklem		 * we should be ok until we vrele() it, if the VI_DOOMED
139191783Srmacklem		 * flag isn't set now.
140191783Srmacklem		 */
141191783Srmacklem		VI_LOCK(nvp);
142191783Srmacklem		if ((nvp->v_iflag & VI_DOOMED)) {
143191783Srmacklem			VI_UNLOCK(nvp);
144191783Srmacklem			vrele(nvp);
145191783Srmacklem			error = ENOENT;
146191783Srmacklem		} else {
147191783Srmacklem			VI_UNLOCK(nvp);
148191783Srmacklem		}
149191783Srmacklem	}
150191783Srmacklem	if (error) {
151191783Srmacklem		FREE((caddr_t)nfhp, M_NFSFH);
152191783Srmacklem		return (error);
153191783Srmacklem	}
154191783Srmacklem	if (nvp != NULL) {
155191783Srmacklem		np = VTONFS(nvp);
156191783Srmacklem		/*
157191783Srmacklem		 * For NFSv4, check to see if it is the same name and
158191783Srmacklem		 * replace the name, if it is different.
159191783Srmacklem		 */
160191783Srmacklem		oldd = newd = NULL;
161191783Srmacklem		if ((nmp->nm_flag & NFSMNT_NFSV4) && np->n_v4 != NULL &&
162191783Srmacklem		    nvp->v_type == VREG &&
163191783Srmacklem		    (np->n_v4->n4_namelen != cnp->cn_namelen ||
164191783Srmacklem		     NFSBCMP(cnp->cn_nameptr, NFS4NODENAME(np->n_v4),
165191783Srmacklem		     cnp->cn_namelen) ||
166191783Srmacklem		     dnp->n_fhp->nfh_len != np->n_v4->n4_fhlen ||
167191783Srmacklem		     NFSBCMP(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
168191783Srmacklem		     dnp->n_fhp->nfh_len))) {
169191783Srmacklem		    MALLOC(newd, struct nfsv4node *,
170191783Srmacklem			sizeof (struct nfsv4node) + dnp->n_fhp->nfh_len +
171191783Srmacklem			+ cnp->cn_namelen - 1, M_NFSV4NODE, M_WAITOK);
172191783Srmacklem		    NFSLOCKNODE(np);
173191783Srmacklem		    if (newd != NULL && np->n_v4 != NULL && nvp->v_type == VREG
174191783Srmacklem			&& (np->n_v4->n4_namelen != cnp->cn_namelen ||
175191783Srmacklem			 NFSBCMP(cnp->cn_nameptr, NFS4NODENAME(np->n_v4),
176191783Srmacklem			 cnp->cn_namelen) ||
177191783Srmacklem			 dnp->n_fhp->nfh_len != np->n_v4->n4_fhlen ||
178191783Srmacklem			 NFSBCMP(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
179191783Srmacklem			 dnp->n_fhp->nfh_len))) {
180191783Srmacklem			oldd = np->n_v4;
181191783Srmacklem			np->n_v4 = newd;
182191783Srmacklem			newd = NULL;
183191783Srmacklem			np->n_v4->n4_fhlen = dnp->n_fhp->nfh_len;
184191783Srmacklem			np->n_v4->n4_namelen = cnp->cn_namelen;
185191783Srmacklem			NFSBCOPY(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
186191783Srmacklem			    dnp->n_fhp->nfh_len);
187191783Srmacklem			NFSBCOPY(cnp->cn_nameptr, NFS4NODENAME(np->n_v4),
188191783Srmacklem			    cnp->cn_namelen);
189191783Srmacklem		    }
190191783Srmacklem		    NFSUNLOCKNODE(np);
191191783Srmacklem		}
192191783Srmacklem		if (newd != NULL)
193191783Srmacklem			FREE((caddr_t)newd, M_NFSV4NODE);
194191783Srmacklem		if (oldd != NULL)
195191783Srmacklem			FREE((caddr_t)oldd, M_NFSV4NODE);
196191783Srmacklem		*npp = np;
197191783Srmacklem		FREE((caddr_t)nfhp, M_NFSFH);
198191783Srmacklem		return (0);
199191783Srmacklem	}
200191783Srmacklem	np = uma_zalloc(newnfsnode_zone, M_WAITOK | M_ZERO);
201191783Srmacklem
202191783Srmacklem	error = getnewvnode("newnfs", mntp, &newnfs_vnodeops, &nvp);
203191783Srmacklem	if (error) {
204191783Srmacklem		uma_zfree(newnfsnode_zone, np);
205191783Srmacklem		FREE((caddr_t)nfhp, M_NFSFH);
206191783Srmacklem		return (error);
207191783Srmacklem	}
208191783Srmacklem	vp = nvp;
209230605Srmacklem	KASSERT(vp->v_bufobj.bo_bsize != 0, ("nfscl_nget: bo_bsize == 0"));
210191783Srmacklem	vp->v_bufobj.bo_ops = &buf_ops_newnfs;
211191783Srmacklem	vp->v_data = np;
212191783Srmacklem	np->n_vnode = vp;
213191783Srmacklem	/*
214191783Srmacklem	 * Initialize the mutex even if the vnode is going to be a loser.
215191783Srmacklem	 * This simplifies the logic in reclaim, which can then unconditionally
216191783Srmacklem	 * destroy the mutex (in the case of the loser, or if hash_insert
217191783Srmacklem	 * happened to return an error no special casing is needed).
218191783Srmacklem	 */
219191783Srmacklem	mtx_init(&np->n_mtx, "NEWNFSnode lock", NULL, MTX_DEF | MTX_DUPOK);
220191783Srmacklem
221191783Srmacklem	/*
222191783Srmacklem	 * Are we getting the root? If so, make sure the vnode flags
223191783Srmacklem	 * are correct
224191783Srmacklem	 */
225191783Srmacklem	if ((nfhp->nfh_len == nmp->nm_fhsize) &&
226191783Srmacklem	    !bcmp(nfhp->nfh_fh, nmp->nm_fh, nfhp->nfh_len)) {
227191783Srmacklem		if (vp->v_type == VNON)
228191783Srmacklem			vp->v_type = VDIR;
229191783Srmacklem		vp->v_vflag |= VV_ROOT;
230191783Srmacklem	}
231191783Srmacklem
232191783Srmacklem	np->n_fhp = nfhp;
233191783Srmacklem	/*
234191783Srmacklem	 * For NFSv4, we have to attach the directory file handle and
235191783Srmacklem	 * file name, so that Open Ops can be done later.
236191783Srmacklem	 */
237191783Srmacklem	if (nmp->nm_flag & NFSMNT_NFSV4) {
238191783Srmacklem		MALLOC(np->n_v4, struct nfsv4node *, sizeof (struct nfsv4node)
239191783Srmacklem		    + dnp->n_fhp->nfh_len + cnp->cn_namelen - 1, M_NFSV4NODE,
240191783Srmacklem		    M_WAITOK);
241191783Srmacklem		np->n_v4->n4_fhlen = dnp->n_fhp->nfh_len;
242191783Srmacklem		np->n_v4->n4_namelen = cnp->cn_namelen;
243191783Srmacklem		NFSBCOPY(dnp->n_fhp->nfh_fh, np->n_v4->n4_data,
244191783Srmacklem		    dnp->n_fhp->nfh_len);
245191783Srmacklem		NFSBCOPY(cnp->cn_nameptr, NFS4NODENAME(np->n_v4),
246191783Srmacklem		    cnp->cn_namelen);
247191783Srmacklem	} else {
248191783Srmacklem		np->n_v4 = NULL;
249191783Srmacklem	}
250191783Srmacklem
251191783Srmacklem	/*
252191783Srmacklem	 * NFS supports recursive and shared locking.
253191783Srmacklem	 */
254211531Sjhb	lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_NOWITNESS, NULL);
255191783Srmacklem	VN_LOCK_AREC(vp);
256191783Srmacklem	VN_LOCK_ASHARE(vp);
257191783Srmacklem	error = insmntque(vp, mntp);
258191783Srmacklem	if (error != 0) {
259191783Srmacklem		*npp = NULL;
260191783Srmacklem		mtx_destroy(&np->n_mtx);
261191783Srmacklem		FREE((caddr_t)nfhp, M_NFSFH);
262191783Srmacklem		if (np->n_v4 != NULL)
263191783Srmacklem			FREE((caddr_t)np->n_v4, M_NFSV4NODE);
264191783Srmacklem		uma_zfree(newnfsnode_zone, np);
265191783Srmacklem		return (error);
266191783Srmacklem	}
267220732Srmacklem	error = vfs_hash_insert(vp, hash, lkflags,
268191783Srmacklem	    td, &nvp, newnfs_vncmpf, nfhp);
269191783Srmacklem	if (error)
270191783Srmacklem		return (error);
271191783Srmacklem	if (nvp != NULL) {
272191783Srmacklem		*npp = VTONFS(nvp);
273191783Srmacklem		/* vfs_hash_insert() vput()'s the losing vnode */
274191783Srmacklem		return (0);
275191783Srmacklem	}
276191783Srmacklem	*npp = np;
277191783Srmacklem
278191783Srmacklem	return (0);
279191783Srmacklem}
280191783Srmacklem
281191783Srmacklem/*
282191783Srmacklem * Anothe variant of nfs_nget(). This one is only used by reopen. It
283191783Srmacklem * takes almost the same args as nfs_nget(), but only succeeds if an entry
284191783Srmacklem * exists in the cache. (Since files should already be "open" with a
285191783Srmacklem * vnode ref cnt on the node when reopen calls this, it should always
286191783Srmacklem * succeed.)
287191783Srmacklem * Also, don't get a vnode lock, since it may already be locked by some
288191783Srmacklem * other process that is handling it. This is ok, since all other threads
289191783Srmacklem * on the client are blocked by the nfsc_lock being exclusively held by the
290191783Srmacklem * caller of this function.
291191783Srmacklem */
292191783Srmacklemint
293191783Srmacklemnfscl_ngetreopen(struct mount *mntp, u_int8_t *fhp, int fhsize,
294191783Srmacklem    struct thread *td, struct nfsnode **npp)
295191783Srmacklem{
296191783Srmacklem	struct vnode *nvp;
297191783Srmacklem	u_int hash;
298191783Srmacklem	struct nfsfh *nfhp;
299191783Srmacklem	int error;
300191783Srmacklem
301191783Srmacklem	*npp = NULL;
302191783Srmacklem	/* For forced dismounts, just return error. */
303191783Srmacklem	if ((mntp->mnt_kern_flag & MNTK_UNMOUNTF))
304191783Srmacklem		return (EINTR);
305191783Srmacklem	MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + fhsize,
306191783Srmacklem	    M_NFSFH, M_WAITOK);
307191783Srmacklem	bcopy(fhp, &nfhp->nfh_fh[0], fhsize);
308191783Srmacklem	nfhp->nfh_len = fhsize;
309191783Srmacklem
310191783Srmacklem	hash = fnv_32_buf(fhp, fhsize, FNV1_32_INIT);
311191783Srmacklem
312191783Srmacklem	/*
313191783Srmacklem	 * First, try to get the vnode locked, but don't block for the lock.
314191783Srmacklem	 */
315191783Srmacklem	error = vfs_hash_get(mntp, hash, (LK_EXCLUSIVE | LK_NOWAIT), td, &nvp,
316191783Srmacklem	    newnfs_vncmpf, nfhp);
317191783Srmacklem	if (error == 0 && nvp != NULL) {
318224082Szack		NFSVOPUNLOCK(nvp, 0);
319191783Srmacklem	} else if (error == EBUSY) {
320191783Srmacklem		/*
321191783Srmacklem		 * The LK_EXCLOTHER lock type tells nfs_lock1() to not try
322191783Srmacklem		 * and lock the vnode, but just get a v_usecount on it.
323191783Srmacklem		 * LK_NOWAIT is set so that when vget() returns ENOENT,
324191783Srmacklem		 * vfs_hash_get() fails instead of looping.
325191783Srmacklem		 * If this succeeds, it is safe so long as a vflush() with
326191783Srmacklem		 * FORCECLOSE has not been done. Since the Renew thread is
327191783Srmacklem		 * stopped and the MNTK_UNMOUNTF flag is set before doing
328191783Srmacklem		 * a vflush() with FORCECLOSE, we should be ok here.
329191783Srmacklem		 */
330191783Srmacklem		if ((mntp->mnt_kern_flag & MNTK_UNMOUNTF))
331191783Srmacklem			error = EINTR;
332191783Srmacklem		else
333191783Srmacklem			error = vfs_hash_get(mntp, hash,
334191783Srmacklem			    (LK_EXCLOTHER | LK_NOWAIT), td, &nvp,
335191783Srmacklem			    newnfs_vncmpf, nfhp);
336191783Srmacklem	}
337191783Srmacklem	FREE(nfhp, M_NFSFH);
338191783Srmacklem	if (error)
339191783Srmacklem		return (error);
340191783Srmacklem	if (nvp != NULL) {
341191783Srmacklem		*npp = VTONFS(nvp);
342191783Srmacklem		return (0);
343191783Srmacklem	}
344191783Srmacklem	return (EINVAL);
345191783Srmacklem}
346191783Srmacklem
347191783Srmacklem/*
348191783Srmacklem * Load the attribute cache (that lives in the nfsnode entry) with
349191783Srmacklem * the attributes of the second argument and
350191783Srmacklem * Iff vaper not NULL
351191783Srmacklem *    copy the attributes to *vaper
352191783Srmacklem * Similar to nfs_loadattrcache(), except the attributes are passed in
353191783Srmacklem * instead of being parsed out of the mbuf list.
354191783Srmacklem */
355191783Srmacklemint
356191783Srmacklemnfscl_loadattrcache(struct vnode **vpp, struct nfsvattr *nap, void *nvaper,
357191783Srmacklem    void *stuff, int writeattr, int dontshrink)
358191783Srmacklem{
359191783Srmacklem	struct vnode *vp = *vpp;
360191783Srmacklem	struct vattr *vap, *nvap = &nap->na_vattr, *vaper = nvaper;
361191783Srmacklem	struct nfsnode *np;
362191783Srmacklem	struct nfsmount *nmp;
363191783Srmacklem	struct timespec mtime_save;
364248567Skib	u_quad_t nsize;
365248567Skib	int setnsize;
366191783Srmacklem
367191783Srmacklem	/*
368191783Srmacklem	 * If v_type == VNON it is a new node, so fill in the v_type,
369191783Srmacklem	 * n_mtime fields. Check to see if it represents a special
370191783Srmacklem	 * device, and if so, check for a possible alias. Once the
371191783Srmacklem	 * correct vnode has been obtained, fill in the rest of the
372191783Srmacklem	 * information.
373191783Srmacklem	 */
374191783Srmacklem	np = VTONFS(vp);
375191783Srmacklem	NFSLOCKNODE(np);
376191783Srmacklem	if (vp->v_type != nvap->va_type) {
377191783Srmacklem		vp->v_type = nvap->va_type;
378191783Srmacklem		if (vp->v_type == VFIFO)
379191783Srmacklem			vp->v_op = &newnfs_fifoops;
380191783Srmacklem		np->n_mtime = nvap->va_mtime;
381191783Srmacklem	}
382191783Srmacklem	nmp = VFSTONFS(vp->v_mount);
383191783Srmacklem	vap = &np->n_vattr.na_vattr;
384191783Srmacklem	mtime_save = vap->va_mtime;
385191783Srmacklem	if (writeattr) {
386191783Srmacklem		np->n_vattr.na_filerev = nap->na_filerev;
387191783Srmacklem		np->n_vattr.na_size = nap->na_size;
388191783Srmacklem		np->n_vattr.na_mtime = nap->na_mtime;
389191783Srmacklem		np->n_vattr.na_ctime = nap->na_ctime;
390191783Srmacklem		np->n_vattr.na_fsid = nap->na_fsid;
391223657Srmacklem		np->n_vattr.na_mode = nap->na_mode;
392191783Srmacklem	} else {
393191783Srmacklem		NFSBCOPY((caddr_t)nap, (caddr_t)&np->n_vattr,
394191783Srmacklem		    sizeof (struct nfsvattr));
395191783Srmacklem	}
396191783Srmacklem
397191783Srmacklem	/*
398191783Srmacklem	 * For NFSv4, if the node's fsid is not equal to the mount point's
399191783Srmacklem	 * fsid, return the low order 32bits of the node's fsid. This
400191783Srmacklem	 * allows getcwd(3) to work. There is a chance that the fsid might
401191783Srmacklem	 * be the same as a local fs, but since this is in an NFS mount
402191783Srmacklem	 * point, I don't think that will cause any problems?
403191783Srmacklem	 */
404220610Srmacklem	if (NFSHASNFSV4(nmp) && NFSHASHASSETFSID(nmp) &&
405191783Srmacklem	    (nmp->nm_fsid[0] != np->n_vattr.na_filesid[0] ||
406220610Srmacklem	     nmp->nm_fsid[1] != np->n_vattr.na_filesid[1])) {
407220610Srmacklem		/*
408220610Srmacklem		 * va_fsid needs to be set to some value derived from
409220610Srmacklem		 * np->n_vattr.na_filesid that is not equal
410220610Srmacklem		 * vp->v_mount->mnt_stat.f_fsid[0], so that it changes
411220610Srmacklem		 * from the value used for the top level server volume
412220610Srmacklem		 * in the mounted subtree.
413220610Srmacklem		 */
414220610Srmacklem		if (vp->v_mount->mnt_stat.f_fsid.val[0] !=
415220610Srmacklem		    (uint32_t)np->n_vattr.na_filesid[0])
416220610Srmacklem			vap->va_fsid = (uint32_t)np->n_vattr.na_filesid[0];
417220610Srmacklem		else
418220610Srmacklem			vap->va_fsid = (uint32_t)hash32_buf(
419220610Srmacklem			    np->n_vattr.na_filesid, 2 * sizeof(uint64_t), 0);
420220610Srmacklem	} else
421191783Srmacklem		vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
422191783Srmacklem	np->n_attrstamp = time_second;
423248567Skib	setnsize = 0;
424248581Skib	nsize = 0;
425191783Srmacklem	if (vap->va_size != np->n_size) {
426191783Srmacklem		if (vap->va_type == VREG) {
427191783Srmacklem			if (dontshrink && vap->va_size < np->n_size) {
428191783Srmacklem				/*
429191783Srmacklem				 * We've been told not to shrink the file;
430191783Srmacklem				 * zero np->n_attrstamp to indicate that
431191783Srmacklem				 * the attributes are stale.
432191783Srmacklem				 */
433191783Srmacklem				vap->va_size = np->n_size;
434191783Srmacklem				np->n_attrstamp = 0;
435223280Srmacklem				KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
436252528Srmacklem				vnode_pager_setsize(vp, np->n_size);
437191783Srmacklem			} else if (np->n_flag & NMODIFIED) {
438191783Srmacklem				/*
439191783Srmacklem				 * We've modified the file: Use the larger
440191783Srmacklem				 * of our size, and the server's size.
441191783Srmacklem				 */
442191783Srmacklem				if (vap->va_size < np->n_size) {
443191783Srmacklem					vap->va_size = np->n_size;
444191783Srmacklem				} else {
445191783Srmacklem					np->n_size = vap->va_size;
446191783Srmacklem					np->n_flag |= NSIZECHANGED;
447191783Srmacklem				}
448252528Srmacklem				vnode_pager_setsize(vp, np->n_size);
449252528Srmacklem			} else if (vap->va_size < np->n_size) {
450252528Srmacklem				/*
451252528Srmacklem				 * When shrinking the size, the call to
452252528Srmacklem				 * vnode_pager_setsize() cannot be done
453252528Srmacklem				 * with the mutex held, so delay it until
454252528Srmacklem				 * after the mtx_unlock call.
455252528Srmacklem				 */
456252528Srmacklem				nsize = np->n_size = vap->va_size;
457252528Srmacklem				np->n_flag |= NSIZECHANGED;
458252528Srmacklem				setnsize = 1;
459191783Srmacklem			} else {
460191783Srmacklem				np->n_size = vap->va_size;
461191783Srmacklem				np->n_flag |= NSIZECHANGED;
462252528Srmacklem				vnode_pager_setsize(vp, np->n_size);
463191783Srmacklem			}
464191783Srmacklem		} else {
465191783Srmacklem			np->n_size = vap->va_size;
466191783Srmacklem		}
467191783Srmacklem	}
468191783Srmacklem	/*
469191783Srmacklem	 * The following checks are added to prevent a race between (say)
470191783Srmacklem	 * a READDIR+ and a WRITE.
471191783Srmacklem	 * READDIR+, WRITE requests sent out.
472191783Srmacklem	 * READDIR+ resp, WRITE resp received on client.
473191783Srmacklem	 * However, the WRITE resp was handled before the READDIR+ resp
474191783Srmacklem	 * causing the post op attrs from the write to be loaded first
475191783Srmacklem	 * and the attrs from the READDIR+ to be loaded later. If this
476191783Srmacklem	 * happens, we have stale attrs loaded into the attrcache.
477191783Srmacklem	 * We detect this by for the mtime moving back. We invalidate the
478191783Srmacklem	 * attrcache when this happens.
479191783Srmacklem	 */
480223280Srmacklem	if (timespeccmp(&mtime_save, &vap->va_mtime, >)) {
481191783Srmacklem		/* Size changed or mtime went backwards */
482191783Srmacklem		np->n_attrstamp = 0;
483223280Srmacklem		KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
484223280Srmacklem	}
485191783Srmacklem	if (vaper != NULL) {
486191783Srmacklem		NFSBCOPY((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
487191783Srmacklem		if (np->n_flag & NCHG) {
488191783Srmacklem			if (np->n_flag & NACC)
489191783Srmacklem				vaper->va_atime = np->n_atim;
490191783Srmacklem			if (np->n_flag & NUPD)
491191783Srmacklem				vaper->va_mtime = np->n_mtim;
492191783Srmacklem		}
493191783Srmacklem	}
494223280Srmacklem#ifdef KDTRACE_HOOKS
495223280Srmacklem	if (np->n_attrstamp != 0)
496223280Srmacklem		KDTRACE_NFS_ATTRCACHE_LOAD_DONE(vp, vap, 0);
497223280Srmacklem#endif
498191783Srmacklem	NFSUNLOCKNODE(np);
499248567Skib	if (setnsize)
500248567Skib		vnode_pager_setsize(vp, nsize);
501191783Srmacklem	return (0);
502191783Srmacklem}
503191783Srmacklem
504191783Srmacklem/*
505191783Srmacklem * Fill in the client id name. For these bytes:
506191783Srmacklem * 1 - they must be unique
507191783Srmacklem * 2 - they should be persistent across client reboots
508191783Srmacklem * 1 is more critical than 2
509191783Srmacklem * Use the mount point's unique id plus either the uuid or, if that
510191783Srmacklem * isn't set, random junk.
511191783Srmacklem */
512191783Srmacklemvoid
513191783Srmacklemnfscl_fillclid(u_int64_t clval, char *uuid, u_int8_t *cp, u_int16_t idlen)
514191783Srmacklem{
515191783Srmacklem	int uuidlen;
516191783Srmacklem
517191783Srmacklem	/*
518191783Srmacklem	 * First, put in the 64bit mount point identifier.
519191783Srmacklem	 */
520191783Srmacklem	if (idlen >= sizeof (u_int64_t)) {
521191783Srmacklem		NFSBCOPY((caddr_t)&clval, cp, sizeof (u_int64_t));
522191783Srmacklem		cp += sizeof (u_int64_t);
523191783Srmacklem		idlen -= sizeof (u_int64_t);
524191783Srmacklem	}
525191783Srmacklem
526191783Srmacklem	/*
527191783Srmacklem	 * If uuid is non-zero length, use it.
528191783Srmacklem	 */
529191783Srmacklem	uuidlen = strlen(uuid);
530191783Srmacklem	if (uuidlen > 0 && idlen >= uuidlen) {
531191783Srmacklem		NFSBCOPY(uuid, cp, uuidlen);
532191783Srmacklem		cp += uuidlen;
533191783Srmacklem		idlen -= uuidlen;
534191783Srmacklem	}
535191783Srmacklem
536191783Srmacklem	/*
537191783Srmacklem	 * This only normally happens if the uuid isn't set.
538191783Srmacklem	 */
539191783Srmacklem	while (idlen > 0) {
540191783Srmacklem		*cp++ = (u_int8_t)(arc4random() % 256);
541191783Srmacklem		idlen--;
542191783Srmacklem	}
543191783Srmacklem}
544191783Srmacklem
545191783Srmacklem/*
546191783Srmacklem * Fill in a lock owner name. For now, pid + the process's creation time.
547191783Srmacklem */
548191783Srmacklemvoid
549222719Srmacklemnfscl_filllockowner(void *id, u_int8_t *cp, int flags)
550191783Srmacklem{
551191783Srmacklem	union {
552191783Srmacklem		u_int32_t	lval;
553191783Srmacklem		u_int8_t	cval[4];
554191783Srmacklem	} tl;
555191783Srmacklem	struct proc *p;
556191783Srmacklem
557222719Srmacklem	if (id == NULL) {
558222719Srmacklem		printf("NULL id\n");
559222719Srmacklem		bzero(cp, NFSV4CL_LOCKNAMELEN);
560222719Srmacklem		return;
561222719Srmacklem	}
562222719Srmacklem	if ((flags & F_POSIX) != 0) {
563222719Srmacklem		p = (struct proc *)id;
564222719Srmacklem		tl.lval = p->p_pid;
565222719Srmacklem		*cp++ = tl.cval[0];
566222719Srmacklem		*cp++ = tl.cval[1];
567222719Srmacklem		*cp++ = tl.cval[2];
568222719Srmacklem		*cp++ = tl.cval[3];
569222719Srmacklem		tl.lval = p->p_stats->p_start.tv_sec;
570222719Srmacklem		*cp++ = tl.cval[0];
571222719Srmacklem		*cp++ = tl.cval[1];
572222719Srmacklem		*cp++ = tl.cval[2];
573222719Srmacklem		*cp++ = tl.cval[3];
574222719Srmacklem		tl.lval = p->p_stats->p_start.tv_usec;
575222719Srmacklem		*cp++ = tl.cval[0];
576222719Srmacklem		*cp++ = tl.cval[1];
577222719Srmacklem		*cp++ = tl.cval[2];
578222719Srmacklem		*cp = tl.cval[3];
579222722Srmacklem	} else if ((flags & F_FLOCK) != 0) {
580222722Srmacklem		bcopy(&id, cp, sizeof(id));
581222722Srmacklem		bzero(&cp[sizeof(id)], NFSV4CL_LOCKNAMELEN - sizeof(id));
582222719Srmacklem	} else {
583222722Srmacklem		printf("nfscl_filllockowner: not F_POSIX or F_FLOCK\n");
584222719Srmacklem		bzero(cp, NFSV4CL_LOCKNAMELEN);
585222719Srmacklem	}
586191783Srmacklem}
587191783Srmacklem
588191783Srmacklem/*
589191783Srmacklem * Find the parent process for the thread passed in as an argument.
590191783Srmacklem * If none exists, return NULL, otherwise return a thread for the parent.
591191783Srmacklem * (Can be any of the threads, since it is only used for td->td_proc.)
592191783Srmacklem */
593191783SrmacklemNFSPROC_T *
594191783Srmacklemnfscl_getparent(struct thread *td)
595191783Srmacklem{
596191783Srmacklem	struct proc *p;
597191783Srmacklem	struct thread *ptd;
598191783Srmacklem
599191783Srmacklem	if (td == NULL)
600191783Srmacklem		return (NULL);
601191783Srmacklem	p = td->td_proc;
602191783Srmacklem	if (p->p_pid == 0)
603191783Srmacklem		return (NULL);
604191783Srmacklem	p = p->p_pptr;
605191783Srmacklem	if (p == NULL)
606191783Srmacklem		return (NULL);
607191783Srmacklem	ptd = TAILQ_FIRST(&p->p_threads);
608191783Srmacklem	return (ptd);
609191783Srmacklem}
610191783Srmacklem
611191783Srmacklem/*
612191783Srmacklem * Start up the renew kernel thread.
613191783Srmacklem */
614191783Srmacklemstatic void
615191783Srmacklemstart_nfscl(void *arg)
616191783Srmacklem{
617191783Srmacklem	struct nfsclclient *clp;
618191783Srmacklem	struct thread *td;
619191783Srmacklem
620191783Srmacklem	clp = (struct nfsclclient *)arg;
621191783Srmacklem	td = TAILQ_FIRST(&clp->nfsc_renewthread->p_threads);
622191783Srmacklem	nfscl_renewthread(clp, td);
623191783Srmacklem	kproc_exit(0);
624191783Srmacklem}
625191783Srmacklem
626191783Srmacklemvoid
627191783Srmacklemnfscl_start_renewthread(struct nfsclclient *clp)
628191783Srmacklem{
629191783Srmacklem
630191783Srmacklem	kproc_create(start_nfscl, (void *)clp, &clp->nfsc_renewthread, 0, 0,
631191783Srmacklem	    "nfscl");
632191783Srmacklem}
633191783Srmacklem
634191783Srmacklem/*
635191783Srmacklem * Handle wcc_data.
636191783Srmacklem * For NFSv4, it assumes that nfsv4_wccattr() was used to set up the getattr
637191783Srmacklem * as the first Op after PutFH.
638191783Srmacklem * (For NFSv4, the postop attributes are after the Op, so they can't be
639191783Srmacklem *  parsed here. A separate call to nfscl_postop_attr() is required.)
640191783Srmacklem */
641191783Srmacklemint
642191783Srmacklemnfscl_wcc_data(struct nfsrv_descript *nd, struct vnode *vp,
643191783Srmacklem    struct nfsvattr *nap, int *flagp, int *wccflagp, void *stuff)
644191783Srmacklem{
645191783Srmacklem	u_int32_t *tl;
646191783Srmacklem	struct nfsnode *np = VTONFS(vp);
647191783Srmacklem	struct nfsvattr nfsva;
648191783Srmacklem	int error = 0;
649191783Srmacklem
650191783Srmacklem	if (wccflagp != NULL)
651191783Srmacklem		*wccflagp = 0;
652191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
653191783Srmacklem		*flagp = 0;
654191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
655191783Srmacklem		if (*tl == newnfs_true) {
656191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
657191783Srmacklem			if (wccflagp != NULL) {
658191783Srmacklem				mtx_lock(&np->n_mtx);
659191783Srmacklem				*wccflagp = (np->n_mtime.tv_sec ==
660191783Srmacklem				    fxdr_unsigned(u_int32_t, *(tl + 2)) &&
661191783Srmacklem				    np->n_mtime.tv_nsec ==
662191783Srmacklem				    fxdr_unsigned(u_int32_t, *(tl + 3)));
663191783Srmacklem				mtx_unlock(&np->n_mtx);
664191783Srmacklem			}
665191783Srmacklem		}
666191783Srmacklem		error = nfscl_postop_attr(nd, nap, flagp, stuff);
667191783Srmacklem	} else if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR))
668191783Srmacklem	    == (ND_NFSV4 | ND_V4WCCATTR)) {
669191783Srmacklem		error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
670191783Srmacklem		    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
671191783Srmacklem		    NULL, NULL, NULL, NULL, NULL);
672191783Srmacklem		if (error)
673191783Srmacklem			return (error);
674191783Srmacklem		/*
675191783Srmacklem		 * Get rid of Op# and status for next op.
676191783Srmacklem		 */
677191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
678191783Srmacklem		if (*++tl)
679191783Srmacklem			nd->nd_flag |= ND_NOMOREDATA;
680191783Srmacklem		if (wccflagp != NULL &&
681191783Srmacklem		    nfsva.na_vattr.va_mtime.tv_sec != 0) {
682191783Srmacklem			mtx_lock(&np->n_mtx);
683191783Srmacklem			*wccflagp = (np->n_mtime.tv_sec ==
684191783Srmacklem			    nfsva.na_vattr.va_mtime.tv_sec &&
685191783Srmacklem			    np->n_mtime.tv_nsec ==
686191783Srmacklem			    nfsva.na_vattr.va_mtime.tv_sec);
687191783Srmacklem			mtx_unlock(&np->n_mtx);
688191783Srmacklem		}
689191783Srmacklem	}
690191783Srmacklemnfsmout:
691191783Srmacklem	return (error);
692191783Srmacklem}
693191783Srmacklem
694191783Srmacklem/*
695191783Srmacklem * Get postop attributes.
696191783Srmacklem */
697191783Srmacklemint
698191783Srmacklemnfscl_postop_attr(struct nfsrv_descript *nd, struct nfsvattr *nap, int *retp,
699191783Srmacklem    void *stuff)
700191783Srmacklem{
701191783Srmacklem	u_int32_t *tl;
702191783Srmacklem	int error = 0;
703191783Srmacklem
704191783Srmacklem	*retp = 0;
705191783Srmacklem	if (nd->nd_flag & ND_NOMOREDATA)
706191783Srmacklem		return (error);
707191783Srmacklem	if (nd->nd_flag & ND_NFSV3) {
708191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
709191783Srmacklem		*retp = fxdr_unsigned(int, *tl);
710191783Srmacklem	} else if (nd->nd_flag & ND_NFSV4) {
711191783Srmacklem		/*
712191783Srmacklem		 * For NFSv4, the postop attr are at the end, so no point
713191783Srmacklem		 * in looking if nd_repstat != 0.
714191783Srmacklem		 */
715191783Srmacklem		if (!nd->nd_repstat) {
716191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
717191783Srmacklem			if (*(tl + 1))
718191783Srmacklem				/* should never happen since nd_repstat != 0 */
719191783Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
720191783Srmacklem			else
721191783Srmacklem				*retp = 1;
722191783Srmacklem		}
723191783Srmacklem	} else if (!nd->nd_repstat) {
724191783Srmacklem		/* For NFSv2, the attributes are here iff nd_repstat == 0 */
725191783Srmacklem		*retp = 1;
726191783Srmacklem	}
727191783Srmacklem	if (*retp) {
728191783Srmacklem		error = nfsm_loadattr(nd, nap);
729191783Srmacklem		if (error)
730191783Srmacklem			*retp = 0;
731191783Srmacklem	}
732191783Srmacklemnfsmout:
733191783Srmacklem	return (error);
734191783Srmacklem}
735191783Srmacklem
736191783Srmacklem/*
737191783Srmacklem * Fill in the setable attributes. The full argument indicates whether
738191783Srmacklem * to fill in them all or just mode and time.
739191783Srmacklem */
740191783Srmacklemvoid
741191783Srmacklemnfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
742191783Srmacklem    struct vnode *vp, int flags, u_int32_t rdev)
743191783Srmacklem{
744191783Srmacklem	u_int32_t *tl;
745191783Srmacklem	struct nfsv2_sattr *sp;
746191783Srmacklem	nfsattrbit_t attrbits;
747191783Srmacklem
748191783Srmacklem	switch (nd->nd_flag & (ND_NFSV2 | ND_NFSV3 | ND_NFSV4)) {
749191783Srmacklem	case ND_NFSV2:
750191783Srmacklem		NFSM_BUILD(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
751191783Srmacklem		if (vap->va_mode == (mode_t)VNOVAL)
752191783Srmacklem			sp->sa_mode = newnfs_xdrneg1;
753191783Srmacklem		else
754191783Srmacklem			sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
755191783Srmacklem		if (vap->va_uid == (uid_t)VNOVAL)
756191783Srmacklem			sp->sa_uid = newnfs_xdrneg1;
757191783Srmacklem		else
758191783Srmacklem			sp->sa_uid = txdr_unsigned(vap->va_uid);
759191783Srmacklem		if (vap->va_gid == (gid_t)VNOVAL)
760191783Srmacklem			sp->sa_gid = newnfs_xdrneg1;
761191783Srmacklem		else
762191783Srmacklem			sp->sa_gid = txdr_unsigned(vap->va_gid);
763191783Srmacklem		if (flags & NFSSATTR_SIZE0)
764191783Srmacklem			sp->sa_size = 0;
765191783Srmacklem		else if (flags & NFSSATTR_SIZENEG1)
766191783Srmacklem			sp->sa_size = newnfs_xdrneg1;
767191783Srmacklem		else if (flags & NFSSATTR_SIZERDEV)
768191783Srmacklem			sp->sa_size = txdr_unsigned(rdev);
769191783Srmacklem		else
770191783Srmacklem			sp->sa_size = txdr_unsigned(vap->va_size);
771191783Srmacklem		txdr_nfsv2time(&vap->va_atime, &sp->sa_atime);
772191783Srmacklem		txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime);
773191783Srmacklem		break;
774191783Srmacklem	case ND_NFSV3:
775191783Srmacklem		if (vap->va_mode != (mode_t)VNOVAL) {
776191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
777191783Srmacklem			*tl++ = newnfs_true;
778191783Srmacklem			*tl = txdr_unsigned(vap->va_mode);
779191783Srmacklem		} else {
780191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
781191783Srmacklem			*tl = newnfs_false;
782191783Srmacklem		}
783191783Srmacklem		if ((flags & NFSSATTR_FULL) && vap->va_uid != (uid_t)VNOVAL) {
784191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
785191783Srmacklem			*tl++ = newnfs_true;
786191783Srmacklem			*tl = txdr_unsigned(vap->va_uid);
787191783Srmacklem		} else {
788191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
789191783Srmacklem			*tl = newnfs_false;
790191783Srmacklem		}
791191783Srmacklem		if ((flags & NFSSATTR_FULL) && vap->va_gid != (gid_t)VNOVAL) {
792191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
793191783Srmacklem			*tl++ = newnfs_true;
794191783Srmacklem			*tl = txdr_unsigned(vap->va_gid);
795191783Srmacklem		} else {
796191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
797191783Srmacklem			*tl = newnfs_false;
798191783Srmacklem		}
799191783Srmacklem		if ((flags & NFSSATTR_FULL) && vap->va_size != VNOVAL) {
800191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
801191783Srmacklem			*tl++ = newnfs_true;
802191783Srmacklem			txdr_hyper(vap->va_size, tl);
803191783Srmacklem		} else {
804191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
805191783Srmacklem			*tl = newnfs_false;
806191783Srmacklem		}
807191783Srmacklem		if (vap->va_atime.tv_sec != VNOVAL) {
808245508Sjhb			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
809191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
810191783Srmacklem				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
811191783Srmacklem				txdr_nfsv3time(&vap->va_atime, tl);
812191783Srmacklem			} else {
813191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
814191783Srmacklem				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
815191783Srmacklem			}
816191783Srmacklem		} else {
817191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
818191783Srmacklem			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
819191783Srmacklem		}
820191783Srmacklem		if (vap->va_mtime.tv_sec != VNOVAL) {
821245508Sjhb			if ((vap->va_vaflags & VA_UTIMES_NULL) == 0) {
822191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
823191783Srmacklem				*tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
824191783Srmacklem				txdr_nfsv3time(&vap->va_mtime, tl);
825191783Srmacklem			} else {
826191783Srmacklem				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
827191783Srmacklem				*tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
828191783Srmacklem			}
829191783Srmacklem		} else {
830191783Srmacklem			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
831191783Srmacklem			*tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
832191783Srmacklem		}
833191783Srmacklem		break;
834191783Srmacklem	case ND_NFSV4:
835191783Srmacklem		NFSZERO_ATTRBIT(&attrbits);
836191783Srmacklem		if (vap->va_mode != (mode_t)VNOVAL)
837191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_MODE);
838191783Srmacklem		if ((flags & NFSSATTR_FULL) && vap->va_uid != (uid_t)VNOVAL)
839191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
840191783Srmacklem		if ((flags & NFSSATTR_FULL) && vap->va_gid != (gid_t)VNOVAL)
841191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP);
842191783Srmacklem		if ((flags & NFSSATTR_FULL) && vap->va_size != VNOVAL)
843191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_SIZE);
844191783Srmacklem		if (vap->va_atime.tv_sec != VNOVAL)
845191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
846191783Srmacklem		if (vap->va_mtime.tv_sec != VNOVAL)
847191783Srmacklem			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET);
848220645Srmacklem		(void) nfsv4_fillattr(nd, vp->v_mount, vp, NULL, vap, NULL, 0,
849220648Srmacklem		    &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0);
850191783Srmacklem		break;
851191783Srmacklem	};
852191783Srmacklem}
853191783Srmacklem
854191783Srmacklem/*
855191783Srmacklem * nfscl_request() - mostly a wrapper for newnfs_request().
856191783Srmacklem */
857191783Srmacklemint
858191783Srmacklemnfscl_request(struct nfsrv_descript *nd, struct vnode *vp, NFSPROC_T *p,
859191783Srmacklem    struct ucred *cred, void *stuff)
860191783Srmacklem{
861191783Srmacklem	int ret, vers;
862191783Srmacklem	struct nfsmount *nmp;
863191783Srmacklem
864191783Srmacklem	nmp = VFSTONFS(vp->v_mount);
865191783Srmacklem	if (nd->nd_flag & ND_NFSV4)
866191783Srmacklem		vers = NFS_VER4;
867191783Srmacklem	else if (nd->nd_flag & ND_NFSV3)
868191783Srmacklem		vers = NFS_VER3;
869191783Srmacklem	else
870191783Srmacklem		vers = NFS_VER2;
871191783Srmacklem	ret = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, vp, p, cred,
872244042Srmacklem		NFS_PROG, vers, NULL, 1, NULL, NULL);
873191783Srmacklem	return (ret);
874191783Srmacklem}
875191783Srmacklem
876191783Srmacklem/*
877191783Srmacklem * fill in this bsden's variant of statfs using nfsstatfs.
878191783Srmacklem */
879191783Srmacklemvoid
880191783Srmacklemnfscl_loadsbinfo(struct nfsmount *nmp, struct nfsstatfs *sfp, void *statfs)
881191783Srmacklem{
882191783Srmacklem	struct statfs *sbp = (struct statfs *)statfs;
883191783Srmacklem
884191783Srmacklem	if (nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) {
885191783Srmacklem		sbp->f_bsize = NFS_FABLKSIZE;
886221467Srmacklem		sbp->f_blocks = sfp->sf_tbytes / NFS_FABLKSIZE;
887221467Srmacklem		sbp->f_bfree = sfp->sf_fbytes / NFS_FABLKSIZE;
888221467Srmacklem		/*
889221467Srmacklem		 * Although sf_abytes is uint64_t and f_bavail is int64_t,
890221467Srmacklem		 * the value after dividing by NFS_FABLKSIZE is small
891221467Srmacklem		 * enough that it will fit in 63bits, so it is ok to
892221467Srmacklem		 * assign it to f_bavail without fear that it will become
893221467Srmacklem		 * negative.
894221467Srmacklem		 */
895221467Srmacklem		sbp->f_bavail = sfp->sf_abytes / NFS_FABLKSIZE;
896221467Srmacklem		sbp->f_files = sfp->sf_tfiles;
897221467Srmacklem		/* Since f_ffree is int64_t, clip it to 63bits. */
898221467Srmacklem		if (sfp->sf_ffiles > INT64_MAX)
899221467Srmacklem			sbp->f_ffree = INT64_MAX;
900221467Srmacklem		else
901221467Srmacklem			sbp->f_ffree = sfp->sf_ffiles;
902191783Srmacklem	} else if ((nmp->nm_flag & NFSMNT_NFSV4) == 0) {
903221467Srmacklem		/*
904221467Srmacklem		 * The type casts to (int32_t) ensure that this code is
905221467Srmacklem		 * compatible with the old NFS client, in that it will
906221467Srmacklem		 * propagate bit31 to the high order bits. This may or may
907221467Srmacklem		 * not be correct for NFSv2, but since it is a legacy
908221467Srmacklem		 * environment, I'd rather retain backwards compatibility.
909221467Srmacklem		 */
910191783Srmacklem		sbp->f_bsize = (int32_t)sfp->sf_bsize;
911191783Srmacklem		sbp->f_blocks = (int32_t)sfp->sf_blocks;
912191783Srmacklem		sbp->f_bfree = (int32_t)sfp->sf_bfree;
913191783Srmacklem		sbp->f_bavail = (int32_t)sfp->sf_bavail;
914191783Srmacklem		sbp->f_files = 0;
915191783Srmacklem		sbp->f_ffree = 0;
916191783Srmacklem	}
917191783Srmacklem}
918191783Srmacklem
919191783Srmacklem/*
920191783Srmacklem * Use the fsinfo stuff to update the mount point.
921191783Srmacklem */
922191783Srmacklemvoid
923191783Srmacklemnfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp)
924191783Srmacklem{
925191783Srmacklem
926191783Srmacklem	if ((nmp->nm_wsize == 0 || fsp->fs_wtpref < nmp->nm_wsize) &&
927191783Srmacklem	    fsp->fs_wtpref >= NFS_FABLKSIZE)
928191783Srmacklem		nmp->nm_wsize = (fsp->fs_wtpref + NFS_FABLKSIZE - 1) &
929191783Srmacklem		    ~(NFS_FABLKSIZE - 1);
930191783Srmacklem	if (fsp->fs_wtmax < nmp->nm_wsize && fsp->fs_wtmax > 0) {
931191783Srmacklem		nmp->nm_wsize = fsp->fs_wtmax & ~(NFS_FABLKSIZE - 1);
932191783Srmacklem		if (nmp->nm_wsize == 0)
933191783Srmacklem			nmp->nm_wsize = fsp->fs_wtmax;
934191783Srmacklem	}
935191783Srmacklem	if (nmp->nm_wsize < NFS_FABLKSIZE)
936191783Srmacklem		nmp->nm_wsize = NFS_FABLKSIZE;
937191783Srmacklem	if ((nmp->nm_rsize == 0 || fsp->fs_rtpref < nmp->nm_rsize) &&
938191783Srmacklem	    fsp->fs_rtpref >= NFS_FABLKSIZE)
939191783Srmacklem		nmp->nm_rsize = (fsp->fs_rtpref + NFS_FABLKSIZE - 1) &
940191783Srmacklem		    ~(NFS_FABLKSIZE - 1);
941191783Srmacklem	if (fsp->fs_rtmax < nmp->nm_rsize && fsp->fs_rtmax > 0) {
942191783Srmacklem		nmp->nm_rsize = fsp->fs_rtmax & ~(NFS_FABLKSIZE - 1);
943191783Srmacklem		if (nmp->nm_rsize == 0)
944191783Srmacklem			nmp->nm_rsize = fsp->fs_rtmax;
945191783Srmacklem	}
946191783Srmacklem	if (nmp->nm_rsize < NFS_FABLKSIZE)
947191783Srmacklem		nmp->nm_rsize = NFS_FABLKSIZE;
948191783Srmacklem	if ((nmp->nm_readdirsize == 0 || fsp->fs_dtpref < nmp->nm_readdirsize)
949191783Srmacklem	    && fsp->fs_dtpref >= NFS_DIRBLKSIZ)
950191783Srmacklem		nmp->nm_readdirsize = (fsp->fs_dtpref + NFS_DIRBLKSIZ - 1) &
951191783Srmacklem		    ~(NFS_DIRBLKSIZ - 1);
952191783Srmacklem	if (fsp->fs_rtmax < nmp->nm_readdirsize && fsp->fs_rtmax > 0) {
953191783Srmacklem		nmp->nm_readdirsize = fsp->fs_rtmax & ~(NFS_DIRBLKSIZ - 1);
954191783Srmacklem		if (nmp->nm_readdirsize == 0)
955191783Srmacklem			nmp->nm_readdirsize = fsp->fs_rtmax;
956191783Srmacklem	}
957191783Srmacklem	if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
958191783Srmacklem		nmp->nm_readdirsize = NFS_DIRBLKSIZ;
959191783Srmacklem	if (fsp->fs_maxfilesize > 0 &&
960191783Srmacklem	    fsp->fs_maxfilesize < nmp->nm_maxfilesize)
961191783Srmacklem		nmp->nm_maxfilesize = fsp->fs_maxfilesize;
962191783Srmacklem	nmp->nm_mountp->mnt_stat.f_iosize = newnfs_iosize(nmp);
963191783Srmacklem	nmp->nm_state |= NFSSTA_GOTFSINFO;
964191783Srmacklem}
965191783Srmacklem
966191783Srmacklem/*
967191783Srmacklem * Get a pointer to my IP addrress and return it.
968191783Srmacklem * Return NULL if you can't find one.
969191783Srmacklem */
970191783Srmacklemu_int8_t *
971191783Srmacklemnfscl_getmyip(struct nfsmount *nmp, int *isinet6p)
972191783Srmacklem{
973191783Srmacklem	struct sockaddr_in sad, *sin;
974191783Srmacklem	struct rtentry *rt;
975191783Srmacklem	u_int8_t *retp = NULL;
976191783Srmacklem	static struct in_addr laddr;
977191783Srmacklem
978191783Srmacklem	*isinet6p = 0;
979191783Srmacklem	/*
980191783Srmacklem	 * Loop up a route for the destination address.
981191783Srmacklem	 */
982191783Srmacklem	if (nmp->nm_nam->sa_family == AF_INET) {
983191783Srmacklem		bzero(&sad, sizeof (sad));
984191783Srmacklem		sin = (struct sockaddr_in *)nmp->nm_nam;
985191783Srmacklem		sad.sin_family = AF_INET;
986191783Srmacklem		sad.sin_len = sizeof (struct sockaddr_in);
987191783Srmacklem		sad.sin_addr.s_addr = sin->sin_addr.s_addr;
988222718Srmacklem		CURVNET_SET(CRED_TO_VNET(nmp->nm_sockreq.nr_cred));
989231852Sbz		rt = rtalloc1_fib((struct sockaddr *)&sad, 0, 0UL,
990231852Sbz		     curthread->td_proc->p_fibnum);
991191783Srmacklem		if (rt != NULL) {
992191783Srmacklem			if (rt->rt_ifp != NULL &&
993191783Srmacklem			    rt->rt_ifa != NULL &&
994191783Srmacklem			    ((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) &&
995191783Srmacklem			    rt->rt_ifa->ifa_addr->sa_family == AF_INET) {
996191783Srmacklem				sin = (struct sockaddr_in *)
997191783Srmacklem				    rt->rt_ifa->ifa_addr;
998191783Srmacklem				laddr.s_addr = sin->sin_addr.s_addr;
999191783Srmacklem				retp = (u_int8_t *)&laddr;
1000191783Srmacklem			}
1001191783Srmacklem			RTFREE_LOCKED(rt);
1002191783Srmacklem		}
1003222718Srmacklem		CURVNET_RESTORE();
1004191783Srmacklem#ifdef INET6
1005191783Srmacklem	} else if (nmp->nm_nam->sa_family == AF_INET6) {
1006191783Srmacklem		struct sockaddr_in6 sad6, *sin6;
1007191783Srmacklem		static struct in6_addr laddr6;
1008191783Srmacklem
1009191783Srmacklem		bzero(&sad6, sizeof (sad6));
1010191783Srmacklem		sin6 = (struct sockaddr_in6 *)nmp->nm_nam;
1011191783Srmacklem		sad6.sin6_family = AF_INET6;
1012191783Srmacklem		sad6.sin6_len = sizeof (struct sockaddr_in6);
1013191783Srmacklem		sad6.sin6_addr = sin6->sin6_addr;
1014222718Srmacklem		CURVNET_SET(CRED_TO_VNET(nmp->nm_sockreq.nr_cred));
1015231852Sbz		rt = rtalloc1_fib((struct sockaddr *)&sad6, 0, 0UL,
1016231852Sbz		     curthread->td_proc->p_fibnum);
1017191783Srmacklem		if (rt != NULL) {
1018191783Srmacklem			if (rt->rt_ifp != NULL &&
1019191783Srmacklem			    rt->rt_ifa != NULL &&
1020191783Srmacklem			    ((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) &&
1021191783Srmacklem			    rt->rt_ifa->ifa_addr->sa_family == AF_INET6) {
1022191783Srmacklem				sin6 = (struct sockaddr_in6 *)
1023191783Srmacklem				    rt->rt_ifa->ifa_addr;
1024191783Srmacklem				laddr6 = sin6->sin6_addr;
1025191783Srmacklem				retp = (u_int8_t *)&laddr6;
1026191783Srmacklem				*isinet6p = 1;
1027191783Srmacklem			}
1028191783Srmacklem			RTFREE_LOCKED(rt);
1029191783Srmacklem		}
1030222718Srmacklem		CURVNET_RESTORE();
1031191783Srmacklem#endif
1032191783Srmacklem	}
1033191783Srmacklem	return (retp);
1034191783Srmacklem}
1035191783Srmacklem
1036191783Srmacklem/*
1037191783Srmacklem * Copy NFS uid, gids from the cred structure.
1038191783Srmacklem */
1039191783Srmacklemvoid
1040191783Srmacklemnewnfs_copyincred(struct ucred *cr, struct nfscred *nfscr)
1041191783Srmacklem{
1042194498Sbrooks	int i;
1043191783Srmacklem
1044206688Srmacklem	KASSERT(cr->cr_ngroups >= 0,
1045206688Srmacklem	    ("newnfs_copyincred: negative cr_ngroups"));
1046191783Srmacklem	nfscr->nfsc_uid = cr->cr_uid;
1047194541Srmacklem	nfscr->nfsc_ngroups = MIN(cr->cr_ngroups, NFS_MAXGRPS + 1);
1048194498Sbrooks	for (i = 0; i < nfscr->nfsc_ngroups; i++)
1049191783Srmacklem		nfscr->nfsc_groups[i] = cr->cr_groups[i];
1050191783Srmacklem}
1051191783Srmacklem
1052191783Srmacklem
1053191783Srmacklem/*
1054191783Srmacklem * Do any client specific initialization.
1055191783Srmacklem */
1056191783Srmacklemvoid
1057191783Srmacklemnfscl_init(void)
1058191783Srmacklem{
1059191783Srmacklem	static int inited = 0;
1060191783Srmacklem
1061191783Srmacklem	if (inited)
1062191783Srmacklem		return;
1063191783Srmacklem	inited = 1;
1064191783Srmacklem	nfscl_inited = 1;
1065191783Srmacklem	ncl_pbuf_freecnt = nswbuf / 2 + 1;
1066191783Srmacklem}
1067191783Srmacklem
1068191783Srmacklem/*
1069191783Srmacklem * Check each of the attributes to be set, to ensure they aren't already
1070191783Srmacklem * the correct value. Disable setting ones already correct.
1071191783Srmacklem */
1072191783Srmacklemint
1073191783Srmacklemnfscl_checksattr(struct vattr *vap, struct nfsvattr *nvap)
1074191783Srmacklem{
1075191783Srmacklem
1076191783Srmacklem	if (vap->va_mode != (mode_t)VNOVAL) {
1077191783Srmacklem		if (vap->va_mode == nvap->na_mode)
1078191783Srmacklem			vap->va_mode = (mode_t)VNOVAL;
1079191783Srmacklem	}
1080191783Srmacklem	if (vap->va_uid != (uid_t)VNOVAL) {
1081191783Srmacklem		if (vap->va_uid == nvap->na_uid)
1082191783Srmacklem			vap->va_uid = (uid_t)VNOVAL;
1083191783Srmacklem	}
1084191783Srmacklem	if (vap->va_gid != (gid_t)VNOVAL) {
1085191783Srmacklem		if (vap->va_gid == nvap->na_gid)
1086191783Srmacklem			vap->va_gid = (gid_t)VNOVAL;
1087191783Srmacklem	}
1088191783Srmacklem	if (vap->va_size != VNOVAL) {
1089191783Srmacklem		if (vap->va_size == nvap->na_size)
1090191783Srmacklem			vap->va_size = VNOVAL;
1091191783Srmacklem	}
1092191783Srmacklem
1093191783Srmacklem	/*
1094191783Srmacklem	 * We are normally called with only a partially initialized
1095191783Srmacklem	 * VAP.  Since the NFSv3 spec says that server may use the
1096191783Srmacklem	 * file attributes to store the verifier, the spec requires
1097191783Srmacklem	 * us to do a SETATTR RPC. FreeBSD servers store the verifier
1098191783Srmacklem	 * in atime, but we can't really assume that all servers will
1099191783Srmacklem	 * so we ensure that our SETATTR sets both atime and mtime.
1100191783Srmacklem	 */
1101191783Srmacklem	if (vap->va_mtime.tv_sec == VNOVAL)
1102191783Srmacklem		vfs_timestamp(&vap->va_mtime);
1103191783Srmacklem	if (vap->va_atime.tv_sec == VNOVAL)
1104191783Srmacklem		vap->va_atime = vap->va_mtime;
1105191783Srmacklem	return (1);
1106191783Srmacklem}
1107191783Srmacklem
1108191783Srmacklem/*
1109191783Srmacklem * Map nfsv4 errors to errno.h errors.
1110191783Srmacklem * The uid and gid arguments are only used for NFSERR_BADOWNER and that
1111191783Srmacklem * error should only be returned for the Open, Create and Setattr Ops.
1112191783Srmacklem * As such, most calls can just pass in 0 for those arguments.
1113191783Srmacklem */
1114191783SrmacklemAPPLESTATIC int
1115191783Srmacklemnfscl_maperr(struct thread *td, int error, uid_t uid, gid_t gid)
1116191783Srmacklem{
1117191783Srmacklem	struct proc *p;
1118191783Srmacklem
1119191783Srmacklem	if (error < 10000)
1120191783Srmacklem		return (error);
1121191783Srmacklem	if (td != NULL)
1122191783Srmacklem		p = td->td_proc;
1123191783Srmacklem	else
1124191783Srmacklem		p = NULL;
1125191783Srmacklem	switch (error) {
1126191783Srmacklem	case NFSERR_BADOWNER:
1127191783Srmacklem		tprintf(p, LOG_INFO,
1128191783Srmacklem		    "No name and/or group mapping for uid,gid:(%d,%d)\n",
1129191783Srmacklem		    uid, gid);
1130191783Srmacklem		return (EPERM);
1131244042Srmacklem	case NFSERR_BADNAME:
1132244042Srmacklem	case NFSERR_BADCHAR:
1133244042Srmacklem		printf("nfsv4 char/name not handled by server\n");
1134244042Srmacklem		return (ENOENT);
1135191783Srmacklem	case NFSERR_STALECLIENTID:
1136191783Srmacklem	case NFSERR_STALESTATEID:
1137191783Srmacklem	case NFSERR_EXPIRED:
1138191783Srmacklem	case NFSERR_BADSTATEID:
1139244042Srmacklem	case NFSERR_BADSESSION:
1140191783Srmacklem		printf("nfsv4 recover err returned %d\n", error);
1141191783Srmacklem		return (EIO);
1142191783Srmacklem	case NFSERR_BADHANDLE:
1143191783Srmacklem	case NFSERR_SERVERFAULT:
1144191783Srmacklem	case NFSERR_BADTYPE:
1145191783Srmacklem	case NFSERR_FHEXPIRED:
1146191783Srmacklem	case NFSERR_RESOURCE:
1147191783Srmacklem	case NFSERR_MOVED:
1148191783Srmacklem	case NFSERR_NOFILEHANDLE:
1149191783Srmacklem	case NFSERR_MINORVERMISMATCH:
1150191783Srmacklem	case NFSERR_OLDSTATEID:
1151191783Srmacklem	case NFSERR_BADSEQID:
1152191783Srmacklem	case NFSERR_LEASEMOVED:
1153191783Srmacklem	case NFSERR_RECLAIMBAD:
1154191783Srmacklem	case NFSERR_BADXDR:
1155191783Srmacklem	case NFSERR_OPILLEGAL:
1156191783Srmacklem		printf("nfsv4 client/server protocol prob err=%d\n",
1157191783Srmacklem		    error);
1158191783Srmacklem		return (EIO);
1159191783Srmacklem	default:
1160191783Srmacklem		tprintf(p, LOG_INFO, "nfsv4 err=%d\n", error);
1161191783Srmacklem		return (EIO);
1162191783Srmacklem	};
1163191783Srmacklem}
1164191783Srmacklem
1165191783Srmacklem/*
1166191783Srmacklem * Check to see if the process for this owner exists. Return 1 if it doesn't
1167191783Srmacklem * and 0 otherwise.
1168191783Srmacklem */
1169191783Srmacklemint
1170191783Srmacklemnfscl_procdoesntexist(u_int8_t *own)
1171191783Srmacklem{
1172191783Srmacklem	union {
1173191783Srmacklem		u_int32_t	lval;
1174191783Srmacklem		u_int8_t	cval[4];
1175191783Srmacklem	} tl;
1176191783Srmacklem	struct proc *p;
1177191783Srmacklem	pid_t pid;
1178191783Srmacklem	int ret = 0;
1179191783Srmacklem
1180191783Srmacklem	tl.cval[0] = *own++;
1181191783Srmacklem	tl.cval[1] = *own++;
1182191783Srmacklem	tl.cval[2] = *own++;
1183191783Srmacklem	tl.cval[3] = *own++;
1184191783Srmacklem	pid = tl.lval;
1185191783Srmacklem	p = pfind_locked(pid);
1186191783Srmacklem	if (p == NULL)
1187191783Srmacklem		return (1);
1188191783Srmacklem	if (p->p_stats == NULL) {
1189191783Srmacklem		PROC_UNLOCK(p);
1190191783Srmacklem		return (0);
1191191783Srmacklem	}
1192191783Srmacklem	tl.cval[0] = *own++;
1193191783Srmacklem	tl.cval[1] = *own++;
1194191783Srmacklem	tl.cval[2] = *own++;
1195191783Srmacklem	tl.cval[3] = *own++;
1196191783Srmacklem	if (tl.lval != p->p_stats->p_start.tv_sec) {
1197191783Srmacklem		ret = 1;
1198191783Srmacklem	} else {
1199191783Srmacklem		tl.cval[0] = *own++;
1200191783Srmacklem		tl.cval[1] = *own++;
1201191783Srmacklem		tl.cval[2] = *own++;
1202191783Srmacklem		tl.cval[3] = *own;
1203191783Srmacklem		if (tl.lval != p->p_stats->p_start.tv_usec)
1204191783Srmacklem			ret = 1;
1205191783Srmacklem	}
1206191783Srmacklem	PROC_UNLOCK(p);
1207191783Srmacklem	return (ret);
1208191783Srmacklem}
1209191783Srmacklem
1210191783Srmacklem/*
1211191783Srmacklem * - nfs pseudo system call for the client
1212191783Srmacklem */
1213191783Srmacklem/*
1214191783Srmacklem * MPSAFE
1215191783Srmacklem */
1216191783Srmacklemstatic int
1217191783Srmacklemnfssvc_nfscl(struct thread *td, struct nfssvc_args *uap)
1218191783Srmacklem{
1219191783Srmacklem	struct file *fp;
1220191783Srmacklem	struct nfscbd_args nfscbdarg;
1221191783Srmacklem	struct nfsd_nfscbd_args nfscbdarg2;
1222243782Srmacklem	struct nameidata nd;
1223243782Srmacklem	struct nfscl_dumpmntopts dumpmntopts;
1224255219Spjd	cap_rights_t rights;
1225243782Srmacklem	char *buf;
1226255219Spjd	int error;
1227191783Srmacklem
1228191783Srmacklem	if (uap->flag & NFSSVC_CBADDSOCK) {
1229191783Srmacklem		error = copyin(uap->argp, (caddr_t)&nfscbdarg, sizeof(nfscbdarg));
1230191783Srmacklem		if (error)
1231191783Srmacklem			return (error);
1232224778Srwatson		/*
1233224778Srwatson		 * Since we don't know what rights might be required,
1234224778Srwatson		 * pretend that we need them all. It is better to be too
1235224778Srwatson		 * careful than too reckless.
1236224778Srwatson		 */
1237255219Spjd		error = fget(td, nfscbdarg.sock,
1238255219Spjd		    cap_rights_init(&rights, CAP_SOCK_CLIENT), &fp);
1239255219Spjd		if (error)
1240191783Srmacklem			return (error);
1241191783Srmacklem		if (fp->f_type != DTYPE_SOCKET) {
1242191783Srmacklem			fdrop(fp, td);
1243191783Srmacklem			return (EPERM);
1244191783Srmacklem		}
1245191783Srmacklem		error = nfscbd_addsock(fp);
1246191783Srmacklem		fdrop(fp, td);
1247191783Srmacklem		if (!error && nfscl_enablecallb == 0) {
1248191783Srmacklem			nfsv4_cbport = nfscbdarg.port;
1249191783Srmacklem			nfscl_enablecallb = 1;
1250191783Srmacklem		}
1251191783Srmacklem	} else if (uap->flag & NFSSVC_NFSCBD) {
1252191783Srmacklem		if (uap->argp == NULL)
1253191783Srmacklem			return (EINVAL);
1254191783Srmacklem		error = copyin(uap->argp, (caddr_t)&nfscbdarg2,
1255191783Srmacklem		    sizeof(nfscbdarg2));
1256191783Srmacklem		if (error)
1257191783Srmacklem			return (error);
1258191783Srmacklem		error = nfscbd_nfsd(td, &nfscbdarg2);
1259243782Srmacklem	} else if (uap->flag & NFSSVC_DUMPMNTOPTS) {
1260243782Srmacklem		error = copyin(uap->argp, &dumpmntopts, sizeof(dumpmntopts));
1261243782Srmacklem		if (error == 0 && (dumpmntopts.ndmnt_blen < 256 ||
1262243782Srmacklem		    dumpmntopts.ndmnt_blen > 1024))
1263243782Srmacklem			error = EINVAL;
1264243782Srmacklem		if (error == 0)
1265243782Srmacklem			error = nfsrv_lookupfilename(&nd,
1266243782Srmacklem			    dumpmntopts.ndmnt_fname, td);
1267243782Srmacklem		if (error == 0 && strcmp(nd.ni_vp->v_mount->mnt_vfc->vfc_name,
1268243782Srmacklem		    "nfs") != 0) {
1269243782Srmacklem			vput(nd.ni_vp);
1270243782Srmacklem			error = EINVAL;
1271243782Srmacklem		}
1272243782Srmacklem		if (error == 0) {
1273243782Srmacklem			buf = malloc(dumpmntopts.ndmnt_blen, M_TEMP, M_WAITOK);
1274243782Srmacklem			nfscl_retopts(VFSTONFS(nd.ni_vp->v_mount), buf,
1275243782Srmacklem			    dumpmntopts.ndmnt_blen);
1276243782Srmacklem			vput(nd.ni_vp);
1277243782Srmacklem			error = copyout(buf, dumpmntopts.ndmnt_buf,
1278243782Srmacklem			    dumpmntopts.ndmnt_blen);
1279243782Srmacklem			free(buf, M_TEMP);
1280243782Srmacklem		}
1281191783Srmacklem	} else {
1282191783Srmacklem		error = EINVAL;
1283191783Srmacklem	}
1284191783Srmacklem	return (error);
1285191783Srmacklem}
1286191783Srmacklem
1287191783Srmacklemextern int (*nfsd_call_nfscl)(struct thread *, struct nfssvc_args *);
1288191783Srmacklem
1289191783Srmacklem/*
1290191783Srmacklem * Called once to initialize data structures...
1291191783Srmacklem */
1292191783Srmacklemstatic int
1293191783Srmacklemnfscl_modevent(module_t mod, int type, void *data)
1294191783Srmacklem{
1295191783Srmacklem	int error = 0;
1296191783Srmacklem	static int loaded = 0;
1297191783Srmacklem
1298191783Srmacklem	switch (type) {
1299191783Srmacklem	case MOD_LOAD:
1300191783Srmacklem		if (loaded)
1301191783Srmacklem			return (0);
1302191783Srmacklem		newnfs_portinit();
1303191783Srmacklem		mtx_init(&nfs_clstate_mutex, "nfs_clstate_mutex", NULL,
1304191783Srmacklem		    MTX_DEF);
1305191783Srmacklem		mtx_init(&ncl_iod_mutex, "ncl_iod_mutex", NULL, MTX_DEF);
1306191783Srmacklem		nfscl_init();
1307191783Srmacklem		NFSD_LOCK();
1308191783Srmacklem		nfsrvd_cbinit(0);
1309191783Srmacklem		NFSD_UNLOCK();
1310191783Srmacklem		ncl_call_invalcaches = ncl_invalcaches;
1311191783Srmacklem		nfsd_call_nfscl = nfssvc_nfscl;
1312191783Srmacklem		loaded = 1;
1313191783Srmacklem		break;
1314191783Srmacklem
1315191783Srmacklem	case MOD_UNLOAD:
1316191783Srmacklem		if (nfs_numnfscbd != 0) {
1317191783Srmacklem			error = EBUSY;
1318191783Srmacklem			break;
1319191783Srmacklem		}
1320191783Srmacklem
1321198291Sjh		/*
1322198291Sjh		 * XXX: Unloading of nfscl module is unsupported.
1323198291Sjh		 */
1324198291Sjh#if 0
1325191783Srmacklem		ncl_call_invalcaches = NULL;
1326191783Srmacklem		nfsd_call_nfscl = NULL;
1327191783Srmacklem		/* and get rid of the mutexes */
1328191783Srmacklem		mtx_destroy(&nfs_clstate_mutex);
1329191783Srmacklem		mtx_destroy(&ncl_iod_mutex);
1330191783Srmacklem		loaded = 0;
1331191783Srmacklem		break;
1332198291Sjh#else
1333198291Sjh		/* FALLTHROUGH */
1334198291Sjh#endif
1335191783Srmacklem	default:
1336191783Srmacklem		error = EOPNOTSUPP;
1337191783Srmacklem		break;
1338191783Srmacklem	}
1339191783Srmacklem	return error;
1340191783Srmacklem}
1341191783Srmacklemstatic moduledata_t nfscl_mod = {
1342191783Srmacklem	"nfscl",
1343191783Srmacklem	nfscl_modevent,
1344191783Srmacklem	NULL,
1345191783Srmacklem};
1346198290SjhDECLARE_MODULE(nfscl, nfscl_mod, SI_SUB_VFS, SI_ORDER_FIRST);
1347191783Srmacklem
1348191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */
1349191783SrmacklemMODULE_VERSION(nfscl, 1);
1350192601SrmacklemMODULE_DEPEND(nfscl, nfscommon, 1, 1, 1);
1351209191SrmacklemMODULE_DEPEND(nfscl, krpc, 1, 1, 1);
1352209191SrmacklemMODULE_DEPEND(nfscl, nfssvc, 1, 1, 1);
1353210455SrmacklemMODULE_DEPEND(nfscl, nfslock, 1, 1, 1);
1354191783Srmacklem
1355