update for new VM
[unix-history] / usr / src / sys / nfs / nfs_serv.c
index 0843471..b2157ea 100644 (file)
@@ -5,19 +5,9 @@
  * This code is derived from software contributed to Berkeley by
  * Rick Macklem at The University of Guelph.
  *
  * This code is derived from software contributed to Berkeley by
  * Rick Macklem at The University of Guelph.
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * %sccs.include.redist.c%
  *
  *
- *     @(#)nfs_serv.c  7.12 (Berkeley) %G%
+ *     @(#)nfs_serv.c  7.30 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
  *   3 - build the rpc reply in an mbuf list
  *   nb:
  *     - do not mix the phases, since the nfsm_?? macros can return failures
  *   3 - build the rpc reply in an mbuf list
  *   nb:
  *     - do not mix the phases, since the nfsm_?? macros can return failures
- *       on mbuf exhaustion or similar and do not do any vrele() or vput()'s
+ *       on a bad rpc or similar and do not do any vrele() or vput()'s
  *
  *      - the nfsm_reply() macro generates an nfs rpc reply with the nfs
  *     error number iff error != 0 whereas
  *
  *      - the nfsm_reply() macro generates an nfs rpc reply with the nfs
  *     error number iff error != 0 whereas
- *       nfsm_srverr simply drops the mbufs and gives up
- *       (==> nfsm_srverr implies an error here at the server, usually mbuf
- *       exhaustion)
+ *     returning an error from the server function implies a fatal error
+ *     such as a badly constructed rpc request that should be dropped without
+ *     a reply.
  */
 
  */
 
-#include "strings.h"
-#include "time.h"
 #include "param.h"
 #include "param.h"
-#include "mount.h"
-#include "malloc.h"
-#include "mbuf.h"
-#include "file.h"
 #include "user.h"
 #include "user.h"
-#include "../ufs/dir.h"
+#include "file.h"
 #include "vnode.h"
 #include "vnode.h"
-#include "uio.h"
-#include "ucred.h"
-#include "namei.h"
+#include "mount.h"
+#include "mbuf.h"
 #include "errno.h"
 #include "errno.h"
+#include "../ufs/quota.h"
 #include "../ufs/inode.h"
 #include "nfsv2.h"
 #include "nfs.h"
 #include "../ufs/inode.h"
 #include "nfsv2.h"
 #include "nfs.h"
@@ -66,7 +50,8 @@
 extern u_long nfs_procids[NFS_NPROCS];
 extern u_long nfs_xdrneg1;
 extern u_long nfs_false, nfs_true;
 extern u_long nfs_procids[NFS_NPROCS];
 extern u_long nfs_xdrneg1;
 extern u_long nfs_false, nfs_true;
-nfstype nfs_type[VSOCK+1]={ NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON, };
+nfstype nfs_type[9]={ NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
+                     NFCHR, NFNON };
 
 /*
  * nfs getattr service
 
 /*
  * nfs getattr service
@@ -136,7 +121,7 @@ nfsrv_setattr(mrep, md, dpos, cred, xid, mrq, repstat)
                nfsm_reply(0);
        if (error = nfsrv_access(vp, VWRITE, cred))
                goto out;
                nfsm_reply(0);
        if (error = nfsrv_access(vp, VWRITE, cred))
                goto out;
-       vattr_null(vap);
+       VATTR_NULL(vap);
        /*
         * Nah nah nah nah na nah
         * There is a bug in the Sun client that puts 0xffff in the mode
        /*
         * Nah nah nah nah na nah
         * There is a bug in the Sun client that puts 0xffff in the mode
@@ -215,7 +200,7 @@ nfsrv_lookup(mrep, md, dpos, cred, xid, mrq, repstat)
                nfsm_reply(0);
        vp = ndp->ni_vp;
        bzero((caddr_t)fhp, sizeof(nfh));
                nfsm_reply(0);
        vp = ndp->ni_vp;
        bzero((caddr_t)fhp, sizeof(nfh));
-       fhp->fh_fsid = vp->v_mount->m_fsid;
+       fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
        if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
                vput(vp);
                nfsm_reply(0);
        if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
                vput(vp);
                nfsm_reply(0);
@@ -240,7 +225,7 @@ nfsrv_readlink(mrep, md, dpos, cred, xid, mrq, repstat)
        u_long xid;
        int *repstat;
 {
        u_long xid;
        int *repstat;
 {
-       struct iovec iv[NFS_MAXPATHLEN/MLEN+1];
+       struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN];
        register struct iovec *ivp = iv;
        register struct mbuf *mp;
        register u_long *p;
        register struct iovec *ivp = iv;
        register struct mbuf *mp;
        register u_long *p;
@@ -261,12 +246,14 @@ nfsrv_readlink(mrep, md, dpos, cred, xid, mrq, repstat)
        i = 0;
        while (len < NFS_MAXPATHLEN) {
                MGET(mp, M_WAIT, MT_DATA);
        i = 0;
        while (len < NFS_MAXPATHLEN) {
                MGET(mp, M_WAIT, MT_DATA);
-               NFSMCLGET(mp, M_WAIT);
+               MCLGET(mp, M_WAIT);
                mp->m_len = NFSMSIZ(mp);
                if (len == 0)
                        mp3 = mp2 = mp;
                mp->m_len = NFSMSIZ(mp);
                if (len == 0)
                        mp3 = mp2 = mp;
-               else
+               else {
                        mp2->m_next = mp;
                        mp2->m_next = mp;
+                       mp2 = mp;
+               }
                if ((len+mp->m_len) > NFS_MAXPATHLEN) {
                        mp->m_len = NFS_MAXPATHLEN-len;
                        len = NFS_MAXPATHLEN;
                if ((len+mp->m_len) > NFS_MAXPATHLEN) {
                        mp->m_len = NFS_MAXPATHLEN-len;
                        len = NFS_MAXPATHLEN;
@@ -319,9 +306,9 @@ nfsrv_read(mrep, md, dpos, cred, xid, mrq, repstat)
        u_long xid;
        int *repstat;
 {
        u_long xid;
        int *repstat;
 {
-       struct iovec iv[NFS_MAXDATA/MCLBYTES+1];
-       register struct iovec *ivp = iv;
-       register struct mbuf *mp;
+       register struct iovec *iv;
+       struct iovec *iv2;
+       register struct mbuf *m;
        register struct nfsv2_fattr *fp;
        register u_long *p;
        register long t1;
        register struct nfsv2_fattr *fp;
        register u_long *p;
        register long t1;
@@ -329,13 +316,13 @@ nfsrv_read(mrep, md, dpos, cred, xid, mrq, repstat)
        int error = 0;
        char *cp2;
        struct mbuf *mb, *mb2, *mreq;
        int error = 0;
        char *cp2;
        struct mbuf *mb, *mb2, *mreq;
-       struct mbuf *mp2, *mp3;
+       struct mbuf *m2, *m3;
        struct vnode *vp;
        nfsv2fh_t nfh;
        fhandle_t *fhp;
        struct uio io, *uiop = &io;
        struct vattr va, *vap = &va;
        struct vnode *vp;
        nfsv2fh_t nfh;
        fhandle_t *fhp;
        struct uio io, *uiop = &io;
        struct vattr va, *vap = &va;
-       int i, tlen, cnt, len, nio, left;
+       int i, cnt, len, left, siz, tlen;
        off_t off;
 
        fhp = &nfh.fh_generic;
        off_t off;
 
        fhp = &nfh.fh_generic;
@@ -350,48 +337,52 @@ nfsrv_read(mrep, md, dpos, cred, xid, mrq, repstat)
                nfsm_reply(0);
        }
        len = left = cnt;
                nfsm_reply(0);
        }
        len = left = cnt;
-       nio = (cnt+MCLBYTES-1)/MCLBYTES;
-       uiop->uio_iov = ivp;
-       uiop->uio_iovcnt = nio;
+       /*
+        * Generate the mbuf list with the uio_iov ref. to it.
+        */
+       i = 0;
+       m3 = (struct mbuf *)0;
+#ifdef lint
+       m2 = (struct mbuf *)0;
+#endif /* lint */
+       MALLOC(iv, struct iovec *,
+              ((NFS_MAXDATA+MLEN-1)/MLEN) * sizeof (struct iovec), M_TEMP,
+              M_WAITOK);
+       iv2 = iv;
+       while (left > 0) {
+               MGET(m, M_WAIT, MT_DATA);
+               if (left > MINCLSIZE)
+                       MCLGET(m, M_WAIT);
+               m->m_len = 0;
+               siz = min(M_TRAILINGSPACE(m), left);
+               m->m_len = siz;
+               iv->iov_base = mtod(m, caddr_t);
+               iv->iov_len = siz;
+               iv++;
+               i++;
+               left -= siz;
+               if (m3) {
+                       m2->m_next = m;
+                       m2 = m;
+               } else
+                       m3 = m2 = m;
+       }
+       uiop->uio_iov = iv2;
+       uiop->uio_iovcnt = i;
        uiop->uio_offset = off;
        uiop->uio_resid = cnt;
        uiop->uio_rw = UIO_READ;
        uiop->uio_segflg = UIO_SYSSPACE;
        uiop->uio_offset = off;
        uiop->uio_resid = cnt;
        uiop->uio_rw = UIO_READ;
        uiop->uio_segflg = UIO_SYSSPACE;
-       for (i = 0; i < nio; i++) {
-               MGET(mp, M_WAIT, MT_DATA);
-               if (left > MLEN)
-                       NFSMCLGET(mp, M_WAIT);
-               mp->m_len = (M_HASCL(mp)) ? MCLBYTES : MLEN;
-               if (i == 0) {
-                       mp3 = mp2 = mp;
-               } else {
-                       mp2->m_next = mp;
-                       mp2 = mp;
-               }
-               if (left > MLEN && !M_HASCL(mp)) {
-                       m_freem(mp3);
-                       vput(vp);
-                       nfsm_srverr;
-               }
-               ivp->iov_base = mtod(mp, caddr_t);
-               if (left > mp->m_len) {
-                       ivp->iov_len = mp->m_len;
-                       left -= mp->m_len;
-               } else {
-                       ivp->iov_len = mp->m_len = left;
-                       left = 0;
-               }
-               ivp++;
-       }
        error = VOP_READ(vp, uiop, IO_NODELOCKED, cred);
        off = uiop->uio_offset;
        error = VOP_READ(vp, uiop, IO_NODELOCKED, cred);
        off = uiop->uio_offset;
+       FREE((caddr_t)iv2, M_TEMP);
        if (error) {
        if (error) {
-               m_freem(mp3);
+               m_freem(m3);
                vput(vp);
                nfsm_reply(0);
        }
        if (error = VOP_GETATTR(vp, vap, cred))
                vput(vp);
                nfsm_reply(0);
        }
        if (error = VOP_GETATTR(vp, vap, cred))
-               m_freem(mp3);
+               m_freem(m3);
        vput(vp);
        nfsm_reply(NFSX_FATTR+NFSX_UNSIGNED);
        nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR);
        vput(vp);
        nfsm_reply(NFSX_FATTR+NFSX_UNSIGNED);
        nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR);
