xref: /freebsd/sys/fs/nfsclient/nfs_clcomsubs.c (revision aa77200569e397d6ff1fdb4d255d0fa254d0a128)
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 | ND_NFSCL;
130 	else if (NFSHASNFSV3(nmp))
131 		nd->nd_flag = ND_NFSV3 | ND_NFSCL;
132 	else
133 		nd->nd_flag = ND_NFSV2 | ND_NFSCL;
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_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 	}
329 nfsmout:
330 	return (error);
331 }
332 
333 /*
334  * This function finds the directory cookie that corresponds to the
335  * logical byte offset given.
336  */
337 APPLESTATIC nfsuint64 *
338 nfscl_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  */
389 APPLESTATIC int
390 nfscl_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 	}
433 nfsmout:
434 	return (error);
435 }
436 
437 /*
438  * Put a state Id in the mbuf list.
439  */
440 APPLESTATIC void
441 nfsm_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  */
467 APPLESTATIC void
468 nfscl_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  */
479 APPLESTATIC void
480 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
481 {
482 	int igotlock;
483 
484 	do {
485 		igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
486 	} while (!igotlock);
487 }
488 
489 /*
490  * Release an exclusive lock.
491  */
492 APPLESTATIC void
493 nfscl_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  */
502 APPLESTATIC void
503 nfscl_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