kernel - Fix endless nfs error 70 during shutdown
[dragonfly.git] / sys / vfs / nfs / nfsm_subs.c
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * Copyright (c) 1989, 1993
36  *      The Regents of the University of California.  All rights reserved.
37  *
38  * This code is derived from software contributed to Berkeley by
39  * Rick Macklem at The University of Guelph.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. All advertising materials mentioning features or use of this software
50  *    must display the following acknowledgement:
51  *      This product includes software developed by the University of
52  *      California, Berkeley and its contributors.
53  * 4. Neither the name of the University nor the names of its contributors
54  *    may be used to endorse or promote products derived from this software
55  *    without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
58  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
59  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
60  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
61  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
62  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
63  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
64  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
65  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
66  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
67  * SUCH DAMAGE.
68  */
69
70 /*
71  * These functions support the macros and help fiddle mbuf chains for
72  * the nfs op functions. They do things like create the rpc header and
73  * copy data between mbuf chains and uio lists.
74  */
75 #include <sys/param.h>
76 #include <sys/systm.h>
77 #include <sys/kernel.h>
78 #include <sys/buf.h>
79 #include <sys/proc.h>
80 #include <sys/mount.h>
81 #include <sys/vnode.h>
82 #include <sys/nlookup.h>
83 #include <sys/namei.h>
84 #include <sys/mbuf.h>
85 #include <sys/socket.h>
86 #include <sys/stat.h>
87 #include <sys/malloc.h>
88 #include <sys/sysent.h>
89 #include <sys/syscall.h>
90 #include <sys/conf.h>
91 #include <sys/objcache.h>
92
93 #include <vm/vm.h>
94 #include <vm/vm_object.h>
95 #include <vm/vm_extern.h>
96
97 #include <sys/buf2.h>
98
99 #include "rpcv2.h"
100 #include "nfsproto.h"
101 #include "nfs.h"
102 #include "nfsmount.h"
103 #include "nfsnode.h"
104 #include "xdr_subs.h"
105 #include "nfsm_subs.h"
106 #include "nfsrtt.h"
107
108 #include <netinet/in.h>
109
110 static u_int32_t nfs_xid = 0;
111
112 /*
113  * Create the header for an rpc request packet
114  * The hsiz is the size of the rest of the nfs request header.
115  * (just used to decide if a cluster is a good idea)
116  */
117 void
118 nfsm_reqhead(nfsm_info_t info, struct vnode *vp, u_long procid, int hsiz)
119 {
120         info->mb = m_getl(hsiz, MB_WAIT, MT_DATA, 0, NULL);
121         info->mb->m_len = 0;
122         info->mreq = info->mb;
123         info->bpos = mtod(info->mb, caddr_t);
124 }
125
126 /*
127  * Build the RPC header and fill in the authorization info.
128  * The authorization string argument is only used when the credentials
129  * come from outside of the kernel.
130  * Returns the head of the mbuf list.
131  */
132 struct mbuf *
133 nfsm_rpchead(struct ucred *cr, int nmflag, int procid, int auth_type,
134              int auth_len, char *auth_str, int verf_len, char *verf_str,
135              struct mbuf *mrest, int mrest_len, struct mbuf **mbp,
136              u_int32_t *xidp)
137 {
138         struct nfsm_info info;
139         struct mbuf *mb2;
140         u_int32_t *tl;
141         u_int32_t xid;
142         int siz, grpsiz, authsiz, dsiz;
143         int i;
144
145         authsiz = nfsm_rndup(auth_len);
146         dsiz = authsiz + 10 * NFSX_UNSIGNED;
147         info.mb = m_getl(dsiz, MB_WAIT, MT_DATA, M_PKTHDR, NULL);
148         if (dsiz < MINCLSIZE) {
149                 if (dsiz < MHLEN)
150                         MH_ALIGN(info.mb, dsiz);
151                 else
152                         MH_ALIGN(info.mb, 8 * NFSX_UNSIGNED);
153         }
154         info.mb->m_len = info.mb->m_pkthdr.len = 0;
155         info.mreq = info.mb;
156         info.bpos = mtod(info.mb, caddr_t);
157
158         /*
159          * First the RPC header.
160          */
161         tl = nfsm_build(&info, 8 * NFSX_UNSIGNED);
162
163         /* Get a pretty random xid to start with */
164         if (!nfs_xid)
165                 nfs_xid = krandom();
166
167         do {
168                 xid = atomic_fetchadd_int(&nfs_xid, 1);
169         } while (xid == 0);
170
171         *tl++ = *xidp = txdr_unsigned(xid);
172         *tl++ = rpc_call;
173         *tl++ = rpc_vers;
174         *tl++ = txdr_unsigned(NFS_PROG);
175         if (nmflag & NFSMNT_NFSV3)
176                 *tl++ = txdr_unsigned(NFS_VER3);
177         else
178                 *tl++ = txdr_unsigned(NFS_VER2);
179         if (nmflag & NFSMNT_NFSV3)
180                 *tl++ = txdr_unsigned(procid);
181         else
182                 *tl++ = txdr_unsigned(nfsv2_procid[procid]);
183
184         /*
185          * And then the authorization cred.
186          */
187         *tl++ = txdr_unsigned(auth_type);
188         *tl = txdr_unsigned(authsiz);
189         switch (auth_type) {
190         case RPCAUTH_UNIX:
191                 tl = nfsm_build(&info, auth_len);
192                 *tl++ = 0;              /* stamp ?? */
193                 *tl++ = 0;              /* NULL hostname */
194                 *tl++ = txdr_unsigned(cr->cr_uid);
195                 *tl++ = txdr_unsigned(cr->cr_groups[0]);
196                 grpsiz = (auth_len >> 2) - 5;
197                 *tl++ = txdr_unsigned(grpsiz);
198                 for (i = 1; i <= grpsiz; i++)
199                         *tl++ = txdr_unsigned(cr->cr_groups[i]);
200                 break;
201         case RPCAUTH_KERB4:
202                 siz = auth_len;
203                 while (siz > 0) {
204                         if (M_TRAILINGSPACE(info.mb) == 0) {
205                                 mb2 = m_getl(siz, MB_WAIT, MT_DATA, 0, NULL);
206                                 mb2->m_len = 0;
207                                 info.mb->m_next = mb2;
208                                 info.mb = mb2;
209                                 info.bpos = mtod(info.mb, caddr_t);
210                         }
211                         i = min(siz, M_TRAILINGSPACE(info.mb));
212                         bcopy(auth_str, info.bpos, i);
213                         info.mb->m_len += i;
214                         auth_str += i;
215                         info.bpos += i;
216                         siz -= i;
217                 }
218                 if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
219                         for (i = 0; i < siz; i++)
220                                 *info.bpos++ = '\0';
221                         info.mb->m_len += siz;
222                 }
223                 break;
224         };
225
226         /*
227          * And the verifier...
228          */
229         tl = nfsm_build(&info, 2 * NFSX_UNSIGNED);
230         if (verf_str) {
231                 *tl++ = txdr_unsigned(RPCAUTH_KERB4);
232                 *tl = txdr_unsigned(verf_len);
233                 siz = verf_len;
234                 while (siz > 0) {
235                         if (M_TRAILINGSPACE(info.mb) == 0) {
236                                 mb2 = m_getl(siz, MB_WAIT, MT_DATA,
237                                                   0, NULL);
238                                 mb2->m_len = 0;
239                                 info.mb->m_next = mb2;
240                                 info.mb = mb2;
241                                 info.bpos = mtod(info.mb, caddr_t);
242                         }
243                         i = min(siz, M_TRAILINGSPACE(info.mb));
244                         bcopy(verf_str, info.bpos, i);
245                         info.mb->m_len += i;
246                         verf_str += i;
247                         info.bpos += i;
248                         siz -= i;
249                 }
250                 if ((siz = (nfsm_rndup(verf_len) - verf_len)) > 0) {
251                         for (i = 0; i < siz; i++)
252                                 *info.bpos++ = '\0';
253                         info.mb->m_len += siz;
254                 }
255         } else {
256                 *tl++ = txdr_unsigned(RPCAUTH_NULL);
257                 *tl = 0;
258         }
259         info.mb->m_next = mrest;
260         info.mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len;
261         info.mreq->m_pkthdr.rcvif = NULL;
262         *mbp = info.mb;
263         return (info.mreq);
264 }
265
266 void *
267 nfsm_build(nfsm_info_t info, int bytes)
268 {
269         struct mbuf *mb2;
270         void *ptr;
271
272         if (bytes > M_TRAILINGSPACE(info->mb)) {
273                 MGET(mb2, MB_WAIT, MT_DATA);
274                 if (bytes > MLEN)
275                         panic("build > MLEN");
276                 info->mb->m_next = mb2;
277                 info->mb = mb2;
278                 info->mb->m_len = 0;
279                 info->bpos = mtod(info->mb, caddr_t);
280         }
281         ptr = info->bpos;
282         info->mb->m_len += bytes;
283         info->bpos += bytes;
284         return (ptr);
285 }
286
287 /*
288  *
289  * If NULL returned caller is expected to abort with an EBADRPC error.
290  * Caller will usually use the NULLOUT macro.
291  */
292 void *
293 nfsm_dissect(nfsm_info_t info, int bytes)
294 {
295         caddr_t cp2;
296         void *ptr;
297         int error;
298         int n;
299
300         n = mtod(info->md, caddr_t) + info->md->m_len - info->dpos;
301         if (bytes <= n) {
302                 ptr = info->dpos;
303                 info->dpos += bytes;
304         } else {
305                 error = nfsm_disct(&info->md, &info->dpos, bytes, n, &cp2);
306                 if (error) {
307                         m_freem(info->mrep);
308                         info->mrep = NULL;
309                         ptr = NULL;
310                 } else {
311                         ptr = cp2;
312                 }
313         }
314         return (ptr);
315 }
316
317 /*
318  *
319  * Caller is expected to abort if non-zero error is returned.
320  */
321 int
322 nfsm_fhtom(nfsm_info_t info, struct vnode *vp)
323 {
324         u_int32_t *tl;
325         caddr_t cp;
326         int error;
327         int n;
328
329         if (info->v3) {
330                 n = nfsm_rndup(VTONFS(vp)->n_fhsize) + NFSX_UNSIGNED;
331                 if (n <= M_TRAILINGSPACE(info->mb)) {
332                         tl = nfsm_build(info, n);
333                         *tl++ = txdr_unsigned(VTONFS(vp)->n_fhsize);
334                         *(tl + ((n >> 2) - 2)) = 0;
335                         bcopy((caddr_t)VTONFS(vp)->n_fhp,(caddr_t)tl,
336                                 VTONFS(vp)->n_fhsize);
337                         error = 0;
338                 } else if ((error = nfsm_strtmbuf(&info->mb, &info->bpos,
339                                                 (caddr_t)VTONFS(vp)->n_fhp,
340                                                 VTONFS(vp)->n_fhsize)) != 0) {
341                         m_freem(info->mreq);
342                         info->mreq = NULL;
343                 }
344         } else {
345                 cp = nfsm_build(info, NFSX_V2FH);
346                 bcopy(VTONFS(vp)->n_fhp, cp, NFSX_V2FH);
347                 error = 0;
348         }
349         return (error);
350 }
351
352 void
353 nfsm_srvfhtom(nfsm_info_t info, fhandle_t *fhp)
354 {
355         u_int32_t *tl;
356
357         if (info->v3) {
358                 tl = nfsm_build(info, NFSX_UNSIGNED + NFSX_V3FH);
359                 *tl++ = txdr_unsigned(NFSX_V3FH);
360                 bcopy(fhp, tl, NFSX_V3FH);
361         } else {
362                 tl = nfsm_build(info, NFSX_V2FH);
363                 bcopy(fhp, tl, NFSX_V2FH);
364         }
365 }
366
367 void
368 nfsm_srvpostop_fh(nfsm_info_t info, fhandle_t *fhp)
369 {
370         u_int32_t *tl;
371
372         tl = nfsm_build(info, 2 * NFSX_UNSIGNED + NFSX_V3FH);
373         *tl++ = nfs_true;
374         *tl++ = txdr_unsigned(NFSX_V3FH);
375         bcopy(fhp, tl, NFSX_V3FH);
376 }
377
378 /*
379  * Caller is expected to abort if non-zero error is returned.
380  *
381  * NOTE: (*vpp) may be loaded with a valid vnode even if (*gotvpp)
382  *       winds up 0.  The caller is responsible for dealing with (*vpp).
383  */
384 int
385 nfsm_mtofh(nfsm_info_t info, struct vnode *dvp, struct vnode **vpp, int *gotvpp)
386 {
387         struct nfsnode *ttnp;
388         nfsfh_t *ttfhp;
389         u_int32_t *tl;
390         int ttfhsize;
391         int error = 0;
392
393         if (info->v3) {
394                 tl = nfsm_dissect(info, NFSX_UNSIGNED);
395                 if (tl == NULL)
396                         return(EBADRPC);
397                 *gotvpp = fxdr_unsigned(int, *tl);
398         } else {
399                 *gotvpp = 1;
400         }
401         if (*gotvpp) {
402                 NEGATIVEOUT(ttfhsize = nfsm_getfh(info, &ttfhp));
403                 error = nfs_nget(dvp->v_mount, ttfhp, ttfhsize, &ttnp);
404                 if (error) {
405                         m_freem(info->mrep);
406                         info->mrep = NULL;
407                         return (error);
408                 }
409                 *vpp = NFSTOV(ttnp);
410         }
411         if (info->v3) {
412                 tl = nfsm_dissect(info, NFSX_UNSIGNED);
413                 if (tl == NULL)
414                         return (EBADRPC);
415                 if (*gotvpp) {
416                         *gotvpp = fxdr_unsigned(int, *tl);
417                 } else if (fxdr_unsigned(int, *tl)) {
418                         error = nfsm_adv(info, NFSX_V3FATTR);
419                         if (error)
420                                 return (error);
421                 }
422         }
423         if (*gotvpp)
424                 error = nfsm_loadattr(info, *vpp, NULL);
425 nfsmout:
426         return (error);
427 }
428
429 /*
430  *
431  * Caller is expected to abort with EBADRPC if a negative length is returned.
432  */
433 int
434 nfsm_getfh(nfsm_info_t info, nfsfh_t **fhpp)
435 {
436         u_int32_t *tl;
437         int n;
438
439         *fhpp = NULL;
440         if (info->v3) {
441                 tl = nfsm_dissect(info, NFSX_UNSIGNED);
442                 if (tl == NULL)
443                         return(-1);
444                 if ((n = fxdr_unsigned(int, *tl)) <= 0 || n > NFSX_V3FHMAX) {
445                         m_freem(info->mrep);
446                         info->mrep = NULL;
447                         return(-1);
448                 }
449         } else {
450                 n = NFSX_V2FH;
451         }
452         *fhpp = nfsm_dissect(info, nfsm_rndup(n));
453         if (*fhpp == NULL)
454                 return(-1);
455         return(n);
456 }
457
458 /*
459  * Caller is expected to abort if a non-zero error is returned.
460  */
461 int
462 nfsm_loadattr(nfsm_info_t info, struct vnode *vp, struct vattr *vap)
463 {
464         int error;
465
466         error = nfs_loadattrcache(vp, &info->md, &info->dpos, vap, 0);
467         if (error) {
468                 m_freem(info->mrep);
469                 info->mrep = NULL;
470                 return (error);
471         }
472         return (0);
473 }
474
475 /*
476  * Caller is expected to abort if a non-zero error is returned.
477  */
478 int
479 nfsm_postop_attr(nfsm_info_t info, struct vnode *vp, int *attrp, int lflags)
480 {
481         u_int32_t *tl;
482         int error;
483
484         tl = nfsm_dissect(info, NFSX_UNSIGNED);
485         if (tl == NULL)
486                 return(EBADRPC);
487         *attrp = fxdr_unsigned(int, *tl);
488         if (*attrp) {
489                 error = nfs_loadattrcache(vp, &info->md, &info->dpos,
490                                           NULL, lflags);
491                 if (error) {
492                         *attrp = 0;
493                         m_freem(info->mrep);
494                         info->mrep = NULL;
495                         return (error);
496                 }
497         }
498         return (0);
499 }
500
501 /*
502  * Caller is expected to abort if a non-zero error is returned.
503  */
504 int
505 nfsm_wcc_data(nfsm_info_t info, struct vnode *vp, int *attrp)
506 {
507         u_int32_t *tl;
508         int error;
509         int ttattrf;
510         int ttretf = 0;
511
512         tl = nfsm_dissect(info, NFSX_UNSIGNED);
513         if (tl == NULL)
514                 return (EBADRPC);
515         if (*tl == nfs_true) {
516                 tl = nfsm_dissect(info, 6 * NFSX_UNSIGNED);
517                 if (tl == NULL)
518                         return (EBADRPC);
519                 if (*attrp) {
520                         ttretf = (VTONFS(vp)->n_mtime ==
521                                 fxdr_unsigned(u_int32_t, *(tl + 2)));
522                         if (ttretf == 0)
523                                 VTONFS(vp)->n_flag |= NRMODIFIED;
524                 }
525                 error = nfsm_postop_attr(info, vp, &ttattrf,
526                                  NFS_LATTR_NOSHRINK|NFS_LATTR_NOMTIMECHECK);
527                 if (error)
528                         return(error);
529         } else {
530                 error = nfsm_postop_attr(info, vp, &ttattrf,
531                                          NFS_LATTR_NOSHRINK);
532                 if (error)
533                         return(error);
534         }
535         if (*attrp)
536                 *attrp = ttretf;
537         else
538                 *attrp = ttattrf;
539         return(0);
540 }
541
542 /*
543  * This function updates the attribute cache based on data returned in the
544  * NFS reply for NFS RPCs that modify the target file.  If the RPC succeeds
545  * a 'before' and 'after' mtime is returned that allows us to determine if
546  * the new mtime attribute represents our modification or someone else's
547  * modification.
548  *
549  * The flag argument returns non-0 if the original times matched, zero if
550  * they did not match.  NRMODIFIED is automatically set if the before time
551  * does not match the original n_mtime, and n_mtime is automatically updated
552  * to the new after time (by nfsm_postop_attr()).
553  *
554  * If full is true, set all fields, otherwise just set mode and time fields
555  */
556 void
557 nfsm_v3attrbuild(nfsm_info_t info, struct vattr *vap, int full)
558 {
559         u_int32_t *tl;
560
561         if (vap->va_mode != (mode_t)VNOVAL) {
562                 tl = nfsm_build(info, 2 * NFSX_UNSIGNED);
563                 *tl++ = nfs_true;
564                 *tl = txdr_unsigned(vap->va_mode);
565         } else {
566                 tl = nfsm_build(info, NFSX_UNSIGNED);
567                 *tl = nfs_false;
568         }
569         if (full && vap->va_uid != (uid_t)VNOVAL) {
570                 tl = nfsm_build(info, 2 * NFSX_UNSIGNED);
571                 *tl++ = nfs_true;
572                 *tl = txdr_unsigned(vap->va_uid);
573         } else {
574                 tl = nfsm_build(info, NFSX_UNSIGNED);
575                 *tl = nfs_false;
576         }
577         if (full && vap->va_gid != (gid_t)VNOVAL) {
578                 tl = nfsm_build(info, 2 * NFSX_UNSIGNED);
579                 *tl++ = nfs_true;
580                 *tl = txdr_unsigned(vap->va_gid);
581         } else {
582                 tl = nfsm_build(info, NFSX_UNSIGNED);
583                 *tl = nfs_false;
584         }
585         if (full && vap->va_size != VNOVAL) {
586                 tl = nfsm_build(info, 3 * NFSX_UNSIGNED);
587                 *tl++ = nfs_true;
588                 txdr_hyper(vap->va_size, tl);
589         } else {
590                 tl = nfsm_build(info, NFSX_UNSIGNED);
591                 *tl = nfs_false;
592         }
593         if (vap->va_atime.tv_sec != VNOVAL) {
594                 if (vap->va_atime.tv_sec != time_second) {
595                         tl = nfsm_build(info, 3 * NFSX_UNSIGNED);
596                         *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
597                         txdr_nfsv3time(&vap->va_atime, tl);
598                 } else {
599                         tl = nfsm_build(info, NFSX_UNSIGNED);
600                         *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
601                 }
602         } else {
603                 tl = nfsm_build(info, NFSX_UNSIGNED);
604                 *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
605         }
606         if (vap->va_mtime.tv_sec != VNOVAL) {
607                 if (vap->va_mtime.tv_sec != time_second) {
608                         tl = nfsm_build(info, 3 * NFSX_UNSIGNED);
609                         *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
610                         txdr_nfsv3time(&vap->va_mtime, tl);
611                 } else {
612                         tl = nfsm_build(info, NFSX_UNSIGNED);
613                         *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER);
614                 }
615         } else {
616                 tl = nfsm_build(info, NFSX_UNSIGNED);
617                 *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE);
618         }
619 }
620
621 /*
622  * Caller is expected to abort with EBADRPC if a negative length is returned.
623  */
624 int
625 nfsm_strsiz(nfsm_info_t info, int maxlen)
626 {
627         u_int32_t *tl;
628         int len;
629
630         tl = nfsm_dissect(info, NFSX_UNSIGNED);
631         if (tl == NULL)
632                 return(-1);
633         len = fxdr_unsigned(int32_t, *tl);
634         if (len < 0 || len > maxlen)
635                 return(-1);
636         return (len);
637 }
638
639 /*
640  * Caller is expected to abort if a negative length is returned, but also
641  * call nfsm_reply(0) if -2 is returned.
642  *
643  * This function sets *errorp.  Caller should not modify the error code.
644  */
645 int
646 nfsm_srvstrsiz(nfsm_info_t info, int maxlen, int *errorp)
647 {
648         u_int32_t *tl;
649         int len;
650
651         tl = nfsm_dissect(info, NFSX_UNSIGNED);
652         if (tl == NULL) {
653                 *errorp = EBADRPC;
654                 return(-1);
655         }
656         len = fxdr_unsigned(int32_t,*tl);
657         if (len > maxlen || len <= 0) {
658                 *errorp = EBADRPC;
659                 return(-2);
660         }
661         return(len);
662 }
663
664 /*
665  * Caller is expected to abort if a negative length is returned, but also
666  * call nfsm_reply(0) if -2 is returned.
667  *
668  * This function sets *errorp.  Caller should not modify the error code.
669  */
670 int
671 nfsm_srvnamesiz(nfsm_info_t info, int *errorp)
672 {
673         u_int32_t *tl;
674         int len;
675
676         tl = nfsm_dissect(info, NFSX_UNSIGNED);
677         if (tl == NULL) {
678                 *errorp = EBADRPC;
679                 return(-1);
680         }
681
682         /*
683          * In this case if *errorp is not EBADRPC and we are NFSv3,
684          * nfsm_reply() will not return a negative number.  But all
685          * call cases assume len is valid so we really do want
686          * to return -1.
687          */
688         len = fxdr_unsigned(int32_t,*tl);
689         if (len > NFS_MAXNAMLEN)
690                 *errorp = NFSERR_NAMETOL;
691         if (len <= 0)
692                 *errorp = EBADRPC;
693         if (*errorp)
694                 return(-2);
695         return (len);
696 }
697
698 /*
699  * Caller is expected to abort if a non-zero error is returned.
700  */
701 int
702 nfsm_mtouio(nfsm_info_t info, struct uio *uiop, int len)
703 {
704         int error;
705
706         if (len > 0 &&
707            (error = nfsm_mbuftouio(&info->md, uiop, len, &info->dpos)) != 0) {
708                 m_freem(info->mrep);
709                 info->mrep = NULL;
710                 return(error);
711         }
712         return(0);
713 }
714
715 /*
716  * Caller is expected to abort if a non-zero error is returned.
717  */
718 int
719 nfsm_mtobio(nfsm_info_t info, struct bio *bio, int len)
720 {
721         int error;
722
723         if (len > 0 &&
724            (error = nfsm_mbuftobio(&info->md, bio, len, &info->dpos)) != 0) {
725                 m_freem(info->mrep);
726                 info->mrep = NULL;
727                 return(error);
728        }
729        return (0);
730 }
731
732 /*
733  * Caller is expected to abort if a non-zero error is returned.
734  */
735 int
736 nfsm_uiotom(nfsm_info_t info, struct uio *uiop, int len)
737 {
738         int error;
739
740         error = nfsm_uiotombuf(uiop, &info->mb, len, &info->bpos);
741         if (error) {
742                 m_freem(info->mreq);
743                 info->mreq = NULL;
744                 return (error);
745         }
746         return(0);
747 }
748
749 int
750 nfsm_biotom(nfsm_info_t info, struct bio *bio, int off, int len)
751 {
752         int error;
753
754         error = nfsm_biotombuf(bio, &info->mb, off, len, &info->bpos);
755         if (error) {
756                 m_freem(info->mreq);
757                 info->mreq = NULL;
758                 return (error);
759         }
760         return(0);
761 }
762
763 /*
764  * Caller is expected to abort if a negative value is returned.  This
765  * function sets *errorp.  Caller should not modify the error code.
766  *
767  * We load up the remaining info fields and run the request state
768  * machine until it is done.
769  *
770  * This call runs the entire state machine and does not return until
771  * the command is complete.
772  */
773 int
774 nfsm_request(nfsm_info_t info, struct vnode *vp, int procnum,
775              thread_t td, struct ucred *cred, int *errorp)
776 {
777         info->state = NFSM_STATE_SETUP;
778         info->procnum = procnum;
779         info->vp = vp;
780         info->td = td;
781         info->cred = cred;
782         info->bio = NULL;
783         info->nmp = VFSTONFS(vp->v_mount);
784
785         *errorp = nfs_request(info, NFSM_STATE_SETUP, NFSM_STATE_DONE);
786         if (*errorp) {
787                 if ((*errorp & NFSERR_RETERR) == 0)
788                         return(-1);
789                 *errorp &= ~NFSERR_RETERR;
790         }
791         return(0);
792 }
793
794 /*
795  * This call starts the state machine through the initial transmission.
796  * Completion is via the bio.  The info structure must have installed
797  * a 'done' callback.
798  *
799  * If we are unable to do the initial tx we generate the bio completion
800  * ourselves.
801  */
802 void
803 nfsm_request_bio(nfsm_info_t info, struct vnode *vp, int procnum,
804              thread_t td, struct ucred *cred)
805 {
806         struct buf *bp;
807         int error;
808
809         info->state = NFSM_STATE_SETUP;
810         info->procnum = procnum;
811         info->vp = vp;
812         info->td = td;
813         info->cred = cred;
814         info->nmp = VFSTONFS(vp->v_mount);
815
816         error = nfs_request(info, NFSM_STATE_SETUP, NFSM_STATE_WAITREPLY);
817         if (error != EINPROGRESS) {
818                 kprintf("nfsm_request_bio: early abort %d\n", error);
819                 bp = info->bio->bio_buf;
820                 if (error) {
821                         bp->b_flags |= B_ERROR;
822                         if (error == EIO)               /* unrecoverable */
823                                 bp->b_flags |= B_INVAL;
824                 }
825
826                 /*
827                  * This can retry endlessly during a shutdown, change ESTALE
828                  * to EIO in this case.
829                  */
830                 if (shutdown_inprog && error == ESTALE)
831                         error = EIO;
832                 bp->b_error = error;
833                 biodone(info->bio);
834         }
835 }
836
837 /*
838  * Caller is expected to abort if a non-zero error is returned.
839  */
840 int
841 nfsm_strtom(nfsm_info_t info, const void *data, int len, int maxlen)
842 {
843         u_int32_t *tl;
844         int error;
845         int n;
846
847         if (len > maxlen) {
848                 m_freem(info->mreq);
849                 info->mreq = NULL;
850                 return(ENAMETOOLONG);
851         }
852         n = nfsm_rndup(len) + NFSX_UNSIGNED;
853         if (n <= M_TRAILINGSPACE(info->mb)) {
854                 tl = nfsm_build(info, n);
855                 *tl++ = txdr_unsigned(len);
856                 *(tl + ((n >> 2) - 2)) = 0;
857                 bcopy(data, tl, len);
858                 error = 0;
859         } else {
860                 error = nfsm_strtmbuf(&info->mb, &info->bpos, data, len);
861                 if (error) {
862                         m_freem(info->mreq);
863                         info->mreq = NULL;
864                 }
865         }
866         return (error);
867 }
868
869 /*
870  * Caller is expected to abort if a negative value is returned.  This
871  * function sets *errorp.  Caller should not modify the error code.
872  */
873 int
874 nfsm_reply(nfsm_info_t info,
875            struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
876            int siz, int *errorp)
877 {
878         nfsd->nd_repstat = *errorp;
879         if (*errorp && !(nfsd->nd_flag & ND_NFSV3))
880                 siz = 0;
881         nfs_rephead(siz, nfsd, slp, *errorp, &info->mreq,
882                     &info->mb, &info->bpos);
883         if (info->mrep != NULL) {
884                 m_freem(info->mrep);
885                 info->mrep = NULL;
886         }
887         if (*errorp && (!(nfsd->nd_flag & ND_NFSV3) || *errorp == EBADRPC)) {
888                 *errorp = 0;
889                 return(-1);
890         }
891         return(0);
892 }
893
894 void
895 nfsm_writereply(nfsm_info_t info,
896                 struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
897                 int error, int siz)
898 {
899         nfsd->nd_repstat = error;
900         if (error && !(info->v3))
901                 siz = 0;
902         nfs_rephead(siz, nfsd, slp, error, &info->mreq, &info->mb, &info->bpos);
903 }
904
905 /*
906  * Caller is expected to abort if a non-zero error is returned.
907  */
908 int
909 nfsm_adv(nfsm_info_t info, int len)
910 {
911         int error;
912         int n;
913
914         n = mtod(info->md, caddr_t) + info->md->m_len - info->dpos;
915         if (n >= len) {
916                 info->dpos += len;
917                 error = 0;
918         } else if ((error = nfs_adv(&info->md, &info->dpos, len, n)) != 0) {
919                 m_freem(info->mrep);
920                 info->mrep = NULL;
921         }
922         return (error);
923 }
924
925 /*
926  * Caller is expected to abort if a negative length is returned, but also
927  * call nfsm_reply(0) if -2 is returned.
928  *
929  * This function sets *errorp.  Caller should not modify the error code.
930  */
931 int
932 nfsm_srvmtofh(nfsm_info_t info, struct nfsrv_descript *nfsd,
933               fhandle_t *fhp, int *errorp)
934 {
935         u_int32_t *tl;
936         int fhlen;
937
938         if (nfsd->nd_flag & ND_NFSV3) {
939                 tl = nfsm_dissect(info, NFSX_UNSIGNED);
940                 if (tl == NULL) {
941                         *errorp = EBADRPC;
942                         return(-1);
943                 }
944                 fhlen = fxdr_unsigned(int, *tl);
945                 if (fhlen != 0 && fhlen != NFSX_V3FH) {
946                         *errorp = EBADRPC;
947                         return(-2);
948                 }
949         } else {
950                 fhlen = NFSX_V2FH;
951         }
952         if (fhlen != 0) {
953                 tl = nfsm_dissect(info, fhlen);
954                 if (tl == NULL) {
955                         *errorp = EBADRPC;
956                         return(-1);
957                 }
958                 bcopy(tl, fhp, fhlen);
959         } else {
960                 bzero(fhp, NFSX_V3FH);
961         }
962         return(0);
963 }
964
965 void *
966 _nfsm_clget(nfsm_info_t info, struct mbuf **mp1, struct mbuf **mp2,
967             char **bp, char **be)
968 {
969         if (*bp >= *be) {
970                 if (*mp1 == info->mb)
971                         (*mp1)->m_len += *bp - info->bpos;
972                 *mp1 = m_getcl(MB_WAIT, MT_DATA, 0);
973                 (*mp1)->m_len = MCLBYTES;
974                 (*mp2)->m_next = *mp1;
975                 *mp2 = *mp1;
976                 *bp = mtod(*mp1, caddr_t);
977                 *be = *bp + (*mp1)->m_len;
978         }
979         return(*bp);
980 }
981
982 int
983 nfsm_srvsattr(nfsm_info_t info, struct vattr *vap)
984 {
985         u_int32_t *tl;
986         int error = 0;
987
988         NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
989         if (*tl == nfs_true) {
990                 NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
991                 vap->va_mode = nfstov_mode(*tl);
992         }
993         NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
994         if (*tl == nfs_true) {
995                 NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
996                 vap->va_uid = fxdr_unsigned(uid_t, *tl);
997         }
998         NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
999         if (*tl == nfs_true) {
1000                 NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
1001                 vap->va_gid = fxdr_unsigned(gid_t, *tl);
1002         }
1003         NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
1004         if (*tl == nfs_true) {
1005                 NULLOUT(tl = nfsm_dissect(info, 2 * NFSX_UNSIGNED));
1006                 vap->va_size = fxdr_hyper(tl);
1007         }
1008         NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
1009         switch (fxdr_unsigned(int, *tl)) {
1010         case NFSV3SATTRTIME_TOCLIENT:
1011                 NULLOUT(tl = nfsm_dissect(info, 2 * NFSX_UNSIGNED));
1012                 fxdr_nfsv3time(tl, &vap->va_atime);
1013                 break;
1014         case NFSV3SATTRTIME_TOSERVER:
1015                 getnanotime(&vap->va_atime);
1016                 break;
1017         };
1018         NULLOUT(tl = nfsm_dissect(info, NFSX_UNSIGNED));
1019         switch (fxdr_unsigned(int, *tl)) {
1020         case NFSV3SATTRTIME_TOCLIENT:
1021                 NULLOUT(tl = nfsm_dissect(info, 2 * NFSX_UNSIGNED));
1022                 fxdr_nfsv3time(tl, &vap->va_mtime);
1023                 break;
1024         case NFSV3SATTRTIME_TOSERVER:
1025                 getnanotime(&vap->va_mtime);
1026                 break;
1027         }
1028 nfsmout:
1029         return (error);
1030 }
1031
1032 /*
1033  * copies mbuf chain to the uio scatter/gather list
1034  */
1035 int
1036 nfsm_mbuftouio(struct mbuf **mrep, struct uio *uiop, int siz, caddr_t *dpos)
1037 {
1038         char *mbufcp, *uiocp;
1039         int xfer, left, len;
1040         struct mbuf *mp;
1041         long uiosiz, rem;
1042         int error = 0;
1043
1044         mp = *mrep;
1045         mbufcp = *dpos;
1046         len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
1047         rem = nfsm_rndup(siz)-siz;
1048         while (siz > 0) {
1049                 if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
1050                         return (EFBIG);
1051                 left = uiop->uio_iov->iov_len;
1052                 uiocp = uiop->uio_iov->iov_base;
1053                 if (left > siz)
1054                         left = siz;
1055                 uiosiz = left;
1056                 while (left > 0) {
1057                         while (len == 0) {
1058                                 mp = mp->m_next;
1059                                 if (mp == NULL)
1060                                         return (EBADRPC);
1061                                 mbufcp = mtod(mp, caddr_t);
1062                                 len = mp->m_len;
1063                         }
1064                         xfer = (left > len) ? len : left;
1065 #ifdef notdef
1066                         /* Not Yet.. */
1067                         if (uiop->uio_iov->iov_op != NULL)
1068                                 (*(uiop->uio_iov->iov_op))
1069                                 (mbufcp, uiocp, xfer);
1070                         else
1071 #endif
1072                         if (uiop->uio_segflg == UIO_SYSSPACE)
1073                                 bcopy(mbufcp, uiocp, xfer);
1074                         else
1075                                 copyout(mbufcp, uiocp, xfer);
1076                         left -= xfer;
1077                         len -= xfer;
1078                         mbufcp += xfer;
1079                         uiocp += xfer;
1080                         uiop->uio_offset += xfer;
1081                         uiop->uio_resid -= xfer;
1082                 }
1083                 if (uiop->uio_iov->iov_len <= siz) {
1084                         uiop->uio_iovcnt--;
1085                         uiop->uio_iov++;
1086                 } else {
1087                         uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + uiosiz;
1088                         uiop->uio_iov->iov_len -= uiosiz;
1089                 }
1090                 siz -= uiosiz;
1091         }
1092         *dpos = mbufcp;
1093         *mrep = mp;
1094         if (rem > 0) {
1095                 if (len < rem)
1096                         error = nfs_adv(mrep, dpos, rem, len);
1097                 else
1098                         *dpos += rem;
1099         }
1100         return (error);
1101 }
1102
1103 /*
1104  * copies mbuf chain to the bio buffer
1105  */
1106 int
1107 nfsm_mbuftobio(struct mbuf **mrep, struct bio *bio, int size, caddr_t *dpos)
1108 {
1109         struct buf *bp = bio->bio_buf;
1110         char *mbufcp;
1111         char *bio_cp;
1112         int xfer, len;
1113         struct mbuf *mp;
1114         long rem;
1115         int error = 0;
1116         int bio_left;
1117
1118         mp = *mrep;
1119         mbufcp = *dpos;
1120         len = mtod(mp, caddr_t) + mp->m_len - mbufcp;
1121         rem = nfsm_rndup(size) - size;
1122
1123         bio_left = bp->b_bcount;
1124         bio_cp = bp->b_data;
1125
1126         while (size > 0) {
1127                 while (len == 0) {
1128                         mp = mp->m_next;
1129                         if (mp == NULL)
1130                                 return (EBADRPC);
1131                         mbufcp = mtod(mp, caddr_t);
1132                         len = mp->m_len;
1133                 }
1134                 if ((xfer = len) > size)
1135                         xfer = size;
1136                 if (bio_left) {
1137                         if (xfer > bio_left)
1138                                 xfer = bio_left;
1139                         bcopy(mbufcp, bio_cp, xfer);
1140                 } else {
1141                         /*
1142                          * Not enough buffer space in the bio.
1143                          */
1144                         return(EFBIG);
1145                 }
1146                 size -= xfer;
1147                 bio_left -= xfer;
1148                 bio_cp += xfer;
1149                 len -= xfer;
1150                 mbufcp += xfer;
1151         }
1152         *dpos = mbufcp;
1153         *mrep = mp;
1154         if (rem > 0) {
1155                 if (len < rem)
1156                         error = nfs_adv(mrep, dpos, rem, len);
1157                 else
1158                         *dpos += rem;
1159         }
1160         return (error);
1161 }
1162
1163 /*
1164  * copies a uio scatter/gather list to an mbuf chain.
1165  * NOTE: can ony handle iovcnt == 1
1166  */
1167 int
1168 nfsm_uiotombuf(struct uio *uiop, struct mbuf **mq, int siz, caddr_t *bpos)
1169 {
1170         char *uiocp;
1171         struct mbuf *mp, *mp2;
1172         int xfer, left, mlen;
1173         int uiosiz, rem;
1174         boolean_t getcluster;
1175         char *cp;
1176
1177 #ifdef DIAGNOSTIC
1178         if (uiop->uio_iovcnt != 1)
1179                 panic("nfsm_uiotombuf: iovcnt != 1");
1180 #endif
1181
1182         if (siz >= MINCLSIZE)
1183                 getcluster = TRUE;
1184         else
1185                 getcluster = FALSE;
1186         rem = nfsm_rndup(siz) - siz;
1187         mp = mp2 = *mq;
1188         while (siz > 0) {
1189                 left = uiop->uio_iov->iov_len;
1190                 uiocp = uiop->uio_iov->iov_base;
1191                 if (left > siz)
1192                         left = siz;
1193                 uiosiz = left;
1194                 while (left > 0) {
1195                         mlen = M_TRAILINGSPACE(mp);
1196                         if (mlen == 0) {
1197                                 if (getcluster)
1198                                         mp = m_getcl(MB_WAIT, MT_DATA, 0);
1199                                 else
1200                                         mp = m_get(MB_WAIT, MT_DATA);
1201                                 mp->m_len = 0;
1202                                 mp2->m_next = mp;
1203                                 mp2 = mp;
1204                                 mlen = M_TRAILINGSPACE(mp);
1205                         }
1206                         xfer = (left > mlen) ? mlen : left;
1207 #ifdef notdef
1208                         /* Not Yet.. */
1209                         if (uiop->uio_iov->iov_op != NULL)
1210                                 (*(uiop->uio_iov->iov_op))
1211                                 (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
1212                         else
1213 #endif
1214                         if (uiop->uio_segflg == UIO_SYSSPACE)
1215                                 bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
1216                         else
1217                                 copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
1218                         mp->m_len += xfer;
1219                         left -= xfer;
1220                         uiocp += xfer;
1221                         uiop->uio_offset += xfer;
1222                         uiop->uio_resid -= xfer;
1223                 }
1224                 uiop->uio_iov->iov_base = (char *)uiop->uio_iov->iov_base + uiosiz;
1225                 uiop->uio_iov->iov_len -= uiosiz;
1226                 siz -= uiosiz;
1227         }
1228         if (rem > 0) {
1229                 if (rem > M_TRAILINGSPACE(mp)) {
1230                         MGET(mp, MB_WAIT, MT_DATA);
1231                         mp->m_len = 0;
1232                         mp2->m_next = mp;
1233                 }
1234                 cp = mtod(mp, caddr_t)+mp->m_len;
1235                 for (left = 0; left < rem; left++)
1236                         *cp++ = '\0';
1237                 mp->m_len += rem;
1238                 *bpos = cp;
1239         } else
1240                 *bpos = mtod(mp, caddr_t)+mp->m_len;
1241         *mq = mp;
1242         return (0);
1243 }
1244
1245 int
1246 nfsm_biotombuf(struct bio *bio, struct mbuf **mq, int off,
1247                int siz, caddr_t *bpos)
1248 {
1249         struct buf *bp = bio->bio_buf;
1250         struct mbuf *mp, *mp2;
1251         char *bio_cp;
1252         int bio_left;
1253         int xfer, mlen;
1254         int rem;
1255         boolean_t getcluster;
1256         char *cp;
1257
1258         if (siz >= MINCLSIZE)
1259                 getcluster = TRUE;
1260         else
1261                 getcluster = FALSE;
1262         rem = nfsm_rndup(siz) - siz;
1263         mp = mp2 = *mq;
1264
1265         bio_cp = bp->b_data + off;
1266         bio_left = siz;
1267
1268         while (bio_left) {
1269                 mlen = M_TRAILINGSPACE(mp);
1270                 if (mlen == 0) {
1271                         if (getcluster)
1272                                 mp = m_getcl(MB_WAIT, MT_DATA, 0);
1273                         else
1274                                 mp = m_get(MB_WAIT, MT_DATA);
1275                         mp->m_len = 0;
1276                         mp2->m_next = mp;
1277                         mp2 = mp;
1278                         mlen = M_TRAILINGSPACE(mp);
1279                 }
1280                 xfer = (bio_left < mlen) ? bio_left : mlen;
1281                 bcopy(bio_cp, mtod(mp, caddr_t) + mp->m_len, xfer);
1282                 mp->m_len += xfer;
1283                 bio_left -= xfer;
1284                 bio_cp += xfer;
1285         }
1286         if (rem > 0) {
1287                 if (rem > M_TRAILINGSPACE(mp)) {
1288                         MGET(mp, MB_WAIT, MT_DATA);
1289                         mp->m_len = 0;
1290                         mp2->m_next = mp;
1291                 }
1292                 cp = mtod(mp, caddr_t) + mp->m_len;
1293                 for (mlen = 0; mlen < rem; mlen++)
1294                         *cp++ = '\0';
1295                 mp->m_len += rem;
1296                 *bpos = cp;
1297         } else {
1298                 *bpos = mtod(mp, caddr_t) + mp->m_len;
1299         }
1300         *mq = mp;
1301         return(0);
1302 }
1303
1304 /*
1305  * Help break down an mbuf chain by setting the first siz bytes contiguous
1306  * pointed to by returned val.
1307  * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
1308  * cases. (The macros use the vars. dpos and dpos2)
1309  */
1310 int
1311 nfsm_disct(struct mbuf **mdp, caddr_t *dposp, int siz, int left, caddr_t *cp2)
1312 {
1313         struct mbuf *mp, *mp2;
1314         int siz2, xfer;
1315         caddr_t p;
1316
1317         mp = *mdp;
1318         while (left == 0) {
1319                 *mdp = mp = mp->m_next;
1320                 if (mp == NULL)
1321                         return (EBADRPC);
1322                 left = mp->m_len;
1323                 *dposp = mtod(mp, caddr_t);
1324         }
1325         if (left >= siz) {
1326                 *cp2 = *dposp;
1327                 *dposp += siz;
1328         } else if (mp->m_next == NULL) {
1329                 return (EBADRPC);
1330         } else if (siz > MHLEN) {
1331                 panic("nfs S too big");
1332         } else {
1333                 MGET(mp2, MB_WAIT, MT_DATA);
1334                 mp2->m_next = mp->m_next;
1335                 mp->m_next = mp2;
1336                 mp->m_len -= left;
1337                 mp = mp2;
1338                 *cp2 = p = mtod(mp, caddr_t);
1339                 bcopy(*dposp, p, left);         /* Copy what was left */
1340                 siz2 = siz-left;
1341                 p += left;
1342                 mp2 = mp->m_next;
1343                 /* Loop around copying up the siz2 bytes */
1344                 while (siz2 > 0) {
1345                         if (mp2 == NULL)
1346                                 return (EBADRPC);
1347                         xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
1348                         if (xfer > 0) {
1349                                 bcopy(mtod(mp2, caddr_t), p, xfer);
1350                                 mp2->m_len -= xfer;
1351                                 mp2->m_data += xfer;
1352                                 p += xfer;
1353                                 siz2 -= xfer;
1354                         }
1355                         if (siz2 > 0)
1356                                 mp2 = mp2->m_next;
1357                 }
1358                 mp->m_len = siz;
1359                 *mdp = mp2;
1360                 *dposp = mtod(mp2, caddr_t);
1361         }
1362         return (0);
1363 }
1364
1365 /*
1366  * Advance the position in the mbuf chain.
1367  */
1368 int
1369 nfs_adv(struct mbuf **mdp, caddr_t *dposp, int offs, int left)
1370 {
1371         struct mbuf *m;
1372         int s;
1373
1374         m = *mdp;
1375         s = left;
1376         while (s < offs) {
1377                 offs -= s;
1378                 m = m->m_next;
1379                 if (m == NULL)
1380                         return (EBADRPC);
1381                 s = m->m_len;
1382         }
1383         *mdp = m;
1384         *dposp = mtod(m, caddr_t)+offs;
1385         return (0);
1386 }
1387
1388 /*
1389  * Copy a string into mbufs for the hard cases...
1390  */
1391 int
1392 nfsm_strtmbuf(struct mbuf **mb, char **bpos, const char *cp, long siz)
1393 {
1394         struct mbuf *m1 = NULL, *m2;
1395         long left, xfer, len, tlen;
1396         u_int32_t *tl;
1397         int putsize;
1398
1399         putsize = 1;
1400         m2 = *mb;
1401         left = M_TRAILINGSPACE(m2);
1402         if (left > 0) {
1403                 tl = ((u_int32_t *)(*bpos));
1404                 *tl++ = txdr_unsigned(siz);
1405                 putsize = 0;
1406                 left -= NFSX_UNSIGNED;
1407                 m2->m_len += NFSX_UNSIGNED;
1408                 if (left > 0) {
1409                         bcopy(cp, (caddr_t) tl, left);
1410                         siz -= left;
1411                         cp += left;
1412                         m2->m_len += left;
1413                         left = 0;
1414                 }
1415         }
1416         /* Loop around adding mbufs */
1417         while (siz > 0) {
1418                 int msize;
1419
1420                 m1 = m_getl(siz, MB_WAIT, MT_DATA, 0, &msize);
1421                 m1->m_len = msize;
1422                 m2->m_next = m1;
1423                 m2 = m1;
1424                 tl = mtod(m1, u_int32_t *);
1425                 tlen = 0;
1426                 if (putsize) {
1427                         *tl++ = txdr_unsigned(siz);
1428                         m1->m_len -= NFSX_UNSIGNED;
1429                         tlen = NFSX_UNSIGNED;
1430                         putsize = 0;
1431                 }
1432                 if (siz < m1->m_len) {
1433                         len = nfsm_rndup(siz);
1434                         xfer = siz;
1435                         if (xfer < len)
1436                                 *(tl+(xfer>>2)) = 0;
1437                 } else {
1438                         xfer = len = m1->m_len;
1439                 }
1440                 bcopy(cp, (caddr_t) tl, xfer);
1441                 m1->m_len = len+tlen;
1442                 siz -= xfer;
1443                 cp += xfer;
1444         }
1445         *mb = m1;
1446         *bpos = mtod(m1, caddr_t)+m1->m_len;
1447         return (0);
1448 }
1449
1450 /*
1451  * A fiddled version of m_adj() that ensures null fill to a long
1452  * boundary and only trims off the back end
1453  */
1454 void
1455 nfsm_adj(struct mbuf *mp, int len, int nul)
1456 {
1457         struct mbuf *m;
1458         int count, i;
1459         char *cp;
1460
1461         /*
1462          * Trim from tail.  Scan the mbuf chain,
1463          * calculating its length and finding the last mbuf.
1464          * If the adjustment only affects this mbuf, then just
1465          * adjust and return.  Otherwise, rescan and truncate
1466          * after the remaining size.
1467          */
1468         count = 0;
1469         m = mp;
1470         for (;;) {
1471                 count += m->m_len;
1472                 if (m->m_next == NULL)
1473                         break;
1474                 m = m->m_next;
1475         }
1476         if (m->m_len > len) {
1477                 m->m_len -= len;
1478                 if (nul > 0) {
1479                         cp = mtod(m, caddr_t)+m->m_len-nul;
1480                         for (i = 0; i < nul; i++)
1481                                 *cp++ = '\0';
1482                 }
1483                 return;
1484         }
1485         count -= len;
1486         if (count < 0)
1487                 count = 0;
1488         /*
1489          * Correct length for chain is "count".
1490          * Find the mbuf with last data, adjust its length,
1491          * and toss data from remaining mbufs on chain.
1492          */
1493         for (m = mp; m; m = m->m_next) {
1494                 if (m->m_len >= count) {
1495                         m->m_len = count;
1496                         if (nul > 0) {
1497                                 cp = mtod(m, caddr_t)+m->m_len-nul;
1498                                 for (i = 0; i < nul; i++)
1499                                         *cp++ = '\0';
1500                         }
1501                         break;
1502                 }
1503                 count -= m->m_len;
1504         }
1505         for (m = m->m_next;m;m = m->m_next)
1506                 m->m_len = 0;
1507 }
1508
1509 /*
1510  * Make these functions instead of macros, so that the kernel text size
1511  * doesn't get too big...
1512  */
1513 void
1514 nfsm_srvwcc_data(nfsm_info_t info, struct nfsrv_descript *nfsd,
1515                  int before_ret, struct vattr *before_vap,
1516                  int after_ret, struct vattr *after_vap)
1517 {
1518         u_int32_t *tl;
1519
1520         /*
1521          * before_ret is 0 if before_vap is valid, non-zero if it isn't.
1522          */
1523         if (before_ret) {
1524                 tl = nfsm_build(info, NFSX_UNSIGNED);
1525                 *tl = nfs_false;
1526         } else {
1527                 tl = nfsm_build(info, 7 * NFSX_UNSIGNED);
1528                 *tl++ = nfs_true;
1529                 txdr_hyper(before_vap->va_size, tl);
1530                 tl += 2;
1531                 txdr_nfsv3time(&(before_vap->va_mtime), tl);
1532                 tl += 2;
1533                 txdr_nfsv3time(&(before_vap->va_ctime), tl);
1534         }
1535         nfsm_srvpostop_attr(info, nfsd, after_ret, after_vap);
1536 }
1537
1538 void
1539 nfsm_srvpostop_attr(nfsm_info_t info, struct nfsrv_descript *nfsd,
1540                    int after_ret, struct vattr *after_vap)
1541 {
1542         struct nfs_fattr *fp;
1543         u_int32_t *tl;
1544
1545         if (after_ret) {
1546                 tl = nfsm_build(info, NFSX_UNSIGNED);
1547                 *tl = nfs_false;
1548         } else {
1549                 tl = nfsm_build(info, NFSX_UNSIGNED + NFSX_V3FATTR);
1550                 *tl++ = nfs_true;
1551                 fp = (struct nfs_fattr *)tl;
1552                 nfsm_srvfattr(nfsd, after_vap, fp);
1553         }
1554 }
1555
1556 void
1557 nfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
1558               struct nfs_fattr *fp)
1559 {
1560         /*
1561          * NFS seems to truncate nlink to 16 bits, don't let it overflow.
1562          */
1563         if (vap->va_nlink > 65535)
1564                 fp->fa_nlink = 65535;
1565         else
1566                 fp->fa_nlink = txdr_unsigned(vap->va_nlink);
1567         fp->fa_uid = txdr_unsigned(vap->va_uid);
1568         fp->fa_gid = txdr_unsigned(vap->va_gid);
1569         if (nfsd->nd_flag & ND_NFSV3) {
1570                 fp->fa_type = vtonfsv3_type(vap->va_type);
1571                 fp->fa_mode = vtonfsv3_mode(vap->va_mode);
1572                 txdr_hyper(vap->va_size, &fp->fa3_size);
1573                 txdr_hyper(vap->va_bytes, &fp->fa3_used);
1574                 fp->fa3_rdev.specdata1 = txdr_unsigned(vap->va_rmajor);
1575                 fp->fa3_rdev.specdata2 = txdr_unsigned(vap->va_rminor);
1576                 fp->fa3_fsid.nfsuquad[0] = 0;
1577                 fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
1578                 txdr_hyper(vap->va_fileid, &fp->fa3_fileid);
1579                 txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
1580                 txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
1581                 txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
1582         } else {
1583                 fp->fa_type = vtonfsv2_type(vap->va_type);
1584                 fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
1585                 fp->fa2_size = txdr_unsigned(vap->va_size);
1586                 fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
1587                 if (vap->va_type == VFIFO)
1588                         fp->fa2_rdev = 0xffffffff;
1589                 else
1590                         fp->fa2_rdev = txdr_unsigned(makeudev(vap->va_rmajor, vap->va_rminor));
1591                 fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
1592                 fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
1593                 fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
1594                 txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
1595                 txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
1596                 txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
1597         }
1598 }