@@ -400,15 +391,15 @@ nfsrv_read(mrep, md, dpos, cred, xid, mrq, repstat)
                len -= uiop->uio_resid;
                if (len > 0) {
                        tlen = nfsm_rndup(len);
                len -= uiop->uio_resid;
                if (len > 0) {
                        tlen = nfsm_rndup(len);
-                       nfsm_adj(mp3, cnt-tlen, tlen-len);
+                       nfsm_adj(m3, cnt-tlen, tlen-len);
                } else {
                } else {
-                       m_freem(mp3);
-                       mp3 = (struct mbuf *)0;
+                       m_freem(m3);
+                       m3 = (struct mbuf *)0;
                }
        }
        nfsm_build(p, u_long *, NFSX_UNSIGNED);
        *p = txdr_unsigned(len);
                }
        }
        nfsm_build(p, u_long *, NFSX_UNSIGNED);
        *p = txdr_unsigned(len);
-       mb->m_next = mp3;
+       mb->m_next = m3;
        nfsm_srvdone;
 }
 
        nfsm_srvdone;
 }
 
@@ -425,7 +416,7 @@ nfsrv_write(mrep, md, dpos, cred, xid, mrq, repstat)
        register struct iovec *ivp;
        register struct mbuf *mp;
        register struct nfsv2_fattr *fp;
        register struct iovec *ivp;
        register struct mbuf *mp;
        register struct nfsv2_fattr *fp;
