nfs_clcomsubs.c revision 220152
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clcomsubs.c 220152 2011-03-30 01:10:11Z zack $");
36
37/*
38 * These functions support the macros and help fiddle mbuf chains for
39 * the nfs op functions. They do things like create the rpc header and
40 * copy data between mbuf chains and uio lists.
41 */
42#ifndef APPLEKEXT
43#include <fs/nfs/nfsport.h>
44
45extern struct nfsstats newnfsstats;
46extern struct nfsv4_opflag nfsv4_opflag[NFSV4OP_NOPS];
47extern int ncl_mbuf_mlen;
48extern enum vtype newnv2tov_type[8];
49extern enum vtype nv34tov_type[8];
50NFSCLSTATEMUTEX;
51#endif	/* !APPLEKEXT */
52
53static nfsuint64 nfs_nullcookie = {{ 0, 0 }};
54static struct {
55	int	op;
56	int	opcnt;
57	const u_char *tag;
58	int	taglen;
59} nfsv4_opmap[NFS_NPROCS] = {
60	{ 0, 1, "Null", 4 },
61	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
62	{ NFSV4OP_SETATTR, 2, "Setattr", 7, },
63	{ NFSV4OP_LOOKUP, 3, "Lookup", 6, },
64	{ NFSV4OP_ACCESS, 2, "Access", 6, },
65	{ NFSV4OP_READLINK, 2, "Readlink", 8, },
66	{ NFSV4OP_READ, 1, "Read", 4, },
67	{ NFSV4OP_WRITE, 2, "Write", 5, },
68	{ NFSV4OP_OPEN, 3, "Open", 4, },
69	{ NFSV4OP_CREATE, 3, "Create", 6, },
70	{ NFSV4OP_CREATE, 1, "Create", 6, },
71	{ NFSV4OP_CREATE, 3, "Create", 6, },
72	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
73	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
74	{ NFSV4OP_SAVEFH, 5, "Rename", 6, },
75	{ NFSV4OP_SAVEFH, 4, "Link", 4, },
76	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
77	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
78	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
79	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
80	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
81	{ NFSV4OP_COMMIT, 2, "Commit", 6, },
82	{ NFSV4OP_LOOKUPP, 3, "Lookupp", 7, },
83	{ NFSV4OP_SETCLIENTID, 1, "SetClientID", 11, },
84	{ NFSV4OP_SETCLIENTIDCFRM, 1, "SetClientIDConfirm", 18, },
85	{ NFSV4OP_LOCK, 1, "Lock", 4, },
86	{ NFSV4OP_LOCKU, 1, "LockU", 5, },
87	{ NFSV4OP_OPEN, 2, "Open", 4, },
88	{ NFSV4OP_CLOSE, 1, "Close", 5, },
89	{ NFSV4OP_OPENCONFIRM, 1, "Openconfirm", 11, },
90	{ NFSV4OP_LOCKT, 1, "LockT", 5, },
91	{ NFSV4OP_OPENDOWNGRADE, 1, "Opendowngrade", 13, },
92	{ NFSV4OP_RENEW, 1, "Renew", 5, },
93	{ NFSV4OP_PUTROOTFH, 1, "Dirpath", 7, },
94	{ NFSV4OP_RELEASELCKOWN, 1, "Rellckown", 9, },
95	{ NFSV4OP_DELEGRETURN, 1, "Delegret", 8, },
96	{ NFSV4OP_DELEGRETURN, 3, "DelegRemove", 11, },
97	{ NFSV4OP_DELEGRETURN, 7, "DelegRename1", 12, },
98	{ NFSV4OP_DELEGRETURN, 9, "DelegRename2", 12, },
99	{ NFSV4OP_GETATTR, 1, "Getacl", 6, },
100	{ NFSV4OP_SETATTR, 1, "Setacl", 6, },
101};
102
103
104/*
105 * NFS RPCS that have large request message size.
106 */
107static int nfs_bigrequest[NFS_NPROCS] = {
108	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110};
111
112/*
113 * Start building a request. Mostly just put the first file handle in
114 * place.
115 */
116APPLESTATIC void
117nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
118    u_int8_t *nfhp, int fhlen, u_int32_t **opcntpp)
119{
120	struct mbuf *mb;
121	u_int32_t *tl;
122	int opcnt;
123	nfsattrbit_t attrbits;
124
125	/*
126	 * First, fill in some of the fields of nd.
127	 */
128	if (NFSHASNFSV4(nmp))
129		nd->nd_flag = ND_NFSV4;
130	else if (NFSHASNFSV3(nmp))
131		nd->nd_flag = ND_NFSV3;
132	else
133		nd->nd_flag = ND_NFSV2;
134	nd->nd_procnum = procnum;
135	nd->nd_repstat = 0;
136
137	/*
138	 * Get the first mbuf for the request.
139	 */
140	if (nfs_bigrequest[procnum])
141		NFSMCLGET(mb, M_WAIT);
142	else
143		NFSMGET(mb);
144	mbuf_setlen(mb, 0);
145	nd->nd_mreq = nd->nd_mb = mb;
146	nd->nd_bpos = NFSMTOD(mb, caddr_t);
147
148	/*
149	 * And fill the first file handle into the request.
150	 */
151	if (nd->nd_flag & ND_NFSV4) {
152		opcnt = nfsv4_opmap[procnum].opcnt +
153		    nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh;
154		/*
155		 * What should the tag really be?
156		 */
157		(void) nfsm_strtom(nd, nfsv4_opmap[procnum].tag,
158			nfsv4_opmap[procnum].taglen);
159		NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
160		*tl++ = txdr_unsigned(NFSV4_MINORVERSION);
161		if (opcntpp != NULL)
162			*opcntpp = tl;
163		*tl++ = txdr_unsigned(opcnt);
164		if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
165			*tl = txdr_unsigned(NFSV4OP_PUTFH);
166			(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
167			if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh==2){
168				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
169				*tl = txdr_unsigned(NFSV4OP_GETATTR);
170				NFSWCCATTR_ATTRBIT(&attrbits);
171				(void) nfsrv_putattrbit(nd, &attrbits);
172				nd->nd_flag |= ND_V4WCCATTR;
173			}
174			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
175		}
176		*tl = txdr_unsigned(nfsv4_opmap[procnum].op);
177	} else {
178		(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
179	}
180	NFSINCRGLOBAL(newnfsstats.rpccnt[procnum]);
181}
182
183#ifndef APPLE
184/*
185 * copies a uio scatter/gather list to an mbuf chain.
186 * NOTE: can ony handle iovcnt == 1
187 */
188APPLESTATIC void
189nfsm_uiombuf(struct nfsrv_descript *nd, struct uio *uiop, int siz)
190{
191	char *uiocp;
192	struct mbuf *mp, *mp2;
193	int xfer, left, mlen;
194	int uiosiz, clflg, rem;
195	char *cp, *tcp;
196
197	KASSERT(uiop->uio_iovcnt == 1, ("nfsm_uiotombuf: iovcnt != 1"));
198
199	if (siz > ncl_mbuf_mlen)	/* or should it >= MCLBYTES ?? */
200		clflg = 1;
201	else
202		clflg = 0;
203	rem = NFSM_RNDUP(siz) - siz;
204	mp = mp2 = nd->nd_mb;
205	while (siz > 0) {
206		left = uiop->uio_iov->iov_len;
207		uiocp = uiop->uio_iov->iov_base;
208		if (left > siz)
209			left = siz;
210		uiosiz = left;
211		while (left > 0) {
212			mlen = M_TRAILINGSPACE(mp);
213			if (mlen == 0) {
214				if (clflg)
215					NFSMCLGET(mp, M_WAIT);
216				else
217					NFSMGET(mp);
218				mbuf_setlen(mp, 0);
219				mbuf_setnext(mp2, mp);
220				mp2 = mp;
221				mlen = M_TRAILINGSPACE(mp);
222			}
223			xfer = (left > mlen) ? mlen : left;
224#ifdef notdef
225			/* Not Yet.. */
226			if (uiop->uio_iov->iov_op != NULL)
227				(*(uiop->uio_iov->iov_op))
228				(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
229				    xfer);
230			else
231#endif
232			if (uiop->uio_segflg == UIO_SYSSPACE)
233			    NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
234				xfer);
235			else
236			    copyin(CAST_USER_ADDR_T(uiocp), NFSMTOD(mp, caddr_t)
237				+ mbuf_len(mp), xfer);
238			mbuf_setlen(mp, mbuf_len(mp) + xfer);
239			left -= xfer;
240			uiocp += xfer;
241			uiop->uio_offset += xfer;
242			uiop->uio_resid -= xfer;
243		}
244		tcp = (char *)uiop->uio_iov->iov_base;
245		tcp += uiosiz;
246		uiop->uio_iov->iov_base = (void *)tcp;
247		uiop->uio_iov->iov_len -= uiosiz;
248		siz -= uiosiz;
249	}
250	if (rem > 0) {
251		if (rem > M_TRAILINGSPACE(mp)) {
252			NFSMGET(mp);
253			mbuf_setlen(mp, 0);
254			mbuf_setnext(mp2, mp);
255		}
256		cp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
257		for (left = 0; left < rem; left++)
258			*cp++ = '\0';
259		mbuf_setlen(mp, mbuf_len(mp) + rem);
260		nd->nd_bpos = cp;
261	} else
262		nd->nd_bpos = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
263	nd->nd_mb = mp;
264}
265#endif	/* !APPLE */
266
267/*
268 * Load vnode attributes from the xdr file attributes.
269 * Returns EBADRPC if they can't be parsed, 0 otherwise.
270 */
271APPLESTATIC int
272nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
273{
274	struct nfs_fattr *fp;
275	int error = 0;
276
277	if (nd->nd_flag & ND_NFSV4) {
278		error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
279		    NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
280	} else if (nd->nd_flag & ND_NFSV3) {
281		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
282		nap->na_type = nfsv34tov_type(fp->fa_type);
283		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
284		nap->na_rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
285			fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
286		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
287		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
288		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
289		nap->na_size = fxdr_hyper(&fp->fa3_size);
290		nap->na_blocksize = NFS_FABLKSIZE;
291		nap->na_bytes = fxdr_hyper(&fp->fa3_used);
292		nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
293		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
294		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
295		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
296		nap->na_flags = 0;
297		nap->na_filerev = 0;
298	} else {
299		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
300		nap->na_type = nfsv2tov_type(fp->fa_type);
301		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
302		if (nap->na_type == VNON || nap->na_type == VREG)
303			nap->na_type = IFTOVT(nap->na_mode);
304		nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
305
306		/*
307		 * Really ugly NFSv2 kludge.
308		 */
309		if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
310			nap->na_type = VFIFO;
311		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
312		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
313		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
314		nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
315		nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
316		nap->na_bytes =
317		    (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
318		    NFS_FABLKSIZE;
319		nap->na_fileid = fxdr_unsigned(uint64_t, fp->fa2_fileid);
320		fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
321		fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
322		nap->na_flags = 0;
323		nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
324		    fp->fa2_ctime.nfsv2_sec);
325		nap->na_ctime.tv_nsec = 0;
326		nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
327		nap->na_filerev = 0;
328	}
329nfsmout:
330	return (error);
331}
332
333/*
334 * This function finds the directory cookie that corresponds to the
335 * logical byte offset given.
336 */
337APPLESTATIC nfsuint64 *
338nfscl_getcookie(struct nfsnode *np, off_t off, int add)
339{
340	struct nfsdmap *dp, *dp2;
341	int pos;
342
343	pos = off / NFS_DIRBLKSIZ;
344	if (pos == 0) {
345		KASSERT(!add, ("nfs getcookie add at 0"));
346		return (&nfs_nullcookie);
347	}
348	pos--;
349	dp = LIST_FIRST(&np->n_cookies);
350	if (!dp) {
351		if (add) {
352			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
353				M_NFSDIROFF, M_WAITOK);
354			dp->ndm_eocookie = 0;
355			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
356		} else
357			return (NULL);
358	}
359	while (pos >= NFSNUMCOOKIES) {
360		pos -= NFSNUMCOOKIES;
361		if (LIST_NEXT(dp, ndm_list) != NULL) {
362			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
363				pos >= dp->ndm_eocookie)
364				return (NULL);
365			dp = LIST_NEXT(dp, ndm_list);
366		} else if (add) {
367			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
368				M_NFSDIROFF, M_WAITOK);
369			dp2->ndm_eocookie = 0;
370			LIST_INSERT_AFTER(dp, dp2, ndm_list);
371			dp = dp2;
372		} else
373			return (NULL);
374	}
375	if (pos >= dp->ndm_eocookie) {
376		if (add)
377			dp->ndm_eocookie = pos + 1;
378		else
379			return (NULL);
380	}
381	return (&dp->ndm_cookies[pos]);
382}
383
384/*
385 * Gets a file handle out of an nfs reply sent to the client and returns
386 * the file handle and the file's attributes.
387 * For V4, it assumes that Getfh and Getattr Op's results are here.
388 */
389APPLESTATIC int
390nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
391    struct nfsvattr *nap, int *attrflagp)
392{
393	u_int32_t *tl;
394	int error = 0, flag = 1;
395
396	*nfhpp = NULL;
397	*attrflagp = 0;
398	/*
399	 * First get the file handle and vnode.
400	 */
401	if (nd->nd_flag & ND_NFSV3) {
402		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
403		flag = fxdr_unsigned(int, *tl);
404	} else if (nd->nd_flag & ND_NFSV4) {
405		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
406	}
407	if (flag) {
408		error = nfsm_getfh(nd, nfhpp);
409		if (error)
410			return (error);
411	}
412
413	/*
414	 * Now, get the attributes.
415	 */
416	if (nd->nd_flag & ND_NFSV4) {
417		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
418	} else if (nd->nd_flag & ND_NFSV3) {
419		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
420		if (flag) {
421			flag = fxdr_unsigned(int, *tl);
422		} else if (fxdr_unsigned(int, *tl)) {
423			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
424			if (error)
425				return (error);
426		}
427	}
428	if (flag) {
429		error = nfsm_loadattr(nd, nap);
430		if (!error)
431			*attrflagp = 1;
432	}
433nfsmout:
434	return (error);
435}
436
437/*
438 * Put a state Id in the mbuf list.
439 */
440APPLESTATIC void
441nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
442{
443	nfsv4stateid_t *st;
444
445	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
446	if (flag == NFSSTATEID_PUTALLZERO) {
447		st->seqid = 0;
448		st->other[0] = 0;
449		st->other[1] = 0;
450		st->other[2] = 0;
451	} else if (flag == NFSSTATEID_PUTALLONE) {
452		st->seqid = 0xffffffff;
453		st->other[0] = 0xffffffff;
454		st->other[1] = 0xffffffff;
455		st->other[2] = 0xffffffff;
456	} else {
457		st->seqid = stateidp->seqid;
458		st->other[0] = stateidp->other[0];
459		st->other[1] = stateidp->other[1];
460		st->other[2] = stateidp->other[2];
461	}
462}
463
464/*
465 * Initialize the owner/delegation sleep lock.
466 */
467APPLESTATIC void
468nfscl_lockinit(struct nfsv4lock *lckp)
469{
470
471	lckp->nfslock_usecnt = 0;
472	lckp->nfslock_lock = 0;
473}
474
475/*
476 * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
477 * thread for each posix process in the kernel.)
478 */
479APPLESTATIC void
480nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
481{
482	int igotlock;
483
484	do {
485		igotlock = nfsv4_lock(lckp, 1, NULL, mutex);
486	} while (!igotlock);
487}
488
489/*
490 * Release an exclusive lock.
491 */
492APPLESTATIC void
493nfscl_lockunlock(struct nfsv4lock *lckp)
494{
495
496	nfsv4_unlock(lckp, 0);
497}
498
499/*
500 * Called to derefernce a lock on a stateid (delegation or open owner).
501 */
502APPLESTATIC void
503nfscl_lockderef(struct nfsv4lock *lckp)
504{
505
506	NFSLOCKCLSTATE();
507	lckp->nfslock_usecnt--;
508	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
509		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
510		wakeup((caddr_t)lckp);
511	}
512	NFSUNLOCKCLSTATE();
513}
514
515