[BACK]Return to nfs_syscalls.c CVS log [TXT][DIR] Up to [local] / sys / nfs

Annotation of sys/nfs/nfs_syscalls.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: nfs_syscalls.c,v 1.55 2007/06/25 20:40:00 thib Exp $  */
        !             2: /*     $NetBSD: nfs_syscalls.c,v 1.19 1996/02/18 11:53:52 fvdl Exp $   */
        !             3:
        !             4: /*
        !             5:  * Copyright (c) 1989, 1993
        !             6:  *     The Regents of the University of California.  All rights reserved.
        !             7:  *
        !             8:  * This code is derived from software contributed to Berkeley by
        !             9:  * Rick Macklem at The University of Guelph.
        !            10:  *
        !            11:  * Redistribution and use in source and binary forms, with or without
        !            12:  * modification, are permitted provided that the following conditions
        !            13:  * are met:
        !            14:  * 1. Redistributions of source code must retain the above copyright
        !            15:  *    notice, this list of conditions and the following disclaimer.
        !            16:  * 2. Redistributions in binary form must reproduce the above copyright
        !            17:  *    notice, this list of conditions and the following disclaimer in the
        !            18:  *    documentation and/or other materials provided with the distribution.
        !            19:  * 3. Neither the name of the University nor the names of its contributors
        !            20:  *    may be used to endorse or promote products derived from this software
        !            21:  *    without specific prior written permission.
        !            22:  *
        !            23:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            24:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            25:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            26:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            27:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            28:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            29:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            31:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            32:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            33:  * SUCH DAMAGE.
        !            34:  *
        !            35:  *     @(#)nfs_syscalls.c      8.5 (Berkeley) 3/30/95
        !            36:  */
        !            37:
        !            38: #include <sys/param.h>
        !            39: #include <sys/systm.h>
        !            40: #include <sys/kernel.h>
        !            41: #include <sys/file.h>
        !            42: #include <sys/stat.h>
        !            43: #include <sys/vnode.h>
        !            44: #include <sys/mount.h>
        !            45: #include <sys/proc.h>
        !            46: #include <sys/uio.h>
        !            47: #include <sys/malloc.h>
        !            48: #include <sys/buf.h>
        !            49: #include <sys/mbuf.h>
        !            50: #include <sys/socket.h>
        !            51: #include <sys/socketvar.h>
        !            52: #include <sys/domain.h>
        !            53: #include <sys/protosw.h>
        !            54: #include <sys/namei.h>
        !            55: #include <sys/syslog.h>
        !            56: #include <sys/filedesc.h>
        !            57: #include <sys/signalvar.h>
        !            58: #include <sys/kthread.h>
        !            59:
        !            60: #include <sys/syscallargs.h>
        !            61:
        !            62: #include <netinet/in.h>
        !            63: #include <netinet/tcp.h>
        !            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/nfsrtt.h>
        !            73: #include <nfs/nfs_var.h>
        !            74:
        !            75: void   nfsrv_zapsock(struct nfssvc_sock *);
        !            76:
        !            77: /* Global defs. */
        !            78: extern int32_t (*nfsrv3_procs[NFS_NPROCS])(struct nfsrv_descript *,
        !            79:                                                struct nfssvc_sock *,
        !            80:                                                struct proc *, struct mbuf **);
        !            81: extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
        !            82: extern int nfs_numasync;
        !            83: extern int nfsrtton;
        !            84: extern struct nfsstats nfsstats;
        !            85: extern int nfsrvw_procrastinate;
        !            86: struct nfssvc_sock *nfs_udpsock;
        !            87: int nuidhash_max = NFS_MAXUIDHASH;
        !            88: int nfsd_waiting = 0;
        !            89: #ifdef NFSSERVER
        !            90: static int nfs_numnfsd = 0;
        !            91: static struct nfsdrt nfsdrt;
        !            92: #endif
        !            93:
        !            94: struct nfssvc_sockhead nfssvc_sockhead;
        !            95: struct nfsdhead nfsd_head;
        !            96:
        !            97: int nfssvc_sockhead_flag;
        !            98: int nfsd_head_flag;
        !            99:
        !           100: #define        TRUE    1
        !           101: #define        FALSE   0
        !           102:
        !           103: #ifdef NFSCLIENT
        !           104: struct proc *nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
        !           105: int nfs_niothreads = -1;
        !           106: #endif
        !           107:
        !           108: #ifdef NFSSERVER
        !           109: static void nfsd_rt(int, struct nfsrv_descript *, int);
        !           110: #endif
        !           111:
        !           112: /*
        !           113:  * NFS server pseudo system call for the nfsd's
        !           114:  * Based on the flag value it either:
        !           115:  * - adds a socket to the selection list
        !           116:  * - remains in the kernel as an nfsd
        !           117:  */
        !           118: int
        !           119: sys_nfssvc(struct proc *p, void *v, register_t *retval)
        !           120: {
        !           121:        int error = 0;
        !           122: #ifdef NFSSERVER
        !           123:        struct sys_nfssvc_args /* {
        !           124:                syscallarg(int) flag;
        !           125:                syscallarg(caddr_t) argp;
        !           126:        } */ *uap = v;
        !           127:        int flags = SCARG(uap, flag);
        !           128:        struct file *fp;
        !           129:        struct mbuf *nam;
        !           130:        struct nfsd_args nfsdarg;
        !           131:        struct nfsd_srvargs nfsd_srvargs, *nsd = &nfsd_srvargs;
        !           132: #endif
        !           133:
        !           134:        /* Must be super user */
        !           135:        error = suser(p, 0);
        !           136:        if (error)
        !           137:                return (error);
        !           138:
        !           139: #ifndef NFSSERVER
        !           140:        error = ENOSYS;
        !           141: #else
        !           142:
        !           143:        while (nfssvc_sockhead_flag & SLP_INIT) {
        !           144:                nfssvc_sockhead_flag |= SLP_WANTINIT;
        !           145:                tsleep(&nfssvc_sockhead, PSOCK, "nfsd init", 0);
        !           146:        }
        !           147:
        !           148:        switch (flags) {
        !           149:        case NFSSVC_ADDSOCK:
        !           150:                error = copyin(SCARG(uap, argp), &nfsdarg, sizeof(nfsdarg));
        !           151:                if (error)
        !           152:                        return (error);
        !           153:
        !           154:                error = getsock(p->p_fd, nfsdarg.sock, &fp);
        !           155:                if (error)
        !           156:                        return (error);
        !           157:
        !           158:                /*
        !           159:                 * Get the client address for connected sockets.
        !           160:                 */
        !           161:                if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
        !           162:                        nam = NULL;
        !           163:                else {
        !           164:                        error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
        !           165:                                MT_SONAME);
        !           166:                        if (error) {
        !           167:                                FRELE(fp);
        !           168:                                return (error);
        !           169:                        }
        !           170:                }
        !           171:                error = nfssvc_addsock(fp, nam);
        !           172:                FRELE(fp);
        !           173:                break;
        !           174:        case NFSSVC_NFSD:
        !           175:                error = copyin(SCARG(uap, argp), nsd, sizeof(*nsd));
        !           176:                if (error)
        !           177:                        return (error);
        !           178:
        !           179:                error = nfssvc_nfsd(nsd, SCARG(uap, argp), p);
        !           180:                break;
        !           181:        default:
        !           182:                error = EINVAL;
        !           183:                break;
        !           184:        }
        !           185:
        !           186:        if (error == EINTR || error == ERESTART)
        !           187:                error = 0;
        !           188: #endif /* !NFSSERVER */
        !           189:
        !           190:        return (error);
        !           191: }
        !           192:
        !           193: #ifdef NFSSERVER
        !           194: /*
        !           195:  * Adds a socket to the list for servicing by nfsds.
        !           196:  */
        !           197: int
        !           198: nfssvc_addsock(fp, mynam)
        !           199:        struct file *fp;
        !           200:        struct mbuf *mynam;
        !           201: {
        !           202:        struct mbuf *m;
        !           203:        int siz;
        !           204:        struct nfssvc_sock *slp;
        !           205:        struct socket *so;
        !           206:        struct nfssvc_sock *tslp;
        !           207:        int error, s;
        !           208:
        !           209:        so = (struct socket *)fp->f_data;
        !           210:        tslp = (struct nfssvc_sock *)0;
        !           211:        /*
        !           212:         * Add it to the list, as required.
        !           213:         */
        !           214:        if (so->so_proto->pr_protocol == IPPROTO_UDP) {
        !           215:                tslp = nfs_udpsock;
        !           216:                if (tslp->ns_flag & SLP_VALID) {
        !           217:                        m_freem(mynam);
        !           218:                        return (EPERM);
        !           219:                }
        !           220:        }
        !           221:        if (so->so_type == SOCK_STREAM)
        !           222:                siz = NFS_MAXPACKET + sizeof (u_long);
        !           223:        else
        !           224:                siz = NFS_MAXPACKET;
        !           225:        error = soreserve(so, siz, siz);
        !           226:        if (error) {
        !           227:                m_freem(mynam);
        !           228:                return (error);
        !           229:        }
        !           230:
        !           231:        /*
        !           232:         * Set protocol specific options { for now TCP only } and
        !           233:         * reserve some space. For datagram sockets, this can get called
        !           234:         * repeatedly for the same socket, but that isn't harmful.
        !           235:         */
        !           236:        if (so->so_type == SOCK_STREAM) {
        !           237:                MGET(m, M_WAIT, MT_SOOPTS);
        !           238:                *mtod(m, int32_t *) = 1;
        !           239:                m->m_len = sizeof(int32_t);
        !           240:                sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
        !           241:        }
        !           242:        if (so->so_proto->pr_domain->dom_family == AF_INET &&
        !           243:            so->so_proto->pr_protocol == IPPROTO_TCP) {
        !           244:                MGET(m, M_WAIT, MT_SOOPTS);
        !           245:                *mtod(m, int32_t *) = 1;
        !           246:                m->m_len = sizeof(int32_t);
        !           247:                sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
        !           248:        }
        !           249:        so->so_rcv.sb_flags &= ~SB_NOINTR;
        !           250:        so->so_rcv.sb_timeo = 0;
        !           251:        so->so_snd.sb_flags &= ~SB_NOINTR;
        !           252:        so->so_snd.sb_timeo = 0;
        !           253:        if (tslp)
        !           254:                slp = tslp;
        !           255:        else {
        !           256:                slp = (struct nfssvc_sock *)
        !           257:                        malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
        !           258:                bzero((caddr_t)slp, sizeof (struct nfssvc_sock));
        !           259:                TAILQ_INIT(&slp->ns_uidlruhead);
        !           260:                TAILQ_INSERT_TAIL(&nfssvc_sockhead, slp, ns_chain);
        !           261:        }
        !           262:        slp->ns_so = so;
        !           263:        slp->ns_nam = mynam;
        !           264:        fp->f_count++;
        !           265:        slp->ns_fp = fp;
        !           266:        s = splsoftnet();
        !           267:        so->so_upcallarg = (caddr_t)slp;
        !           268:        so->so_upcall = nfsrv_rcv;
        !           269:        slp->ns_flag = (SLP_VALID | SLP_NEEDQ);
        !           270:        nfsrv_wakenfsd(slp);
        !           271:        splx(s);
        !           272:        return (0);
        !           273: }
        !           274:
        !           275: /*
        !           276:  * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
        !           277:  * until it is killed by a signal.
        !           278:  */
        !           279: int
        !           280: nfssvc_nfsd(nsd, argp, p)
        !           281:        struct nfsd_srvargs *nsd;
        !           282:        caddr_t argp;
        !           283:        struct proc *p;
        !           284: {
        !           285:        struct mbuf *m;
        !           286:        int siz;
        !           287:        struct nfssvc_sock *slp;
        !           288:        struct socket *so;
        !           289:        int *solockp;
        !           290:        struct nfsd *nfsd = nsd->nsd_nfsd;
        !           291:        struct nfsrv_descript *nd = NULL;
        !           292:        struct mbuf *mreq;
        !           293:        int error = 0, cacherep, s, sotype, writes_todo;
        !           294:        u_quad_t cur_usec;
        !           295:        struct timeval tv;
        !           296:
        !           297: #ifndef nolint
        !           298:        cacherep = RC_DOIT;
        !           299:        writes_todo = 0;
        !           300: #endif
        !           301:        s = splsoftnet();
        !           302:        if (nfsd == (struct nfsd *)0) {
        !           303:                nsd->nsd_nfsd = nfsd = (struct nfsd *)
        !           304:                        malloc(sizeof (struct nfsd), M_NFSD, M_WAITOK);
        !           305:                bzero((caddr_t)nfsd, sizeof (struct nfsd));
        !           306:                nfsd->nfsd_procp = p;
        !           307:                TAILQ_INSERT_TAIL(&nfsd_head, nfsd, nfsd_chain);
        !           308:                nfs_numnfsd++;
        !           309:        }
        !           310:        /*
        !           311:         * Loop getting rpc requests until SIGKILL.
        !           312:         */
        !           313:        for (;;) {
        !           314:                if ((nfsd->nfsd_flag & NFSD_REQINPROG) == 0) {
        !           315:                        while (nfsd->nfsd_slp == (struct nfssvc_sock *)0 &&
        !           316:                            (nfsd_head_flag & NFSD_CHECKSLP) == 0) {
        !           317:                                nfsd->nfsd_flag |= NFSD_WAITING;
        !           318:                                nfsd_waiting++;
        !           319:                                error = tsleep((caddr_t)nfsd, PSOCK | PCATCH,
        !           320:                                    "nfsd", 0);
        !           321:                                nfsd_waiting--;
        !           322:                                if (error)
        !           323:                                        goto done;
        !           324:                        }
        !           325:                        if (nfsd->nfsd_slp == NULL &&
        !           326:                            (nfsd_head_flag & NFSD_CHECKSLP) != 0) {
        !           327:                                for (slp = TAILQ_FIRST(&nfssvc_sockhead);
        !           328:                                    slp != 0; slp = TAILQ_NEXT(slp, ns_chain)) {
        !           329:                                    if ((slp->ns_flag & (SLP_VALID | SLP_DOREC))
        !           330:                                        == (SLP_VALID | SLP_DOREC)) {
        !           331:                                            slp->ns_flag &= ~SLP_DOREC;
        !           332:                                            slp->ns_sref++;
        !           333:                                            nfsd->nfsd_slp = slp;
        !           334:                                            break;
        !           335:                                    }
        !           336:                                }
        !           337:                                if (slp == 0)
        !           338:                                        nfsd_head_flag &= ~NFSD_CHECKSLP;
        !           339:                        }
        !           340:                        if ((slp = nfsd->nfsd_slp) == (struct nfssvc_sock *)0)
        !           341:                                continue;
        !           342:                        if (slp->ns_flag & SLP_VALID) {
        !           343:                                struct timeval tv;
        !           344:
        !           345:                                if (slp->ns_flag & SLP_DISCONN)
        !           346:                                        nfsrv_zapsock(slp);
        !           347:                                else if (slp->ns_flag & SLP_NEEDQ) {
        !           348:                                        slp->ns_flag &= ~SLP_NEEDQ;
        !           349:                                        (void) nfs_sndlock(&slp->ns_solock,
        !           350:                                                (struct nfsreq *)0);
        !           351:                                        nfsrv_rcv(slp->ns_so, (caddr_t)slp,
        !           352:                                                M_WAIT);
        !           353:                                        nfs_sndunlock(&slp->ns_solock);
        !           354:                                }
        !           355:                                error = nfsrv_dorec(slp, nfsd, &nd);
        !           356:                                getmicrotime(&tv);
        !           357:                                cur_usec = (u_quad_t)tv.tv_sec * 1000000 +
        !           358:                                        (u_quad_t)tv.tv_usec;
        !           359:                                if (error && LIST_FIRST(&slp->ns_tq) &&
        !           360:                                    LIST_FIRST(&slp->ns_tq)->nd_time
        !           361:                                    <= cur_usec) {
        !           362:                                        error = 0;
        !           363:                                        cacherep = RC_DOIT;
        !           364:                                        writes_todo = 1;
        !           365:                                } else
        !           366:                                        writes_todo = 0;
        !           367:                                nfsd->nfsd_flag |= NFSD_REQINPROG;
        !           368:                        }
        !           369:                } else {
        !           370:                        error = 0;
        !           371:                        slp = nfsd->nfsd_slp;
        !           372:                }
        !           373:                if (error || (slp->ns_flag & SLP_VALID) == 0) {
        !           374:                        if (nd) {
        !           375:                                free((caddr_t)nd, M_NFSRVDESC);
        !           376:                                nd = NULL;
        !           377:                        }
        !           378:                        nfsd->nfsd_slp = (struct nfssvc_sock *)0;
        !           379:                        nfsd->nfsd_flag &= ~NFSD_REQINPROG;
        !           380:                        nfsrv_slpderef(slp);
        !           381:                        continue;
        !           382:                }
        !           383:                splx(s);
        !           384:                so = slp->ns_so;
        !           385:                sotype = so->so_type;
        !           386:                if (so->so_proto->pr_flags & PR_CONNREQUIRED)
        !           387:                        solockp = &slp->ns_solock;
        !           388:                else
        !           389:                        solockp = (int *)0;
        !           390:                if (nd) {
        !           391:                    getmicrotime(&nd->nd_starttime);
        !           392:                    if (nd->nd_nam2)
        !           393:                        nd->nd_nam = nd->nd_nam2;
        !           394:                    else
        !           395:                        nd->nd_nam = slp->ns_nam;
        !           396:
        !           397:                    /*
        !           398:                     * Check to see if authorization is needed.
        !           399:                     */
        !           400:                    if (nfsd->nfsd_flag & NFSD_NEEDAUTH) {
        !           401:                        nfsd->nfsd_flag &= ~NFSD_NEEDAUTH;
        !           402:                        nsd->nsd_haddr = mtod(nd->nd_nam,
        !           403:                            struct sockaddr_in *)->sin_addr.s_addr;
        !           404:                        nsd->nsd_authlen = nfsd->nfsd_authlen;
        !           405:                        nsd->nsd_verflen = nfsd->nfsd_verflen;
        !           406:                        if (!copyout(nfsd->nfsd_authstr,nsd->nsd_authstr,
        !           407:                                nfsd->nfsd_authlen) &&
        !           408:                            !copyout(nfsd->nfsd_verfstr, nsd->nsd_verfstr,
        !           409:                                nfsd->nfsd_verflen) &&
        !           410:                            !copyout((caddr_t)nsd, argp, sizeof (*nsd))) {
        !           411:                            return (ENEEDAUTH);
        !           412:                        }
        !           413:                        cacherep = RC_DROPIT;
        !           414:                    } else
        !           415:                        cacherep = nfsrv_getcache(nd, slp, &mreq);
        !           416:
        !           417:                    if (nfsd->nfsd_flag & NFSD_AUTHFAIL) {
        !           418:                            nfsd->nfsd_flag &= ~NFSD_AUTHFAIL;
        !           419:                            nd->nd_procnum = NFSPROC_NOOP;
        !           420:                            nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK);
        !           421:                            cacherep = RC_DOIT;
        !           422:                    }
        !           423:                }
        !           424:
        !           425:                /*
        !           426:                 * Loop to get all the write rpc relies that have been
        !           427:                 * gathered together.
        !           428:                 */
        !           429:                do {
        !           430:                    switch (cacherep) {
        !           431:                    case RC_DOIT:
        !           432:                        if (writes_todo || (!(nd->nd_flag & ND_NFSV3) &&
        !           433:                            nd->nd_procnum == NFSPROC_WRITE &&
        !           434:                            nfsrvw_procrastinate > 0))
        !           435:                                error = nfsrv_writegather(&nd, slp,
        !           436:                                    nfsd->nfsd_procp, &mreq);
        !           437:                        else
        !           438:                                error = (*(nfsrv3_procs[nd->nd_procnum]))(nd,
        !           439:                                    slp, nfsd->nfsd_procp, &mreq);
        !           440:                        if (mreq == NULL)
        !           441:                                break;
        !           442:                        if (error) {
        !           443:                                nfsstats.srv_errs++;
        !           444:                                nfsrv_updatecache(nd, FALSE, mreq);
        !           445:                                if (nd->nd_nam2)
        !           446:                                        m_freem(nd->nd_nam2);
        !           447:                                break;
        !           448:                        }
        !           449:                        nfsstats.srvrpccnt[nd->nd_procnum]++;
        !           450:                        nfsrv_updatecache(nd, TRUE, mreq);
        !           451:                        nd->nd_mrep = (struct mbuf *)0;
        !           452:                    case RC_REPLY:
        !           453:                        m = mreq;
        !           454:                        siz = 0;
        !           455:                        while (m) {
        !           456:                                siz += m->m_len;
        !           457:                                m = m->m_next;
        !           458:                        }
        !           459:                        if (siz <= 0 || siz > NFS_MAXPACKET) {
        !           460:                                printf("mbuf siz=%d\n",siz);
        !           461:                                panic("Bad nfs svc reply");
        !           462:                        }
        !           463:                        m = mreq;
        !           464:                        m->m_pkthdr.len = siz;
        !           465:                        m->m_pkthdr.rcvif = (struct ifnet *)0;
        !           466:                        /*
        !           467:                         * For stream protocols, prepend a Sun RPC
        !           468:                         * Record Mark.
        !           469:                         */
        !           470:                        if (sotype == SOCK_STREAM) {
        !           471:                                M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
        !           472:                                *mtod(m, u_int32_t *) = htonl(0x80000000 | siz);
        !           473:                        }
        !           474:                        if (solockp)
        !           475:                                (void) nfs_sndlock(solockp, (struct nfsreq *)0);
        !           476:                        if (slp->ns_flag & SLP_VALID)
        !           477:                            error = nfs_send(so, nd->nd_nam2, m, NULL);
        !           478:                        else {
        !           479:                            error = EPIPE;
        !           480:                            m_freem(m);
        !           481:                        }
        !           482:                        if (nfsrtton)
        !           483:                                nfsd_rt(sotype, nd, cacherep);
        !           484:                        if (nd->nd_nam2)
        !           485:                                MFREE(nd->nd_nam2, m);
        !           486:                        if (nd->nd_mrep)
        !           487:                                m_freem(nd->nd_mrep);
        !           488:                        if (error == EPIPE)
        !           489:                                nfsrv_zapsock(slp);
        !           490:                        if (solockp)
        !           491:                                nfs_sndunlock(solockp);
        !           492:                        if (error == EINTR || error == ERESTART) {
        !           493:                                free((caddr_t)nd, M_NFSRVDESC);
        !           494:                                nfsrv_slpderef(slp);
        !           495:                                s = splsoftnet();
        !           496:                                goto done;
        !           497:                        }
        !           498:                        break;
        !           499:                    case RC_DROPIT:
        !           500:                        if (nfsrtton)
        !           501:                                nfsd_rt(sotype, nd, cacherep);
        !           502:                        m_freem(nd->nd_mrep);
        !           503:                        m_freem(nd->nd_nam2);
        !           504:                        break;
        !           505:                    };
        !           506:                    if (nd) {
        !           507:                        FREE((caddr_t)nd, M_NFSRVDESC);
        !           508:                        nd = NULL;
        !           509:                    }
        !           510:
        !           511:                    /*
        !           512:                     * Check to see if there are outstanding writes that
        !           513:                     * need to be serviced.
        !           514:                     */
        !           515:                    getmicrotime(&tv);
        !           516:                    cur_usec = (u_quad_t)tv.tv_sec * 1000000 +
        !           517:                        (u_quad_t)tv.tv_usec;
        !           518:                    s = splsoftclock();
        !           519:                    if (LIST_FIRST(&slp->ns_tq) &&
        !           520:                        LIST_FIRST(&slp->ns_tq)->nd_time <= cur_usec) {
        !           521:                        cacherep = RC_DOIT;
        !           522:                        writes_todo = 1;
        !           523:                    } else
        !           524:                        writes_todo = 0;
        !           525:                    splx(s);
        !           526:                } while (writes_todo);
        !           527:                s = splsoftnet();
        !           528:                if (nfsrv_dorec(slp, nfsd, &nd)) {
        !           529:                        nfsd->nfsd_flag &= ~NFSD_REQINPROG;
        !           530:                        nfsd->nfsd_slp = NULL;
        !           531:                        nfsrv_slpderef(slp);
        !           532:                }
        !           533:        }
        !           534: done:
        !           535:        TAILQ_REMOVE(&nfsd_head, nfsd, nfsd_chain);
        !           536:        splx(s);
        !           537:        free((caddr_t)nfsd, M_NFSD);
        !           538:        nsd->nsd_nfsd = (struct nfsd *)0;
        !           539:        if (--nfs_numnfsd == 0)
        !           540:                nfsrv_init(TRUE);       /* Reinitialize everything */
        !           541:        return (error);
        !           542: }
        !           543:
        !           544: /*
        !           545:  * Shut down a socket associated with an nfssvc_sock structure.
        !           546:  * Should be called with the send lock set, if required.
        !           547:  * The trick here is to increment the sref at the start, so that the nfsds
        !           548:  * will stop using it and clear ns_flag at the end so that it will not be
        !           549:  * reassigned during cleanup.
        !           550:  */
        !           551: void
        !           552: nfsrv_zapsock(slp)
        !           553:        struct nfssvc_sock *slp;
        !           554: {
        !           555:        struct nfsuid *nuidp, *nnuidp;
        !           556:        struct nfsrv_descript *nwp, *nnwp;
        !           557:        struct socket *so;
        !           558:        struct file *fp;
        !           559:        struct mbuf *m;
        !           560:        int s;
        !           561:
        !           562:        slp->ns_flag &= ~SLP_ALLFLAGS;
        !           563:        fp = slp->ns_fp;
        !           564:        if (fp) {
        !           565:                FREF(fp);
        !           566:                slp->ns_fp = NULL;
        !           567:                so = slp->ns_so;
        !           568:                so->so_upcall = NULL;
        !           569:                soshutdown(so, SHUT_RDWR);
        !           570:                closef(fp, NULL);
        !           571:                if (slp->ns_nam)
        !           572:                        MFREE(slp->ns_nam, m);
        !           573:                m_freem(slp->ns_raw);
        !           574:                m_freem(slp->ns_rec);
        !           575:                for (nuidp = TAILQ_FIRST(&slp->ns_uidlruhead); nuidp != NULL;
        !           576:                    nuidp = nnuidp) {
        !           577:                        nnuidp = TAILQ_NEXT(nuidp, nu_lru);
        !           578:                        LIST_REMOVE(nuidp, nu_hash);
        !           579:                        TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, nu_lru);
        !           580:                        if (nuidp->nu_flag & NU_NAM)
        !           581:                                m_freem(nuidp->nu_nam);
        !           582:                        free((caddr_t)nuidp, M_NFSUID);
        !           583:                }
        !           584:                s = splsoftclock();
        !           585:                for (nwp = LIST_FIRST(&slp->ns_tq); nwp != NULL; nwp = nnwp) {
        !           586:                        nnwp = LIST_NEXT(nwp, nd_tq);
        !           587:                        LIST_REMOVE(nwp, nd_tq);
        !           588:                        free((caddr_t)nwp, M_NFSRVDESC);
        !           589:                }
        !           590:                LIST_INIT(&slp->ns_tq);
        !           591:                splx(s);
        !           592:        }
        !           593: }
        !           594:
        !           595: /*
        !           596:  * Derefence a server socket structure. If it has no more references and
        !           597:  * is no longer valid, you can throw it away.
        !           598:  */
        !           599: void
        !           600: nfsrv_slpderef(slp)
        !           601:        struct nfssvc_sock *slp;
        !           602: {
        !           603:        if (--(slp->ns_sref) == 0 && (slp->ns_flag & SLP_VALID) == 0) {
        !           604:                TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain);
        !           605:                free((caddr_t)slp, M_NFSSVC);
        !           606:        }
        !           607: }
        !           608:
        !           609: /*
        !           610:  * Initialize the data structures for the server.
        !           611:  * Handshake with any new nfsds starting up to avoid any chance of
        !           612:  * corruption.
        !           613:  */
        !           614: void
        !           615: nfsrv_init(terminating)
        !           616:        int terminating;
        !           617: {
        !           618:        struct nfssvc_sock *slp, *nslp;
        !           619:
        !           620:        if (nfssvc_sockhead_flag & SLP_INIT)
        !           621:                panic("nfsd init");
        !           622:        nfssvc_sockhead_flag |= SLP_INIT;
        !           623:        if (terminating) {
        !           624:                for (slp = TAILQ_FIRST(&nfssvc_sockhead); slp != NULL;
        !           625:                    slp = nslp) {
        !           626:                        nslp = TAILQ_NEXT(slp, ns_chain);
        !           627:                        if (slp->ns_flag & SLP_VALID)
        !           628:                                nfsrv_zapsock(slp);
        !           629:                        TAILQ_REMOVE(&nfssvc_sockhead, slp, ns_chain);
        !           630:                        free((caddr_t)slp, M_NFSSVC);
        !           631:                }
        !           632:                nfsrv_cleancache();     /* And clear out server cache */
        !           633:        }
        !           634:
        !           635:        TAILQ_INIT(&nfssvc_sockhead);
        !           636:        nfssvc_sockhead_flag &= ~SLP_INIT;
        !           637:        if (nfssvc_sockhead_flag & SLP_WANTINIT) {
        !           638:                nfssvc_sockhead_flag &= ~SLP_WANTINIT;
        !           639:                wakeup((caddr_t)&nfssvc_sockhead);
        !           640:        }
        !           641:
        !           642:        TAILQ_INIT(&nfsd_head);
        !           643:        nfsd_head_flag &= ~NFSD_CHECKSLP;
        !           644:
        !           645:        nfs_udpsock = (struct nfssvc_sock *)
        !           646:            malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
        !           647:        bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock));
        !           648:        TAILQ_INIT(&nfs_udpsock->ns_uidlruhead);
        !           649:        TAILQ_INSERT_HEAD(&nfssvc_sockhead, nfs_udpsock, ns_chain);
        !           650: }
        !           651:
        !           652: /*
        !           653:  * Add entries to the server monitor log.
        !           654:  */
        !           655: static void
        !           656: nfsd_rt(sotype, nd, cacherep)
        !           657:        int sotype;
        !           658:        struct nfsrv_descript *nd;
        !           659:        int cacherep;
        !           660: {
        !           661:        struct drt *rt;
        !           662:
        !           663:        rt = &nfsdrt.drt[nfsdrt.pos];
        !           664:        if (cacherep == RC_DOIT)
        !           665:                rt->flag = 0;
        !           666:        else if (cacherep == RC_REPLY)
        !           667:                rt->flag = DRT_CACHEREPLY;
        !           668:        else
        !           669:                rt->flag = DRT_CACHEDROP;
        !           670:        if (sotype == SOCK_STREAM)
        !           671:                rt->flag |= DRT_TCP;
        !           672:        else if (nd->nd_flag & ND_NFSV3)
        !           673:                rt->flag |= DRT_NFSV3;
        !           674:        rt->proc = nd->nd_procnum;
        !           675:        if (mtod(nd->nd_nam, struct sockaddr *)->sa_family == AF_INET)
        !           676:                rt->ipadr = mtod(nd->nd_nam, struct sockaddr_in *)->sin_addr.s_addr;
        !           677:        else
        !           678:                rt->ipadr = INADDR_ANY;
        !           679:        getmicrotime(&rt->tstamp);
        !           680:        rt->resptime =
        !           681:            ((rt->tstamp.tv_sec - nd->nd_starttime.tv_sec) * 1000000) +
        !           682:                (rt->tstamp.tv_usec - nd->nd_starttime.tv_usec);
        !           683:        nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ;
        !           684: }
        !           685: #endif /* NFSSERVER */
        !           686:
        !           687: #ifdef NFSCLIENT
        !           688: /*
        !           689:  * Asynchronous I/O threads for client nfs.
        !           690:  * They do read-ahead and write-behind operations on the block I/O cache.
        !           691:  * Never returns unless it fails or gets killed.
        !           692:  */
        !           693: int
        !           694: nfssvc_iod(p)
        !           695:        struct proc *p;
        !           696: {
        !           697:        struct buf *bp, *nbp;
        !           698:        int i, myiod;
        !           699:        struct vnode *vp;
        !           700:        int error = 0, s;
        !           701:
        !           702:        /*
        !           703:         * Assign my position or return error if too many already running
        !           704:         */
        !           705:        myiod = -1;
        !           706:        for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
        !           707:                if (nfs_asyncdaemon[i] == NULL) {
        !           708:                        myiod = i;
        !           709:                        break;
        !           710:                }
        !           711:        if (myiod == -1)
        !           712:                return (EBUSY);
        !           713:        nfs_asyncdaemon[myiod] = p;
        !           714:        nfs_numasync++;
        !           715:        /*
        !           716:         * Just loop around doin our stuff until SIGKILL
        !           717:         */
        !           718:        for (;;) {
        !           719:            while (TAILQ_FIRST(&nfs_bufq) == NULL && error == 0) {
        !           720:                nfs_iodwant[myiod] = p;
        !           721:                error = tsleep((caddr_t)&nfs_iodwant[myiod],
        !           722:                        PWAIT | PCATCH, "nfsidl", 0);
        !           723:            }
        !           724:            while ((bp = TAILQ_FIRST(&nfs_bufq)) != NULL) {
        !           725:                /* Take one off the front of the list */
        !           726:                TAILQ_REMOVE(&nfs_bufq, bp, b_freelist);
        !           727:                if (bp->b_flags & B_READ)
        !           728:                    (void) nfs_doio(bp, NULL);
        !           729:                else do {
        !           730:                    /*
        !           731:                     * Look for a delayed write for the same vnode, so I can do
        !           732:                     * it now. We must grab it before calling nfs_doio() to
        !           733:                     * avoid any risk of the vnode getting vclean()'d while
        !           734:                     * we are doing the write rpc.
        !           735:                     */
        !           736:                    vp = bp->b_vp;
        !           737:                    s = splbio();
        !           738:                    for (nbp = LIST_FIRST(&vp->v_dirtyblkhd); nbp != NULL;
        !           739:                        nbp = LIST_NEXT(nbp, b_vnbufs)) {
        !           740:                        if ((nbp->b_flags &
        !           741:                            (B_BUSY|B_DELWRI|B_NEEDCOMMIT|B_NOCACHE))!=B_DELWRI)
        !           742:                            continue;
        !           743:                        bremfree(nbp);
        !           744:                        nbp->b_flags |= (B_BUSY|B_ASYNC);
        !           745:                        break;
        !           746:                    }
        !           747:                    /*
        !           748:                     * For the delayed write, do the first part of nfs_bwrite()
        !           749:                     * up to, but not including nfs_strategy().
        !           750:                     */
        !           751:                    if (nbp) {
        !           752:                        nbp->b_flags &= ~(B_READ|B_DONE|B_ERROR);
        !           753:                        buf_undirty(nbp);
        !           754:                        nbp->b_vp->v_numoutput++;
        !           755:                    }
        !           756:                    splx(s);
        !           757:
        !           758:                    (void) nfs_doio(bp, NULL);
        !           759:                } while ((bp = nbp) != NULL);
        !           760:            }
        !           761:            if (error) {
        !           762:                nfs_asyncdaemon[myiod] = NULL;
        !           763:                nfs_numasync--;
        !           764:                return (error);
        !           765:            }
        !           766:        }
        !           767: }
        !           768:
        !           769: void
        !           770: start_nfsio(arg)
        !           771:        void *arg;
        !           772: {
        !           773:        nfssvc_iod(curproc);
        !           774:
        !           775:        kthread_exit(0);
        !           776: }
        !           777:
        !           778: void
        !           779: nfs_getset_niothreads(set)
        !           780:        int set;
        !           781: {
        !           782:        int i, have, start;
        !           783:
        !           784:        for (have = 0, i = 0; i < NFS_MAXASYNCDAEMON; i++)
        !           785:                if (nfs_asyncdaemon[i] != NULL)
        !           786:                        have++;
        !           787:
        !           788:        if (set) {
        !           789:                /* clamp to sane range */
        !           790:                nfs_niothreads = max(0, min(nfs_niothreads, NFS_MAXASYNCDAEMON));
        !           791:
        !           792:                start = nfs_niothreads - have;
        !           793:
        !           794:                while (start > 0) {
        !           795:                        kthread_create(start_nfsio, NULL, NULL, "nfsio");
        !           796:                        start--;
        !           797:                }
        !           798:
        !           799:                for (i = 0; (start < 0) && (i < NFS_MAXASYNCDAEMON); i++)
        !           800:                        if (nfs_asyncdaemon[i] != NULL) {
        !           801:                                psignal(nfs_asyncdaemon[i], SIGKILL);
        !           802:                                start++;
        !           803:                        }
        !           804:        } else {
        !           805:                if (nfs_niothreads >= 0)
        !           806:                        nfs_niothreads = have;
        !           807:        }
        !           808: }
        !           809:
        !           810: /*
        !           811:  * Get an authorization string for the uid by having the mount_nfs sitting
        !           812:  * on this mount point porpoise out of the kernel and do it.
        !           813:  */
        !           814: int
        !           815: nfs_getauth(nmp, rep, cred, auth_str, auth_len, verf_str, verf_len, key)
        !           816:        struct nfsmount *nmp;
        !           817:        struct nfsreq *rep;
        !           818:        struct ucred *cred;
        !           819:        char **auth_str;
        !           820:        int *auth_len;
        !           821:        char *verf_str;
        !           822:        int *verf_len;
        !           823:        NFSKERBKEY_T key;               /* return session key */
        !           824: {
        !           825:        int error = 0;
        !           826:
        !           827:        while ((nmp->nm_flag & NFSMNT_WAITAUTH) == 0) {
        !           828:                nmp->nm_flag |= NFSMNT_WANTAUTH;
        !           829:                (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK,
        !           830:                        "nfsauth1", 2 * hz);
        !           831:                error = nfs_sigintr(nmp, rep, rep->r_procp);
        !           832:                if (error) {
        !           833:                        nmp->nm_flag &= ~NFSMNT_WANTAUTH;
        !           834:                        return (error);
        !           835:                }
        !           836:        }
        !           837:        nmp->nm_flag &= ~(NFSMNT_WAITAUTH | NFSMNT_WANTAUTH);
        !           838:        nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK);
        !           839:        nmp->nm_authlen = RPCAUTH_MAXSIZ;
        !           840:        nmp->nm_verfstr = verf_str;
        !           841:        nmp->nm_verflen = *verf_len;
        !           842:        nmp->nm_authuid = cred->cr_uid;
        !           843:        wakeup((caddr_t)&nmp->nm_authstr);
        !           844:
        !           845:        /*
        !           846:         * And wait for mount_nfs to do its stuff.
        !           847:         */
        !           848:        while ((nmp->nm_flag & NFSMNT_HASAUTH) == 0 && error == 0) {
        !           849:                (void) tsleep((caddr_t)&nmp->nm_authlen, PSOCK,
        !           850:                        "nfsauth2", 2 * hz);
        !           851:                error = nfs_sigintr(nmp, rep, rep->r_procp);
        !           852:        }
        !           853:        if (nmp->nm_flag & NFSMNT_AUTHERR) {
        !           854:                nmp->nm_flag &= ~NFSMNT_AUTHERR;
        !           855:                error = EAUTH;
        !           856:        }
        !           857:        if (error)
        !           858:                free((caddr_t)*auth_str, M_TEMP);
        !           859:        else {
        !           860:                *auth_len = nmp->nm_authlen;
        !           861:                *verf_len = nmp->nm_verflen;
        !           862:                bcopy((caddr_t)nmp->nm_key, (caddr_t)key, sizeof (NFSKERBKEY_T));
        !           863:        }
        !           864:        nmp->nm_flag &= ~NFSMNT_HASAUTH;
        !           865:        nmp->nm_flag |= NFSMNT_WAITAUTH;
        !           866:        if (nmp->nm_flag & NFSMNT_WANTAUTH) {
        !           867:                nmp->nm_flag &= ~NFSMNT_WANTAUTH;
        !           868:                wakeup((caddr_t)&nmp->nm_authtype);
        !           869:        }
        !           870:        return (error);
        !           871: }
        !           872:
        !           873: /*
        !           874:  * Get a nickname authenticator and verifier.
        !           875:  */
        !           876: int
        !           877: nfs_getnickauth(nmp, cred, auth_str, auth_len, verf_str, verf_len)
        !           878:        struct nfsmount *nmp;
        !           879:        struct ucred *cred;
        !           880:        char **auth_str;
        !           881:        int *auth_len;
        !           882:        char *verf_str;
        !           883:        int verf_len;
        !           884: {
        !           885:        struct nfsuid *nuidp;
        !           886:        u_int32_t *nickp, *verfp;
        !           887:        struct timeval ktvin, ktvout;
        !           888:        struct timeval tv;
        !           889:
        !           890: #ifdef DIAGNOSTIC
        !           891:        if (verf_len < (4 * NFSX_UNSIGNED))
        !           892:                panic("nfs_getnickauth verf too small");
        !           893: #endif
        !           894:        LIST_FOREACH(nuidp, NMUIDHASH(nmp, cred->cr_uid), nu_hash) {
        !           895:                if (nuidp->nu_cr.cr_uid == cred->cr_uid)
        !           896:                        break;
        !           897:        }
        !           898:        if (!nuidp || nuidp->nu_expire < time_second)
        !           899:                return (EACCES);
        !           900:
        !           901:        /*
        !           902:         * Move to the end of the lru list (end of lru == most recently used).
        !           903:         */
        !           904:        TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, nu_lru);
        !           905:        TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp, nu_lru);
        !           906:
        !           907:        nickp = (u_int32_t *)malloc(2 * NFSX_UNSIGNED, M_TEMP, M_WAITOK);
        !           908:        *nickp++ = txdr_unsigned(RPCAKN_NICKNAME);
        !           909:        *nickp = txdr_unsigned(nuidp->nu_nickname);
        !           910:        *auth_str = (char *)nickp;
        !           911:        *auth_len = 2 * NFSX_UNSIGNED;
        !           912:
        !           913:        /*
        !           914:         * Now we must encrypt the verifier and package it up.
        !           915:         */
        !           916:        verfp = (u_int32_t *)verf_str;
        !           917:        *verfp++ = txdr_unsigned(RPCAKN_NICKNAME);
        !           918:        getmicrotime(&tv);
        !           919:        if (tv.tv_sec > nuidp->nu_timestamp.tv_sec ||
        !           920:            (tv.tv_sec == nuidp->nu_timestamp.tv_sec &&
        !           921:             tv.tv_usec > nuidp->nu_timestamp.tv_usec))
        !           922:                nuidp->nu_timestamp = tv;
        !           923:        else
        !           924:                nuidp->nu_timestamp.tv_usec++;
        !           925:        ktvin.tv_sec = txdr_unsigned(nuidp->nu_timestamp.tv_sec);
        !           926:        ktvin.tv_usec = txdr_unsigned(nuidp->nu_timestamp.tv_usec);
        !           927:
        !           928:        *verfp++ = ktvout.tv_sec;
        !           929:        *verfp++ = ktvout.tv_usec;
        !           930:        *verfp = 0;
        !           931:        return (0);
        !           932: }
        !           933:
        !           934: /*
        !           935:  * Save the current nickname in a hash list entry on the mount point.
        !           936:  */
        !           937: int
        !           938: nfs_savenickauth(nmp, cred, len, key, mdp, dposp, mrep)
        !           939:        struct nfsmount *nmp;
        !           940:        struct ucred *cred;
        !           941:        int len;
        !           942:        NFSKERBKEY_T key;
        !           943:        struct mbuf **mdp;
        !           944:        char **dposp;
        !           945:        struct mbuf *mrep;
        !           946: {
        !           947:        struct nfsuid *nuidp;
        !           948:        u_int32_t *tl;
        !           949:        int32_t t1;
        !           950:        struct mbuf *md = *mdp;
        !           951:        struct timeval ktvin, ktvout;
        !           952:        u_int32_t nick;
        !           953:        char *dpos = *dposp, *cp2;
        !           954:        int deltasec, error = 0;
        !           955:
        !           956:        if (len == (3 * NFSX_UNSIGNED)) {
        !           957:                nfsm_dissect(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
        !           958:                ktvin.tv_sec = *tl++;
        !           959:                ktvin.tv_usec = *tl++;
        !           960:                nick = fxdr_unsigned(u_int32_t, *tl);
        !           961:
        !           962:                ktvout.tv_sec = fxdr_unsigned(long, ktvout.tv_sec);
        !           963:                ktvout.tv_usec = fxdr_unsigned(long, ktvout.tv_usec);
        !           964:                deltasec = time_second - ktvout.tv_sec;
        !           965:                if (deltasec < 0)
        !           966:                        deltasec = -deltasec;
        !           967:                /*
        !           968:                 * If ok, add it to the hash list for the mount point.
        !           969:                 */
        !           970:                if (deltasec <= NFS_KERBCLOCKSKEW) {
        !           971:                        if (nmp->nm_numuids < nuidhash_max) {
        !           972:                                nmp->nm_numuids++;
        !           973:                                nuidp = (struct nfsuid *)
        !           974:                                   malloc(sizeof (struct nfsuid), M_NFSUID,
        !           975:                                        M_WAITOK);
        !           976:                        } else {
        !           977:                                nuidp = TAILQ_FIRST(&nmp->nm_uidlruhead);
        !           978:                                LIST_REMOVE(nuidp, nu_hash);
        !           979:                                TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp,
        !           980:                                        nu_lru);
        !           981:                        }
        !           982:                        nuidp->nu_flag = 0;
        !           983:                        nuidp->nu_cr.cr_uid = cred->cr_uid;
        !           984:                        nuidp->nu_expire = time_second + NFS_KERBTTL;
        !           985:                        nuidp->nu_timestamp = ktvout;
        !           986:                        nuidp->nu_nickname = nick;
        !           987:                        bcopy(key, nuidp->nu_key, sizeof (NFSKERBKEY_T));
        !           988:                        TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp,
        !           989:                                nu_lru);
        !           990:                        LIST_INSERT_HEAD(NMUIDHASH(nmp, cred->cr_uid),
        !           991:                                nuidp, nu_hash);
        !           992:                }
        !           993:        } else
        !           994:                nfsm_adv(nfsm_rndup(len));
        !           995: nfsmout:
        !           996:        *mdp = md;
        !           997:        *dposp = dpos;
        !           998:        return (error);
        !           999: }
        !          1000: #endif /* NFSCLIENT */

CVSweb