-       struct iovec iv[MAX_IOVEC];
+       struct iovec iv[NFS_MAXIOVEC];
        struct vattr va;
        register struct vattr *vap = &va;
        register u_long *p;
        struct vattr va;
        register struct vattr *vap = &va;
        register u_long *p;
@@ -473,7 +464,7 @@ nfsrv_write(mrep, md, dpos, cred, xid, mrq, repstat)
        uiop->uio_rw = UIO_WRITE;
        uiop->uio_segflg = UIO_SYSSPACE;
        /*
        uiop->uio_rw = UIO_WRITE;
        uiop->uio_segflg = UIO_SYSSPACE;
        /*
-        * Do up to MAX_IOVEC mbufs of write each iteration of the
+        * Do up to NFS_MAXIOVEC mbufs of write each iteration of the
         * loop until done.
         */
        while (len > 0 && uiop->uio_resid == 0) {
         * loop until done.
         */
        while (len > 0 && uiop->uio_resid == 0) {
@@ -482,7 +473,7 @@ nfsrv_write(mrep, md, dpos, cred, xid, mrq, repstat)
                uiop->uio_iov = ivp;
                uiop->uio_iovcnt = 0;
                uiop->uio_offset = off;
                uiop->uio_iov = ivp;
                uiop->uio_iovcnt = 0;
                uiop->uio_offset = off;
-               while (len > 0 && uiop->uio_iovcnt < MAX_IOVEC && mp != NULL) {
+               while (len > 0 && uiop->uio_iovcnt < NFS_MAXIOVEC && mp != NULL) {
                        ivp->iov_base = mtod(mp, caddr_t);
                        if (len < mp->m_len)
                                ivp->iov_len = xfer = len;
                        ivp->iov_base = mtod(mp, caddr_t);
                        if (len < mp->m_len)
                                ivp->iov_len = xfer = len;
@@ -542,6 +533,7 @@ nfsrv_create(mrep, md, dpos, cred, xid, mrq, repstat)
        register u_long *p;
        register long t1;
        caddr_t bpos;
        register u_long *p;
        register long t1;
        caddr_t bpos;
+       long rdev;
        int error = 0;
        char *cp2;
        struct mbuf *mb, *mb2, *mreq;
        int error = 0;
        char *cp2;
        struct mbuf *mb, *mb2, *mreq;
@@ -558,29 +550,67 @@ nfsrv_create(mrep, md, dpos, cred, xid, mrq, repstat)
        ndp->ni_nameiop = CREATE | LOCKPARENT | LOCKLEAF;
        if (error = nfs_namei(ndp, fhp, len, &md, &dpos))
                nfsm_reply(0);
        ndp->ni_nameiop = CREATE | LOCKPARENT | LOCKLEAF;
        if (error = nfs_namei(ndp, fhp, len, &md, &dpos))
                nfsm_reply(0);
-       vattr_null(vap);
-       nfsm_disect(p, u_long *, NFSX_UNSIGNED);
+       VATTR_NULL(vap);
+       nfsm_disect(p, u_long *, NFSX_SATTR);
        /*
         * Iff doesn't exist, create it
         * otherwise just truncate to 0 length
         *   should I set the mode too ??
         */
        if (ndp->ni_vp == NULL) {
        /*
         * Iff doesn't exist, create it
         * otherwise just truncate to 0 length
         *   should I set the mode too ??
         */
        if (ndp->ni_vp == NULL) {
-               vap->va_type = VREG;
-               vap->va_mode = nfstov_mode(*p++);
-               if (error = VOP_CREATE(ndp, vap))
+               vap->va_type = IFTOVT(fxdr_unsigned(u_long, *p));
+               if (vap->va_type == VNON)
+                       vap->va_type = VREG;
+               vap->va_mode = nfstov_mode(*p);
+               rdev = fxdr_unsigned(long, *(p+3));
+               if (vap->va_type == VREG) {
+                       if (error = VOP_CREATE(ndp, vap))
+                               nfsm_reply(0);
+               } else if (vap->va_type == VCHR || vap->va_type == VBLK ||
+                       vap->va_type == VFIFO) {
+                       if (vap->va_type == VCHR && rdev == 0xffffffff)
+                               vap->va_type = VFIFO;
+                       if (vap->va_type == VFIFO) {
+#ifndef FIFO
+                               VOP_ABORTOP(ndp);
+                               vput(ndp->ni_dvp);
+                               error = ENXIO;
+                               nfsm_reply(0);
+#endif /* FIFO */
+                       } else if (error = suser(cred, (short *)0)) {
+                               VOP_ABORTOP(ndp);
+                               vput(ndp->ni_dvp);
+                               nfsm_reply(0);
+                       } else
+                               vap->va_rdev = (dev_t)rdev;
+                       if (error = VOP_MKNOD(ndp, vap, cred))
+                               nfsm_reply(0);
+                       ndp->ni_nameiop = LOOKUP | LOCKLEAF | HASBUF;
+                       if (error = nfs_namei(ndp, fhp, len, &md, &dpos))
+                               nfsm_reply(0);
+               } else {
+                       VOP_ABORTOP(ndp);
+                       vput(ndp->ni_dvp);
+                       error = ENXIO;
                        nfsm_reply(0);
                        nfsm_reply(0);
+               }
                vp = ndp->ni_vp;
        } else {
                vp = ndp->ni_vp;
                vp = ndp->ni_vp;
        } else {
                vp = ndp->ni_vp;
-               ndp->ni_vp = (struct vnode *)0;
+               ndp->ni_vp = NULLVP;
                VOP_ABORTOP(ndp);
                VOP_ABORTOP(ndp);
+               if (ndp->ni_dvp == vp)
+                       vrele(ndp->ni_dvp);
+               else
+                       vput(ndp->ni_dvp);
                vap->va_size = 0;
                vap->va_size = 0;
-               if (error = VOP_SETATTR(vp, vap, cred))
+               if (error = VOP_SETATTR(vp, vap, cred)) {
+                       vput(vp);
                        nfsm_reply(0);
                        nfsm_reply(0);
+               }
        }
        bzero((caddr_t)fhp, sizeof(nfh));
        }
        bzero((caddr_t)fhp, sizeof(nfh));
-       fhp->fh_fsid = vp->v_mount->m_fsid;
+       fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
        if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
                vput(vp);
                nfsm_reply(0);
        if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
                vput(vp);
                nfsm_reply(0);
@@ -594,6 +624,12 @@ nfsrv_create(mrep, md, dpos, cred, xid, mrq, repstat)
        return (error);
 nfsmout:
        VOP_ABORTOP(ndp);
        return (error);
 nfsmout:
        VOP_ABORTOP(ndp);
+       if (ndp->ni_dvp == ndp->ni_vp)
+               vrele(ndp->ni_dvp);
+       else
+               vput(ndp->ni_dvp);
+       if (ndp->ni_vp)
+               vput(ndp->ni_vp);
        return (error);
 }
 
        return (error);
 }
 
@@ -640,12 +676,18 @@ nfsrv_remove(mrep, md, dpos, cred, xid, mrq, repstat)
                goto out;
        }
        if (vp->v_flag & VTEXT)
                goto out;
        }
        if (vp->v_flag & VTEXT)
