xref: /freebsd/sys/fs/nfsclient/nfs_clcomsubs.c (revision 7aa383846770374466b1dcb2cefd71bde9acf463)
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$");
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 
45 extern struct nfsstats newnfsstats;
46 extern struct nfsv4_opflag nfsv4_opflag[NFSV4OP_NOPS];
47 extern int ncl_mbuf_mlen;
48 extern enum vtype newnv2tov_type[8];
49 extern enum vtype nv34tov_type[8];
50 NFSCLSTATEMUTEX;
51 #endif	/* !APPLEKEXT */
52 
53 static nfsuint64 nfs_nullcookie = {{ 0, 0 }};
54 static 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  */
107 static 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  */
116 APPLESTATIC void
117 nfscl_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  */
188 APPLESTATIC void
189 nfsm_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  */
271 APPLESTATIC int
272 nfsm_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_unsigned(int32_t,
293 		    fp->fa3_fileid.nfsuquad[1]);
294 		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
295 		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
296 		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
297 		nap->na_flags = 0;
298 		nap->na_filerev = 0;
299 	} else {
300 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
301 		nap->na_type = nfsv2tov_type(fp->fa_type);
302 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
303 		if (nap->na_type == VNON || nap->na_type == VREG)
304 			nap->na_type = IFTOVT(nap->na_mode);
305 		nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
306 
307 		/*
308 		 * Really ugly NFSv2 kludge.
309 		 */
310 		if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
311 			nap->na_type = VFIFO;
312 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
313 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
314 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
315 		nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
316 		nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
317 		nap->na_bytes =
318 		    (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
319 		    NFS_FABLKSIZE;
320 		nap->na_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid);
321 		fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
322 		fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
323 		nap->na_flags = 0;
324 		nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
325 		    fp->fa2_ctime.nfsv2_sec);
326 		nap->na_ctime.tv_nsec = 0;
327 		nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
328 		nap->na_filerev = 0;
329 	}
330 nfsmout:
331 	return (error);
332 }
333 
334 /*
335  * This function finds the directory cookie that corresponds to the
336  * logical byte offset given.
337  */
338 APPLESTATIC nfsuint64 *
339 nfscl_getcookie(struct nfsnode *np, off_t off, int add)
340 {
341 	struct nfsdmap *dp, *dp2;
342 	int pos;
343 
344 	pos = off / NFS_DIRBLKSIZ;
345 	if (pos == 0) {
346 		KASSERT(!add, ("nfs getcookie add at 0"));
347 		return (&nfs_nullcookie);
348 	}
349 	pos--;
350 	dp = LIST_FIRST(&np->n_cookies);
351 	if (!dp) {
352 		if (add) {
353 			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
354 				M_NFSDIROFF, M_WAITOK);
355 			dp->ndm_eocookie = 0;
356 			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
357 		} else
358 			return (NULL);
359 	}
360 	while (pos >= NFSNUMCOOKIES) {
361 		pos -= NFSNUMCOOKIES;
362 		if (LIST_NEXT(dp, ndm_list) != NULL) {
363 			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
364 				pos >= dp->ndm_eocookie)
365 				return (NULL);
366 			dp = LIST_NEXT(dp, ndm_list);
367 		} else if (add) {
368 			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
369 				M_NFSDIROFF, M_WAITOK);
370 			dp2->ndm_eocookie = 0;
371 			LIST_INSERT_AFTER(dp, dp2, ndm_list);
372 			dp = dp2;
373 		} else
374 			return (NULL);
375 	}
376 	if (pos >= dp->ndm_eocookie) {
377 		if (add)
378 			dp->ndm_eocookie = pos + 1;
379 		else
380 			return (NULL);
381 	}
382 	return (&dp->ndm_cookies[pos]);
383 }
384 
385 /*
386  * Gets a file handle out of an nfs reply sent to the client and returns
387  * the file handle and the file's attributes.
388  * For V4, it assumes that Getfh and Getattr Op's results are here.
389  */
390 APPLESTATIC int
391 nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
392     struct nfsvattr *nap, int *attrflagp)
393 {
394 	u_int32_t *tl;
395 	int error = 0, flag = 1;
396 
397 	*nfhpp = NULL;
398 	*attrflagp = 0;
399 	/*
400 	 * First get the file handle and vnode.
401 	 */
402 	if (nd->nd_flag & ND_NFSV3) {
403 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
404 		flag = fxdr_unsigned(int, *tl);
405 	} else if (nd->nd_flag & ND_NFSV4) {
406 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
407 	}
408 	if (flag) {
409 		error = nfsm_getfh(nd, nfhpp);
410 		if (error)
411 			return (error);
412 	}
413 
414 	/*
415 	 * Now, get the attributes.
416 	 */
417 	if (nd->nd_flag & ND_NFSV4) {
418 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
419 	} else if (nd->nd_flag & ND_NFSV3) {
420 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
421 		if (flag) {
422 			flag = fxdr_unsigned(int, *tl);
423 		} else if (fxdr_unsigned(int, *tl)) {
424 			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
425 			if (error)
426 				return (error);
427 		}
428 	}
429 	if (flag) {
430 		error = nfsm_loadattr(nd, nap);
431 		if (!error)
432 			*attrflagp = 1;
433 	}
434 nfsmout:
435 	return (error);
436 }
437 
438 /*
439  * Put a state Id in the mbuf list.
440  */
441 APPLESTATIC void
442 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
443 {
444 	nfsv4stateid_t *st;
445 
446 	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
447 	if (flag == NFSSTATEID_PUTALLZERO) {
448 		st->seqid = 0;
449 		st->other[0] = 0;
450 		st->other[1] = 0;
451 		st->other[2] = 0;
452 	} else if (flag == NFSSTATEID_PUTALLONE) {
453 		st->seqid = 0xffffffff;
454 		st->other[0] = 0xffffffff;
455 		st->other[1] = 0xffffffff;
456 		st->other[2] = 0xffffffff;
457 	} else {
458 		st->seqid = stateidp->seqid;
459 		st->other[0] = stateidp->other[0];
460 		st->other[1] = stateidp->other[1];
461 		st->other[2] = stateidp->other[2];
462 	}
463 }
464 
465 /*
466  * Initialize the owner/delegation sleep lock.
467  */
468 APPLESTATIC void
469 nfscl_lockinit(struct nfsv4lock *lckp)
470 {
471 
472 	lckp->nfslock_usecnt = 0;
473 	lckp->nfslock_lock = 0;
474 }
475 
476 /*
477  * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
478  * thread for each posix process in the kernel.)
479  */
480 APPLESTATIC void
481 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
482 {
483 	int igotlock;
484 
485 	do {
486 		igotlock = nfsv4_lock(lckp, 1, NULL, mutex);
487 	} while (!igotlock);
488 }
489 
490 /*
491  * Release an exclusive lock.
492  */
493 APPLESTATIC void
494 nfscl_lockunlock(struct nfsv4lock *lckp)
495 {
496 
497 	nfsv4_unlock(lckp, 0);
498 }
499 
500 /*
501  * Called to derefernce a lock on a stateid (delegation or open owner).
502  */
503 APPLESTATIC void
504 nfscl_lockderef(struct nfsv4lock *lckp)
505 {
506 
507 	NFSLOCKCLSTATE();
508 	lckp->nfslock_usecnt--;
509 	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
510 		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
511 		wakeup((caddr_t)lckp);
512 	}
513 	NFSUNLOCKCLSTATE();
514 }
515 
516