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 * 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.
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 nfsstats newnfsstats;
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, },
118 * NFS RPCS that have large request message size.
120 static int nfs_bigrequest[NFSV41_NPROCS] = {
121 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 1, 0, 0
127 * Start building a request. Mostly just put the first file handle in
131 nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
132 u_int8_t *nfhp, int fhlen, u_int32_t **opcntpp, struct nfsclsession *sep)
137 nfsattrbit_t attrbits;
140 * First, fill in some of the fields of nd.
142 nd->nd_slotseq = NULL;
143 if (NFSHASNFSV4(nmp)) {
144 nd->nd_flag = ND_NFSV4 | ND_NFSCL;
145 if (NFSHASNFSV4N(nmp))
146 nd->nd_flag |= ND_NFSV41;
147 } else if (NFSHASNFSV3(nmp))
148 nd->nd_flag = ND_NFSV3 | ND_NFSCL;
150 nd->nd_flag = ND_NFSV2 | ND_NFSCL;
151 nd->nd_procnum = procnum;
155 * Get the first mbuf for the request.
157 if (nfs_bigrequest[procnum])
158 NFSMCLGET(mb, M_WAITOK);
162 nd->nd_mreq = nd->nd_mb = mb;
163 nd->nd_bpos = NFSMTOD(mb, caddr_t);
166 * And fill the first file handle into the request.
168 if (nd->nd_flag & ND_NFSV4) {
169 opcnt = nfsv4_opmap[procnum].opcnt +
170 nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh;
171 if ((nd->nd_flag & ND_NFSV41) != 0) {
172 opcnt += nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq;
173 if (procnum == NFSPROC_RENEW)
175 * For the special case of Renew, just do a
179 else if (procnum == NFSPROC_WRITEDS ||
180 procnum == NFSPROC_COMMITDS)
182 * For the special case of a Writeor Commit to
183 * a DS, the opcnt == 3, for Sequence, PutFH,
189 * What should the tag really be?
191 (void) nfsm_strtom(nd, nfsv4_opmap[procnum].tag,
192 nfsv4_opmap[procnum].taglen);
193 NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
194 if ((nd->nd_flag & ND_NFSV41) != 0)
195 *tl++ = txdr_unsigned(NFSV41_MINORVERSION);
197 *tl++ = txdr_unsigned(NFSV4_MINORVERSION);
200 *tl = txdr_unsigned(opcnt);
201 if ((nd->nd_flag & ND_NFSV41) != 0 &&
202 nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq > 0) {
203 if (nfsv4_opflag[nfsv4_opmap[procnum].op].loopbadsess >
205 nd->nd_flag |= ND_LOOPBADSESS;
206 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
207 *tl = txdr_unsigned(NFSV4OP_SEQUENCE);
209 sep = nfsmnt_mdssession(nmp);
210 nfsv4_setsequence(nmp, nd, sep,
211 nfs_bigreply[procnum]);
213 nfsv4_setsequence(nmp, nd, sep,
214 nfs_bigreply[procnum]);
216 if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
217 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
218 *tl = txdr_unsigned(NFSV4OP_PUTFH);
219 (void) nfsm_fhtom(nd, nfhp, fhlen, 0);
220 if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh
221 == 2 && procnum != NFSPROC_WRITEDS &&
222 procnum != NFSPROC_COMMITDS) {
223 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
224 *tl = txdr_unsigned(NFSV4OP_GETATTR);
226 * For Lookup Ops, we want all the directory
227 * attributes, so we can load the name cache.
229 if (procnum == NFSPROC_LOOKUP ||
230 procnum == NFSPROC_LOOKUPP)
231 NFSGETATTR_ATTRBIT(&attrbits);
233 NFSWCCATTR_ATTRBIT(&attrbits);
234 nd->nd_flag |= ND_V4WCCATTR;
236 (void) nfsrv_putattrbit(nd, &attrbits);
239 if (procnum != NFSPROC_RENEW ||
240 (nd->nd_flag & ND_NFSV41) == 0) {
241 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
242 *tl = txdr_unsigned(nfsv4_opmap[procnum].op);
245 (void) nfsm_fhtom(nd, nfhp, fhlen, 0);
247 if (procnum < NFSV4_NPROCS)
248 NFSINCRGLOBAL(newnfsstats.rpccnt[procnum]);
253 * copies a uio scatter/gather list to an mbuf chain.
254 * NOTE: can ony handle iovcnt == 1
257 nfsm_uiombuf(struct nfsrv_descript *nd, struct uio *uiop, int siz)
260 struct mbuf *mp, *mp2;
261 int xfer, left, mlen;
262 int uiosiz, clflg, rem;
265 KASSERT(uiop->uio_iovcnt == 1, ("nfsm_uiotombuf: iovcnt != 1"));
267 if (siz > ncl_mbuf_mlen) /* or should it >= MCLBYTES ?? */
271 rem = NFSM_RNDUP(siz) - siz;
272 mp = mp2 = nd->nd_mb;
274 left = uiop->uio_iov->iov_len;
275 uiocp = uiop->uio_iov->iov_base;
280 mlen = M_TRAILINGSPACE(mp);
283 NFSMCLGET(mp, M_WAITOK);
287 mbuf_setnext(mp2, mp);
289 mlen = M_TRAILINGSPACE(mp);
291 xfer = (left > mlen) ? mlen : left;
294 if (uiop->uio_iov->iov_op != NULL)
295 (*(uiop->uio_iov->iov_op))
296 (uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
300 if (uiop->uio_segflg == UIO_SYSSPACE)
301 NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
304 copyin(CAST_USER_ADDR_T(uiocp), NFSMTOD(mp, caddr_t)
305 + mbuf_len(mp), xfer);
306 mbuf_setlen(mp, mbuf_len(mp) + xfer);
309 uiop->uio_offset += xfer;
310 uiop->uio_resid -= xfer;
312 tcp = (char *)uiop->uio_iov->iov_base;
314 uiop->uio_iov->iov_base = (void *)tcp;
315 uiop->uio_iov->iov_len -= uiosiz;
319 if (rem > M_TRAILINGSPACE(mp)) {
322 mbuf_setnext(mp2, mp);
324 cp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
325 for (left = 0; left < rem; left++)
327 mbuf_setlen(mp, mbuf_len(mp) + rem);
330 nd->nd_bpos = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
336 * Load vnode attributes from the xdr file attributes.
337 * Returns EBADRPC if they can't be parsed, 0 otherwise.
340 nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
342 struct nfs_fattr *fp;
345 if (nd->nd_flag & ND_NFSV4) {
346 error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
347 NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
348 } else if (nd->nd_flag & ND_NFSV3) {
349 NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
350 nap->na_type = nfsv34tov_type(fp->fa_type);
351 nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
352 nap->na_rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
353 fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
354 nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
355 nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
356 nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
357 nap->na_size = fxdr_hyper(&fp->fa3_size);
358 nap->na_blocksize = NFS_FABLKSIZE;
359 nap->na_bytes = fxdr_hyper(&fp->fa3_used);
360 nap->na_fileid = fxdr_hyper(&fp->fa3_fileid);
361 fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
362 fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
363 fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
367 NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
368 nap->na_type = nfsv2tov_type(fp->fa_type);
369 nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
370 if (nap->na_type == VNON || nap->na_type == VREG)
371 nap->na_type = IFTOVT(nap->na_mode);
372 nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
375 * Really ugly NFSv2 kludge.
377 if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
378 nap->na_type = VFIFO;
379 nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
380 nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
381 nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
382 nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
383 nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
385 (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
387 nap->na_fileid = fxdr_unsigned(uint64_t, fp->fa2_fileid);
388 fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
389 fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
391 nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
392 fp->fa2_ctime.nfsv2_sec);
393 nap->na_ctime.tv_nsec = 0;
394 nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
402 * This function finds the directory cookie that corresponds to the
403 * logical byte offset given.
405 APPLESTATIC nfsuint64 *
406 nfscl_getcookie(struct nfsnode *np, off_t off, int add)
408 struct nfsdmap *dp, *dp2;
411 pos = off / NFS_DIRBLKSIZ;
413 KASSERT(!add, ("nfs getcookie add at 0"));
414 return (&nfs_nullcookie);
417 dp = LIST_FIRST(&np->n_cookies);
420 MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
421 M_NFSDIROFF, M_WAITOK);
422 dp->ndm_eocookie = 0;
423 LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
427 while (pos >= NFSNUMCOOKIES) {
428 pos -= NFSNUMCOOKIES;
429 if (LIST_NEXT(dp, ndm_list) != NULL) {
430 if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
431 pos >= dp->ndm_eocookie)
433 dp = LIST_NEXT(dp, ndm_list);
435 MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
436 M_NFSDIROFF, M_WAITOK);
437 dp2->ndm_eocookie = 0;
438 LIST_INSERT_AFTER(dp, dp2, ndm_list);
443 if (pos >= dp->ndm_eocookie) {
445 dp->ndm_eocookie = pos + 1;
449 return (&dp->ndm_cookies[pos]);
453 * Gets a file handle out of an nfs reply sent to the client and returns
454 * the file handle and the file's attributes.
455 * For V4, it assumes that Getfh and Getattr Op's results are here.
458 nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
459 struct nfsvattr *nap, int *attrflagp)
462 int error = 0, flag = 1;
467 * First get the file handle and vnode.
469 if (nd->nd_flag & ND_NFSV3) {
470 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
471 flag = fxdr_unsigned(int, *tl);
472 } else if (nd->nd_flag & ND_NFSV4) {
473 NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
474 /* If the GetFH failed, clear flag. */
476 nd->nd_flag |= ND_NOMOREDATA;
478 error = ENXIO; /* Return ENXIO so *nfhpp isn't used. */
482 error = nfsm_getfh(nd, nfhpp);
488 * Now, get the attributes.
490 if (flag != 0 && (nd->nd_flag & ND_NFSV4) != 0) {
491 NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
493 nd->nd_flag |= ND_NOMOREDATA;
496 } else if (nd->nd_flag & ND_NFSV3) {
497 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
499 flag = fxdr_unsigned(int, *tl);
500 } else if (fxdr_unsigned(int, *tl)) {
501 error = nfsm_advance(nd, NFSX_V3FATTR, -1);
507 error = nfsm_loadattr(nd, nap);
516 * Put a state Id in the mbuf list.
519 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
523 NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
524 if (flag == NFSSTATEID_PUTALLZERO) {
529 } else if (flag == NFSSTATEID_PUTALLONE) {
530 st->seqid = 0xffffffff;
531 st->other[0] = 0xffffffff;
532 st->other[1] = 0xffffffff;
533 st->other[2] = 0xffffffff;
534 } else if (flag == NFSSTATEID_PUTSEQIDZERO) {
536 st->other[0] = stateidp->other[0];
537 st->other[1] = stateidp->other[1];
538 st->other[2] = stateidp->other[2];
540 st->seqid = stateidp->seqid;
541 st->other[0] = stateidp->other[0];
542 st->other[1] = stateidp->other[1];
543 st->other[2] = stateidp->other[2];
548 * Initialize the owner/delegation sleep lock.
551 nfscl_lockinit(struct nfsv4lock *lckp)
554 lckp->nfslock_usecnt = 0;
555 lckp->nfslock_lock = 0;
559 * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
560 * thread for each posix process in the kernel.)
563 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
568 igotlock = nfsv4_lock(lckp, 1, NULL, mutex, NULL);
573 * Release an exclusive lock.
576 nfscl_lockunlock(struct nfsv4lock *lckp)
579 nfsv4_unlock(lckp, 0);
583 * Called to derefernce a lock on a stateid (delegation or open owner).
586 nfscl_lockderef(struct nfsv4lock *lckp)
590 lckp->nfslock_usecnt--;
591 if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
592 lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
593 wakeup((caddr_t)lckp);