-               xrele(vp);      /* try once to free text */
+               (void) vnode_pager_uncache(vp);
 out:
 out:
-       if (error)
-               VOP_ABORTOP(ndp);
-       else
+       if (!error) {
                error = VOP_REMOVE(ndp);
                error = VOP_REMOVE(ndp);
+       } else {
+               VOP_ABORTOP(ndp);
+               if (ndp->ni_dvp == vp)
+                       vrele(ndp->ni_dvp);
+               else
+                       vput(ndp->ni_dvp);
+               vput(vp);
+       }
        nfsm_reply(0);
        nfsm_srvdone;
 }
        nfsm_reply(0);
        nfsm_srvdone;
 }
@@ -692,14 +734,19 @@ nfsrv_rename(mrep, md, dpos, cred, xid, mrq, repstat)
                nfsm_reply(0);
        fvp = ndp->ni_vp;
        nfsm_srvmtofh(tfhp);
                nfsm_reply(0);
        fvp = ndp->ni_vp;
        nfsm_srvmtofh(tfhp);
-       nfsm_srvstrsiz(len2, NFS_MAXNAMLEN);
+       nfsm_strsiz(len2, NFS_MAXNAMLEN);
        if (rootflg)
                cred->cr_uid = 0;
        ndinit(&tond);
        crhold(cred);
        tond.ni_cred = cred;
        tond.ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE;
        if (rootflg)
                cred->cr_uid = 0;
        ndinit(&tond);
        crhold(cred);
        tond.ni_cred = cred;
        tond.ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE;
