2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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 * 3. 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.
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
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
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.
43 #include <fs/nfs/nfsport.h>
45 extern struct nfsstatsv1 nfsstatsv1;
46 extern struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS];
47 extern int ncl_mbuf_mlen;
48 extern enum vtype newnv2tov_type[8];
49 extern enum vtype nv34tov_type[8];
50 extern int nfs_bigreply[NFSV41_NPROCS];
52 #endif /* !APPLEKEXT */
54 static nfsuint64 nfs_nullcookie = {{ 0, 0 }};
60 } nfsv4_opmap[NFSV41_NPROCS] = {
62 { NFSV4OP_GETATTR, 1, "Getattr", 7, },
63 { NFSV4OP_SETATTR, 2, "Setattr", 7, },
64 { NFSV4OP_LOOKUP, 3, "Lookup", 6, },
65 { NFSV4OP_ACCESS, 2, "Access", 6, },
66 { NFSV4OP_READLINK, 2, "Readlink", 8, },
67 { NFSV4OP_READ, 1, "Read", 4, },
68 { NFSV4OP_WRITE, 2, "Write", 5, },
69 { NFSV4OP_OPEN, 5, "Open", 4, },
70 { NFSV4OP_CREATE, 5, "Create", 6, },
71 { NFSV4OP_CREATE, 1, "Create", 6, },
72 { NFSV4OP_CREATE, 3, "Create", 6, },
73 { NFSV4OP_REMOVE, 1, "Remove", 6, },
74 { NFSV4OP_REMOVE, 1, "Remove", 6, },
75 { NFSV4OP_SAVEFH, 5, "Rename", 6, },
76 { NFSV4OP_SAVEFH, 4, "Link", 4, },
77 { NFSV4OP_READDIR, 2, "Readdir", 7, },
78 { NFSV4OP_READDIR, 2, "Readdir", 7, },
79 { NFSV4OP_GETATTR, 1, "Getattr", 7, },
80 { NFSV4OP_GETATTR, 1, "Getattr", 7, },
81 { NFSV4OP_GETATTR, 1, "Getattr", 7, },
82 { NFSV4OP_COMMIT, 2, "Commit", 6, },
83 { NFSV4OP_LOOKUPP, 3, "Lookupp", 7, },
84 { NFSV4OP_SETCLIENTID, 1, "SetClientID", 11, },
85 { NFSV4OP_SETCLIENTIDCFRM, 1, "SetClientIDConfirm", 18, },
86 { NFSV4OP_LOCK, 1, "Lock", 4, },
87 { NFSV4OP_LOCKU, 1, "LockU", 5, },
88 { NFSV4OP_OPEN, 2, "Open", 4, },
89 { NFSV4OP_CLOSE, 1, "Close", 5, },
90 { NFSV4OP_OPENCONFIRM, 1, "Openconfirm", 11, },
91 { NFSV4OP_LOCKT, 1, "LockT", 5, },
92 { NFSV4OP_OPENDOWNGRADE, 1, "Opendowngrade", 13, },
93 { NFSV4OP_RENEW, 1, "Renew", 5, },
94 { NFSV4OP_PUTROOTFH, 1, "Dirpath", 7, },
95 { NFSV4OP_RELEASELCKOWN, 1, "Rellckown", 9, },
96 { NFSV4OP_DELEGRETURN, 1, "Delegret", 8, },
97 { NFSV4OP_DELEGRETURN, 3, "DelegRemove", 11, },
98 { NFSV4OP_DELEGRETURN, 7, "DelegRename1", 12, },
99 { NFSV4OP_DELEGRETURN, 9, "DelegRename2", 12, },
100 { NFSV4OP_GETATTR, 1, "Getacl", 6, },
101 { NFSV4OP_SETATTR, 1, "Setacl", 6, },
102 { NFSV4OP_EXCHANGEID, 1, "ExchangeID", 10, },
103 { NFSV4OP_CREATESESSION, 1, "CreateSession", 13, },
104 { NFSV4OP_DESTROYSESSION, 1, "DestroySession", 14, },
105 { NFSV4OP_DESTROYCLIENTID, 1, "DestroyClient", 13, },
106 { NFSV4OP_FREESTATEID, 1, "FreeStateID", 11, },
107 { NFSV4OP_LAYOUTGET, 1, "LayoutGet", 9, },
108 { NFSV4OP_GETDEVINFO, 1, "GetDeviceInfo", 13, },
109 { NFSV4OP_LAYOUTCOMMIT, 1, "LayoutCommit", 12, },
110 { NFSV4OP_LAYOUTRETURN, 1, "LayoutReturn", 12, },
111 { NFSV4OP_RECLAIMCOMPL, 1, "ReclaimComplete", 15, },
112 { NFSV4OP_WRITE, 1, "WriteDS", 7, },
113 { NFSV4OP_READ, 1, "ReadDS", 6, },
114 { NFSV4OP_COMMIT, 1, "CommitDS", 8, },
115 { NFSV4OP_OPEN, 3, "OpenLayoutGet", 13, },
116 { NFSV4OP_OPEN, 8, "CreateLayGet", 12, },
120 * NFS RPCS that have large request message size.
122 static int nfs_bigrequest[NFSV41_NPROCS] = {
123 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0
129 * Start building a request. Mostly just put the first file handle in
133 nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
134 u_int8_t *nfhp, int fhlen, u_int32_t **opcntpp, struct nfsclsession *sep)
139 nfsattrbit_t attrbits;
142 * First, fill in some of the fields of nd.
144 nd->nd_slotseq = NULL;
145 if (NFSHASNFSV4(nmp)) {
146 nd->nd_flag = ND_NFSV4 | ND_NFSCL;
147 if (NFSHASNFSV4N(nmp))
148 nd->nd_flag |= ND_NFSV41;
149 } else if (NFSHASNFSV3(nmp))
150 nd->nd_flag = ND_NFSV3 | ND_NFSCL;
152 nd->nd_flag = ND_NFSV2 | ND_NFSCL;
153 nd->nd_procnum = procnum;
157 * Get the first mbuf for the request.
159 if (nfs_bigrequest[procnum])
160 NFSMCLGET(mb, M_WAITOK);
164 nd->nd_mreq = nd->nd_mb = mb;
165 nd->nd_bpos = NFSMTOD(mb, caddr_t);
168 * And fill the first file handle into the request.
170 if (nd->nd_flag & ND_NFSV4) {
171 opcnt = nfsv4_opmap[procnum].opcnt +
172 nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh;
173 if ((nd->nd_flag & ND_NFSV41) != 0) {
174 opcnt += nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq;
175 if (procnum == NFSPROC_RENEW)
177 * For the special case of Renew, just do a
181 else if (procnum == NFSPROC_WRITEDS ||
182 procnum == NFSPROC_COMMITDS)
184 * For the special case of a Writeor Commit to
185 * a DS, the opcnt == 3, for Sequence, PutFH,
191 * What should the tag really be?
193 (void) nfsm_strtom(nd, nfsv4_opmap[procnum].tag,
194 nfsv4_opmap[procnum].taglen);
195 NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
196 if ((nd->nd_flag & ND_NFSV41) != 0)
197 *tl++ = txdr_unsigned(NFSV41_MINORVERSION);
199 *tl++ = txdr_unsigned(NFSV4_MINORVERSION);
202 *tl = txdr_unsigned(opcnt);
203 if ((nd->nd_flag & ND_NFSV41) != 0 &&
204 nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq > 0) {
205 if (nfsv4_opflag[nfsv4_opmap[procnum].op].loopbadsess >
207 nd->nd_flag |= ND_LOOPBADSESS;
208 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
209 *tl = txdr_unsigned(NFSV4OP_SEQUENCE);
211 sep = nfsmnt_mdssession(nmp);
212 nfsv4_setsequence(nmp, nd, sep,
213 nfs_bigreply[procnum]);
215 nfsv4_setsequence(nmp, nd, sep,
216 nfs_bigreply[procnum]);
218 if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
219 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
220 *tl = txdr_unsigned(NFSV4OP_PUTFH);
221 (void) nfsm_fhtom(nd, nfhp, fhlen, 0);
222 if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh
223 == 2 && procnum != NFSPROC_WRITEDS &&
224 procnum != NFSPROC_COMMITDS) {
225 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
226 *tl = txdr_unsigned(NFSV4OP_GETATTR);
228 * For Lookup Ops, we want all the directory
229 * attributes, so we can load the name cache.
231 if (procnum == NFSPROC_LOOKUP ||
232 procnum == NFSPROC_LOOKUPP)
233 NFSGETATTR_ATTRBIT(&attrbits);
235 NFSWCCATTR_ATTRBIT(&attrbits);
236 nd->nd_flag |= ND_V4WCCATTR;
238 (void) nfsrv_putattrbit(nd, &attrbits);
241 if (procnum != NFSPROC_RENEW ||
242 (nd->nd_flag & ND_NFSV41) == 0) {
243 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
244 *tl = txdr_unsigned(nfsv4_opmap[procnum].op);
247 (void) nfsm_fhtom(nd, nfhp, fhlen, 0);
249 if (procnum < NFSV41_NPROCS)
250 NFSINCRGLOBAL(nfsstatsv1.rpccnt[procnum]);
255 * copies a uio scatter/gather list to an mbuf chain.
256 * NOTE: can ony handle iovcnt == 1
259 nfsm_uiombuf(struct nfsrv_descript *nd, struct uio *uiop, int siz)
262 struct mbuf *mp, *mp2;
263 int xfer, left, mlen;
264 int uiosiz, clflg, rem;
267 KASSERT(uiop->uio_iovcnt == 1, ("nfsm_uiotombuf: iovcnt != 1"));
269 if (siz > ncl_mbuf_mlen) /* or should it >= MCLBYTES ?? */
273 rem = NFSM_RNDUP(siz) - siz;
274 mp = mp2 = nd->nd_mb;
276 left = uiop->uio_iov->iov_len;
277 uiocp = uiop->uio_iov->iov_base;
282 mlen = M_TRAILINGSPACE(mp);
285 NFSMCLGET(mp, M_WAITOK);
289 mbuf_setnext(mp2, mp);
291 mlen = M_TRAILINGSPACE(mp);
293 xfer = (left > mlen) ? mlen : left;
296 if (uiop->uio_iov->iov_op != NULL)
297 (*(uiop->uio_iov->iov_op))
298 (uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
302 if (uiop->uio_segflg == UIO_SYSSPACE)
303 NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
306 copyin(CAST_USER_ADDR_T(uiocp), NFSMTOD(mp, caddr_t)
307 + mbuf_len(mp), xfer);
308 mbuf_setlen(mp, mbuf_len(mp) + xfer);
311 uiop->uio_offset += xfer;
312 uiop->uio_resid -= xfer;
314 tcp = (char *)uiop->uio_iov->iov_base;
316 uiop->uio_iov->iov_base = (void *)tcp;
317 uiop->uio_iov->iov_len -= uiosiz;
321 if (rem > M_TRAILINGSPACE(mp)) {
324 mbuf_setnext(mp2, mp);
326 cp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
327 for (left = 0; left < rem; left++)
329 mbuf_setlen(mp, mbuf_len(mp) + rem);
332 nd->nd_bpos = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
338 * Load vnode attributes from the xdr file attributes.
339 * Returns EBADRPC if they can't be parsed, 0 otherwise.
342 nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
344 struct nfs_fattr *fp;
347 if (nd->nd_flag & ND_NFSV4) {
348 error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
349 NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
350 } else if (nd->nd_flag & ND_NFSV3) {
351 NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
352 nap->na_type = nfsv34tov_type(fp->fa_type);
353 nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
354 nap->na_rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
355 fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
356 nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
357 nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
358 nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
359 nap->na_size = fxdr_hyper(&fp->fa3_size);
360 nap->na_blocksize = NFS_FABLKSIZE;
361 nap->na_bytes = fxdr_hyper(&fp->fa3_used);
362 nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
363 fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
364 fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
365 fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
369 NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
370 nap->na_type = nfsv2tov_type(fp->fa_type);
371 nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
372 if (nap->na_type == VNON || nap->na_type == VREG)
373 nap->na_type = IFTOVT(nap->na_mode);
374 nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
377 * Really ugly NFSv2 kludge.
379 if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
380 nap->na_type = VFIFO;
381 nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
382 nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
383 nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
384 nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
385 nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
387 (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
389 nap->na_fileid = fxdr_unsigned(uint64_t, fp->fa2_fileid);
390 fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
391 fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
393 nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
394 fp->fa2_ctime.nfsv2_sec);
395 nap->na_ctime.tv_nsec = 0;
396 nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
404 * This function finds the directory cookie that corresponds to the
405 * logical byte offset given.
407 APPLESTATIC nfsuint64 *
408 nfscl_getcookie(struct nfsnode *np, off_t off, int add)
410 struct nfsdmap *dp, *dp2;
413 pos = off / NFS_DIRBLKSIZ;
415 KASSERT(!add, ("nfs getcookie add at 0"));
416 return (&nfs_nullcookie);
419 dp = LIST_FIRST(&np->n_cookies);
422 MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
423 M_NFSDIROFF, M_WAITOK);
424 dp->ndm_eocookie = 0;
425 LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
429 while (pos >= NFSNUMCOOKIES) {
430 pos -= NFSNUMCOOKIES;
431 if (LIST_NEXT(dp, ndm_list) != NULL) {
432 if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
433 pos >= dp->ndm_eocookie)
435 dp = LIST_NEXT(dp, ndm_list);
437 MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
438 M_NFSDIROFF, M_WAITOK);
439 dp2->ndm_eocookie = 0;
440 LIST_INSERT_AFTER(dp, dp2, ndm_list);
445 if (pos >= dp->ndm_eocookie) {
447 dp->ndm_eocookie = pos + 1;
451 return (&dp->ndm_cookies[pos]);
455 * Gets a file handle out of an nfs reply sent to the client and returns
456 * the file handle and the file's attributes.
457 * For V4, it assumes that Getfh and Getattr Op's results are here.
460 nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
461 struct nfsvattr *nap, int *attrflagp)
464 int error = 0, flag = 1;
469 * First get the file handle and vnode.
471 if (nd->nd_flag & ND_NFSV3) {
472 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
473 flag = fxdr_unsigned(int, *tl);
474 } else if (nd->nd_flag & ND_NFSV4) {
475 NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
476 /* If the GetFH failed, clear flag. */
478 nd->nd_flag |= ND_NOMOREDATA;
480 error = ENXIO; /* Return ENXIO so *nfhpp isn't used. */
484 error = nfsm_getfh(nd, nfhpp);
490 * Now, get the attributes.
492 if (flag != 0 && (nd->nd_flag & ND_NFSV4) != 0) {
493 NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
495 nd->nd_flag |= ND_NOMOREDATA;
498 } else if (nd->nd_flag & ND_NFSV3) {
499 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
501 flag = fxdr_unsigned(int, *tl);
502 } else if (fxdr_unsigned(int, *tl)) {
503 error = nfsm_advance(nd, NFSX_V3FATTR, -1);
509 error = nfsm_loadattr(nd, nap);
518 * Put a state Id in the mbuf list.
521 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
525 NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
526 if (flag == NFSSTATEID_PUTALLZERO) {
531 } else if (flag == NFSSTATEID_PUTALLONE) {
532 st->seqid = 0xffffffff;
533 st->other[0] = 0xffffffff;
534 st->other[1] = 0xffffffff;
535 st->other[2] = 0xffffffff;
536 } else if (flag == NFSSTATEID_PUTSEQIDZERO) {
538 st->other[0] = stateidp->other[0];
539 st->other[1] = stateidp->other[1];
540 st->other[2] = stateidp->other[2];
542 st->seqid = stateidp->seqid;
543 st->other[0] = stateidp->other[0];
544 st->other[1] = stateidp->other[1];
545 st->other[2] = stateidp->other[2];
550 * Initialize the owner/delegation sleep lock.
553 nfscl_lockinit(struct nfsv4lock *lckp)
556 lckp->nfslock_usecnt = 0;
557 lckp->nfslock_lock = 0;
561 * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
562 * thread for each posix process in the kernel.)
565 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
570 igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
575 * Release an exclusive lock.
578 nfscl_lockunlock(struct nfsv4lock *lckp)
581 nfsv4_unlock(lckp, 0);
585 * Called to derefernce a lock on a stateid (delegation or open owner).
588 nfscl_lockderef(struct nfsv4lock *lckp)
592 lckp->nfslock_usecnt--;
593 if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
594 lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
595 wakeup((caddr_t)lckp);