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 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)nfs_syscalls.c 8.5 (Berkeley) 3/30/95
37 * $FreeBSD$
38 */
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/sysproto.h>
43 #include <sys/kernel.h>
44 #include <sys/sysctl.h>
45 #include <sys/file.h>
46 #include <sys/filedesc.h>
47 #include <sys/vnode.h>
48 #include <sys/malloc.h>
49 #include <sys/mount.h>
50 #include <sys/proc.h>
51 #include <sys/buf.h>
52 #include <sys/mbuf.h>
53 #include <sys/socket.h>
54 #include <sys/socketvar.h>
55 #include <sys/domain.h>
56 #include <sys/protosw.h>
57 #include <sys/namei.h>
58
59 #include <netinet/in.h>
60 #include <netinet/tcp.h>
61 #ifdef ISO
62 #include <netiso/iso.h>
63 #endif
64 #include <nfs/xdr_subs.h>
65 #include <nfs/rpcv2.h>
66 #include <nfs/nfsproto.h>
67 #include <nfs/nfs.h>
68 #include <nfs/nfsm_subs.h>
69 #include <nfs/nfsrvcache.h>
70 #include <nfs/nfsmount.h>
71 #include <nfs/nfsnode.h>
72 #include <nfs/nqnfs.h>
73 #include <nfs/nfsrtt.h>
74
75 static MALLOC_DEFINE(M_NFSSVC, "NFS srvsock", "Nfs server structure");
76
77 /* Global defs. */
78 extern int32_t (*nfsrv3_procs[NFS_NPROCS]) __P((struct nfsrv_descript *nd,
79 struct nfssvc_sock *slp,
80 struct proc *procp,
81 struct mbuf **mreqp));
82 extern int nfs_numasync;
83 extern time_t nqnfsstarttime;
84 extern int nqsrv_writeslack;
85 extern int nfsrtton;
86 extern struct nfsstats nfsstats;
87 extern int nfsrvw_procrastinate;
88 extern int nfsrvw_procrastinate_v3;
89 struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
90 static int nuidhash_max = NFS_MAXUIDHASH;
91
92 static void nfsrv_zapsock __P((struct nfssvc_sock *slp));
93 static int nfssvc_iod __P((struct proc *));
94
95 #define TRUE 1
96 #define FALSE 0
97
98 static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
99
100 #ifndef NFS_NOSERVER
101 int nfsd_waiting = 0;
102 static struct nfsdrt nfsdrt;
103 static int nfs_numnfsd = 0;
104 static int notstarted = 1;
105 static int modify_flag = 0;
106 static void nfsd_rt __P((int sotype, struct nfsrv_descript *nd,
107 int cacherep));
108 static int nfssvc_addsock __P((struct file *, struct sockaddr *,
109 struct proc *));
110 static int nfssvc_nfsd __P((struct nfsd_srvargs *,caddr_t,struct proc *));
111
112 static int nfs_privport = 0;
113 SYSCTL_INT(_vfs_nfs, NFS_NFSPRIVPORT, nfs_privport, CTLFLAG_RW, &nfs_privport, 0, "");
114 SYSCTL_INT(_vfs_nfs, OID_AUTO, gatherdelay, CTLFLAG_RW, &nfsrvw_procrastinate, 0, "");
115 SYSCTL_INT(_vfs_nfs, OID_AUTO, gatherdelay_v3, CTLFLAG_RW, &nfsrvw_procrastinate_v3, 0, "");
116
117 /*
118 * NFS server system calls
119 * getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
120 */
121
122 /*
123 * Get file handle system call
124 */
125 #ifndef _SYS_SYSPROTO_H_
126 struct getfh_args {
127 char *fname;
128 fhandle_t *fhp;
129 };
130 #endif
131 int
132 getfh(p, uap)
133 struct proc *p;
134 register struct getfh_args *uap;
135 {
136 register struct vnode *vp;
137 fhandle_t fh;
138 int error;
139 struct nameidata nd;
140
141 /*
142 * Must be super user
143 */
144 error = suser(p->p_ucred, &p->p_acflag);
145 if(error)
146 return (error);
147 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p);
148 error = namei(&nd);
149 if (error)
150 return (error);
151 vp = nd.ni_vp;
152 bzero((caddr_t)&fh, sizeof(fh));
153 fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
154 error = VFS_VPTOFH(vp, &fh.fh_fid);
155 vput(vp);
156 if (error)
157 return (error);
158 error = copyout((caddr_t)&fh, (caddr_t)uap->fhp, sizeof (fh));
159 return (error);
160 }
161
162 #endif /* NFS_NOSERVER */
163 /*
164 * Nfs server psuedo system call for the nfsd's
165 * Based on the flag value it either:
166 * - adds a socket to the selection list
167 * - remains in the kernel as an nfsd
168 * - remains in the kernel as an nfsiod
169 */
170 #ifndef _SYS_SYSPROTO_H_
171 struct nfssvc_args {
172 int flag;
173 caddr_t argp;
174 };
175 #endif
176 int
177 nfssvc(p, uap)
178 struct proc *p;
179 register struct nfssvc_args *uap;
180 {
181 #ifndef NFS_NOSERVER
182 struct nameidata nd;
183 struct file *fp;
184 struct sockaddr *nam;
185 struct nfsd_args nfsdarg;
186 struct nfsd_srvargs nfsd_srvargs, *nsd = &nfsd_srvargs;
187 struct nfsd_cargs ncd;
188 struct nfsd *nfsd;
189 struct nfssvc_sock *slp;
190 struct nfsuid *nuidp;
191 struct nfsmount *nmp;
192 #endif /* NFS_NOSERVER */
193 int error;
194
195 /*
196 * Must be super user
197 */
198 error = suser(p->p_ucred, &p->p_acflag);
199 if(error)
200 return (error);
201 while (nfssvc_sockhead_flag & SLP_INIT) {
202 nfssvc_sockhead_flag |= SLP_WANTINIT;
203 (void) tsleep((caddr_t)&nfssvc_sockhead, PSOCK, "nfsd init", 0);
204 }
205 if (uap->flag & NFSSVC_BIOD)
206 error = nfssvc_iod(p);
207 #ifdef NFS_NOSERVER
208 else
209 error = ENXIO;
210 #else /* !NFS_NOSERVER */
211 else if (uap->flag & NFSSVC_MNTD) {
212 error = copyin(uap->argp, (caddr_t)&ncd, sizeof (ncd));
213 if (error)
214 return (error);
215 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
216 ncd.ncd_dirp, p);
217 error = namei(&nd);
218 if (error)
219 return (error);
220 if ((nd.ni_vp->v_flag & VROOT) == 0)
221 error = EINVAL;
222 nmp = VFSTONFS(nd.ni_vp->v_mount);
223 vput(nd.ni_vp);
224 if (error)
225 return (error);
226 if ((nmp->nm_state & NFSSTA_MNTD) &&
227 (uap->flag & NFSSVC_GOTAUTH) == 0)
228 return (0);
229 nmp->nm_state |= NFSSTA_MNTD;
230 error = nqnfs_clientd(nmp, p->p_ucred, &ncd, uap->flag,
231 uap->argp, p);
232 } else if (uap->flag & NFSSVC_ADDSOCK) {
233 error = copyin(uap->argp, (caddr_t)&nfsdarg, sizeof(nfsdarg));
234 if (error)
235 return (error);
236 error = getsock(p->p_fd, nfsdarg.sock, &fp);
237 if (error)
238 return (error);
239 /*
240 * Get the client address for connected sockets.
241 */
242 if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
243 nam = (struct sockaddr *)0;
244 else {
245 error = getsockaddr(&nam, nfsdarg.name,
246 nfsdarg.namelen);
247 if (error)
248 return (error);
249 }
250 error = nfssvc_addsock(fp, nam, p);
251 } else {
252 error = copyin(uap->argp, (caddr_t)nsd, sizeof (*nsd));
253 if (error)
254 return (error);
255 if ((uap->flag & NFSSVC_AUTHIN) &&
256 ((nfsd = nsd->nsd_nfsd)) != NULL &&
257 (nfsd->nfsd_slp->ns_flag & SLP_VALID)) {
258 slp = nfsd->nfsd_slp;
259
260 /*
261 * First check to see if another nfsd has already
262 * added this credential.
263 */
264 for (nuidp = NUIDHASH(slp,nsd->nsd_cr.cr_uid)->lh_first;
265 nuidp != 0; nuidp = nuidp->nu_hash.le_next) {
266 if (nuidp->nu_cr.cr_uid == nsd->nsd_cr.cr_uid &&
267 (!nfsd->nfsd_nd->nd_nam2 ||
268 netaddr_match(NU_NETFAM(nuidp),
269 &nuidp->nu_haddr, nfsd->nfsd_nd->nd_nam2)))
270 break;
271 }
272 if (nuidp) {
273 nfsrv_setcred(&nuidp->nu_cr,&nfsd->nfsd_nd->nd_cr);
274 nfsd->nfsd_nd->nd_flag |= ND_KERBFULL;
275 } else {
276 /*
277 * Nope, so we will.
278 */
279 if (slp->ns_numuids < nuidhash_max) {
280 slp->ns_numuids++;
281 nuidp = (struct nfsuid *)
282 malloc(sizeof (struct nfsuid), M_NFSUID,
283 M_WAITOK);
284 } else
285 nuidp = (struct nfsuid *)0;
286 if ((slp->ns_flag & SLP_VALID) == 0) {
287 if (nuidp)
288 free((caddr_t)nuidp, M_NFSUID);
289 } else {
290 if (nuidp == (struct nfsuid *)0) {
291 nuidp = slp->ns_uidlruhead.tqh_first;
292 LIST_REMOVE(nuidp, nu_hash);
293 TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp,
294 nu_lru);
295 if (nuidp->nu_flag & NU_NAM)
296 FREE(nuidp->nu_nam, M_SONAME);
297 }
298 nuidp->nu_flag = 0;
299 nuidp->nu_cr = nsd->nsd_cr;
300 if (nuidp->nu_cr.cr_ngroups > NGROUPS)
301 nuidp->nu_cr.cr_ngroups = NGROUPS;
302 nuidp->nu_cr.cr_ref = 1;
303 nuidp->nu_timestamp = nsd->nsd_timestamp;
304 nuidp->nu_expire = time_second + nsd->nsd_ttl;
305 /*
306 * and save the session key in nu_key.
307 */
308 bcopy(nsd->nsd_key, nuidp->nu_key,
309 sizeof (nsd->nsd_key));
310 if (nfsd->nfsd_nd->nd_nam2) {
311 struct sockaddr_in *saddr;
312
313 saddr = (struct sockaddr_in *)
314 nfsd->nfsd_nd->nd_nam2;
315 switch (saddr->sin_family) {
316 case AF_INET:
317 nuidp->nu_flag |= NU_INETADDR;
318 nuidp->nu_inetaddr =
319 saddr->sin_addr.s_addr;
320 break;
321 case AF_ISO:
322 default:
323 nuidp->nu_flag |= NU_NAM;
324 nuidp->nu_nam =
325 dup_sockaddr(nfsd->nfsd_nd->
326 nd_nam2, 1);
327 break;
328 };
329 }
330 TAILQ_INSERT_TAIL(&slp->ns_uidlruhead, nuidp,
331 nu_lru);
332 LIST_INSERT_HEAD(NUIDHASH(slp, nsd->nsd_uid),
333 nuidp, nu_hash);
334 nfsrv_setcred(&nuidp->nu_cr,
335 &nfsd->nfsd_nd->nd_cr);
336 nfsd->nfsd_nd->nd_flag |= ND_KERBFULL;
337 }
338 }
339 }
340 if ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd))
341 nfsd->nfsd_flag |= NFSD_AUTHFAIL;
342 error = nfssvc_nfsd(nsd, uap->argp, p);
343 }
344 #endif /* NFS_NOSERVER */
345 if (error == EINTR || error == ERESTART)
346 error = 0;
347 return (error);
348 }
349
350 #ifndef NFS_NOSERVER
351 /*
352 * Adds a socket to the list for servicing by nfsds.
353 */
354 static int
355 nfssvc_addsock(fp, mynam, p)
356 struct file *fp;
357 struct sockaddr *mynam;
358 struct proc *p;
359 {
360 register int siz;
361 register struct nfssvc_sock *slp;
362 register struct socket *so;
363 struct nfssvc_sock *tslp;
364 int error, s;
365
366 so = (struct socket *)fp->f_data;
367 tslp = (struct nfssvc_sock *)0;
368 /*
369 * Add it to the list, as required.
370 */
371 if (so->so_proto->pr_protocol == IPPROTO_UDP) {
372 tslp = nfs_udpsock;
373 if (tslp->ns_flag & SLP_VALID) {
374 FREE(mynam, M_SONAME);
375 return (EPERM);
376 }
377 #ifdef ISO
378 } else if (so->so_proto->pr_protocol == ISOPROTO_CLTP) {
379 tslp = nfs_cltpsock;
380 if (tslp->ns_flag & SLP_VALID) {
381 FREE(mynam, M_SONAME);
382 return (EPERM);
383 }
384 #endif /* ISO */
385 }
386 if (so->so_type == SOCK_STREAM)
387 siz = NFS_MAXPACKET + sizeof (u_long);
388 else
389 siz = NFS_MAXPACKET;
390 error = soreserve(so, siz, siz);
391 if (error) {
392 FREE(mynam, M_SONAME);
393 return (error);
394 }
395
396 /*
397 * Set protocol specific options { for now TCP only } and
398 * reserve some space. For datagram sockets, this can get called
399 * repeatedly for the same socket, but that isn't harmful.
400 */
401 if (so->so_type == SOCK_STREAM) {
402 struct sockopt sopt;
403 int val;
404
405 bzero(&sopt, sizeof sopt);
406 sopt.sopt_level = SOL_SOCKET;
407 sopt.sopt_name = SO_KEEPALIVE;
408 sopt.sopt_val = &val;
409 sopt.sopt_valsize = sizeof val;
410 val = 1;
411 sosetopt(so, &sopt);
412 }
413 if (so->so_proto->pr_domain->dom_family == AF_INET &&
414 so->so_proto->pr_protocol == IPPROTO_TCP) {
415 struct sockopt sopt;
416 int val;
417
418 bzero(&sopt, sizeof sopt);
419 sopt.sopt_level = IPPROTO_TCP;
420 sopt.sopt_name = TCP_NODELAY;
421 sopt.sopt_val = &val;
422 sopt.sopt_valsize = sizeof val;
423 val = 1;
424 sosetopt(so, &sopt);
425 }
426 so->so_rcv.sb_flags &= ~SB_NOINTR;
427 so->so_rcv.sb_timeo = 0;
428 so->so_snd.sb_flags &= ~SB_NOINTR;
429 so->so_snd.sb_timeo = 0;
430 if (tslp)
431 slp = tslp;
432 else {
433 slp = (struct nfssvc_sock *)
434 malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
435 bzero((caddr_t)slp, sizeof (struct nfssvc_sock));
436 STAILQ_INIT(&slp->ns_rec);
437 TAILQ_INIT(&slp->ns_uidlruhead);
438 TAILQ_INSERT_TAIL(&nfssvc_sockhead, slp, ns_chain);
439 }
440 slp->ns_so = so;
441 slp->ns_nam = mynam;
442 fp->f_count++;
443 slp->ns_fp = fp;
444 s = splnet();
445 so->so_upcallarg = (caddr_t)slp;
446 so->so_upcall = nfsrv_rcv;
447 so->so_rcv.sb_flags |= SB_UPCALL;
448 slp->ns_flag = (SLP_VALID | SLP_NEEDQ);
449 nfsrv_wakenfsd(slp);
450 splx(s);
451 return (0);
452 }
453
454 /*
455 * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
456 * until it is killed by a signal.
457 */
458 static int
459 nfssvc_nfsd(nsd, argp, p)
460 struct nfsd_srvargs *nsd;
461 caddr_t argp;
462 struct proc *p;
463 {
464 register struct mbuf *m;
465 register int siz;
466 register struct nfssvc_sock *slp;
467 register struct socket *so;
468 register int *solockp;
469 struct nfsd *nfsd = nsd->nsd_nfsd;
470 struct nfsrv_descript *nd = NULL;
471 struct mbuf *mreq;
472 int error = 0, cacherep, s, sotype, writes_todo;
473 int procrastinate;
474 u_quad_t cur_usec;
475
476 #ifndef nolint
477 cacherep = RC_DOIT;
478 writes_todo = 0;
479 #endif
480 if (nfsd == (struct nfsd *)0) {
481 nsd->nsd_nfsd = nfsd = (struct nfsd *)
482 malloc(sizeof (struct nfsd), M_NFSD, M_WAITOK);
483 bzero((caddr_t)nfsd, sizeof (struct nfsd));
484 s = splnet();
485 nfsd->nfsd_procp = p;
486 TAILQ_INSERT_TAIL(&nfsd_head, nfsd, nfsd_chain);
487 nfs_numnfsd++;
488 } else
489 s = splnet();
490
491 /*
492 * Loop getting rpc requests until SIGKILL.
493 */
494 for (;;) {
495 if ((nfsd->nfsd_flag & NFSD_REQINPROG) == 0) {
496 while (nfsd->nfsd_slp == (struct nfssvc_sock *)0 &&
497 (nfsd_head_flag & NFSD_CHECKSLP) == 0) {
498 nfsd->nfsd_flag |= NFSD_WAITING;
499 nfsd_waiting++;
500 error = tsleep((caddr_t)nfsd, PSOCK | PCATCH,
501 "nfsd", 0);
502 nfsd_waiting--;
503 if (error)
504 goto done;
505 }
506 if (nfsd->nfsd_slp == (struct nfssvc_sock *)0 &&
507 (nfsd_head_flag & NFSD_CHECKSLP) != 0) {
508 for (slp = nfssvc_sockhead.tqh_first; slp != 0;
509 slp = slp->ns_chain.tqe_next) {
510 if ((slp->ns_flag & (SLP_VALID | SLP_DOREC))
511 == (SLP_VALID | SLP_DOREC)) {
512 slp->ns_flag &= ~SLP_DOREC;
513 slp->ns_sref++;
514 nfsd->nfsd_slp = slp;
515 break;
516 }
517 }
518 if (slp == 0)
519 nfsd_head_flag &= ~NFSD_CHECKSLP;
520 }
521 if ((slp = nfsd->nfsd_slp) == (struct nfssvc_sock *)0)
522 continue;
523 if (slp->ns_flag & SLP_VALID) {
524 if (slp->ns_flag & SLP_DISCONN)
525 nfsrv_zapsock(slp);
526 else if (slp->ns_flag & SLP_NEEDQ) {
527 slp->ns_flag &= ~SLP_NEEDQ;
528 (void) nfs_sndlock(&slp->ns_solock,
529 &slp->ns_solock,
530 (struct nfsreq *)0);
531 nfsrv_rcv(slp->ns_so, (caddr_t)slp,
532 M_WAIT);
533 nfs_sndunlock(&slp->ns_solock,
534 &slp->ns_solock);
535 }
536 error = nfsrv_dorec(slp, nfsd, &nd);
537 cur_usec = nfs_curusec();
538 if (error && slp->ns_tq.lh_first &&
539 slp->ns_tq.lh_first->nd_time <= cur_usec) {
540 error = 0;
541 cacherep = RC_DOIT;
542 writes_todo = 1;
543 } else
544 writes_todo = 0;
545 nfsd->nfsd_flag |= NFSD_REQINPROG;
546 }
547 } else {
548 error = 0;
549 slp = nfsd->nfsd_slp;
550 }
551 if (error || (slp->ns_flag & SLP_VALID) == 0) {
552 if (nd) {
553 free((caddr_t)nd, M_NFSRVDESC);
554 nd = NULL;
555 }
556 nfsd->nfsd_slp = (struct nfssvc_sock *)0;
557 nfsd->nfsd_flag &= ~NFSD_REQINPROG;
558 nfsrv_slpderef(slp);
559 continue;
560 }
561 splx(s);
562 so = slp->ns_so;
563 sotype = so->so_type;
564 if (so->so_proto->pr_flags & PR_CONNREQUIRED)
565 solockp = &slp->ns_solock;
566 else
567 solockp = (int *)0;
568 if (nd) {
569 getmicrotime(&nd->nd_starttime);
570 if (nd->nd_nam2)
571 nd->nd_nam = nd->nd_nam2;
572 else
573 nd->nd_nam = slp->ns_nam;
574
575 /*
576 * Check to see if authorization is needed.
577 */
578 if (nfsd->nfsd_flag & NFSD_NEEDAUTH) {
579 nfsd->nfsd_flag &= ~NFSD_NEEDAUTH;
580 nsd->nsd_haddr =
581 ((struct sockaddr_in *)
582 nd->nd_nam)->sin_addr.s_addr;
583 nsd->nsd_authlen = nfsd->nfsd_authlen;
584 nsd->nsd_verflen = nfsd->nfsd_verflen;
585 if (!copyout(nfsd->nfsd_authstr,nsd->nsd_authstr,
586 nfsd->nfsd_authlen) &&
587 !copyout(nfsd->nfsd_verfstr, nsd->nsd_verfstr,
588 nfsd->nfsd_verflen) &&
589 !copyout((caddr_t)nsd, argp, sizeof (*nsd)))
590 return (ENEEDAUTH);
591 cacherep = RC_DROPIT;
592 } else
593 cacherep = nfsrv_getcache(nd, slp, &mreq);
594
595 /*
596 * Check for just starting up for NQNFS and send
597 * fake "try again later" replies to the NQNFS clients.
598 */
599 if (notstarted && nqnfsstarttime <= time_second) {
600 if (modify_flag) {
601 nqnfsstarttime = time_second + nqsrv_writeslack;
602 modify_flag = 0;
603 } else
604 notstarted = 0;
605 }
606 if (notstarted) {
607 if ((nd->nd_flag & ND_NQNFS) == 0)
608 cacherep = RC_DROPIT;
609 else if (nd->nd_procnum != NFSPROC_WRITE) {
610 nd->nd_procnum = NFSPROC_NOOP;
611 nd->nd_repstat = NQNFS_TRYLATER;
612 cacherep = RC_DOIT;
613 } else
614 modify_flag = 1;
615 } else if (nfsd->nfsd_flag & NFSD_AUTHFAIL) {
616 nfsd->nfsd_flag &= ~NFSD_AUTHFAIL;
617 nd->nd_procnum = NFSPROC_NOOP;
618 nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK);
619 cacherep = RC_DOIT;
620 } else if (nfs_privport) {
621 /* Check if source port is privileged */
622 u_short port;
623 struct sockaddr *nam = nd->nd_nam;
624 struct sockaddr_in *sin;
625
626 sin = (struct sockaddr_in *)nam;
627 port = ntohs(sin->sin_port);
628 if (port >= IPPORT_RESERVED &&
629 nd->nd_procnum != NFSPROC_NULL) {
630 nd->nd_procnum = NFSPROC_NOOP;
631 nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK);
632 cacherep = RC_DOIT;
633 printf("NFS request from unprivileged port (%s:%d)\n",
634 inet_ntoa(sin->sin_addr), port);
635 }
636 }
637
638 }
639
640 /*
641 * Loop to get all the write rpc relies that have been
642 * gathered together.
643 */
644 do {
645 switch (cacherep) {
646 case RC_DOIT:
647 if (nd && (nd->nd_flag & ND_NFSV3))
648 procrastinate = nfsrvw_procrastinate_v3;
649 else
650 procrastinate = nfsrvw_procrastinate;
651 if (writes_todo || (nd->nd_procnum == NFSPROC_WRITE &&
652 procrastinate > 0 && !notstarted))
653 error = nfsrv_writegather(&nd, slp,
654 nfsd->nfsd_procp, &mreq);
655 else
656 error = (*(nfsrv3_procs[nd->nd_procnum]))(nd,
657 slp, nfsd->nfsd_procp, &mreq);
658 if (mreq == NULL)
659 break;
660 if (error != 0 && error != NFSERR_RETVOID) {
661 if (nd->nd_procnum != NQNFSPROC_VACATED)
662 nfsstats.srv_errs++;
663 nfsrv_updatecache(nd, FALSE, mreq);
664 if (nd->nd_nam2)
665 FREE(nd->nd_nam2, M_SONAME);
666 break;
667 }
668 nfsstats.srvrpccnt[nd->nd_procnum]++;
669 nfsrv_updatecache(nd, TRUE, mreq);
670 nd->nd_mrep = (struct mbuf *)0;
671 case RC_REPLY:
672 m = mreq;
673 siz = 0;
674 while (m) {
675 siz += m->m_len;
676 m = m->m_next;
677 }
678 if (siz <= 0 || siz > NFS_MAXPACKET) {
679 printf("mbuf siz=%d\n",siz);
680 panic("Bad nfs svc reply");
681 }
682 m = mreq;
683 m->m_pkthdr.len = siz;
684 m->m_pkthdr.rcvif = (struct ifnet *)0;
685 /*
686 * For stream protocols, prepend a Sun RPC
687 * Record Mark.
688 */
689 if (sotype == SOCK_STREAM) {
690 M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
691 *mtod(m, u_int32_t *) = htonl(0x80000000 | siz);
692 }
693 if (solockp)
694 (void) nfs_sndlock(solockp, solockp,
695 (struct nfsreq *)0);
696 if (slp->ns_flag & SLP_VALID)
697 error = nfs_send(so, nd->nd_nam2, m, NULL);
698 else {
699 error = EPIPE;
700 m_freem(m);
701 }
702 if (nfsrtton)
703 nfsd_rt(sotype, nd, cacherep);
704 if (nd->nd_nam2)
705 FREE(nd->nd_nam2, M_SONAME);
706 if (nd->nd_mrep)
707 m_freem(nd->nd_mrep);
708 if (error == EPIPE)
709 nfsrv_zapsock(slp);
710 if (solockp)
711 nfs_sndunlock(solockp, solockp);
712 if (error == EINTR || error == ERESTART) {
713 free((caddr_t)nd, M_NFSRVDESC);
714 nfsrv_slpderef(slp);
715 s = splnet();
716 goto done;
717 }
718 break;
719 case RC_DROPIT:
720 if (nfsrtton)
721 nfsd_rt(sotype, nd, cacherep);
722 m_freem(nd->nd_mrep);
723 if (nd->nd_nam2)
724 FREE(nd->nd_nam2, M_SONAME);
725 break;
726 };
727 if (nd) {
728 FREE((caddr_t)nd, M_NFSRVDESC);
729 nd = NULL;
730 }
731
732 /*
733 * Check to see if there are outstanding writes that
734 * need to be serviced.
735 */
736 cur_usec = nfs_curusec();
737 s = splsoftclock();
738 if (slp->ns_tq.lh_first &&
739 slp->ns_tq.lh_first->nd_time <= cur_usec) {
740 cacherep = RC_DOIT;
741 writes_todo = 1;
742 } else
743 writes_todo = 0;
744 splx(s);
745 } while (writes_todo);
746 s = splnet();
747 if (nfsrv_dorec(slp, nfsd, &nd)) {
748 nfsd->nfsd_flag &= ~NFSD_REQINPROG;
749 nfsd->nfsd_slp = NULL;
750 nfsrv_slpderef(slp);
751 }
752 }
753 done:
754 TAILQ_REMOVE(&nfsd_head, nfsd, nfsd_chain);
755 splx(s);
756 free((caddr_t)nfsd, M_NFSD);
757 nsd->nsd_nfsd = (struct nfsd *)0;
758 if (--nfs_numnfsd == 0)
759 nfsrv_init(TRUE); /* Reinitialize everything */
760 return (error);
761 }
762
763 /*
764 * Shut down a socket associated with an nfssvc_sock structure.
765 * Should be called with the send lock set, if required.
766 * The trick here is to increment the sref at the start, so that the nfsds
767 * will stop using it and clear ns_flag at the end so that it will not be
768 * reassigned during cleanup.
769 */
770 static void
771 nfsrv_zapsock(slp)
772 register struct nfssvc_sock *slp;
773 {
774 register struct nfsuid *nuidp, *nnuidp;
775 register struct nfsrv_descript *nwp, *nnwp;
776 struct socket *so;
777 struct file *fp;
778 struct nfsrv_rec *rec;
779 int s;
780
781 slp->ns_flag &= ~SLP_ALLFLAGS;
782 fp = slp->ns_fp;
783 if (fp) {
784 slp->ns_fp = (struct file *)0;
785 so = slp->ns_so;
786 so->so_rcv.sb_flags &= ~SB_UPCALL;
787 so->so_upcall = NULL;
788 so->so_upcallarg = NULL;
789 soshutdown(so, 2);
790 closef(fp, (struct proc *)0);
791 if (slp->ns_nam)
792 FREE(slp->ns_nam, M_SONAME);
793 m_freem(slp->ns_raw);
794 while (rec = STAILQ_FIRST(&slp->ns_rec)) {
795 STAILQ_REMOVE_HEAD(&slp->ns_rec, nr_link);
796 if (rec->nr_address)
797 FREE(rec->nr_address, M_SONAME);
798 m_freem(rec->nr_packet);
799 free(rec, M_NFSRVDESC);
800 }
801 for (nuidp = slp->ns_uidlruhead.tqh_first; nuidp != 0;
802 nuidp = nnuidp) {
803 nnuidp = nuidp->nu_lru.tqe_next;
804 LIST_REMOVE(nuidp, nu_hash);
805 TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, nu_lru);
806 if (nuidp->nu_flag & NU_NAM)
807 FREE(nuidp->nu_nam, M_SONAME);
808 free((caddr_t)nuidp, M_NFSUID);
809 }
810 s = splsoftclock();
811 for (nwp = slp->ns_tq.lh_first; nwp; nwp = nnwp) {
812 nnwp = nwp->nd_tq.le_next;
813 LIST_REMOVE(nwp, nd_tq);
814 free((caddr_t)nwp, M_NFSRVDESC);
815 }
816 LIST_INIT(&slp->ns_tq);
817 splx(s);
818 }
819 }
820
821 /*
822 * Derefence a server socket structure. If it has no more references and
823 * is no longer valid, you can throw it away.
824 */
825 void
826 nfsrv_slpderef(slp)
827 register struct nfssvc_sock *slp;
828 {
829 if (--(slp->ns_sref) == 0 && (slp->ns_flag & SLP_VALID) == 0) {
830 TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain);
831 free((caddr_t)slp, M_NFSSVC);
832 }
833 }
834
835 /*
836 * Initialize the data structures for the server.
837 * Handshake with any new nfsds starting up to avoid any chance of
838 * corruption.
839 */
840 void
841 nfsrv_init(terminating)
842 int terminating;
843 {
844 register struct nfssvc_sock *slp, *nslp;
845
846 if (nfssvc_sockhead_flag & SLP_INIT)
847 panic("nfsd init");
848 nfssvc_sockhead_flag |= SLP_INIT;
849 if (terminating) {
850 for (slp = nfssvc_sockhead.tqh_first; slp != 0; slp = nslp) {
851 nslp = slp->ns_chain.tqe_next;
852 if (slp->ns_flag & SLP_VALID)
853 nfsrv_zapsock(slp);
854 TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain);
855 free((caddr_t)slp, M_NFSSVC);
856 }
857 nfsrv_cleancache(); /* And clear out server cache */
858 } else
859 nfs_pub.np_valid = 0;
860
861 TAILQ_INIT(&nfssvc_sockhead);
862 nfssvc_sockhead_flag &= ~SLP_INIT;
863 if (nfssvc_sockhead_flag & SLP_WANTINIT) {
864 nfssvc_sockhead_flag &= ~SLP_WANTINIT;
865 wakeup((caddr_t)&nfssvc_sockhead);
866 }
867
868 TAILQ_INIT(&nfsd_head);
869 nfsd_head_flag &= ~NFSD_CHECKSLP;
870
871 nfs_udpsock = (struct nfssvc_sock *)
872 malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
873 bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock));
874 STAILQ_INIT(&nfs_udpsock->ns_rec);
875 TAILQ_INIT(&nfs_udpsock->ns_uidlruhead);
876 TAILQ_INSERT_HEAD(&nfssvc_sockhead, nfs_udpsock, ns_chain);
877
878 nfs_cltpsock = (struct nfssvc_sock *)
879 malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
880 bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock));
881 STAILQ_INIT(&nfs_cltpsock->ns_rec);
882 TAILQ_INIT(&nfs_cltpsock->ns_uidlruhead);
883 TAILQ_INSERT_TAIL(&nfssvc_sockhead, nfs_cltpsock, ns_chain);
884 }
885
886 /*
887 * Add entries to the server monitor log.
888 */
889 static void
890 nfsd_rt(sotype, nd, cacherep)
891 int sotype;
892 register struct nfsrv_descript *nd;
893 int cacherep;
894 {
895 register struct drt *rt;
896
897 rt = &nfsdrt.drt[nfsdrt.pos];
898 if (cacherep == RC_DOIT)
899 rt->flag = 0;
900 else if (cacherep == RC_REPLY)
901 rt->flag = DRT_CACHEREPLY;
902 else
903 rt->flag = DRT_CACHEDROP;
904 if (sotype == SOCK_STREAM)
905 rt->flag |= DRT_TCP;
906 if (nd->nd_flag & ND_NQNFS)
907 rt->flag |= DRT_NQNFS;
908 else if (nd->nd_flag & ND_NFSV3)
909 rt->flag |= DRT_NFSV3;
910 rt->proc = nd->nd_procnum;
911 if (nd->nd_nam->sa_family == AF_INET)
912 rt->ipadr = ((struct sockaddr_in *)nd->nd_nam)->sin_addr.s_addr;
913 else
914 rt->ipadr = INADDR_ANY;
915 rt->resptime = nfs_curusec() - (nd->nd_starttime.tv_sec * 1000000 + nd->nd_starttime.tv_usec);
916 getmicrotime(&rt->tstamp);
917 nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ;
918 }
919 #endif /* NFS_NOSERVER */
920
921 static int nfs_defect = 0;
922 SYSCTL_INT(_vfs_nfs, OID_AUTO, defect, CTLFLAG_RW, &nfs_defect, 0, "");
923
924 /*
925 * Asynchronous I/O daemons for client nfs.
926 * They do read-ahead and write-behind operations on the block I/O cache.
927 * Never returns unless it fails or gets killed.
928 */
929 static int
930 nfssvc_iod(p)
931 struct proc *p;
932 {
933 register struct buf *bp;
934 register int i, myiod;
935 struct nfsmount *nmp;
936 int error = 0;
937
938 /*
939 * Assign my position or return error if too many already running
940 */
941 myiod = -1;
942 for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
943 if (nfs_asyncdaemon[i] == 0) {
944 nfs_asyncdaemon[i]++;
945 myiod = i;
946 break;
947 }
948 if (myiod == -1)
949 return (EBUSY);
950 nfs_numasync++;
951 /*
952 * Just loop around doin our stuff until SIGKILL
953 */
954 for (;;) {
955 while (((nmp = nfs_iodmount[myiod]) == NULL
956 || nmp->nm_bufq.tqh_first == NULL)
957 && error == 0) {
958 if (nmp)
959 nmp->nm_bufqiods--;
960 nfs_iodwant[myiod] = p;
961 nfs_iodmount[myiod] = NULL;
962 error = tsleep((caddr_t)&nfs_iodwant[myiod],
963 PWAIT | PCATCH, "nfsidl", 0);
964 }
965 if (error) {
966 nfs_asyncdaemon[myiod] = 0;
967 if (nmp)
968 nmp->nm_bufqiods--;
969 nfs_iodwant[myiod] = NULL;
970 nfs_iodmount[myiod] = NULL;
971 nfs_numasync--;
972 return (error);
973 }
974 while ((bp = nmp->nm_bufq.tqh_first) != NULL) {
975 /* Take one off the front of the list */
976 TAILQ_REMOVE(&nmp->nm_bufq, bp, b_freelist);
977 nmp->nm_bufqlen--;
978 if (nmp->nm_bufqwant && nmp->nm_bufqlen < 2 * nfs_numasync) {
979 nmp->nm_bufqwant = FALSE;
980 wakeup(&nmp->nm_bufq);
981 }
982 if (bp->b_flags & B_READ)
983 (void) nfs_doio(bp, bp->b_rcred, (struct proc *)0);
984 else
985 (void) nfs_doio(bp, bp->b_wcred, (struct proc *)0);
986 /*
987 * If there are more than one iod on this mount, then defect
988 * so that the iods can be shared out fairly between the mounts
989 */
990 if (nfs_defect && nmp->nm_bufqiods > 1) {
991 NFS_DPF(ASYNCIO,
992 ("nfssvc_iod: iod %d defecting from mount %p\n",
993 myiod, nmp));
994 nfs_iodmount[myiod] = NULL;
995 nmp->nm_bufqiods--;
996 break;
997 }
998 }
999 }
1000 }
1001
1002
1003 /*
1004 * Get an authorization string for the uid by having the mount_nfs sitting
1005 * on this mount point porpous out of the kernel and do it.
1006 */
1007 int
1008 nfs_getauth(nmp, rep, cred, auth_str, auth_len, verf_str, verf_len, key)
1009 register struct nfsmount *nmp;
1010 struct nfsreq *rep;
1011 struct ucred *cred;
1012 char **auth_str;
1013 int *auth_len;
1014 char *verf_str;
1015 int *verf_len;
1016 NFSKERBKEY_T key; /* return session key */
1017 {
1018 int error = 0;
1019
1020 while ((nmp->nm_state & NFSSTA_WAITAUTH) == 0) {
1021 nmp->nm_state |= NFSSTA_WANTAUTH;
1022 (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK,
1023 "nfsauth1", 2 * hz);
1024 error = nfs_sigintr(nmp, rep, rep->r_procp);
1025 if (error) {
1026 nmp->nm_state &= ~NFSSTA_WANTAUTH;
1027 return (error);
1028 }
1029 }
1030 nmp->nm_state &= ~(NFSSTA_WAITAUTH | NFSSTA_WANTAUTH);
1031 nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK);
1032 nmp->nm_authlen = RPCAUTH_MAXSIZ;
1033 nmp->nm_verfstr = verf_str;
1034 nmp->nm_verflen = *verf_len;
1035 nmp->nm_authuid = cred->cr_uid;
1036 wakeup((caddr_t)&nmp->nm_authstr);
1037
1038 /*
1039 * And wait for mount_nfs to do its stuff.
1040 */
1041 while ((nmp->nm_state & NFSSTA_HASAUTH) == 0 && error == 0) {
1042 (void) tsleep((caddr_t)&nmp->nm_authlen, PSOCK,
1043 "nfsauth2", 2 * hz);
1044 error = nfs_sigintr(nmp, rep, rep->r_procp);
1045 }
1046 if (nmp->nm_state & NFSSTA_AUTHERR) {
1047 nmp->nm_state &= ~NFSSTA_AUTHERR;
1048 error = EAUTH;
1049 }
1050 if (error)
1051 free((caddr_t)*auth_str, M_TEMP);
1052 else {
1053 *auth_len = nmp->nm_authlen;
1054 *verf_len = nmp->nm_verflen;
1055 bcopy((caddr_t)nmp->nm_key, (caddr_t)key, sizeof (key));
1056 }
1057 nmp->nm_state &= ~NFSSTA_HASAUTH;
1058 nmp->nm_state |= NFSSTA_WAITAUTH;
1059 if (nmp->nm_state & NFSSTA_WANTAUTH) {
1060 nmp->nm_state &= ~NFSSTA_WANTAUTH;
1061 wakeup((caddr_t)&nmp->nm_authtype);
1062 }
1063 return (error);
1064 }
1065
1066 /*
1067 * Get a nickname authenticator and verifier.
1068 */
1069 int
1070 nfs_getnickauth(nmp, cred, auth_str, auth_len, verf_str, verf_len)
1071 struct nfsmount *nmp;
1072 struct ucred *cred;
1073 char **auth_str;
1074 int *auth_len;
1075 char *verf_str;
1076 int verf_len;
1077 {
1078 register struct nfsuid *nuidp;
1079 register u_int32_t *nickp, *verfp;
1080 struct timeval ktvin, ktvout;
1081
1082 #ifdef DIAGNOSTIC
1083 if (verf_len < (4 * NFSX_UNSIGNED))
1084 panic("nfs_getnickauth verf too small");
1085 #endif
1086 for (nuidp = NMUIDHASH(nmp, cred->cr_uid)->lh_first;
1087 nuidp != 0; nuidp = nuidp->nu_hash.le_next) {
1088 if (nuidp->nu_cr.cr_uid == cred->cr_uid)
1089 break;
1090 }
1091 if (!nuidp || nuidp->nu_expire < time_second)
1092 return (EACCES);
1093
1094 /*
1095 * Move to the end of the lru list (end of lru == most recently used).
1096 */
1097 TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, nu_lru);
1098 TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp, nu_lru);
1099
1100 nickp = (u_int32_t *)malloc(2 * NFSX_UNSIGNED, M_TEMP, M_WAITOK);
1101 *nickp++ = txdr_unsigned(RPCAKN_NICKNAME);
1102 *nickp = txdr_unsigned(nuidp->nu_nickname);
1103 *auth_str = (char *)nickp;
1104 *auth_len = 2 * NFSX_UNSIGNED;
1105
1106 /*
1107 * Now we must encrypt the verifier and package it up.
1108 */
1109 verfp = (u_int32_t *)verf_str;
1110 *verfp++ = txdr_unsigned(RPCAKN_NICKNAME);
1111 if (time_second > nuidp->nu_timestamp.tv_sec ||
1112 (time_second == nuidp->nu_timestamp.tv_sec &&
1113 time_second > nuidp->nu_timestamp.tv_usec))
1114 getmicrotime(&nuidp->nu_timestamp);
1115 else
1116 nuidp->nu_timestamp.tv_usec++;
1117 ktvin.tv_sec = txdr_unsigned(nuidp->nu_timestamp.tv_sec);
1118 ktvin.tv_usec = txdr_unsigned(nuidp->nu_timestamp.tv_usec);
1119
1120 /*
1121 * Now encrypt the timestamp verifier in ecb mode using the session
1122 * key.
1123 */
1124 #ifdef NFSKERB
1125 XXX
1126 #endif
1127
1128 *verfp++ = ktvout.tv_sec;
1129 *verfp++ = ktvout.tv_usec;
1130 *verfp = 0;
1131 return (0);
1132 }
1133
1134 /*
1135 * Save the current nickname in a hash list entry on the mount point.
1136 */
1137 int
1138 nfs_savenickauth(nmp, cred, len, key, mdp, dposp, mrep)
1139 register struct nfsmount *nmp;
1140 struct ucred *cred;
1141 int len;
1142 NFSKERBKEY_T key;
1143 struct mbuf **mdp;
1144 char **dposp;
1145 struct mbuf *mrep;
1146 {
1147 register struct nfsuid *nuidp;
1148 register u_int32_t *tl;
1149 register int32_t t1;
1150 struct mbuf *md = *mdp;
1151 struct timeval ktvin, ktvout;
1152 u_int32_t nick;
1153 char *dpos = *dposp, *cp2;
1154 int deltasec, error = 0;
1155
1156 if (len == (3 * NFSX_UNSIGNED)) {
1157 nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
1158 ktvin.tv_sec = *tl++;
1159 ktvin.tv_usec = *tl++;
1160 nick = fxdr_unsigned(u_int32_t, *tl);
1161
1162 /*
1163 * Decrypt the timestamp in ecb mode.
1164 */
1165 #ifdef NFSKERB
1166 XXX
1167 #endif
1168 ktvout.tv_sec = fxdr_unsigned(long, ktvout.tv_sec);
1169 ktvout.tv_usec = fxdr_unsigned(long, ktvout.tv_usec);
1170 deltasec = time_second - ktvout.tv_sec;
1171 if (deltasec < 0)
1172 deltasec = -deltasec;
1173 /*
1174 * If ok, add it to the hash list for the mount point.
1175 */
1176 if (deltasec <= NFS_KERBCLOCKSKEW) {
1177 if (nmp->nm_numuids < nuidhash_max) {
1178 nmp->nm_numuids++;
1179 nuidp = (struct nfsuid *)
1180 malloc(sizeof (struct nfsuid), M_NFSUID,
1181 M_WAITOK);
1182 } else {
1183 nuidp = nmp->nm_uidlruhead.tqh_first;
1184 LIST_REMOVE(nuidp, nu_hash);
1185 TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp,
1186 nu_lru);
1187 }
1188 nuidp->nu_flag = 0;
1189 nuidp->nu_cr.cr_uid = cred->cr_uid;
1190 nuidp->nu_expire = time_second + NFS_KERBTTL;
1191 nuidp->nu_timestamp = ktvout;
1192 nuidp->nu_nickname = nick;
1193 bcopy(key, nuidp->nu_key, sizeof (key));
1194 TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp,
1195 nu_lru);
1196 LIST_INSERT_HEAD(NMUIDHASH(nmp, cred->cr_uid),
1197 nuidp, nu_hash);
1198 }
1199 } else
1200 nfsm_adv(nfsm_rndup(len));
1201 nfsmout:
1202 *mdp = md;
1203 *dposp = dpos;
1204 return (error);
1205 }
Cache object: f9af9d4b89fd48eba34d9fe9200761e9
|