-       error = nfs_namei(&tond, tfhp, len2, &md, &dpos);
+       if (error = nfs_namei(&tond, tfhp, len2, &md, &dpos)) {
+               VOP_ABORTOP(ndp);
+               vrele(ndp->ni_dvp);
+               vrele(fvp);
+               goto out1;
+       }
        tdvp = tond.ni_dvp;
        tvp = tond.ni_vp;
        if (tvp != NULL) {
        tdvp = tond.ni_dvp;
        tvp = tond.ni_vp;
        if (tvp != NULL) {
@@ -711,10 +758,6 @@ nfsrv_rename(mrep, md, dpos, cred, xid, mrq, repstat)
                        goto out;
                }
        }
                        goto out;
                }
        }
-       if (error) {
-               VOP_ABORTOP(ndp);
-               goto out1;
-       }
        if (fvp->v_mount != tdvp->v_mount) {
                error = EXDEV;
                goto out;
        if (fvp->v_mount != tdvp->v_mount) {
                error = EXDEV;
                goto out;
@@ -722,15 +765,23 @@ nfsrv_rename(mrep, md, dpos, cred, xid, mrq, repstat)
        if (fvp == tdvp || fvp == tvp)
                error = EINVAL;
 out:
        if (fvp == tdvp || fvp == tvp)
                error = EINVAL;
 out:
-       if (error) {
-               VOP_ABORTOP(&tond);
-               VOP_ABORTOP(ndp);
-       } else {
+       if (!error) {
                VREF(ndp->ni_cdir);
                VREF(tond.ni_cdir);
                error = VOP_RENAME(ndp, &tond);
                vrele(ndp->ni_cdir);
                vrele(tond.ni_cdir);
                VREF(ndp->ni_cdir);
                VREF(tond.ni_cdir);
                error = VOP_RENAME(ndp, &tond);
                vrele(ndp->ni_cdir);
                vrele(tond.ni_cdir);
+       } else {
+               VOP_ABORTOP(&tond);
+               if (tdvp == tvp)
+                       vrele(tdvp);
+               else
+                       vput(tdvp);
+               if (tvp)
+                       vput(tvp);
+               VOP_ABORTOP(ndp);
+               vrele(ndp->ni_dvp);
+               vrele(fvp);
        }
 out1:
        crfree(cred);
        }
 out1:
        crfree(cred);
@@ -738,6 +789,8 @@ out1:
        return (error);
 nfsmout:
        VOP_ABORTOP(ndp);
        return (error);
 nfsmout:
        VOP_ABORTOP(ndp);
+       vrele(ndp->ni_dvp);
+       vrele(fvp);
        return (error);
 }
 
        return (error);
 }
 
@@ -787,10 +840,17 @@ nfsrv_link(mrep, md, dpos, cred, xid, mrq, repstat)
        if (vp->v_mount != xp->v_mount)
                error = EXDEV;
 out:
        if (vp->v_mount != xp->v_mount)
                error = EXDEV;
 out:
-       if (error)
-               VOP_ABORTOP(ndp);
-       else
+       if (!error) {
                error = VOP_LINK(vp, ndp);
                error = VOP_LINK(vp, ndp);
+       } else {
+               VOP_ABORTOP(ndp);
+               if (ndp->ni_dvp == ndp->ni_vp)
+                       vrele(ndp->ni_dvp);
+               else
+                       vput(ndp->ni_dvp);
+               if (ndp->ni_vp)
+                       vrele(ndp->ni_vp);
+       }
 out1:
        vrele(vp);
        nfsm_reply(0);
 out1:
        vrele(vp);
        nfsm_reply(0);
@@ -813,15 +873,18 @@ nfsrv_symlink(mrep, md, dpos, cred, xid, mrq, repstat)
        register struct vattr *vap = &va;
        register u_long *p;
        register long t1;
        register struct vattr *vap = &va;
        register u_long *p;
        register long t1;
+       struct nfsv2_sattr *sp;
        caddr_t bpos;
        caddr_t bpos;
+       struct uio io;
+       struct iovec iv;
        int error = 0;
        int error = 0;
-       char *cp2;
+       char *pathcp, *cp2;
        struct mbuf *mb, *mreq;
        struct mbuf *mb, *mreq;
-       struct vnode *vp;
        nfsv2fh_t nfh;
        fhandle_t *fhp;
        nfsv2fh_t nfh;
        fhandle_t *fhp;
-       long len, tlen, len2;
+       long len, len2;
 
 
+       pathcp = (char *)0;
        ndinit(ndp);
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
        ndinit(ndp);
        fhp = &nfh.fh_generic;
        nfsm_srvmtofh(fhp);
@@ -829,33 +892,48 @@ nfsrv_symlink(mrep, md, dpos, cred, xid, mrq, repstat)
        ndp->ni_cred = cred;
        ndp->ni_nameiop = CREATE | LOCKPARENT;
        if (error = nfs_namei(ndp, fhp, len, &md, &dpos))
        ndp->ni_cred = cred;
        ndp->ni_nameiop = CREATE | LOCKPARENT;
        if (error = nfs_namei(ndp, fhp, len, &md, &dpos))
-               goto out1;
-       nfsm_srvstrsiz(len2, NFS_MAXPATHLEN);
-       tlen = nfsm_rndup(len2);
-       if (len2 == tlen) {
-               nfsm_disect(cp2, caddr_t, tlen+NFSX_UNSIGNED);
-               *(cp2+tlen) = '\0';
-       } else {
-               nfsm_disect(cp2, caddr_t, tlen);
-       }
-       vp = ndp->ni_vp;
-       if (vp) {
+               goto out;
+       nfsm_strsiz(len2, NFS_MAXPATHLEN);
+       MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK);
+       iv.iov_base = pathcp;
+       iv.iov_len = len2;
+       io.uio_resid = len2;
+       io.uio_offset = 0;
+       io.uio_iov = &iv;
+       io.uio_iovcnt = 1;
+       io.uio_segflg = UIO_SYSSPACE;
+       io.uio_rw = UIO_READ;
+       nfsm_mtouio(&io, len2);
+       nfsm_disect(sp, struct nfsv2_sattr *, NFSX_SATTR);
+       *(pathcp + len2) = '\0';
+       if (ndp->ni_vp) {
+               VOP_ABORTOP(ndp);
+               if (ndp->ni_dvp == ndp->ni_vp)
+                       vrele(ndp->ni_dvp);
+               else
+                       vput(ndp->ni_dvp);
+               vrele(ndp->ni_vp);
                error = EEXIST;
                goto out;
        }
                error = EEXIST;
                goto out;
        }
-       vp = ndp->ni_dvp;
-       vattr_null(vap);
-       vap->va_mode = 0777;
+       VATTR_NULL(vap);
+       vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode);
+       error = VOP_SYMLINK(ndp, vap, pathcp);
 out:
 out:
-       if (error)
-               VOP_ABORTOP(ndp);
-       else
-               error = VOP_SYMLINK(ndp, vap, cp2);
-out1:
+       if (pathcp)
+               FREE(pathcp, M_TEMP);
        nfsm_reply(0);
        return (error);
 nfsmout:
        VOP_ABORTOP(ndp);
        nfsm_reply(0);
        return (error);
 nfsmout:
        VOP_ABORTOP(ndp);
+       if (ndp->ni_dvp == ndp->ni_vp)
+               vrele(ndp->ni_dvp);
+       else
+               vput(ndp->ni_dvp);
+       if (ndp->ni_vp);
+               vrele(ndp->ni_vp);
+       if (pathcp)
+               FREE(pathcp, M_TEMP);
        return (error);
 }
 
        return (error);
 }
 
@@ -895,12 +973,17 @@ nfsrv_mkdir(mrep, md, dpos, cred, xid, mrq, repstat)
        if (error = nfs_namei(ndp, fhp, len, &md, &dpos))
                nfsm_reply(0);
        nfsm_disect(p, u_long *, NFSX_UNSIGNED);
        if (error = nfs_namei(ndp, fhp, len, &md, &dpos))
                nfsm_reply(0);
        nfsm_disect(p, u_long *, NFSX_UNSIGNED);
-       vattr_null(vap);
+       VATTR_NULL(vap);
        vap->va_type = VDIR;
        vap->va_mode = nfstov_mode(*p++);
        vp = ndp->ni_vp;
        if (vp != NULL) {
                VOP_ABORTOP(ndp);
        vap->va_type = VDIR;
        vap->va_mode = nfstov_mode(*p++);
        vp = ndp->ni_vp;
        if (vp != NULL) {
                VOP_ABORTOP(ndp);
+               if (ndp->ni_dvp == vp)
+                       vrele(ndp->ni_dvp);
+               else
+                       vput(ndp->ni_dvp);
+               vrele(vp);
                error = EEXIST;
                nfsm_reply(0);
        }
                error = EEXIST;
                nfsm_reply(0);
        }
@@ -908,7 +991,7 @@ nfsrv_mkdir(mrep, md, dpos, cred, xid, mrq, repstat)
                nfsm_reply(0);
        vp = ndp->ni_vp;
        bzero((caddr_t)fhp, sizeof(nfh));
                nfsm_reply(0);
        vp = ndp->ni_vp;
        bzero((caddr_t)fhp, sizeof(nfh));
-       fhp->fh_fsid = vp->v_mount->m_fsid;
+       fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
        if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
                vput(vp);
                nfsm_reply(0);
        if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
                vput(vp);
                nfsm_reply(0);
@@ -922,6 +1005,12 @@ nfsrv_mkdir(mrep, md, dpos, cred, xid, mrq, repstat)
        return (error);
 nfsmout:
        VOP_ABORTOP(ndp);
        return (error);
 nfsmout:
        VOP_ABORTOP(ndp);
+       if (ndp->ni_dvp == ndp->ni_vp)
+               vrele(ndp->ni_dvp);
+       else
+               vput(ndp->ni_dvp);
+       if (ndp->ni_vp)
+               vrele(ndp->ni_vp);
        return (error);
 }
 
        return (error);
 }
 
@@ -974,10 +1063,16 @@ nfsrv_rmdir(mrep, md, dpos, cred, xid, mrq, repstat)
        if (vp->v_flag & VROOT)
                error = EBUSY;
 out:
        if (vp->v_flag & VROOT)
                error = EBUSY;
 out:
-       if (error)
-               VOP_ABORTOP(ndp);
-       else
+       if (!error) {
                error = VOP_RMDIR(ndp);
                error = VOP_RMDIR(ndp);
+       } else {
+               VOP_ABORTOP(ndp);
+               if (ndp->ni_dvp == ndp->ni_vp)
+                       vrele(ndp->ni_dvp);
+               else
+                       vput(ndp->ni_dvp);
+               vput(vp);
+       }
        nfsm_reply(0);
        nfsm_srvdone;
 }
        nfsm_reply(0);
        nfsm_srvdone;
 }
@@ -985,7 +1080,7 @@ out:
 /*
  * nfs readdir service
  * - mallocs what it thinks is enough to read
 /*
  * nfs readdir service
  * - mallocs what it thinks is enough to read
- *     count rounded up to a multiple of DIRBLKSIZ <= MAX_READDIR
+ *     count rounded up to a multiple of DIRBLKSIZ <= NFS_MAXREADDIR
  * - calls VOP_READDIR()
  * - loops around building the reply
  *     if the output generated exceeds count break out of loop
  * - calls VOP_READDIR()
  * - loops around building the reply
  *     if the output generated exceeds count break out of loop
@@ -1036,7 +1131,7 @@ nfsrv_readdir(mrep, md, dpos, cred, xid, mrq, repstat)
        fhandle_t *fhp;
        struct uio io;
        struct iovec iv;
        fhandle_t *fhp;
        struct uio io;
        struct iovec iv;
-       int siz, cnt, fullsiz;
+       int siz, cnt, fullsiz, eofflag;
        u_long on;
        char *rbuf;
        off_t off, toff;
        u_long on;
        char *rbuf;
        off_t off, toff;
@@ -1048,9 +1143,9 @@ nfsrv_readdir(mrep, md, dpos, cred, xid, mrq, repstat)
        off = (toff & ~(DIRBLKSIZ-1));
        on = (toff & (DIRBLKSIZ-1));
        cnt = fxdr_unsigned(int, *p);
        off = (toff & ~(DIRBLKSIZ-1));
        on = (toff & (DIRBLKSIZ-1));
        cnt = fxdr_unsigned(int, *p);
-       siz = ((cnt+DIRBLKSIZ) & ~(DIRBLKSIZ-1));
-       if (cnt > MAX_READDIR)
-               siz = MAX_READDIR;
+       siz = ((cnt+DIRBLKSIZ-1) & ~(DIRBLKSIZ-1));
+       if (cnt > NFS_MAXREADDIR)
+               siz = NFS_MAXREADDIR;
        fullsiz = siz;
        if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred))
                nfsm_reply(0);
        fullsiz = siz;
        if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred))
                nfsm_reply(0);
@@ -1069,7 +1164,7 @@ again:
        io.uio_resid = fullsiz;
        io.uio_segflg = UIO_SYSSPACE;
        io.uio_rw = UIO_READ;
        io.uio_resid = fullsiz;
        io.uio_segflg = UIO_SYSSPACE;
        io.uio_rw = UIO_READ;
-       error = VOP_READDIR(vp, &io, cred);
+       error = VOP_READDIR(vp, &io, cred, &eofflag);
        off = io.uio_offset;
        if (error) {
                vrele(vp);
        off = io.uio_offset;
        if (error) {
                vrele(vp);
@@ -1133,8 +1228,10 @@ again:
                         * "len".
                         */
                        len += (4*NFSX_UNSIGNED+nlen+rem);
                         * "len".
                         */
                        len += (4*NFSX_UNSIGNED+nlen+rem);
-                       if (len > cnt)
+                       if (len > cnt) {
+                               eofflag = 0;
                                break;
                                break;
+                       }
        
                        /* Build the directory record xdr from the direct entry */
                        nfsm_clget;
        
                        /* Build the directory record xdr from the direct entry */
                        nfsm_clget;
@@ -1180,7 +1277,10 @@ again:
        *p = nfs_false;
        bp += NFSX_UNSIGNED;
        nfsm_clget;
        *p = nfs_false;
        bp += NFSX_UNSIGNED;
        nfsm_clget;
-       *p = nfs_false;
+       if (eofflag)
+               *p = nfs_true;
+       else
+               *p = nfs_false;
        bp += NFSX_UNSIGNED;
        if (bp < be)
                mp->m_len = bp-mtod(mp, caddr_t);
        bp += NFSX_UNSIGNED;
        if (bp < be)
                mp->m_len = bp-mtod(mp, caddr_t);
@@ -1222,7 +1322,7 @@ nfsrv_statfs(mrep, md, dpos, cred, xid, mrq, repstat)
        vput(vp);
        nfsm_reply(NFSX_STATFS);
        nfsm_build(sfp, struct nfsv2_statfs *, NFSX_STATFS);
        vput(vp);
        nfsm_reply(NFSX_STATFS);
        nfsm_build(sfp, struct nfsv2_statfs *, NFSX_STATFS);
-       sfp->sf_tsize = txdr_unsigned(NFS_MAXDATA);
+       sfp->sf_tsize = txdr_unsigned(NFS_MAXDGRAMDATA);
        sfp->sf_bsize = txdr_unsigned(sf->f_fsize);
        sfp->sf_blocks = txdr_unsigned(sf->f_blocks);
        sfp->sf_bfree = txdr_unsigned(sf->f_bfree);
        sfp->sf_bsize = txdr_unsigned(sf->f_fsize);
        sfp->sf_blocks = txdr_unsigned(sf->f_blocks);
        sfp->sf_bfree = txdr_unsigned(sf->f_bfree);
@@ -1276,7 +1376,7 @@ nfsrv_noop(mrep, md, dpos, cred, xid, mrq, repstat)
  * Perform access checking for vnodes obtained from file handles that would
  * refer to files already opened by a Unix client. You cannot just use
  * vn_writechk() and VOP_ACCESS() for two reasons.
  * Perform access checking for vnodes obtained from file handles that would
  * refer to files already opened by a Unix client. You cannot just use
  * vn_writechk() and VOP_ACCESS() for two reasons.
- * 1 - You must check for M_EXRDONLY as well as M_RDONLY for the write case
+ * 1 - You must check for MNT_EXRDONLY as well as MNT_RDONLY for the write case
  * 2 - The owner is to be given access irrespective of mode bits so that
  *     processes that chmod after opening a file don't break. I don't like
  *     this because it opens a security hole, but since the nfs server opens
  * 2 - The owner is to be given access irrespective of mode bits so that
  *     processes that chmod after opening a file don't break. I don't like
  *     this because it opens a security hole, but since the nfs server opens
@@ -1290,35 +1390,30 @@ nfsrv_access(vp, flags, cred)
        struct vattr vattr;
        int error;
        if (flags & VWRITE) {
        struct vattr vattr;
        int error;
        if (flags & VWRITE) {
-               /* Just vn_writechk() changed to check M_EXRDONLY */
+               /* Just vn_writechk() changed to check MNT_EXRDONLY */
                /*
                 * Disallow write attempts on read-only file systems;
                 * unless the file is a socket or a block or character
                 * device resident on the file system.
                 */
                /*
                 * Disallow write attempts on read-only file systems;
                 * unless the file is a socket or a block or character
                 * device resident on the file system.
                 */
-               if ((vp->v_mount->m_flag & (M_RDONLY | M_EXRDONLY)) &&
-                       vp->v_type != VCHR &&
-                       vp->v_type != VBLK &&
-                       vp->v_type != VSOCK)
+               if (vp->v_mount->mnt_flag & (MNT_RDONLY | MNT_EXRDONLY)) {
+                       switch (vp->v_type) {
+                       case VREG: case VDIR: case VLNK:
                                return (EROFS);
                                return (EROFS);
+                       }
+               }
                /*
                 * If there's shared text associated with
                 * the inode, try to free it up once.  If
                 * we fail, we can't allow writing.
                 */
                /*
                 * If there's shared text associated with
                 * the inode, try to free it up once.  If
                 * we fail, we can't allow writing.
                 */
-               if (vp->v_flag & VTEXT)
-                       xrele(vp);
-               if (vp->v_flag & VTEXT)
+               if ((vp->v_flag & VTEXT) && !vnode_pager_uncache(vp))
                        return (ETXTBSY);
                        return (ETXTBSY);
-               if (error = VOP_GETATTR(vp, &vattr, cred))
-                       return (error);
-               if (cred->cr_uid == vattr.va_uid)
-                       return (0);
-       } else {
-               if (error = VOP_GETATTR(vp, &vattr, cred))
-                       return (error);
-               if (cred->cr_uid == vattr.va_uid)
-                       return (0);
        }
        }
-       return (VOP_ACCESS(vp, flags, cred));
+       if (error = VOP_GETATTR(vp, &vattr, cred))
+               return (error);
+       if ((error = VOP_ACCESS(vp, flags, cred)) &&
+           cred->cr_uid != vattr.va_uid)
+               return (error);
+       return (0);
 }
 }