Index: head/sys/nfs/nfs_serv.c =================================================================== --- head/sys/nfs/nfs_serv.c (revision 29290) +++ head/sys/nfs/nfs_serv.c (revision 29291) @@ -1,3464 +1,3460 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)nfs_serv.c 8.3 (Berkeley) 1/12/94 - * $Id: nfs_serv.c,v 1.48 1997/09/02 01:19:33 bde Exp $ + * $Id: nfs_serv.c,v 1.49 1997/09/10 19:52:25 phk Exp $ */ /* * nfs version 2 and 3 server calls to vnode ops * - these routines generally have 3 phases * 1 - break down and validate rpc request in mbuf list * 2 - do the vnode ops for the request * (surprisingly ?? many are very similar to syscalls in vfs_syscalls.c) * 3 - build the rpc reply in an mbuf list * nb: * - do not mix the phases, since the nfsm_?? macros can return failures * 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 * 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. * For Version 3, nfsm_reply() does not return for the error case, since * most version 3 rpcs return more than the status for error cases. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON }; #ifndef NFS_NOSERVER nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON, NFCHR, NFNON }; /* Global vars */ extern u_long nfs_xdrneg1; extern u_long nfs_false, nfs_true; extern enum vtype nv3tov_type[8]; extern struct nfsstats nfsstats; int nfsrvw_procrastinate = NFS_GATHERDELAY * 1000; int nfsrvw_procrastinate_v3 = 0; int nfs_async; SYSCTL_INT(_vfs_nfs, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0, ""); static int nfsrv_access __P((struct vnode *,int,struct ucred *,int, struct proc *)); static void nfsrvw_coalesce __P((struct nfsrv_descript *, struct nfsrv_descript *)); /* * nfs v3 access service */ int nfsrv3_access(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, getret; char *cp2; struct mbuf *mb, *mreq, *mb2; struct vattr vattr, *vap = &vattr; u_long testmode, nfsmode; u_quad_t frev; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(1, (struct vattr *)0); return (0); } nfsmode = fxdr_unsigned(u_long, *tl); if ((nfsmode & NFSV3ACCESS_READ) && nfsrv_access(vp, VREAD, cred, rdonly, procp)) nfsmode &= ~NFSV3ACCESS_READ; if (vp->v_type == VDIR) testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND | NFSV3ACCESS_DELETE); else testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND); if ((nfsmode & testmode) && nfsrv_access(vp, VWRITE, cred, rdonly, procp)) nfsmode &= ~testmode; if (vp->v_type == VDIR) testmode = NFSV3ACCESS_LOOKUP; else testmode = NFSV3ACCESS_EXECUTE; if ((nfsmode & testmode) && nfsrv_access(vp, VEXEC, cred, rdonly, procp)) nfsmode &= ~testmode; getret = VOP_GETATTR(vp, vap, cred, procp); vput(vp); nfsm_reply(NFSX_POSTOPATTR(1) + NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, vap); nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(nfsmode); nfsm_srvdone; } /* * nfs getattr service */ int nfsrv_getattr(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct nfs_fattr *fp; struct vattr va; register struct vattr *vap = &va; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache; char *cp2; struct mbuf *mb, *mb2, *mreq; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(0); return (0); } nqsrv_getl(vp, ND_READ); error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); nfsm_reply(NFSX_FATTR(nfsd->nd_flag & ND_NFSV3)); if (error) return (0); nfsm_build(fp, struct nfs_fattr *, NFSX_FATTR(nfsd->nd_flag & ND_NFSV3)); nfsm_srvfillattr(vap, fp); nfsm_srvdone; } /* * nfs setattr service */ int nfsrv_setattr(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr va, preat; register struct vattr *vap = &va; register struct nfsv2_sattr *sp; register struct nfs_fattr *fp; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, preat_ret = 1, postat_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3), gcheck = 0; char *cp2; struct mbuf *mb, *mb2, *mreq; u_quad_t frev; struct timespec guard; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); VATTR_NULL(vap); if (v3) { nfsm_srvsattr(vap); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); gcheck = fxdr_unsigned(int, *tl); if (gcheck) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fxdr_nfsv3time(tl, &guard); } } else { nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); /* * Nah nah nah nah na nah * There is a bug in the Sun client that puts 0xffff in the mode * field of sattr when it should put in 0xffffffff. The u_short * doesn't sign extend. * --> check the low order 2 bytes for 0xffff */ if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff) vap->va_mode = nfstov_mode(sp->sa_mode); if (sp->sa_uid != nfs_xdrneg1) vap->va_uid = fxdr_unsigned(uid_t, sp->sa_uid); if (sp->sa_gid != nfs_xdrneg1) vap->va_gid = fxdr_unsigned(gid_t, sp->sa_gid); if (sp->sa_size != nfs_xdrneg1) vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_size); if (sp->sa_atime.nfsv2_sec != nfs_xdrneg1) { #ifdef notyet fxdr_nfsv2time(&sp->sa_atime, &vap->va_atime); #else vap->va_atime.tv_sec = fxdr_unsigned(long, sp->sa_atime.nfsv2_sec); vap->va_atime.tv_nsec = 0; #endif } if (sp->sa_mtime.nfsv2_sec != nfs_xdrneg1) fxdr_nfsv2time(&sp->sa_mtime, &vap->va_mtime); } /* * Now that we have all the fields, lets do it. */ if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); return (0); } nqsrv_getl(vp, ND_WRITE); if (v3) { error = preat_ret = VOP_GETATTR(vp, &preat, cred, procp); if (!error && gcheck && (preat.va_ctime.tv_sec != guard.tv_sec || preat.va_ctime.tv_nsec != guard.tv_nsec)) error = NFSERR_NOT_SYNC; if (error) { vput(vp); nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); return (0); } } /* * If the size is being changed write acces is required, otherwise * just check for a read only file system. */ if (vap->va_size == ((u_quad_t)((quad_t) -1))) { if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) { error = EROFS; goto out; } } else { if (vp->v_type == VDIR) { error = EISDIR; goto out; } else if (error = nfsrv_access(vp, VWRITE, cred, rdonly, procp)) goto out; } error = VOP_SETATTR(vp, vap, cred, procp); postat_ret = VOP_GETATTR(vp, vap, cred, procp); if (!error) error = postat_ret; out: vput(vp); nfsm_reply(NFSX_WCCORFATTR(v3)); if (v3) { nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); return (0); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } nfsm_srvdone; } /* * nfs lookup rpc */ int nfsrv_lookup(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct nfs_fattr *fp; struct nameidata nd, ind, *ndp = &nd; struct vnode *vp, *dirp; nfsfh_t nfh; fhandle_t *fhp; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, dirattr_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3), pubflag; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vattr va, dirattr, *vap = &va; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); pubflag = nfs_ispublicfh(fhp); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), pubflag); if (!error && pubflag) { if (nd.ni_vp->v_type == VDIR && nfs_pub.np_index != NULL) { /* * Setup call to lookup() to see if we can find * the index file. Arguably, this doesn't belong * in a kernel.. Ugh. */ ind = nd; VOP_UNLOCK(nd.ni_vp, 0, procp); ind.ni_pathlen = strlen(nfs_pub.np_index); ind.ni_cnd.cn_nameptr = ind.ni_cnd.cn_pnbuf = nfs_pub.np_index; ind.ni_startdir = nd.ni_vp; VREF(ind.ni_startdir); error = lookup(&ind); if (!error) { /* * Found an index file. Get rid of * the old references. */ if (dirp) vrele(dirp); dirp = nd.ni_vp; vrele(nd.ni_startdir); ndp = &ind; } else error = 0; } /* * If the public filehandle was used, check that this lookup * didn't result in a filehandle outside the publicly exported * filesystem. */ if (!error && ndp->ni_vp->v_mount != nfs_pub.np_mount) { vput(nd.ni_vp); error = EPERM; } } if (dirp) { if (v3) dirattr_ret = VOP_GETATTR(dirp, &dirattr, cred, procp); vrele(dirp); } if (error) { nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(dirattr_ret, &dirattr); return (0); } nqsrv_getl(ndp->ni_startdir, ND_READ); vrele(ndp->ni_startdir); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); vp = ndp->ni_vp; bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPORFATTR(v3) + NFSX_POSTOPATTR(v3)); if (error) { nfsm_srvpostop_attr(dirattr_ret, &dirattr); return (0); } nfsm_srvfhtom(fhp, v3); if (v3) { nfsm_srvpostop_attr(0, vap); nfsm_srvpostop_attr(dirattr_ret, &dirattr); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } nfsm_srvdone; } /* * nfs readlink service */ int nfsrv_readlink(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN]; register struct iovec *ivp = iv; register struct mbuf *mp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, i, tlen, len, getret; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mp2, *mp3, *mreq; struct vnode *vp; struct vattr attr; nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; u_quad_t frev; #ifndef nolint mp2 = mp3 = (struct mbuf *)0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); len = 0; i = 0; while (len < NFS_MAXPATHLEN) { MGET(mp, M_WAIT, MT_DATA); MCLGET(mp, M_WAIT); mp->m_len = NFSMSIZ(mp); if (len == 0) mp3 = mp2 = mp; else { mp2->m_next = mp; mp2 = mp; } if ((len+mp->m_len) > NFS_MAXPATHLEN) { mp->m_len = NFS_MAXPATHLEN-len; len = NFS_MAXPATHLEN; } else len += mp->m_len; ivp->iov_base = mtod(mp, caddr_t); ivp->iov_len = mp->m_len; i++; ivp++; } uiop->uio_iov = iv; uiop->uio_iovcnt = i; uiop->uio_offset = 0; uiop->uio_resid = len; uiop->uio_rw = UIO_READ; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_procp = (struct proc *)0; if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { m_freem(mp3); nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvpostop_attr(1, (struct vattr *)0); return (0); } if (vp->v_type != VLNK) { if (v3) error = EINVAL; else error = ENXIO; goto out; } nqsrv_getl(vp, ND_READ); error = VOP_READLINK(vp, uiop, cred); out: getret = VOP_GETATTR(vp, &attr, cred, procp); vput(vp); if (error) m_freem(mp3); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_UNSIGNED); if (v3) { nfsm_srvpostop_attr(getret, &attr); if (error) return (0); } if (uiop->uio_resid > 0) { len -= uiop->uio_resid; tlen = nfsm_rndup(len); nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len); } nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(len); mb->m_next = mp3; nfsm_srvdone; } /* * nfs read service */ int nfsrv_read(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct iovec *iv; struct iovec *iv2; register struct mbuf *m; register struct nfs_fattr *fp; register u_long *tl; register long t1; register int i; caddr_t bpos; int error = 0, rdonly, cache, cnt, len, left, siz, tlen, getret; int v3 = (nfsd->nd_flag & ND_NFSV3), reqlen; char *cp2; struct mbuf *mb, *mb2, *mreq; struct mbuf *m2; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; struct vattr va, *vap = &va; off_t off; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (v3) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fxdr_hyper(tl, &off); } else { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); off = (off_t)fxdr_unsigned(u_long, *tl); } nfsm_srvstrsiz(reqlen, NFS_SRVMAXDATA(nfsd)); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvpostop_attr(1, (struct vattr *)0); return (0); } if (vp->v_type != VREG) { if (v3) error = EINVAL; else error = (vp->v_type == VDIR) ? EISDIR : EACCES; } if (!error) { nqsrv_getl(vp, ND_READ); if (error = nfsrv_access(vp, VREAD, cred, rdonly, procp)) error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); } getret = VOP_GETATTR(vp, vap, cred, procp); if (!error) error = getret; if (error) { vput(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, vap); return (0); } if (off >= vap->va_size) cnt = 0; else if ((off + reqlen) > vap->va_size) cnt = nfsm_rndup(vap->va_size - off); else cnt = reqlen; nfsm_reply(NFSX_POSTOPORFATTR(v3) + 3 * NFSX_UNSIGNED+nfsm_rndup(cnt)); if (v3) { nfsm_build(tl, u_long *, NFSX_V3FATTR + 4 * NFSX_UNSIGNED); *tl++ = nfs_true; fp = (struct nfs_fattr *)tl; tl += (NFSX_V3FATTR / sizeof (u_long)); } else { nfsm_build(tl, u_long *, NFSX_V2FATTR + NFSX_UNSIGNED); fp = (struct nfs_fattr *)tl; tl += (NFSX_V2FATTR / sizeof (u_long)); } len = left = cnt; if (cnt > 0) { /* * Generate the mbuf list with the uio_iov ref. to it. */ i = 0; m = m2 = mb; while (left > 0) { siz = min(M_TRAILINGSPACE(m), left); if (siz > 0) { left -= siz; i++; } if (left > 0) { MGET(m, M_WAIT, MT_DATA); MCLGET(m, M_WAIT); m->m_len = 0; m2->m_next = m; m2 = m; } } MALLOC(iv, struct iovec *, i * sizeof (struct iovec), M_TEMP, M_WAITOK); uiop->uio_iov = iv2 = iv; m = mb; left = cnt; i = 0; while (left > 0) { if (m == NULL) panic("nfsrv_read iov"); siz = min(M_TRAILINGSPACE(m), left); if (siz > 0) { iv->iov_base = mtod(m, caddr_t) + m->m_len; iv->iov_len = siz; m->m_len += siz; left -= siz; iv++; i++; } m = m->m_next; } uiop->uio_iovcnt = i; uiop->uio_offset = off; uiop->uio_resid = cnt; uiop->uio_rw = UIO_READ; uiop->uio_segflg = UIO_SYSSPACE; error = VOP_READ(vp, uiop, IO_NODELOCKED, cred); off = uiop->uio_offset; FREE((caddr_t)iv2, M_TEMP); if (error || (getret = VOP_GETATTR(vp, vap, cred, procp))) { if (!error) error = getret; m_freem(mreq); vput(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, vap); return (0); } } else uiop->uio_resid = 0; vput(vp); nfsm_srvfillattr(vap, fp); len -= uiop->uio_resid; tlen = nfsm_rndup(len); if (cnt != tlen || tlen != len) nfsm_adj(mb, cnt - tlen, tlen - len); if (v3) { *tl++ = txdr_unsigned(len); if (len < reqlen) *tl++ = nfs_true; else *tl++ = nfs_false; } *tl = txdr_unsigned(len); nfsm_srvdone; } /* * nfs write service */ int nfsrv_write(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct iovec *ivp; register int i, cnt; register struct mbuf *mp; register struct nfs_fattr *fp; struct iovec *iv; struct vattr va, forat; register struct vattr *vap = &va; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, len, forat_ret = 1; int ioflags, aftat_ret = 1, retlen, zeroing, adjust; int stable = NFSV3WRITE_FILESYNC; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; off_t off; u_quad_t frev; if (mrep == NULL) { *mrq = NULL; return (0); } fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &off); tl += 3; stable = fxdr_unsigned(int, *tl++); } else { nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); off = (off_t)fxdr_unsigned(u_long, *++tl); tl += 2; if (nfs_async) stable = NFSV3WRITE_UNSTABLE; } retlen = len = fxdr_unsigned(long, *tl); cnt = i = 0; /* * For NFS Version 2, it is not obvious what a write of zero length * should do, but I might as well be consistent with Version 3, * which is to return ok so long as there are no permission problems. */ if (len > 0) { zeroing = 1; mp = mrep; while (mp) { if (mp == md) { zeroing = 0; adjust = dpos - mtod(mp, caddr_t); mp->m_len -= adjust; if (mp->m_len > 0 && adjust > 0) NFSMADV(mp, adjust); } if (zeroing) mp->m_len = 0; else if (mp->m_len > 0) { i += mp->m_len; if (i > len) { mp->m_len -= (i - len); zeroing = 1; } if (mp->m_len > 0) cnt++; } mp = mp->m_next; } } if (len > NFS_MAXDATA || len < 0 || i < len) { error = EIO; nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); return (0); } if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); return (0); } if (v3) forat_ret = VOP_GETATTR(vp, &forat, cred, procp); if (vp->v_type != VREG) { if (v3) error = EINVAL; else error = (vp->v_type == VDIR) ? EISDIR : EACCES; } if (!error) { nqsrv_getl(vp, ND_WRITE); error = nfsrv_access(vp, VWRITE, cred, rdonly, procp); } if (error) { vput(vp); nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); return (0); } if (len > 0) { MALLOC(ivp, struct iovec *, cnt * sizeof (struct iovec), M_TEMP, M_WAITOK); uiop->uio_iov = iv = ivp; uiop->uio_iovcnt = cnt; mp = mrep; while (mp) { if (mp->m_len > 0) { ivp->iov_base = mtod(mp, caddr_t); ivp->iov_len = mp->m_len; ivp++; } mp = mp->m_next; } /* * XXX * The IO_METASYNC flag indicates that all metadata (and not just * enough to ensure data integrity) mus be written to stable storage * synchronously. * (IO_METASYNC is not yet implemented in 4.4BSD-Lite.) */ if (stable == NFSV3WRITE_UNSTABLE) ioflags = IO_NODELOCKED; else if (stable == NFSV3WRITE_DATASYNC) ioflags = (IO_SYNC | IO_NODELOCKED); else ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); uiop->uio_resid = len; uiop->uio_rw = UIO_WRITE; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_procp = (struct proc *)0; uiop->uio_offset = off; error = VOP_WRITE(vp, uiop, ioflags, cred); nfsstats.srvvop_writes++; FREE((caddr_t)iv, M_TEMP); } aftat_ret = VOP_GETATTR(vp, vap, cred, procp); vput(vp); if (!error) error = aftat_ret; nfsm_reply(NFSX_PREOPATTR(v3) + NFSX_POSTOPORFATTR(v3) + 2 * NFSX_UNSIGNED + NFSX_WRITEVERF(v3)); if (v3) { nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); if (error) return (0); nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(retlen); /* * If nfs_async is set, then pretend the write was FILESYNC. */ if (stable == NFSV3WRITE_UNSTABLE && !nfs_async) *tl++ = txdr_unsigned(stable); else *tl++ = txdr_unsigned(NFSV3WRITE_FILESYNC); /* * Actually, there is no need to txdr these fields, * but it may make the values more human readable, * for debugging purposes. */ *tl++ = txdr_unsigned(boottime.tv_sec); *tl = txdr_unsigned(boottime.tv_usec); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } nfsm_srvdone; } /* * NFS write service with write gathering support. Called when * nfsrvw_procrastinate > 0. * See: Chet Juszczak, "Improving the Write Performance of an NFS Server", * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco, * Jan. 1994. */ int nfsrv_writegather(ndp, slp, procp, mrq) struct nfsrv_descript **ndp; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { register struct iovec *ivp; register struct mbuf *mp; register struct nfsrv_descript *wp, *nfsd, *owp, *swp; register struct nfs_fattr *fp; register int i; struct iovec *iov; struct nfsrvw_delayhash *wpp; struct ucred *cred; struct vattr va, forat; register u_long *tl; register long t1; caddr_t bpos, dpos; int error = 0, rdonly, cache, len, forat_ret = 1; int ioflags, aftat_ret = 1, s, adjust, v3, zeroing; char *cp2; struct mbuf *mb, *mb2, *mreq, *mrep, *md; struct vnode *vp; struct uio io, *uiop = &io; u_quad_t frev, cur_usec; #ifndef nolint i = 0; len = 0; #endif *mrq = NULL; if (*ndp) { nfsd = *ndp; *ndp = NULL; mrep = nfsd->nd_mrep; md = nfsd->nd_md; dpos = nfsd->nd_dpos; cred = &nfsd->nd_cr; v3 = (nfsd->nd_flag & ND_NFSV3); LIST_INIT(&nfsd->nd_coalesce); nfsd->nd_mreq = NULL; nfsd->nd_stable = NFSV3WRITE_FILESYNC; cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec; nfsd->nd_time = cur_usec + (v3 ? nfsrvw_procrastinate_v3 : nfsrvw_procrastinate); /* * Now, get the write header.. */ nfsm_srvmtofh(&nfsd->nd_fh); if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &nfsd->nd_off); tl += 3; nfsd->nd_stable = fxdr_unsigned(int, *tl++); } else { nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); nfsd->nd_off = (off_t)fxdr_unsigned(u_long, *++tl); tl += 2; if (nfs_async) nfsd->nd_stable = NFSV3WRITE_UNSTABLE; } len = fxdr_unsigned(long, *tl); nfsd->nd_len = len; nfsd->nd_eoff = nfsd->nd_off + len; /* * Trim the header out of the mbuf list and trim off any trailing * junk so that the mbuf list has only the write data. */ zeroing = 1; i = 0; mp = mrep; while (mp) { if (mp == md) { zeroing = 0; adjust = dpos - mtod(mp, caddr_t); mp->m_len -= adjust; if (mp->m_len > 0 && adjust > 0) NFSMADV(mp, adjust); } if (zeroing) mp->m_len = 0; else { i += mp->m_len; if (i > len) { mp->m_len -= (i - len); zeroing = 1; } } mp = mp->m_next; } if (len > NFS_MAXDATA || len < 0 || i < len) { nfsmout: m_freem(mrep); error = EIO; nfsm_writereply(2 * NFSX_UNSIGNED, v3); if (v3) nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); nfsd->nd_mreq = mreq; nfsd->nd_mrep = NULL; nfsd->nd_time = 0; } /* * Add this entry to the hash and time queues. */ s = splsoftclock(); owp = NULL; wp = slp->ns_tq.lh_first; while (wp && wp->nd_time < nfsd->nd_time) { owp = wp; wp = wp->nd_tq.le_next; } NFS_DPF(WG, ("Q%03x", nfsd->nd_retxid & 0xfff)); if (owp) { LIST_INSERT_AFTER(owp, nfsd, nd_tq); } else { LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq); } if (nfsd->nd_mrep) { wpp = NWDELAYHASH(slp, nfsd->nd_fh.fh_fid.fid_data); owp = NULL; wp = wpp->lh_first; while (wp && bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) { owp = wp; wp = wp->nd_hash.le_next; } while (wp && wp->nd_off < nfsd->nd_off && !bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) { owp = wp; wp = wp->nd_hash.le_next; } if (owp) { LIST_INSERT_AFTER(owp, nfsd, nd_hash); /* * Search the hash list for overlapping entries and * coalesce. */ for(; nfsd && NFSW_CONTIG(owp, nfsd); nfsd = wp) { wp = nfsd->nd_hash.le_next; if (NFSW_SAMECRED(owp, nfsd)) nfsrvw_coalesce(owp, nfsd); } } else { LIST_INSERT_HEAD(wpp, nfsd, nd_hash); } } splx(s); } /* * Now, do VOP_WRITE()s for any one(s) that need to be done now * and generate the associated reply mbuf list(s). */ loop1: cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec; s = splsoftclock(); for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = owp) { owp = nfsd->nd_tq.le_next; if (nfsd->nd_time > cur_usec) break; if (nfsd->nd_mreq) continue; NFS_DPF(WG, ("P%03x", nfsd->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_tq); LIST_REMOVE(nfsd, nd_hash); splx(s); mrep = nfsd->nd_mrep; nfsd->nd_mrep = NULL; cred = &nfsd->nd_cr; v3 = (nfsd->nd_flag & ND_NFSV3); forat_ret = aftat_ret = 1; error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, cred, slp, nfsd->nd_nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE); if (!error) { if (v3) forat_ret = VOP_GETATTR(vp, &forat, cred, procp); if (vp->v_type != VREG) { if (v3) error = EINVAL; else error = (vp->v_type == VDIR) ? EISDIR : EACCES; } } else vp = NULL; if (!error) { nqsrv_getl(vp, ND_WRITE); error = nfsrv_access(vp, VWRITE, cred, rdonly, procp); } if (nfsd->nd_stable == NFSV3WRITE_UNSTABLE) ioflags = IO_NODELOCKED; else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC) ioflags = (IO_SYNC | IO_NODELOCKED); else ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); uiop->uio_rw = UIO_WRITE; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_procp = (struct proc *)0; uiop->uio_offset = nfsd->nd_off; uiop->uio_resid = nfsd->nd_eoff - nfsd->nd_off; if (uiop->uio_resid > 0) { mp = mrep; i = 0; while (mp) { if (mp->m_len > 0) i++; mp = mp->m_next; } uiop->uio_iovcnt = i; MALLOC(iov, struct iovec *, i * sizeof (struct iovec), M_TEMP, M_WAITOK); uiop->uio_iov = ivp = iov; mp = mrep; while (mp) { if (mp->m_len > 0) { ivp->iov_base = mtod(mp, caddr_t); ivp->iov_len = mp->m_len; ivp++; } mp = mp->m_next; } if (!error) { error = VOP_WRITE(vp, uiop, ioflags, cred); nfsstats.srvvop_writes++; } FREE((caddr_t)iov, M_TEMP); } m_freem(mrep); if (vp) { aftat_ret = VOP_GETATTR(vp, &va, cred, procp); vput(vp); } /* * Loop around generating replies for all write rpcs that have * now been completed. */ swp = nfsd; do { NFS_DPF(WG, ("R%03x", nfsd->nd_retxid & 0xfff)); if (error) { nfsm_writereply(NFSX_WCCDATA(v3), v3); if (v3) { nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); } } else { nfsm_writereply(NFSX_PREOPATTR(v3) + NFSX_POSTOPORFATTR(v3) + 2 * NFSX_UNSIGNED + NFSX_WRITEVERF(v3), v3); if (v3) { nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(nfsd->nd_len); *tl++ = txdr_unsigned(swp->nd_stable); /* * Actually, there is no need to txdr these fields, * but it may make the values more human readable, * for debugging purposes. */ *tl++ = txdr_unsigned(boottime.tv_sec); *tl = txdr_unsigned(boottime.tv_usec); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(&va, fp); } } nfsd->nd_mreq = mreq; if (nfsd->nd_mrep) panic("nfsrv_write: nd_mrep not free"); /* * Done. Put it at the head of the timer queue so that * the final phase can return the reply. */ s = splsoftclock(); if (nfsd != swp) { nfsd->nd_time = 0; LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq); } nfsd = swp->nd_coalesce.lh_first; if (nfsd) { LIST_REMOVE(nfsd, nd_tq); } splx(s); } while (nfsd); s = splsoftclock(); swp->nd_time = 0; LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq); splx(s); goto loop1; } splx(s); /* * Search for a reply to return. */ s = splsoftclock(); for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = nfsd->nd_tq.le_next) if (nfsd->nd_mreq) { NFS_DPF(WG, ("X%03x", nfsd->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_tq); *mrq = nfsd->nd_mreq; *ndp = nfsd; break; } splx(s); return (0); } /* * Coalesce the write request nfsd into owp. To do this we must: * - remove nfsd from the queues * - merge nfsd->nd_mrep into owp->nd_mrep * - update the nd_eoff and nd_stable for owp * - put nfsd on owp's nd_coalesce list * NB: Must be called at splsoftclock(). */ static void nfsrvw_coalesce(owp, nfsd) register struct nfsrv_descript *owp; register struct nfsrv_descript *nfsd; { register int overlap; register struct mbuf *mp; struct nfsrv_descript *p; NFS_DPF(WG, ("C%03x-%03x", nfsd->nd_retxid & 0xfff, owp->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_hash); LIST_REMOVE(nfsd, nd_tq); if (owp->nd_eoff < nfsd->nd_eoff) { overlap = owp->nd_eoff - nfsd->nd_off; if (overlap < 0) panic("nfsrv_coalesce: bad off"); if (overlap > 0) m_adj(nfsd->nd_mrep, overlap); mp = owp->nd_mrep; while (mp->m_next) mp = mp->m_next; mp->m_next = nfsd->nd_mrep; owp->nd_eoff = nfsd->nd_eoff; } else m_freem(nfsd->nd_mrep); nfsd->nd_mrep = NULL; if (nfsd->nd_stable == NFSV3WRITE_FILESYNC) owp->nd_stable = NFSV3WRITE_FILESYNC; else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC && owp->nd_stable == NFSV3WRITE_UNSTABLE) owp->nd_stable = NFSV3WRITE_DATASYNC; LIST_INSERT_HEAD(&owp->nd_coalesce, nfsd, nd_tq); /* * If nfsd had anything else coalesced into it, transfer them * to owp, otherwise their replies will never get sent. */ for (p = nfsd->nd_coalesce.lh_first; p; p = nfsd->nd_coalesce.lh_first) { LIST_REMOVE(p, nd_tq); LIST_INSERT_HEAD(&owp->nd_coalesce, p, nd_tq); } } /* * Sort the group list in increasing numerical order. * (Insertion sort by Chris Torek, who was grossed out by the bubble sort * that used to be here.) */ void nfsrvw_sort(list, num) register gid_t *list; register int num; { register int i, j; gid_t v; /* Insertion sort. */ for (i = 1; i < num; i++) { v = list[i]; /* find correct slot for value v, moving others up */ for (j = i; --j >= 0 && v < list[j];) list[j + 1] = list[j]; list[j + 1] = v; } } /* * copy credentials making sure that the result can be compared with bcmp(). */ void nfsrv_setcred(incred, outcred) register struct ucred *incred, *outcred; { register int i; bzero((caddr_t)outcred, sizeof (struct ucred)); outcred->cr_ref = 1; outcred->cr_uid = incred->cr_uid; outcred->cr_ngroups = incred->cr_ngroups; for (i = 0; i < incred->cr_ngroups; i++) outcred->cr_groups[i] = incred->cr_groups[i]; nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups); } /* * nfs create service * now does a truncate to 0 length via. setattr if it already exists */ int nfsrv_create(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct nfs_fattr *fp; struct vattr va, dirfor, diraft; register struct vattr *vap = &va; register struct nfsv2_sattr *sp; register u_long *tl; struct nameidata nd; register caddr_t cp; register long t1; caddr_t bpos; int error = 0, rdev, cache, len, tsize, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3), how, exclusive_flag = 0; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp, *dirp = (struct vnode *)0; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev, tempsize; u_char cverf[NFSX_V3CREATEVERF]; #ifndef nolint rdev = 0; #endif nd.ni_cnd.cn_nameiop = 0; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) { nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); if (dirp) vrele(dirp); return (0); } VATTR_NULL(vap); if (v3) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); how = fxdr_unsigned(int, *tl); switch (how) { case NFSV3CREATE_GUARDED: if (nd.ni_vp) { error = EEXIST; break; } case NFSV3CREATE_UNCHECKED: nfsm_srvsattr(vap); break; case NFSV3CREATE_EXCLUSIVE: nfsm_dissect(cp, caddr_t, NFSX_V3CREATEVERF); bcopy(cp, cverf, NFSX_V3CREATEVERF); exclusive_flag = 1; if (nd.ni_vp == NULL) vap->va_mode = 0; break; }; vap->va_type = VREG; } else { nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); vap->va_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode)); if (vap->va_type == VNON) vap->va_type = VREG; vap->va_mode = nfstov_mode(sp->sa_mode); switch (vap->va_type) { case VREG: tsize = fxdr_unsigned(long, sp->sa_size); if (tsize != -1) vap->va_size = (u_quad_t)tsize; break; case VCHR: case VBLK: case VFIFO: rdev = fxdr_unsigned(long, sp->sa_size); break; }; } /* * Iff doesn't exist, create it * otherwise just truncate to 0 length * should I set the mode too ?? */ if (nd.ni_vp == NULL) { if (vap->va_type == VREG || vap->va_type == VSOCK) { vrele(nd.ni_startdir); nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); if (!error) { nfsrv_object_create(nd.ni_vp); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); if (exclusive_flag) { exclusive_flag = 0; VATTR_NULL(vap); bcopy(cverf, (caddr_t)&vap->va_atime, NFSX_V3CREATEVERF); error = VOP_SETATTR(nd.ni_vp, vap, cred, procp); } } } 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 && (error = suser(cred, (u_short *)0))) { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); nfsm_reply(0); return (error); } else vap->va_rdev = (dev_t)rdev; nqsrv_getl(nd.ni_dvp, ND_WRITE); if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) { vrele(nd.ni_startdir); nfsm_reply(0); } nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART); nd.ni_cnd.cn_proc = procp; nd.ni_cnd.cn_cred = cred; if (error = lookup(&nd)) { free(nd.ni_cnd.cn_pnbuf, M_NAMEI); nfsm_reply(0); } nfsrv_object_create(nd.ni_vp); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); if (nd.ni_cnd.cn_flags & ISSYMLINK) { vrele(nd.ni_dvp); vput(nd.ni_vp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); error = EINVAL; nfsm_reply(0); } } else { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); error = ENXIO; } vp = nd.ni_vp; } else { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); vp = nd.ni_vp; if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (vap->va_size != -1) { error = nfsrv_access(vp, VWRITE, cred, (nd.ni_cnd.cn_flags & RDONLY), procp); if (!error) { nqsrv_getl(vp, ND_WRITE); tempsize = vap->va_size; VATTR_NULL(vap); vap->va_size = tempsize; error = VOP_SETATTR(vp, vap, cred, procp); } if (error) vput(vp); } } if (!error) { bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); } if (v3) { if (exclusive_flag && !error && bcmp(cverf, (caddr_t)&vap->va_atime, NFSX_V3CREATEVERF)) error = EEXIST; diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_SRVFH(v3) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { if (!error) { nfsm_srvpostop_fh(fhp); nfsm_srvpostop_attr(0, vap); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); } else { nfsm_srvfhtom(fhp, v3); nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } return (0); nfsmout: if (dirp) vrele(dirp); if (nd.ni_cnd.cn_nameiop) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); } VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vput(nd.ni_vp); return (error); } /* * nfs v3 mknod service */ int nfsrv_mknod(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr va, dirfor, diraft; register struct vattr *vap = &va; register u_long *tl; struct nameidata nd; register long t1; caddr_t bpos; int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; u_long major, minor; enum vtype vtyp; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp, *dirp = (struct vnode *)0; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; nd.ni_cnd.cn_nameiop = 0; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); if (error) { nfsm_reply(NFSX_WCCDATA(1)); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); if (dirp) vrele(dirp); return (0); } nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); vtyp = nfsv3tov_type(*tl); if (vtyp != VCHR && vtyp != VBLK && vtyp != VSOCK && vtyp != VFIFO) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); error = NFSERR_BADTYPE; VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); goto out; } VATTR_NULL(vap); nfsm_srvsattr(vap); if (vtyp == VCHR || vtyp == VBLK) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); major = fxdr_unsigned(u_long, *tl++); minor = fxdr_unsigned(u_long, *tl); vap->va_rdev = makedev(major, minor); } /* * Iff doesn't exist, create it. */ if (nd.ni_vp) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); error = EEXIST; VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); goto out; } vap->va_type = vtyp; if (vtyp == VSOCK) { vrele(nd.ni_startdir); nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); if (!error) FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); } else { if (vtyp != VFIFO && (error = suser(cred, (u_short *)0))) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); goto out; } nqsrv_getl(nd.ni_dvp, ND_WRITE); if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) { vrele(nd.ni_startdir); goto out; } nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART); nd.ni_cnd.cn_proc = procp; nd.ni_cnd.cn_cred = procp->p_ucred; error = lookup(&nd); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); if (error) goto out; if (nd.ni_cnd.cn_flags & ISSYMLINK) { vrele(nd.ni_dvp); vput(nd.ni_vp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); error = EINVAL; } } out: vp = nd.ni_vp; if (!error) { bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); } diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); nfsm_reply(NFSX_SRVFH(1) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1)); if (!error) { nfsm_srvpostop_fh(fhp); nfsm_srvpostop_attr(0, vap); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); nfsmout: if (dirp) vrele(dirp); if (nd.ni_cnd.cn_nameiop) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); } VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vput(nd.ni_vp); return (error); } /* * nfs remove service */ int nfsrv_remove(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct nameidata nd; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mreq; struct vnode *vp, *dirp; struct vattr dirfor, diraft; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; #ifndef nolint vp = (struct vnode *)0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = DELETE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else vrele(dirp); } if (!error) { vp = nd.ni_vp; if (vp->v_type == VDIR) { error = EPERM; /* POSIX */ goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) { error = EBUSY; goto out; } out: if (!error) { vnode_pager_uncache(vp, procp); nqsrv_getl(nd.ni_dvp, ND_WRITE); nqsrv_getl(vp, ND_WRITE); error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); } } if (dirp && v3) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_WCCDATA(v3)); if (v3) { nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); } nfsm_srvdone; } /* * nfs rename service */ int nfsrv_rename(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, len2, fdirfor_ret = 1, fdiraft_ret = 1; int tdirfor_ret = 1, tdiraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mreq; struct nameidata fromnd, tond; struct vnode *fvp, *tvp, *tdvp, *fdirp = (struct vnode *)0; struct vnode *tdirp = (struct vnode *)0; struct vattr fdirfor, fdiraft, tdirfor, tdiraft; nfsfh_t fnfh, tnfh; fhandle_t *ffhp, *tfhp; u_quad_t frev; uid_t saved_uid; #ifndef nolint fvp = (struct vnode *)0; #endif ffhp = &fnfh.fh_generic; tfhp = &tnfh.fh_generic; fromnd.ni_cnd.cn_nameiop = 0; tond.ni_cnd.cn_nameiop = 0; nfsm_srvmtofh(ffhp); nfsm_srvnamesiz(len); /* * Remember our original uid so that we can reset cr_uid before * the second nfs_namei() call, in case it is remapped. */ saved_uid = cred->cr_uid; fromnd.ni_cnd.cn_cred = cred; fromnd.ni_cnd.cn_nameiop = DELETE; fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART; error = nfs_namei(&fromnd, ffhp, len, slp, nam, &md, &dpos, &fdirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (fdirp) { if (v3) fdirfor_ret = VOP_GETATTR(fdirp, &fdirfor, cred, procp); else { vrele(fdirp); fdirp = (struct vnode *)0; } } if (error) { nfsm_reply(2 * NFSX_WCCDATA(v3)); nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); if (fdirp) vrele(fdirp); return (0); } fvp = fromnd.ni_vp; nfsm_srvmtofh(tfhp); nfsm_strsiz(len2, NFS_MAXNAMLEN); cred->cr_uid = saved_uid; tond.ni_cnd.cn_cred = cred; tond.ni_cnd.cn_nameiop = RENAME; tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART; error = nfs_namei(&tond, tfhp, len2, slp, nam, &md, &dpos, &tdirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (tdirp) { if (v3) tdirfor_ret = VOP_GETATTR(tdirp, &tdirfor, cred, procp); else { vrele(tdirp); tdirp = (struct vnode *)0; } } if (error) { VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { if (v3) error = EEXIST; else error = EISDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { if (v3) error = EEXIST; else error = ENOTDIR; goto out; } if (tvp->v_type == VDIR && tvp->v_mountedhere) { if (v3) error = EXDEV; else error = ENOTEMPTY; goto out; } } if (fvp->v_type == VDIR && fvp->v_mountedhere) { if (v3) error = EXDEV; else error = ENOTEMPTY; goto out; } if (fvp->v_mount != tdvp->v_mount) { if (v3) error = EXDEV; else error = ENOTEMPTY; goto out; } if (fvp == tdvp) if (v3) error = EINVAL; else error = ENOTEMPTY; /* * If source is the same as the destination (that is the * same vnode with the same name in the same directory), * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, fromnd.ni_cnd.cn_namelen)) error = -1; out: if (!error) { nqsrv_getl(fromnd.ni_dvp, ND_WRITE); nqsrv_getl(tdvp, ND_WRITE); if (tvp) { nqsrv_getl(tvp, ND_WRITE); (void) vnode_pager_uncache(tvp, procp); } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); } else { VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); if (error == -1) error = 0; } vrele(tond.ni_startdir); FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); out1: if (fdirp) { fdiraft_ret = VOP_GETATTR(fdirp, &fdiraft, cred, procp); vrele(fdirp); } if (tdirp) { tdiraft_ret = VOP_GETATTR(tdirp, &tdiraft, cred, procp); vrele(tdirp); } vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); nfsm_reply(2 * NFSX_WCCDATA(v3)); if (v3) { nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); } return (0); nfsmout: if (fdirp) vrele(fdirp); if (tdirp) vrele(tdirp); if (tond.ni_cnd.cn_nameiop) { vrele(tond.ni_startdir); FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); } if (fromnd.ni_cnd.cn_nameiop) { vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); } return (error); } /* * nfs link service */ int nfsrv_link(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct nameidata nd; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, len, dirfor_ret = 1, diraft_ret = 1; int getret = 1, v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mreq; struct vnode *vp, *xp, *dirp = (struct vnode *)0; struct vattr dirfor, diraft, at; nfsfh_t nfh, dnfh; fhandle_t *fhp, *dfhp; u_quad_t frev; fhp = &nfh.fh_generic; dfhp = &dnfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvmtofh(dfhp); nfsm_srvnamesiz(len); if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); nfsm_srvpostop_attr(getret, &at); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); } if (vp->v_type == VDIR) { error = EPERM; /* POSIX */ goto out1; } nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT; error = nfs_namei(&nd, dfhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) goto out1; xp = nd.ni_vp; if (xp != NULL) { error = EEXIST; goto out; } xp = nd.ni_dvp; if (vp->v_mount != xp->v_mount) error = EXDEV; out: if (!error) { nqsrv_getl(vp, ND_WRITE); nqsrv_getl(xp, ND_WRITE); -#if defined(__NetBSD__) || defined(__FreeBSD__) error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); -#else /* Lite2 version */ - error = VOP_LINK(vp, nd.ni_dvp, &nd.ni_cnd); -#endif } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); } out1: if (v3) getret = VOP_GETATTR(vp, &at, cred, procp); if (dirp) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } vrele(vp); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { nfsm_srvpostop_attr(getret, &at); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); } nfsm_srvdone; } /* * nfs symbolic link service */ int nfsrv_symlink(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr va, dirfor, diraft; struct nameidata nd; register struct vattr *vap = &va; register u_long *tl; register long t1; struct nfsv2_sattr *sp; char *bpos, *pathcp = (char *)0, *cp2; struct uio io; struct iovec iv; int error = 0, cache, len, len2, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); struct mbuf *mb, *mreq, *mb2; struct vnode *dirp = (struct vnode *)0; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; nd.ni_cnd.cn_nameiop = 0; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) goto out; VATTR_NULL(vap); if (v3) nfsm_srvsattr(vap); 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; io.uio_procp = (struct proc *)0; nfsm_mtouio(&io, len2); if (!v3) { nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode); } *(pathcp + len2) = '\0'; if (nd.ni_vp) { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); error = EEXIST; goto out; } nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap, pathcp); if (error) vrele(nd.ni_startdir); else { if (v3) { nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART | FOLLOW); nd.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF); nd.ni_cnd.cn_proc = procp; nd.ni_cnd.cn_cred = cred; error = lookup(&nd); if (!error) { bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = nd.ni_vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(nd.ni_vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(nd.ni_vp, vap, cred, procp); vput(nd.ni_vp); } } else vrele(nd.ni_startdir); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); } out: if (pathcp) FREE(pathcp, M_TEMP); if (dirp) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { if (!error) { nfsm_srvpostop_fh(fhp); nfsm_srvpostop_attr(0, vap); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); } return (0); nfsmout: if (nd.ni_cnd.cn_nameiop) { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); } if (dirp) vrele(dirp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); if (pathcp) FREE(pathcp, M_TEMP); return (error); } /* * nfs mkdir service */ int nfsrv_mkdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr va, dirfor, diraft; register struct vattr *vap = &va; register struct nfs_fattr *fp; struct nameidata nd; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp, *dirp = (struct vnode *)0; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) { nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); if (dirp) vrele(dirp); return (0); } VATTR_NULL(vap); if (v3) { nfsm_srvsattr(vap); } else { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); vap->va_mode = nfstov_mode(*tl++); } vap->va_type = VDIR; vp = nd.ni_vp; if (vp != NULL) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(vp); error = EEXIST; goto out; } nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); if (!error) { vp = nd.ni_vp; bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); } out: if (dirp) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { if (!error) { nfsm_srvpostop_fh(fhp); nfsm_srvpostop_attr(0, vap); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); } else { nfsm_srvfhtom(fhp, v3); nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } return (0); nfsmout: if (dirp) vrele(dirp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); return (error); } /* * nfs rmdir service */ int nfsrv_rmdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mreq; struct vnode *vp, *dirp = (struct vnode *)0; struct vattr dirfor, diraft; nfsfh_t nfh; fhandle_t *fhp; struct nameidata nd; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = DELETE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) { nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); if (dirp) vrele(dirp); return (0); } vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) error = EBUSY; out: if (!error) { nqsrv_getl(nd.ni_dvp, ND_WRITE); nqsrv_getl(vp, ND_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); } if (dirp) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_WCCDATA(v3)); if (v3) { nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); } nfsm_srvdone; } /* * nfs readdir service * - mallocs what it thinks is enough to read * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR * - calls VOP_READDIR() * - loops around building the reply * if the output generated exceeds count break out of loop * The nfsm_clget macro is used here so that the reply will be packed * tightly in mbuf clusters. * - it only knows that it has encountered eof when the VOP_READDIR() * reads nothing * - as such one readdir rpc will return eof false although you are there * and then the next will return eof * - it trims out records with d_fileno == 0 * this doesn't matter for Unix clients, but they might confuse clients * for other os'. * NB: It is tempting to set eof to true if the VOP_READDIR() reads less * than requested, but this may not apply to all filesystems. For * example, client NFS does not { although it is never remote mounted * anyhow } * The alternate call nfsrv_readdirplus() does lookups as well. * PS: The NFS protocol spec. does not clarify what the "count" byte * argument is a count of.. just name strings and file id's or the * entire reply rpc or ... * I tried just file name and id sizes and it confused the Sun client, * so I am using the full rpc size now. The "paranoia.." comment refers * to including the status longwords that are not a part of the dir. * "entry" structures, but are in the rpc. */ struct flrep { nfsuint64 fl_off; u_long fl_postopok; u_long fl_fattr[NFSX_V3FATTR / sizeof (u_long)]; u_long fl_fhok; u_long fl_fhsize; u_long fl_nfh[NFSX_V3FH / sizeof (u_long)]; }; int nfsrv_readdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register char *bp, *be; register struct mbuf *mp; register struct dirent *dp; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; struct mbuf *mb, *mb2, *mreq, *mp2; char *cpos, *cend, *cp2, *rbuf; struct vnode *vp; struct vattr at; nfsfh_t nfh; fhandle_t *fhp; struct uio io; struct iovec iv; int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1; int siz, cnt, fullsiz, eofflag, rdonly, cache, ncookies; int v3 = (nfsd->nd_flag & ND_NFSV3); u_quad_t frev, off, toff, verf; u_long *cookies = NULL, *cookiep; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &toff); tl += 2; fxdr_hyper(tl, &verf); tl += 2; } else { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); toff = fxdr_unsigned(u_quad_t, *tl++); } off = toff; cnt = fxdr_unsigned(int, *tl); siz = ((cnt + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); xfer = NFS_SRVMAXDATA(nfsd); if (siz > xfer) siz = xfer; fullsiz = siz; if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } nqsrv_getl(vp, ND_READ); if (v3) { error = getret = VOP_GETATTR(vp, &at, cred, procp); if (!error && toff && verf && verf != at.va_filerev) error = NFSERR_BAD_COOKIE; } if (!error) error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); if (error) { vput(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, &at); return (0); } VOP_UNLOCK(vp, 0, procp); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); again: iv.iov_base = rbuf; iv.iov_len = fullsiz; io.uio_iov = &iv; io.uio_iovcnt = 1; io.uio_offset = (off_t)off; io.uio_resid = fullsiz; io.uio_segflg = UIO_SYSSPACE; io.uio_rw = UIO_READ; io.uio_procp = (struct proc *)0; eofflag = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, procp); if (cookies) { free((caddr_t)cookies, M_TEMP); cookies = NULL; } error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies); off = (off_t)io.uio_offset; if (!cookies && !error) error = NFSERR_PERM; if (v3) { getret = VOP_GETATTR(vp, &at, cred, procp); if (!error) error = getret; } VOP_UNLOCK(vp, 0, procp); if (error) { vrele(vp); free((caddr_t)rbuf, M_TEMP); if (cookies) free((caddr_t)cookies, M_TEMP); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, &at); return (0); } if (io.uio_resid) { siz -= io.uio_resid; /* * If nothing read, return eof * rpc reply */ if (siz == 0) { vrele(vp); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + 2 * NFSX_UNSIGNED); if (v3) { nfsm_srvpostop_attr(getret, &at); nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); txdr_hyper(&at.va_filerev, tl); tl += 2; } else nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_false; *tl = nfs_true; FREE((caddr_t)rbuf, M_TEMP); FREE((caddr_t)cookies, M_TEMP); return (0); } } /* * Check for degenerate cases of nothing useful read. * If so go try again */ cpos = rbuf; cend = rbuf + siz; dp = (struct dirent *)cpos; cookiep = cookies; /* * For some reason FreeBSD's ufs_readdir() chooses to back the * directory offset up to a block boundary, so it is necessary to * skip over the records that preceed the requested offset. This * requires the assumption that file offset cookies monotonically * increase. */ while (cpos < cend && ncookies > 0 && (dp->d_fileno == 0 || ((u_quad_t)(*cookiep)) <= toff)) { cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } if (cpos >= cend || ncookies == 0) { toff = off; siz = fullsiz; goto again; } len = 3 * NFSX_UNSIGNED; /* paranoia, probably can be 0 */ nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + siz); if (v3) { nfsm_srvpostop_attr(getret, &at); nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); txdr_hyper(&at.va_filerev, tl); } mp = mp2 = mb; bp = bpos; be = bp + M_TRAILINGSPACE(mp); /* Loop through the records and build reply */ while (cpos < cend && ncookies > 0) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; rem = nfsm_rndup(nlen)-nlen; len += (4 * NFSX_UNSIGNED + nlen + rem); if (v3) len += 2 * NFSX_UNSIGNED; if (len > cnt) { eofflag = 0; break; } /* * Build the directory record xdr from * the dirent entry. */ nfsm_clget; *tl = nfs_true; bp += NFSX_UNSIGNED; if (v3) { nfsm_clget; *tl = 0; bp += NFSX_UNSIGNED; } nfsm_clget; *tl = txdr_unsigned(dp->d_fileno); bp += NFSX_UNSIGNED; nfsm_clget; *tl = txdr_unsigned(nlen); bp += NFSX_UNSIGNED; /* And loop around copying the name */ xfer = nlen; cp = dp->d_name; while (xfer > 0) { nfsm_clget; if ((bp+xfer) > be) tsiz = be-bp; else tsiz = xfer; bcopy(cp, bp, tsiz); bp += tsiz; xfer -= tsiz; if (xfer > 0) cp += tsiz; } /* And null pad to a long boundary */ for (i = 0; i < rem; i++) *bp++ = '\0'; nfsm_clget; /* Finish off the record */ if (v3) { *tl = 0; bp += NFSX_UNSIGNED; nfsm_clget; } *tl = txdr_unsigned(*cookiep); bp += NFSX_UNSIGNED; } cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } vrele(vp); nfsm_clget; *tl = nfs_false; bp += NFSX_UNSIGNED; nfsm_clget; if (eofflag) *tl = nfs_true; else *tl = nfs_false; bp += NFSX_UNSIGNED; if (mp != mb) { if (bp < be) mp->m_len = bp - mtod(mp, caddr_t); } else mp->m_len += bp - bpos; FREE((caddr_t)rbuf, M_TEMP); FREE((caddr_t)cookies, M_TEMP); nfsm_srvdone; } int nfsrv_readdirplus(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register char *bp, *be; register struct mbuf *mp; register struct dirent *dp; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; struct mbuf *mb, *mb2, *mreq, *mp2; char *cpos, *cend, *cp2, *rbuf; struct vnode *vp, *nvp; struct flrep fl; nfsfh_t nfh; fhandle_t *fhp, *nfhp = (fhandle_t *)fl.fl_nfh; struct uio io; struct iovec iv; struct vattr va, at, *vap = &va; struct nfs_fattr *fp; int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1; int siz, cnt, fullsiz, eofflag, rdonly, cache, dirlen, ncookies; u_quad_t frev, off, toff, verf; u_long *cookies = NULL, *cookiep; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_dissect(tl, u_long *, 6 * NFSX_UNSIGNED); fxdr_hyper(tl, &toff); tl += 2; fxdr_hyper(tl, &verf); tl += 2; siz = fxdr_unsigned(int, *tl++); cnt = fxdr_unsigned(int, *tl); off = toff; siz = ((siz + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); xfer = NFS_SRVMAXDATA(nfsd); if (siz > xfer) siz = xfer; fullsiz = siz; if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } error = getret = VOP_GETATTR(vp, &at, cred, procp); if (!error && toff && verf && verf != at.va_filerev) error = NFSERR_BAD_COOKIE; if (!error) { nqsrv_getl(vp, ND_READ); error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); } if (error) { vput(vp); nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } VOP_UNLOCK(vp, 0, procp); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); again: iv.iov_base = rbuf; iv.iov_len = fullsiz; io.uio_iov = &iv; io.uio_iovcnt = 1; io.uio_offset = (off_t)off; io.uio_resid = fullsiz; io.uio_segflg = UIO_SYSSPACE; io.uio_rw = UIO_READ; io.uio_procp = (struct proc *)0; eofflag = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, procp); if (cookies) { free((caddr_t)cookies, M_TEMP); cookies = NULL; } error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies); off = (u_quad_t)io.uio_offset; getret = VOP_GETATTR(vp, &at, cred, procp); VOP_UNLOCK(vp, 0, procp); if (!cookies && !error) error = NFSERR_PERM; if (!error) error = getret; if (error) { vrele(vp); if (cookies) free((caddr_t)cookies, M_TEMP); free((caddr_t)rbuf, M_TEMP); nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } if (io.uio_resid) { siz -= io.uio_resid; /* * If nothing read, return eof * rpc reply */ if (siz == 0) { vrele(vp); nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); txdr_hyper(&at.va_filerev, tl); tl += 2; *tl++ = nfs_false; *tl = nfs_true; FREE((caddr_t)cookies, M_TEMP); FREE((caddr_t)rbuf, M_TEMP); return (0); } } /* * Check for degenerate cases of nothing useful read. * If so go try again */ cpos = rbuf; cend = rbuf + siz; dp = (struct dirent *)cpos; cookiep = cookies; /* * For some reason FreeBSD's ufs_readdir() chooses to back the * directory offset up to a block boundary, so it is necessary to * skip over the records that preceed the requested offset. This * requires the assumption that file offset cookies monotonically * increase. */ while (cpos < cend && ncookies > 0 && (dp->d_fileno == 0 || ((u_quad_t)(*cookiep)) <= toff)) { cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } if (cpos >= cend || ncookies == 0) { toff = off; siz = fullsiz; goto again; } /* * Probe one of the directory entries to see if the filesystem * supports VGET. */ if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp) == EOPNOTSUPP) { error = NFSERR_NOTSUPP; vrele(vp); free((caddr_t)cookies, M_TEMP); free((caddr_t)rbuf, M_TEMP); nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } vput(nvp); dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED; nfsm_reply(cnt); nfsm_srvpostop_attr(getret, &at); nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); txdr_hyper(&at.va_filerev, tl); mp = mp2 = mb; bp = bpos; be = bp + M_TRAILINGSPACE(mp); /* Loop through the records and build reply */ while (cpos < cend && ncookies > 0) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; rem = nfsm_rndup(nlen)-nlen; /* * For readdir_and_lookup get the vnode using * the file number. */ if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp)) goto invalid; bzero((caddr_t)nfhp, NFSX_V3FH); nfhp->fh_fsid = nvp->v_mount->mnt_stat.f_fsid; if (VFS_VPTOFH(nvp, &nfhp->fh_fid)) { vput(nvp); goto invalid; } if (VOP_GETATTR(nvp, vap, cred, procp)) { vput(nvp); goto invalid; } vput(nvp); /* * If either the dircount or maxcount will be * exceeded, get out now. Both of these lengths * are calculated conservatively, including all * XDR overheads. */ len += (7 * NFSX_UNSIGNED + nlen + rem + NFSX_V3FH + NFSX_V3POSTOPATTR); dirlen += (6 * NFSX_UNSIGNED + nlen + rem); if (len > cnt || dirlen > fullsiz) { eofflag = 0; break; } /* * Build the directory record xdr from * the dirent entry. */ fp = (struct nfs_fattr *)&fl.fl_fattr; nfsm_srvfillattr(vap, fp); fl.fl_fhsize = txdr_unsigned(NFSX_V3FH); fl.fl_fhok = nfs_true; fl.fl_postopok = nfs_true; fl.fl_off.nfsuquad[0] = 0; fl.fl_off.nfsuquad[1] = txdr_unsigned(*cookiep); nfsm_clget; *tl = nfs_true; bp += NFSX_UNSIGNED; nfsm_clget; *tl = 0; bp += NFSX_UNSIGNED; nfsm_clget; *tl = txdr_unsigned(dp->d_fileno); bp += NFSX_UNSIGNED; nfsm_clget; *tl = txdr_unsigned(nlen); bp += NFSX_UNSIGNED; /* And loop around copying the name */ xfer = nlen; cp = dp->d_name; while (xfer > 0) { nfsm_clget; if ((bp + xfer) > be) tsiz = be - bp; else tsiz = xfer; bcopy(cp, bp, tsiz); bp += tsiz; xfer -= tsiz; if (xfer > 0) cp += tsiz; } /* And null pad to a long boundary */ for (i = 0; i < rem; i++) *bp++ = '\0'; /* * Now copy the flrep structure out. */ xfer = sizeof (struct flrep); cp = (caddr_t)&fl; while (xfer > 0) { nfsm_clget; if ((bp + xfer) > be) tsiz = be - bp; else tsiz = xfer; bcopy(cp, bp, tsiz); bp += tsiz; xfer -= tsiz; if (xfer > 0) cp += tsiz; } } invalid: cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } vrele(vp); nfsm_clget; *tl = nfs_false; bp += NFSX_UNSIGNED; nfsm_clget; if (eofflag) *tl = nfs_true; else *tl = nfs_false; bp += NFSX_UNSIGNED; if (mp != mb) { if (bp < be) mp->m_len = bp - mtod(mp, caddr_t); } else mp->m_len += bp - bpos; FREE((caddr_t)cookies, M_TEMP); FREE((caddr_t)rbuf, M_TEMP); nfsm_srvdone; } /* * nfs commit service */ int nfsrv_commit(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr bfor, aft; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, for_ret = 1, aft_ret = 1, cnt, cache; char *cp2; struct mbuf *mb, *mb2, *mreq; u_quad_t frev, off; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); /* * XXX At this time VOP_FSYNC() does not accept offset and byte * count parameters, so these arguments are useless (someday maybe). */ fxdr_hyper(tl, &off); tl += 2; cnt = fxdr_unsigned(int, *tl); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft); return (0); } for_ret = VOP_GETATTR(vp, &bfor, cred, procp); error = VOP_FSYNC(vp, cred, MNT_WAIT, procp); aft_ret = VOP_GETATTR(vp, &aft, cred, procp); vput(vp); nfsm_reply(NFSX_V3WCCDATA + NFSX_V3WRITEVERF); nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft); if (!error) { nfsm_build(tl, u_long *, NFSX_V3WRITEVERF); *tl++ = txdr_unsigned(boottime.tv_sec); *tl = txdr_unsigned(boottime.tv_usec); } else return (0); nfsm_srvdone; } /* * nfs statfs service */ int nfsrv_statfs(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct statfs *sf; register struct nfs_statfs *sfp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, getret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; struct vattr at; nfsfh_t nfh; fhandle_t *fhp; struct statfs statfs; u_quad_t frev, tval; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } sf = &statfs; error = VFS_STATFS(vp->v_mount, sf, procp); getret = VOP_GETATTR(vp, &at, cred, procp); vput(vp); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_STATFS(v3)); if (v3) nfsm_srvpostop_attr(getret, &at); if (error) return (0); nfsm_build(sfp, struct nfs_statfs *, NFSX_STATFS(v3)); if (v3) { tval = (u_quad_t)sf->f_blocks; tval *= (u_quad_t)sf->f_bsize; txdr_hyper(&tval, &sfp->sf_tbytes); tval = (u_quad_t)sf->f_bfree; tval *= (u_quad_t)sf->f_bsize; txdr_hyper(&tval, &sfp->sf_fbytes); tval = (u_quad_t)sf->f_bavail; tval *= (u_quad_t)sf->f_bsize; txdr_hyper(&tval, &sfp->sf_abytes); sfp->sf_tfiles.nfsuquad[0] = 0; sfp->sf_tfiles.nfsuquad[1] = txdr_unsigned(sf->f_files); sfp->sf_ffiles.nfsuquad[0] = 0; sfp->sf_ffiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree); sfp->sf_afiles.nfsuquad[0] = 0; sfp->sf_afiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree); sfp->sf_invarsec = 0; } else { sfp->sf_tsize = txdr_unsigned(NFS_MAXDGRAMDATA); sfp->sf_bsize = txdr_unsigned(sf->f_bsize); sfp->sf_blocks = txdr_unsigned(sf->f_blocks); sfp->sf_bfree = txdr_unsigned(sf->f_bfree); sfp->sf_bavail = txdr_unsigned(sf->f_bavail); } nfsm_srvdone; } /* * nfs fsinfo service */ int nfsrv_fsinfo(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register struct nfsv3_fsinfo *sip; register long t1; caddr_t bpos; int error = 0, rdonly, cache, getret = 1, pref; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; struct vattr at; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } getret = VOP_GETATTR(vp, &at, cred, procp); vput(vp); nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3FSINFO); nfsm_srvpostop_attr(getret, &at); nfsm_build(sip, struct nfsv3_fsinfo *, NFSX_V3FSINFO); /* * XXX * There should be file system VFS OP(s) to get this information. * For now, assume ufs. */ if (slp->ns_so->so_type == SOCK_DGRAM) pref = NFS_MAXDGRAMDATA; else pref = NFS_MAXDATA; sip->fs_rtmax = txdr_unsigned(NFS_MAXDATA); sip->fs_rtpref = txdr_unsigned(pref); sip->fs_rtmult = txdr_unsigned(NFS_FABLKSIZE); sip->fs_wtmax = txdr_unsigned(NFS_MAXDATA); sip->fs_wtpref = txdr_unsigned(pref); sip->fs_wtmult = txdr_unsigned(NFS_FABLKSIZE); sip->fs_dtpref = txdr_unsigned(pref); sip->fs_maxfilesize.nfsuquad[0] = 0xffffffff; sip->fs_maxfilesize.nfsuquad[1] = 0xffffffff; sip->fs_timedelta.nfsv3_sec = 0; sip->fs_timedelta.nfsv3_nsec = txdr_unsigned(1); sip->fs_properties = txdr_unsigned(NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK | NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME); nfsm_srvdone; } /* * nfs pathconf service */ int nfsrv_pathconf(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register struct nfsv3_pathconf *pc; register long t1; caddr_t bpos; int error = 0, rdonly, cache, getret = 1, linkmax, namemax; int chownres, notrunc; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; struct vattr at; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } error = VOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax); if (!error) error = VOP_PATHCONF(vp, _PC_NAME_MAX, &namemax); if (!error) error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres); if (!error) error = VOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc); getret = VOP_GETATTR(vp, &at, cred, procp); vput(vp); nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3PATHCONF); nfsm_srvpostop_attr(getret, &at); if (error) return (0); nfsm_build(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF); pc->pc_linkmax = txdr_unsigned(linkmax); pc->pc_namemax = txdr_unsigned(namemax); pc->pc_notrunc = txdr_unsigned(notrunc); pc->pc_chownrestricted = txdr_unsigned(chownres); /* * These should probably be supported by VOP_PATHCONF(), but * until msdosfs is exportable (why would you want to?), the * Unix defaults should be ok. */ pc->pc_caseinsensitive = nfs_false; pc->pc_casepreserving = nfs_true; nfsm_srvdone; } /* * Null operation, used by clients to ping server */ /* ARGSUSED */ int nfsrv_null(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep; caddr_t bpos; int error = NFSERR_RETVOID, cache; struct mbuf *mb, *mreq; u_quad_t frev; #ifndef nolint cache = 0; #endif nfsm_reply(0); return (0); } /* * No operation, used for obsolete procedures */ /* ARGSUSED */ int nfsrv_noop(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep; caddr_t bpos; int error, cache; struct mbuf *mb, *mreq; u_quad_t frev; #ifndef nolint cache = 0; #endif if (nfsd->nd_repstat) error = nfsd->nd_repstat; else error = EPROCUNAVAIL; nfsm_reply(0); return (0); } /* * 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 exported rdonly 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 * a security hole the size of a barn door anyhow, what the heck. */ static int nfsrv_access(vp, flags, cred, rdonly, p) register struct vnode *vp; int flags; register struct ucred *cred; int rdonly; struct proc *p; { struct vattr vattr; int error; if (flags & VWRITE) { /* Just vn_writechk() changed to check rdonly */ /* * 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 (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); } } /* * If there's shared text associated with * the inode, we can't allow writing. */ if (vp->v_flag & VTEXT) return (ETXTBSY); } if (error = VOP_GETATTR(vp, &vattr, cred, p)) return (error); if ((error = VOP_ACCESS(vp, flags, cred, p)) && cred->cr_uid != vattr.va_uid) return (error); return (0); } #endif /* NFS_NOSERVER */ Index: head/sys/nfs/nfs_vnops.c =================================================================== --- head/sys/nfs/nfs_vnops.c (revision 29290) +++ head/sys/nfs/nfs_vnops.c (revision 29291) @@ -1,3459 +1,3450 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)nfs_vnops.c 8.16 (Berkeley) 5/27/95 - * $Id: nfs_vnops.c,v 1.56 1997/08/26 07:32:43 phk Exp $ + * $Id: nfs_vnops.c,v 1.57 1997/09/10 19:52:27 phk Exp $ */ /* * vnode op calls for Sun NFS version 2 and 3 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Defs */ #define TRUE 1 #define FALSE 0 /* * Ifdef for FreeBSD-current merged buffer cache. It is unfortunate that these * calls are not in getblk() and brelse() so that they would not be necessary * here. */ #ifndef B_VMIO #define vfs_busy_pages(bp, f) #endif static int nfsspec_read __P((struct vop_read_args *)); static int nfsspec_write __P((struct vop_write_args *)); static int nfsfifo_read __P((struct vop_read_args *)); static int nfsfifo_write __P((struct vop_write_args *)); static int nfsspec_close __P((struct vop_close_args *)); static int nfsfifo_close __P((struct vop_close_args *)); static int nfs_ioctl __P((struct vop_ioctl_args *)); static int nfs_select __P((struct vop_select_args *)); static int nfs_flush __P((struct vnode *,struct ucred *,int,struct proc *,int)); static int nfs_setattrrpc __P((struct vnode *,struct vattr *,struct ucred *,struct proc *)); static int nfs_lookup __P((struct vop_cachedlookup_args *)); static int nfs_create __P((struct vop_create_args *)); static int nfs_mknod __P((struct vop_mknod_args *)); static int nfs_open __P((struct vop_open_args *)); static int nfs_close __P((struct vop_close_args *)); static int nfs_access __P((struct vop_access_args *)); static int nfs_getattr __P((struct vop_getattr_args *)); static int nfs_setattr __P((struct vop_setattr_args *)); static int nfs_read __P((struct vop_read_args *)); static int nfs_mmap __P((struct vop_mmap_args *)); static int nfs_fsync __P((struct vop_fsync_args *)); static int nfs_remove __P((struct vop_remove_args *)); static int nfs_link __P((struct vop_link_args *)); static int nfs_rename __P((struct vop_rename_args *)); static int nfs_mkdir __P((struct vop_mkdir_args *)); static int nfs_rmdir __P((struct vop_rmdir_args *)); static int nfs_symlink __P((struct vop_symlink_args *)); static int nfs_readdir __P((struct vop_readdir_args *)); static int nfs_bmap __P((struct vop_bmap_args *)); static int nfs_strategy __P((struct vop_strategy_args *)); static int nfs_lookitup __P((struct vnode *,char *,int,struct ucred *,struct proc *,struct nfsnode **)); static int nfs_sillyrename __P((struct vnode *,struct vnode *,struct componentname *)); static int nfsspec_access __P((struct vop_access_args *)); static int nfs_readlink __P((struct vop_readlink_args *)); static int nfs_print __P((struct vop_print_args *)); static int nfs_pathconf __P((struct vop_pathconf_args *)); static int nfs_advlock __P((struct vop_advlock_args *)); static int nfs_blkatoff __P((struct vop_blkatoff_args *)); static int nfs_bwrite __P((struct vop_bwrite_args *)); static int nfs_valloc __P((struct vop_valloc_args *)); static int nfs_vfree __P((struct vop_vfree_args *)); static int nfs_truncate __P((struct vop_truncate_args *)); static int nfs_update __P((struct vop_update_args *)); /* * Global vfs data structures for nfs */ vop_t **nfsv2_vnodeop_p; static struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, /* lookup */ { &vop_cachedlookup_desc, (vop_t *)nfs_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)nfs_create }, /* create */ { &vop_mknod_desc, (vop_t *)nfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)nfs_open }, /* open */ { &vop_close_desc, (vop_t *)nfs_close }, /* close */ { &vop_access_desc, (vop_t *)nfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)nfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)nfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)nfs_read }, /* read */ { &vop_write_desc, (vop_t *)nfs_write }, /* write */ { &vop_lease_desc, (vop_t *)nfs_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)nfs_ioctl }, /* ioctl */ { &vop_select_desc, (vop_t *)nfs_select }, /* select */ { &vop_revoke_desc, (vop_t *)nfs_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)nfs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)nfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)nfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)nfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)nfs_link }, /* link */ { &vop_rename_desc, (vop_t *)nfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)nfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)nfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)nfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)nfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)nfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)nfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)nfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)nfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)nfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)nfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)nfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)nfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)nfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)nfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)nfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)nfs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)nfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)nfs_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)nfs_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)nfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)nfs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)nfs_update }, /* update */ { &vop_bwrite_desc, (vop_t *)nfs_bwrite }, /* bwrite */ { &vop_getpages_desc, (vop_t *)nfs_getpages }, /* getpages */ { NULL, NULL } }; static struct vnodeopv_desc nfsv2_vnodeop_opv_desc = { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries }; VNODEOP_SET(nfsv2_vnodeop_opv_desc); /* * Special device vnode ops */ vop_t **spec_nfsv2nodeop_p; static struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)nfsspec_close }, /* close */ { &vop_access_desc, (vop_t *)nfsspec_access }, /* access */ { &vop_getattr_desc, (vop_t *)nfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)nfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)nfsspec_read }, /* read */ { &vop_write_desc, (vop_t *)nfsspec_write }, /* write */ { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ { &vop_select_desc, (vop_t *)spec_select }, /* select */ { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)nfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)nfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)nfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)nfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)nfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)nfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)nfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)spec_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)spec_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)nfs_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc spec_nfsv2nodeop_opv_desc = { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries }; VNODEOP_SET(spec_nfsv2nodeop_opv_desc); vop_t **fifo_nfsv2nodeop_p; static struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)nfsfifo_close }, /* close */ { &vop_access_desc, (vop_t *)nfsspec_access }, /* access */ { &vop_getattr_desc, (vop_t *)nfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)nfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)nfsfifo_read }, /* read */ { &vop_write_desc, (vop_t *)nfsfifo_write }, /* write */ { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ { &vop_select_desc, (vop_t *)fifo_select }, /* select */ { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)nfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link }, /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)nfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)nfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)nfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)nfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_badop }, /* strategy */ { &vop_print_desc, (vop_t *)nfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)nfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)fifo_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)fifo_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)nfs_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc = { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries }; VNODEOP_SET(fifo_nfsv2nodeop_opv_desc); static int nfs_commit __P((struct vnode *vp, u_quad_t offset, int cnt, struct ucred *cred, struct proc *procp)); static int nfs_mknodrpc __P((struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, struct vattr *vap)); static int nfs_removerpc __P((struct vnode *dvp, char *name, int namelen, struct ucred *cred, struct proc *proc)); static int nfs_renamerpc __P((struct vnode *fdvp, char *fnameptr, int fnamelen, struct vnode *tdvp, char *tnameptr, int tnamelen, struct ucred *cred, struct proc *proc)); static int nfs_renameit __P((struct vnode *sdvp, struct componentname *scnp, struct sillyrename *sp)); /* * Global variables */ extern u_long nfs_true, nfs_false; extern struct nfsstats nfsstats; extern nfstype nfsv3_type[9]; struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; struct nfsmount *nfs_iodmount[NFS_MAXASYNCDAEMON]; int nfs_numasync = 0; #define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1)) /* * nfs access vnode op. * For nfs version 2, just return ok. File accesses may fail later. * For nfs version 3, use the access rpc to check accessibility. If file modes * are changed on the server, accesses might still fail later. */ static int nfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register u_long *tl; register caddr_t cp; register int t1, t2; caddr_t bpos, dpos, cp2; int error = 0, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; u_long mode, rmode; int v3 = NFS_ISV3(vp); /* * Disallow write attempts on filesystems mounted read-only; * unless the file is a socket, fifo, or a block or character * device resident on the filesystem. */ if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); } } /* * For nfs v3, do an access rpc, otherwise you are stuck emulating * ufs_access() locally using the vattr. This may not be correct, * since the server may apply other access criteria such as * client uid-->server uid mapping that we do not know about, but * this is better than just returning anything that is lying about * in the cache. */ if (v3) { nfsstats.rpccnt[NFSPROC_ACCESS]++; nfsm_reqhead(vp, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED); nfsm_fhtom(vp, v3); nfsm_build(tl, u_long *, NFSX_UNSIGNED); if (ap->a_mode & VREAD) mode = NFSV3ACCESS_READ; else mode = 0; if (vp->v_type == VDIR) { if (ap->a_mode & VWRITE) mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND | NFSV3ACCESS_DELETE); if (ap->a_mode & VEXEC) mode |= NFSV3ACCESS_LOOKUP; } else { if (ap->a_mode & VWRITE) mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND); if (ap->a_mode & VEXEC) mode |= NFSV3ACCESS_EXECUTE; } *tl = txdr_unsigned(mode); nfsm_request(vp, NFSPROC_ACCESS, ap->a_p, ap->a_cred); nfsm_postop_attr(vp, attrflag); if (!error) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); rmode = fxdr_unsigned(u_long, *tl); /* * The NFS V3 spec does not clarify whether or not * the returned access bits can be a superset of * the ones requested, so... */ if ((rmode & mode) != mode) error = EACCES; } nfsm_reqdone; return (error); } else { if (error = nfsspec_access(ap)) return (error); /* * Attempt to prevent a mapped root from accessing a file * which it shouldn't. We try to read a byte from the file * if the user is root and the file is not zero length. * After calling nfsspec_access, we should have the correct * file size cached. */ if (ap->a_cred->cr_uid == 0 && (ap->a_mode & VREAD) && VTONFS(vp)->n_size > 0) { struct iovec aiov; struct uio auio; char buf[1]; aiov.iov_base = buf; aiov.iov_len = 1; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_resid = 1; auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_procp = ap->a_p; if (vp->v_type == VREG) error = nfs_readrpc(vp, &auio, ap->a_cred); else if (vp->v_type == VDIR) { char* buf; buf = malloc(NFS_DIRBLKSIZ, M_TEMP, M_WAITOK); aiov.iov_base = buf; aiov.iov_len = auio.uio_resid = NFS_DIRBLKSIZ; error = nfs_readdirrpc(vp, &auio, ap->a_cred); free(buf, M_TEMP); } else if (vp->v_type = VLNK) error = nfs_readlinkrpc(vp, &auio, ap->a_cred); else error = EACCES; } return (error); } } /* * nfs open vnode op * Check to see if the type is ok * and that deletion is not in progress. * For paged in text files, you will need to flush the page cache * if consistency is lost. */ /* ARGSUSED */ static int nfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; struct nfsnode *np = VTONFS(vp); struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct vattr vattr; int error; if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) { printf("open eacces vtyp=%d\n",vp->v_type); return (EACCES); } /* * Get a valid lease. If cached data is stale, flush it. */ if (nmp->nm_flag & NFSMNT_NQNFS) { if (NQNFS_CKINVALID(vp, np, ND_READ)) { do { error = nqnfs_getlease(vp, ND_READ, ap->a_cred, ap->a_p); } while (error == NQNFS_EXPIRED); if (error) return (error); if (np->n_lrev != np->n_brev || (np->n_flag & NQNFSNONCACHE)) { if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); (void) vnode_pager_uncache(vp, ap->a_p); np->n_brev = np->n_lrev; } } } else { if (np->n_flag & NMODIFIED) { if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_attrstamp = 0; if (vp->v_type == VDIR) np->n_direofoffset = 0; error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); if (error) return (error); np->n_mtime = vattr.va_mtime.tv_sec; } else { error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); if (error) return (error); if (np->n_mtime != vattr.va_mtime.tv_sec) { if (vp->v_type == VDIR) np->n_direofoffset = 0; if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_mtime = vattr.va_mtime.tv_sec; } } } if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) np->n_attrstamp = 0; /* For Open/Close consistency */ return (0); } /* * nfs close vnode op * What an NFS client should do upon close after writing is a debatable issue. * Most NFS clients push delayed writes to the server upon close, basically for * two reasons: * 1 - So that any write errors may be reported back to the client process * doing the close system call. By far the two most likely errors are * NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure. * 2 - To put a worst case upper bound on cache inconsistency between * multiple clients for the file. * There is also a consistency problem for Version 2 of the protocol w.r.t. * not being able to tell if other clients are writing a file concurrently, * since there is no way of knowing if the changed modify time in the reply * is only due to the write for this client. * (NFS Version 3 provides weak cache consistency data in the reply that * should be sufficient to detect and handle this case.) * * The current code does the following: * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers * for NFS Version 3 - flush dirty buffers to the server but don't invalidate * or commit them (this satisfies 1 and 2 except for the * case where the server crashes after this close but * before the commit RPC, which is felt to be "good * enough". Changing the last argument to nfs_flush() to * a 1 would force a commit operation, if it is felt a * commit is necessary now. * for NQNFS - do nothing now, since 2 is dealt with via leases and * 1 should be dealt with via an fsync() system call for * cases where write errors are important. */ /* ARGSUSED */ static int nfs_close(ap) struct vop_close_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); int error = 0; if (vp->v_type == VREG) { if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 && (np->n_flag & NMODIFIED)) { if (NFS_ISV3(vp)) { error = nfs_flush(vp, ap->a_cred, MNT_WAIT, ap->a_p, 0); np->n_flag &= ~NMODIFIED; } else error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); np->n_attrstamp = 0; } if (np->n_flag & NWRITEERR) { np->n_flag &= ~NWRITEERR; error = np->n_error; } } return (error); } /* * nfs getattr call from vfs. */ static int nfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register caddr_t cp; register u_long *tl; register int t1, t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(vp); /* * Update local times for special files. */ if (np->n_flag & (NACC | NUPD)) np->n_flag |= NCHG; /* * First look in the cache. */ if (nfs_getattrcache(vp, ap->a_vap) == 0) return (0); nfsstats.rpccnt[NFSPROC_GETATTR]++; nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH(v3)); nfsm_fhtom(vp, v3); nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred); if (!error) nfsm_loadattr(vp, ap->a_vap); nfsm_reqdone; return (error); } /* * nfs setattr call. */ static int nfs_setattr(ap) struct vop_setattr_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct vattr *vap = ap->a_vap; int error = 0; u_quad_t tsize; #ifndef nolint tsize = (u_quad_t)0; #endif /* * Disallow write attempts if the filesystem is mounted read-only. */ if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) && (vp->v_mount->mnt_flag & MNT_RDONLY)) return (EROFS); if (vap->va_size != VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VCHR: case VBLK: case VSOCK: case VFIFO: if (vap->va_mtime.tv_sec == VNOVAL && vap->va_atime.tv_sec == VNOVAL && vap->va_mode == (u_short)VNOVAL && vap->va_uid == (uid_t)VNOVAL && vap->va_gid == (gid_t)VNOVAL) return (0); vap->va_size = VNOVAL; break; default: /* * Disallow write attempts if the filesystem is * mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (np->n_flag & NMODIFIED) { if (vap->va_size == 0) error = nfs_vinvalbuf(vp, 0, ap->a_cred, ap->a_p, 1); else error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); if (error) return (error); } tsize = np->n_size; np->n_size = np->n_vattr.va_size = vap->va_size; vnode_pager_setsize(vp, (u_long)np->n_size); }; } else if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) && (np->n_flag & NMODIFIED) && vp->v_type == VREG && (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); error = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_p); if (error && vap->va_size != VNOVAL) { np->n_size = np->n_vattr.va_size = tsize; vnode_pager_setsize(vp, (u_long)np->n_size); } return (error); } /* * Do an nfs setattr rpc. */ static int nfs_setattrrpc(vp, vap, cred, procp) register struct vnode *vp; register struct vattr *vap; struct ucred *cred; struct proc *procp; { register struct nfsv2_sattr *sp; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; u_long *tl; int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(vp); nfsstats.rpccnt[NFSPROC_SETATTR]++; nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH(v3) + NFSX_SATTR(v3)); nfsm_fhtom(vp, v3); if (v3) { if (vap->va_mode != (u_short)VNOVAL) { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_true; *tl = txdr_unsigned(vap->va_mode); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } if (vap->va_uid != (uid_t)VNOVAL) { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_true; *tl = txdr_unsigned(vap->va_uid); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } if (vap->va_gid != (gid_t)VNOVAL) { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_true; *tl = txdr_unsigned(vap->va_gid); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } if (vap->va_size != VNOVAL) { nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); *tl++ = nfs_true; txdr_hyper(&vap->va_size, tl); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } if (vap->va_atime.tv_sec != VNOVAL) { if (vap->va_atime.tv_sec != time.tv_sec) { nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); txdr_nfsv3time(&vap->va_atime, tl); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER); } } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE); } if (vap->va_mtime.tv_sec != VNOVAL) { if (vap->va_mtime.tv_sec != time.tv_sec) { nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); txdr_nfsv3time(&vap->va_mtime, tl); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER); } } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE); } nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } else { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); if (vap->va_mode == (u_short)VNOVAL) sp->sa_mode = VNOVAL; else sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode); if (vap->va_uid == (uid_t)VNOVAL) sp->sa_uid = VNOVAL; else sp->sa_uid = txdr_unsigned(vap->va_uid); if (vap->va_gid == (gid_t)VNOVAL) sp->sa_gid = VNOVAL; else sp->sa_gid = txdr_unsigned(vap->va_gid); sp->sa_size = txdr_unsigned(vap->va_size); txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(vp, NFSPROC_SETATTR, procp, cred); if (v3) { nfsm_wcc_data(vp, wccflag); } else nfsm_loadattr(vp, (struct vattr *)0); nfsm_reqdone; return (error); } /* * nfs lookup call, one step at a time... * First look in cache * If not found, unlock the directory nfsnode and do the rpc */ static int nfs_lookup(ap) struct vop_cachedlookup_args /* { struct vnodeop_desc *a_desc; struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { register struct componentname *cnp = ap->a_cnp; register struct vnode *dvp = ap->a_dvp; register struct vnode **vpp = ap->a_vpp; register int flags = cnp->cn_flags; register struct vnode *newvp; register u_long *tl; register caddr_t cp; register long t1, t2; struct nfsmount *nmp; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; long len; nfsfh_t *fhp; struct nfsnode *np; int lockparent, wantparent, error = 0, attrflag, fhsize; int v3 = NFS_ISV3(dvp); struct proc *p = cnp->cn_proc; if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); *vpp = NULLVP; if (dvp->v_type != VDIR) return (ENOTDIR); lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT|WANTPARENT); nmp = VFSTONFS(dvp->v_mount); np = VTONFS(dvp); error = 0; newvp = NULLVP; nfsstats.lookupcache_misses++; nfsstats.rpccnt[NFSPROC_LOOKUP]++; len = cnp->cn_namelen; nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred); if (error) { nfsm_postop_attr(dvp, attrflag); m_freem(mrep); goto nfsmout; } nfsm_getfh(fhp, fhsize, v3); /* * Handle RENAME case... */ if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) { if (NFS_CMPFH(np, fhp, fhsize)) { m_freem(mrep); return (EISDIR); } if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) { m_freem(mrep); return (error); } newvp = NFSTOV(np); if (v3) { nfsm_postop_attr(newvp, attrflag); nfsm_postop_attr(dvp, attrflag); } else nfsm_loadattr(newvp, (struct vattr *)0); *vpp = newvp; m_freem(mrep); cnp->cn_flags |= SAVENAME; if (!lockparent) VOP_UNLOCK(dvp, 0, p); return (0); } if (flags & ISDOTDOT) { VOP_UNLOCK(dvp, 0, p); error = nfs_nget(dvp->v_mount, fhp, fhsize, &np); if (error) { vn_lock(dvp, LK_EXCLUSIVE + LK_RETRY, p); return (error); } newvp = NFSTOV(np); if (lockparent && (flags & ISLASTCN) && (error = vn_lock(dvp, LK_EXCLUSIVE, p))) { vput(newvp); return (error); } } else if (NFS_CMPFH(np, fhp, fhsize)) { VREF(dvp); newvp = dvp; } else { if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) { m_freem(mrep); return (error); } if (!lockparent || !(flags & ISLASTCN)) VOP_UNLOCK(dvp, 0, p); newvp = NFSTOV(np); } if (v3) { nfsm_postop_attr(newvp, attrflag); nfsm_postop_attr(dvp, attrflag); } else nfsm_loadattr(newvp, (struct vattr *)0); if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) { np->n_ctime = np->n_vattr.va_ctime.tv_sec; cache_enter(dvp, newvp, cnp); } *vpp = newvp; nfsm_reqdone; if (error) { if (newvp != NULLVP) { vrele(newvp); *vpp = NULLVP; } if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && (flags & ISLASTCN) && error == ENOENT) { if (!lockparent) VOP_UNLOCK(dvp, 0, p); if (dvp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else error = EJUSTRETURN; } if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; } return (error); } /* * nfs read call. * Just call nfs_bioread() to do the work. */ static int nfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; if (vp->v_type != VREG) return (EPERM); return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred, 0)); } /* * nfs readlink call */ static int nfs_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; if (vp->v_type != VLNK) return (EPERM); return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred, 0)); } /* * Do a readlink rpc. * Called by nfs_doio() from below the buffer cache. */ int nfs_readlinkrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, len, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(vp); nfsstats.rpccnt[NFSPROC_READLINK]++; nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH(v3)); nfsm_fhtom(vp, v3); nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred); if (v3) nfsm_postop_attr(vp, attrflag); if (!error) { nfsm_strsiz(len, NFS_MAXPATHLEN); nfsm_mtouio(uiop, len); } nfsm_reqdone; return (error); } /* * nfs read rpc call * Ditto above */ int nfs_readrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; int error = 0, len, retlen, tsiz, eof, attrflag; int v3 = NFS_ISV3(vp); #ifndef nolint eof = 0; #endif nmp = VFSTONFS(vp->v_mount); tsiz = uiop->uio_resid; if (uiop->uio_offset + tsiz > 0xffffffff && !v3) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_READ]++; len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz; nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH(v3) + NFSX_UNSIGNED * 3); nfsm_fhtom(vp, v3); nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3); if (v3) { txdr_hyper(&uiop->uio_offset, tl); *(tl + 2) = txdr_unsigned(len); } else { *tl++ = txdr_unsigned(uiop->uio_offset); *tl++ = txdr_unsigned(len); *tl = 0; } nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred); if (v3) { nfsm_postop_attr(vp, attrflag); if (error) { m_freem(mrep); goto nfsmout; } nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); eof = fxdr_unsigned(int, *(tl + 1)); } else nfsm_loadattr(vp, (struct vattr *)0); nfsm_strsiz(retlen, nmp->nm_rsize); nfsm_mtouio(uiop, retlen); m_freem(mrep); tsiz -= retlen; if (v3) { if (eof || retlen == 0) tsiz = 0; } else if (retlen < len) tsiz = 0; } nfsmout: return (error); } /* * nfs write call */ int nfs_writerpc(vp, uiop, cred, iomode, must_commit) register struct vnode *vp; register struct uio *uiop; struct ucred *cred; int *iomode, *must_commit; { register u_long *tl; register caddr_t cp; register int t1, t2, backup; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp = VFSTONFS(vp->v_mount); int error = 0, len, tsiz, wccflag = NFSV3_WCCRATTR, rlen, commit; int v3 = NFS_ISV3(vp), committed = NFSV3WRITE_FILESYNC; #ifndef DIAGNOSTIC if (uiop->uio_iovcnt != 1) panic("nfs: writerpc iovcnt > 1"); #endif *must_commit = 0; tsiz = uiop->uio_resid; if (uiop->uio_offset + tsiz > 0xffffffff && !v3) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_WRITE]++; len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz; nfsm_reqhead(vp, NFSPROC_WRITE, NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len)); nfsm_fhtom(vp, v3); if (v3) { nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED); txdr_hyper(&uiop->uio_offset, tl); tl += 2; *tl++ = txdr_unsigned(len); *tl++ = txdr_unsigned(*iomode); } else { nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); *++tl = txdr_unsigned(uiop->uio_offset); tl += 2; } *tl = txdr_unsigned(len); nfsm_uiotom(uiop, len); nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred); if (v3) { wccflag = NFSV3_WCCCHK; nfsm_wcc_data(vp, wccflag); if (!error) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED + NFSX_V3WRITEVERF); rlen = fxdr_unsigned(int, *tl++); if (rlen == 0) { error = NFSERR_IO; break; } else if (rlen < len) { backup = len - rlen; uiop->uio_iov->iov_base -= backup; uiop->uio_iov->iov_len += backup; uiop->uio_offset -= backup; uiop->uio_resid += backup; len = rlen; } commit = fxdr_unsigned(int, *tl++); /* * Return the lowest committment level * obtained by any of the RPCs. */ if (committed == NFSV3WRITE_FILESYNC) committed = commit; else if (committed == NFSV3WRITE_DATASYNC && commit == NFSV3WRITE_UNSTABLE) committed = commit; if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0) { bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF); nmp->nm_flag |= NFSMNT_HASWRITEVERF; } else if (bcmp((caddr_t)tl, (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) { *must_commit = 1; bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF); } } } else nfsm_loadattr(vp, (struct vattr *)0); if (wccflag) VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.tv_sec; m_freem(mrep); tsiz -= len; } nfsmout: if (vp->v_mount->mnt_flag & MNT_ASYNC) committed = NFSV3WRITE_FILESYNC; *iomode = committed; if (error) uiop->uio_resid = tsiz; return (error); } /* * nfs mknod rpc * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the * mode set to specify the file type and the size field for rdev. */ static int nfs_mknodrpc(dvp, vpp, cnp, vap) register struct vnode *dvp; register struct vnode **vpp; register struct componentname *cnp; register struct vattr *vap; { register struct nfsv2_sattr *sp; register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; struct vnode *newvp = (struct vnode *)0; struct nfsnode *np = (struct nfsnode *)0; struct vattr vattr; char *cp2; caddr_t bpos, dpos; int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; u_long rdev; int v3 = NFS_ISV3(dvp); if (vap->va_type == VCHR || vap->va_type == VBLK) rdev = txdr_unsigned(vap->va_rdev); else if (vap->va_type == VFIFO || vap->va_type == VSOCK) rdev = 0xffffffff; else { VOP_ABORTOP(dvp, cnp); vput(dvp); return (EOPNOTSUPP); } if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } nfsstats.rpccnt[NFSPROC_MKNOD]++; nfsm_reqhead(dvp, NFSPROC_MKNOD, NFSX_FH(v3) + 4 * NFSX_UNSIGNED + + nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); if (v3) { nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3SRVSATTR); *tl++ = vtonfsv3_type(vap->va_type); sp3 = (struct nfsv3_sattr *)tl; nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); if (vap->va_type == VCHR || vap->va_type == VBLK) { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(major(vap->va_rdev)); *tl = txdr_unsigned(minor(vap->va_rdev)); } } else { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); sp->sa_size = rdev; txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_MKNOD, cnp->cn_proc, cnp->cn_cred); if (!error) { nfsm_mtofh(dvp, newvp, v3, gotvp); if (!gotvp) { if (newvp) { vput(newvp); newvp = (struct vnode *)0; } error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np); if (!error) newvp = NFSTOV(np); } } if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; if (error) { if (newvp) vput(newvp); } else { if (cnp->cn_flags & MAKEENTRY) cache_enter(dvp, newvp, cnp); *vpp = newvp; } FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; vput(dvp); return (error); } /* * nfs mknod vop * just call nfs_mknodrpc() to do the work. */ /* ARGSUSED */ static int nfs_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct vnode *newvp; int error; error = nfs_mknodrpc(ap->a_dvp, &newvp, ap->a_cnp, ap->a_vap); if (!error) vput(newvp); return (error); } static u_long create_verf; /* * nfs file create call */ static int nfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; struct nfsnode *np = (struct nfsnode *)0; struct vnode *newvp = (struct vnode *)0; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0, fmode = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; int v3 = NFS_ISV3(dvp); /* * Oops, not for me.. */ if (vap->va_type == VSOCK) return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap)); if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } if (vap->va_vaflags & VA_EXCLUSIVE) fmode |= O_EXCL; again: nfsstats.rpccnt[NFSPROC_CREATE]++; nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH(v3) + 2 * NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); if (v3) { nfsm_build(tl, u_long *, NFSX_UNSIGNED); if (fmode & O_EXCL) { *tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE); nfsm_build(tl, u_long *, NFSX_V3CREATEVERF); if (!TAILQ_EMPTY(&in_ifaddrhead)) *tl++ = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr.s_addr; else *tl++ = create_verf; *tl = ++create_verf; } else { *tl = txdr_unsigned(NFSV3CREATE_UNCHECKED); nfsm_build(tl, u_long *, NFSX_V3SRVSATTR); sp3 = (struct nfsv3_sattr *)tl; nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); } } else { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); sp->sa_size = 0; txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred); if (!error) { nfsm_mtofh(dvp, newvp, v3, gotvp); if (!gotvp) { if (newvp) { vput(newvp); newvp = (struct vnode *)0; } error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np); if (!error) newvp = NFSTOV(np); } } if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; if (error) { if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) { fmode &= ~O_EXCL; goto again; } if (newvp) vput(newvp); } else if (v3 && (fmode & O_EXCL)) error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_proc); if (!error) { if (cnp->cn_flags & MAKEENTRY) cache_enter(dvp, newvp, cnp); *ap->a_vpp = newvp; } FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; vput(dvp); return (error); } /* * nfs file remove call * To try and make nfs semantics closer to ufs semantics, a file that has * other processes using the vnode is renamed instead of removed and then * removed later on the last close. * - If v_usecount > 1 * If a rename is not already in the works * call nfs_sillyrename() to set it up * else * do the remove rpc */ static int nfs_remove(ap) struct vop_remove_args /* { struct vnodeop_desc *a_desc; struct vnode * a_dvp; struct vnode * a_vp; struct componentname * a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register struct nfsnode *np = VTONFS(vp); int error = 0; struct vattr vattr; #ifndef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("nfs_remove: no name"); if (vp->v_usecount < 1) panic("nfs_remove: bad v_usecount"); #endif if (vp->v_usecount == 1 || (np->n_sillyrename && VOP_GETATTR(vp, &vattr, cnp->cn_cred, cnp->cn_proc) == 0 && vattr.va_nlink > 1)) { /* * Purge the name cache so that the chance of a lookup for * the name succeeding while the remove is in progress is * minimized. Without node locking it can still happen, such * that an I/O op returns ESTALE, but since you get this if * another host removes the file.. */ cache_purge(vp); /* * throw away biocache buffers, mainly to avoid * unnecessary delayed writes later. */ error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1); /* Do the rpc */ if (error != EINTR) error = nfs_removerpc(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc); /* * Kludge City: If the first reply to the remove rpc is lost.. * the reply to the retransmitted request will be ENOENT * since the file was in fact removed * Therefore, we cheat and return success. */ if (error == ENOENT) error = 0; } else if (!np->n_sillyrename) error = nfs_sillyrename(dvp, vp, cnp); FREE(cnp->cn_pnbuf, M_NAMEI); np->n_attrstamp = 0; vput(dvp); if (vp == dvp) vrele(vp); else vput(vp); return (error); } /* * nfs file remove rpc called from nfs_inactive */ int nfs_removeit(sp) register struct sillyrename *sp; { return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred, (struct proc *)0)); } /* * Nfs remove rpc, called from nfs_remove() and nfs_removeit(). */ static int nfs_removerpc(dvp, name, namelen, cred, proc) register struct vnode *dvp; char *name; int namelen; struct ucred *cred; struct proc *proc; { register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_REMOVE]++; nfsm_reqhead(dvp, NFSPROC_REMOVE, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen)); nfsm_fhtom(dvp, v3); nfsm_strtom(name, namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_REMOVE, proc, cred); if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; return (error); } /* * nfs file rename call */ static int nfs_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { register struct vnode *fvp = ap->a_fvp; register struct vnode *tvp = ap->a_tvp; register struct vnode *fdvp = ap->a_fdvp; register struct vnode *tdvp = ap->a_tdvp; register struct componentname *tcnp = ap->a_tcnp; register struct componentname *fcnp = ap->a_fcnp; int error; #ifndef DIAGNOSTIC if ((tcnp->cn_flags & HASBUF) == 0 || (fcnp->cn_flags & HASBUF) == 0) panic("nfs_rename: no name"); #endif /* Check for cross-device rename */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto out; } /* * If the tvp exists and is in use, sillyrename it before doing the * rename of the new file over it. * XXX Can't sillyrename a directory. */ if (tvp && tvp->v_usecount > 1 && !VTONFS(tvp)->n_sillyrename && tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp)) { vput(tvp); tvp = NULL; } error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen, tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred, tcnp->cn_proc); if (fvp->v_type == VDIR) { if (tvp != NULL && tvp->v_type == VDIR) cache_purge(tdvp); cache_purge(fdvp); } out: if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); /* * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nfs file rename rpc called from nfs_remove() above */ static int nfs_renameit(sdvp, scnp, sp) struct vnode *sdvp; struct componentname *scnp; register struct sillyrename *sp; { return (nfs_renamerpc(sdvp, scnp->cn_nameptr, scnp->cn_namelen, sdvp, sp->s_name, sp->s_namlen, scnp->cn_cred, scnp->cn_proc)); } /* * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit(). */ static int nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc) register struct vnode *fdvp; char *fnameptr; int fnamelen; register struct vnode *tdvp; char *tnameptr; int tnamelen; struct ucred *cred; struct proc *proc; { register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, fwccflag = NFSV3_WCCRATTR, twccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(fdvp); nfsstats.rpccnt[NFSPROC_RENAME]++; nfsm_reqhead(fdvp, NFSPROC_RENAME, (NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) + nfsm_rndup(tnamelen)); nfsm_fhtom(fdvp, v3); nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN); nfsm_fhtom(tdvp, v3); nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN); nfsm_request(fdvp, NFSPROC_RENAME, proc, cred); if (v3) { nfsm_wcc_data(fdvp, fwccflag); nfsm_wcc_data(tdvp, twccflag); } nfsm_reqdone; VTONFS(fdvp)->n_flag |= NMODIFIED; VTONFS(tdvp)->n_flag |= NMODIFIED; if (!fwccflag) VTONFS(fdvp)->n_attrstamp = 0; if (!twccflag) VTONFS(tdvp)->n_attrstamp = 0; return (error); } /* * nfs hard link create call */ static int nfs_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { -#if defined(__NetBSD__) - /* - * Since the args are reversed in the VOP_LINK() calls, - * switch them back. Argh! - */ - register struct vnode *vp = ap->a_tdvp; - register struct vnode *tdvp = ap->a_vp; -#else register struct vnode *vp = ap->a_vp; register struct vnode *tdvp = ap->a_tdvp; -#endif register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR, attrflag = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(vp); if (vp->v_mount != tdvp->v_mount) { VOP_ABORTOP(vp, cnp); if (tdvp == vp) vrele(tdvp); else vput(tdvp); return (EXDEV); } /* * Push all writes to the server, so that the attribute cache * doesn't get "out of sync" with the server. * XXX There should be a better way! */ VOP_FSYNC(vp, cnp->cn_cred, MNT_WAIT, cnp->cn_proc); nfsstats.rpccnt[NFSPROC_LINK]++; nfsm_reqhead(vp, NFSPROC_LINK, NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(vp, v3); nfsm_fhtom(tdvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(vp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred); if (v3) { nfsm_postop_attr(vp, attrflag); nfsm_wcc_data(tdvp, wccflag); } nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(tdvp)->n_flag |= NMODIFIED; if (!attrflag) VTONFS(vp)->n_attrstamp = 0; if (!wccflag) VTONFS(tdvp)->n_attrstamp = 0; vput(tdvp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ if (error == EEXIST) error = 0; return (error); } /* * nfs symbolic link create call */ static int nfs_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int slen, error = 0, wccflag = NFSV3_WCCRATTR, gotvp; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vnode *newvp = (struct vnode *)0; int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_SYMLINK]++; slen = strlen(ap->a_target); nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH(v3) + 2*NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); if (v3) { nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR); nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, cnp->cn_cred->cr_gid); } nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN); if (!v3) { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); sp->sa_mode = vtonfsv2_mode(VLNK, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid); sp->sa_size = -1; txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred); if (v3) { if (!error) nfsm_mtofh(dvp, newvp, v3, gotvp); nfsm_wcc_data(dvp, wccflag); } nfsm_reqdone; if (newvp) vput(newvp); FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; vput(dvp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ if (error == EEXIST) error = 0; return (error); } /* * nfs make dir call */ static int nfs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; register int len; struct nfsnode *np = (struct nfsnode *)0; struct vnode *newvp = (struct vnode *)0; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR; int gotvp = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; int v3 = NFS_ISV3(dvp); if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } len = cnp->cn_namelen; nfsstats.rpccnt[NFSPROC_MKDIR]++; nfsm_reqhead(dvp, NFSPROC_MKDIR, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); if (v3) { nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR); nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); } else { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); sp->sa_mode = vtonfsv2_mode(VDIR, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); sp->sa_size = -1; txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred); if (!error) nfsm_mtofh(dvp, newvp, v3, gotvp); if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; /* * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry * if we can succeed in looking up the directory. */ if (error == EEXIST || (!error && !gotvp)) { if (newvp) { vrele(newvp); newvp = (struct vnode *)0; } error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred, cnp->cn_proc, &np); if (!error) { newvp = NFSTOV(np); if (newvp->v_type != VDIR) error = EEXIST; } } if (error) { if (newvp) vrele(newvp); } else *ap->a_vpp = newvp; FREE(cnp->cn_pnbuf, M_NAMEI); vput(dvp); return (error); } /* * nfs remove directory call */ static int nfs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_RMDIR]++; nfsm_reqhead(dvp, NFSPROC_RMDIR, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred); if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; cache_purge(dvp); cache_purge(vp); vput(vp); vput(dvp); /* * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nfs readdir call */ static int nfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct uio *uio = ap->a_uio; int tresid, error; struct vattr vattr; if (vp->v_type != VDIR) return (EPERM); /* * First, check for hit on the EOF offset cache */ if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset && (np->n_flag & NMODIFIED) == 0) { if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) { if (NQNFS_CKCACHABLE(vp, ND_READ)) { nfsstats.direofcache_hits++; return (0); } } else if (VOP_GETATTR(vp, &vattr, ap->a_cred, uio->uio_procp) == 0 && np->n_mtime == vattr.va_mtime.tv_sec) { nfsstats.direofcache_hits++; return (0); } } /* * Call nfs_bioread() to do the real work. */ tresid = uio->uio_resid; error = nfs_bioread(vp, uio, 0, ap->a_cred, 0); if (!error && uio->uio_resid == tresid) nfsstats.direofcache_misses++; return (error); } /* * Readdir rpc call. * Called from below the buffer cache by nfs_doio(). */ int nfs_readdirrpc(vp, uiop, cred) struct vnode *vp; register struct uio *uiop; struct ucred *cred; { register int len, left; register struct dirent *dp; register u_long *tl; register caddr_t cp; register long t1, t2; register nfsuint64 *cookiep; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsuint64 cookie; struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct nfsnode *dnp = VTONFS(vp); u_quad_t fileno; int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1; int attrflag; int v3 = NFS_ISV3(vp); #ifndef nolint dp = (struct dirent *)0; #endif #ifndef DIAGNOSTIC if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) || (uiop->uio_resid & (NFS_DIRBLKSIZ - 1))) panic("nfs readdirrpc bad uio"); #endif /* * If there is no cookie, assume directory was stale. */ cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0); if (cookiep) cookie = *cookiep; else return (NFSERR_BAD_COOKIE); /* * Loop around doing readdir rpc's of size nm_readdirsize * truncated to a multiple of DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ while (more_dirs && bigenough) { nfsstats.rpccnt[NFSPROC_READDIR]++; nfsm_reqhead(vp, NFSPROC_READDIR, NFSX_FH(v3) + NFSX_READDIR(v3)); nfsm_fhtom(vp, v3); if (v3) { nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED); *tl++ = cookie.nfsuquad[0]; *tl++ = cookie.nfsuquad[1]; *tl++ = dnp->n_cookieverf.nfsuquad[0]; *tl++ = dnp->n_cookieverf.nfsuquad[1]; } else { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = cookie.nfsuquad[0]; } *tl = txdr_unsigned(nmp->nm_readdirsize); nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred); if (v3) { nfsm_postop_attr(vp, attrflag); if (!error) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); dnp->n_cookieverf.nfsuquad[0] = *tl++; dnp->n_cookieverf.nfsuquad[1] = *tl; } else { m_freem(mrep); goto nfsmout; } } nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); /* loop thru the dir entries, doctoring them to 4bsd form */ while (more_dirs && bigenough) { if (v3) { nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); fxdr_hyper(tl, &fileno); len = fxdr_unsigned(int, *(tl + 2)); } else { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fileno = fxdr_unsigned(u_quad_t, *tl++); len = fxdr_unsigned(int, *tl); } if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } tlen = nfsm_rndup(len); if (tlen == len) tlen += 4; /* To ensure null termination */ left = DIRBLKSIZ - blksiz; if ((tlen + DIRHDSIZ) > left) { dp->d_reclen += left; uiop->uio_iov->iov_base += left; uiop->uio_iov->iov_len -= left; uiop->uio_offset += left; uiop->uio_resid -= left; blksiz = 0; } if ((tlen + DIRHDSIZ) > uiop->uio_resid) bigenough = 0; if (bigenough) { dp = (struct dirent *)uiop->uio_iov->iov_base; dp->d_fileno = (int)fileno; dp->d_namlen = len; dp->d_reclen = tlen + DIRHDSIZ; dp->d_type = DT_UNKNOWN; blksiz += dp->d_reclen; if (blksiz == DIRBLKSIZ) blksiz = 0; uiop->uio_offset += DIRHDSIZ; uiop->uio_resid -= DIRHDSIZ; uiop->uio_iov->iov_base += DIRHDSIZ; uiop->uio_iov->iov_len -= DIRHDSIZ; nfsm_mtouio(uiop, len); cp = uiop->uio_iov->iov_base; tlen -= len; *cp = '\0'; /* null terminate */ uiop->uio_iov->iov_base += tlen; uiop->uio_iov->iov_len -= tlen; uiop->uio_offset += tlen; uiop->uio_resid -= tlen; } else nfsm_adv(nfsm_rndup(len)); if (v3) { nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); } else { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); } if (bigenough) { cookie.nfsuquad[0] = *tl++; if (v3) cookie.nfsuquad[1] = *tl++; } else if (v3) tl += 2; else tl++; more_dirs = fxdr_unsigned(int, *tl); } /* * If at end of rpc data, get the eof boolean */ if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); } m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of DIRBLKSIZ * by increasing d_reclen for the last record. */ if (blksiz > 0) { left = DIRBLKSIZ - blksiz; dp->d_reclen += left; uiop->uio_iov->iov_base += left; uiop->uio_iov->iov_len -= left; uiop->uio_offset += left; uiop->uio_resid -= left; } /* * We are now either at the end of the directory or have filled the * block. */ if (bigenough) dnp->n_direofoffset = uiop->uio_offset; else { if (uiop->uio_resid > 0) printf("EEK! readdirrpc resid > 0\n"); cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1); *cookiep = cookie; } nfsmout: return (error); } /* * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc(). */ int nfs_readdirplusrpc(vp, uiop, cred) struct vnode *vp; register struct uio *uiop; struct ucred *cred; { register int len, left; register struct dirent *dp; register u_long *tl; register caddr_t cp; register long t1, t2; register struct vnode *newvp; register nfsuint64 *cookiep; caddr_t bpos, dpos, cp2, dpossav1, dpossav2; struct mbuf *mreq, *mrep, *md, *mb, *mb2, *mdsav1, *mdsav2; struct nameidata nami, *ndp = &nami; struct componentname *cnp = &ndp->ni_cnd; nfsuint64 cookie; struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct nfsnode *dnp = VTONFS(vp), *np; nfsfh_t *fhp; u_quad_t fileno; int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i; int attrflag, fhsize; #ifndef nolint dp = (struct dirent *)0; #endif #ifndef DIAGNOSTIC if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) || (uiop->uio_resid & (DIRBLKSIZ - 1))) panic("nfs readdirplusrpc bad uio"); #endif ndp->ni_dvp = vp; newvp = NULLVP; /* * If there is no cookie, assume directory was stale. */ cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0); if (cookiep) cookie = *cookiep; else return (NFSERR_BAD_COOKIE); /* * Loop around doing readdir rpc's of size nm_readdirsize * truncated to a multiple of DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ while (more_dirs && bigenough) { nfsstats.rpccnt[NFSPROC_READDIRPLUS]++; nfsm_reqhead(vp, NFSPROC_READDIRPLUS, NFSX_FH(1) + 6 * NFSX_UNSIGNED); nfsm_fhtom(vp, 1); nfsm_build(tl, u_long *, 6 * NFSX_UNSIGNED); *tl++ = cookie.nfsuquad[0]; *tl++ = cookie.nfsuquad[1]; *tl++ = dnp->n_cookieverf.nfsuquad[0]; *tl++ = dnp->n_cookieverf.nfsuquad[1]; *tl++ = txdr_unsigned(nmp->nm_readdirsize); *tl = txdr_unsigned(nmp->nm_rsize); nfsm_request(vp, NFSPROC_READDIRPLUS, uiop->uio_procp, cred); nfsm_postop_attr(vp, attrflag); if (error) { m_freem(mrep); goto nfsmout; } nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); dnp->n_cookieverf.nfsuquad[0] = *tl++; dnp->n_cookieverf.nfsuquad[1] = *tl++; more_dirs = fxdr_unsigned(int, *tl); /* loop thru the dir entries, doctoring them to 4bsd form */ while (more_dirs && bigenough) { nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); fxdr_hyper(tl, &fileno); len = fxdr_unsigned(int, *(tl + 2)); if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } tlen = nfsm_rndup(len); if (tlen == len) tlen += 4; /* To ensure null termination*/ left = DIRBLKSIZ - blksiz; if ((tlen + DIRHDSIZ) > left) { dp->d_reclen += left; uiop->uio_iov->iov_base += left; uiop->uio_iov->iov_len -= left; uiop->uio_offset += left; uiop->uio_resid -= left; blksiz = 0; } if ((tlen + DIRHDSIZ) > uiop->uio_resid) bigenough = 0; if (bigenough) { dp = (struct dirent *)uiop->uio_iov->iov_base; dp->d_fileno = (int)fileno; dp->d_namlen = len; dp->d_reclen = tlen + DIRHDSIZ; dp->d_type = DT_UNKNOWN; blksiz += dp->d_reclen; if (blksiz == DIRBLKSIZ) blksiz = 0; uiop->uio_offset += DIRHDSIZ; uiop->uio_resid -= DIRHDSIZ; uiop->uio_iov->iov_base += DIRHDSIZ; uiop->uio_iov->iov_len -= DIRHDSIZ; cnp->cn_nameptr = uiop->uio_iov->iov_base; cnp->cn_namelen = len; nfsm_mtouio(uiop, len); cp = uiop->uio_iov->iov_base; tlen -= len; *cp = '\0'; uiop->uio_iov->iov_base += tlen; uiop->uio_iov->iov_len -= tlen; uiop->uio_offset += tlen; uiop->uio_resid -= tlen; } else nfsm_adv(nfsm_rndup(len)); nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); if (bigenough) { cookie.nfsuquad[0] = *tl++; cookie.nfsuquad[1] = *tl++; } else tl += 2; /* * Since the attributes are before the file handle * (sigh), we must skip over the attributes and then * come back and get them. */ attrflag = fxdr_unsigned(int, *tl); if (attrflag) { dpossav1 = dpos; mdsav1 = md; nfsm_adv(NFSX_V3FATTR); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); doit = fxdr_unsigned(int, *tl); if (doit) { nfsm_getfh(fhp, fhsize, 1); if (NFS_CMPFH(dnp, fhp, fhsize)) { VREF(vp); newvp = vp; np = dnp; } else { if (error = nfs_nget(vp->v_mount, fhp, fhsize, &np)) doit = 0; else newvp = NFSTOV(np); } } if (doit) { dpossav2 = dpos; dpos = dpossav1; mdsav2 = md; md = mdsav1; nfsm_loadattr(newvp, (struct vattr *)0); dpos = dpossav2; md = mdsav2; dp->d_type = IFTODT(VTTOIF(np->n_vattr.va_type)); ndp->ni_vp = newvp; cnp->cn_hash = 0; for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++) cnp->cn_hash += (unsigned char)*cp * i; cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp); } } else { /* Just skip over the file handle */ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); i = fxdr_unsigned(int, *tl); nfsm_adv(nfsm_rndup(i)); } if (newvp != NULLVP) { vrele(newvp); newvp = NULLVP; } nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); } /* * If at end of rpc data, get the eof boolean */ if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); } m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ * by increasing d_reclen for the last record. */ if (blksiz > 0) { left = DIRBLKSIZ - blksiz; dp->d_reclen += left; uiop->uio_iov->iov_base += left; uiop->uio_iov->iov_len -= left; uiop->uio_offset += left; uiop->uio_resid -= left; } /* * We are now either at the end of the directory or have filled the * block. */ if (bigenough) dnp->n_direofoffset = uiop->uio_offset; else { if (uiop->uio_resid > 0) printf("EEK! readdirplusrpc resid > 0\n"); cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1); *cookiep = cookie; } nfsmout: if (newvp != NULLVP) { if (newvp == vp) vrele(newvp); else vput(newvp); newvp = NULLVP; } return (error); } /* * Silly rename. To make the NFS filesystem that is stateless look a little * more like the "ufs" a remove of an active vnode is translated to a rename * to a funny looking filename that is removed by nfs_inactive on the * nfsnode. There is the potential for another process on a different client * to create the same funny name between the nfs_lookitup() fails and the * nfs_rename() completes, but... */ static int nfs_sillyrename(dvp, vp, cnp) struct vnode *dvp, *vp; struct componentname *cnp; { register struct sillyrename *sp; struct nfsnode *np; int error; short pid; cache_purge(dvp); np = VTONFS(vp); #ifndef DIAGNOSTIC if (vp->v_type == VDIR) panic("nfs: sillyrename dir"); #endif MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename), M_NFSREQ, M_WAITOK); sp->s_cred = crdup(cnp->cn_cred); sp->s_dvp = dvp; VREF(dvp); /* Fudge together a funny name */ pid = cnp->cn_proc->p_pid; sp->s_namlen = sprintf(sp->s_name, ".nfsA%04x4.4", pid); /* Try lookitups until we get one that isn't there */ while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, cnp->cn_proc, (struct nfsnode **)0) == 0) { sp->s_name[4]++; if (sp->s_name[4] > 'z') { error = EINVAL; goto bad; } } if (error = nfs_renameit(dvp, cnp, sp)) goto bad; error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, cnp->cn_proc, &np); np->n_sillyrename = sp; return (0); bad: vrele(sp->s_dvp); crfree(sp->s_cred); free((caddr_t)sp, M_NFSREQ); return (error); } /* * Look up a file name and optionally either update the file handle or * allocate an nfsnode, depending on the value of npp. * npp == NULL --> just do the lookup * *npp == NULL --> allocate a new nfsnode and make sure attributes are * handled too * *npp != NULL --> update the file handle in the vnode */ static int nfs_lookitup(dvp, name, len, cred, procp, npp) register struct vnode *dvp; char *name; int len; struct ucred *cred; struct proc *procp; struct nfsnode **npp; { register u_long *tl; register caddr_t cp; register long t1, t2; struct vnode *newvp = (struct vnode *)0; struct nfsnode *np, *dnp = VTONFS(dvp); caddr_t bpos, dpos, cp2; int error = 0, fhlen, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsfh_t *nfhp; int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_LOOKUP]++; nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len)); nfsm_fhtom(dvp, v3); nfsm_strtom(name, len, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred); if (npp && !error) { nfsm_getfh(nfhp, fhlen, v3); if (*npp) { np = *npp; if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) { free((caddr_t)np->n_fhp, M_NFSBIGFH); np->n_fhp = &np->n_fh; } else if (np->n_fhsize <= NFS_SMALLFH && fhlen>NFS_SMALLFH) np->n_fhp =(nfsfh_t *)malloc(fhlen,M_NFSBIGFH,M_WAITOK); bcopy((caddr_t)nfhp, (caddr_t)np->n_fhp, fhlen); np->n_fhsize = fhlen; newvp = NFSTOV(np); } else if (NFS_CMPFH(dnp, nfhp, fhlen)) { VREF(dvp); newvp = dvp; } else { error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np); if (error) { m_freem(mrep); return (error); } newvp = NFSTOV(np); } if (v3) { nfsm_postop_attr(newvp, attrflag); if (!attrflag && *npp == NULL) { m_freem(mrep); if (newvp == dvp) vrele(newvp); else vput(newvp); return (ENOENT); } } else nfsm_loadattr(newvp, (struct vattr *)0); } nfsm_reqdone; if (npp && *npp == NULL) { if (error) { if (newvp) if (newvp == dvp) vrele(newvp); else vput(newvp); } else *npp = np; } return (error); } /* * Nfs Version 3 commit rpc */ static int nfs_commit(vp, offset, cnt, cred, procp) register struct vnode *vp; u_quad_t offset; int cnt; struct ucred *cred; struct proc *procp; { register caddr_t cp; register u_long *tl; register int t1, t2; register struct nfsmount *nmp = VFSTONFS(vp->v_mount); caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0) return (0); nfsstats.rpccnt[NFSPROC_COMMIT]++; nfsm_reqhead(vp, NFSPROC_COMMIT, NFSX_FH(1)); nfsm_fhtom(vp, 1); nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); txdr_hyper(&offset, tl); tl += 2; *tl = txdr_unsigned(cnt); nfsm_request(vp, NFSPROC_COMMIT, procp, cred); nfsm_wcc_data(vp, wccflag); if (!error) { nfsm_dissect(tl, u_long *, NFSX_V3WRITEVERF); if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl, NFSX_V3WRITEVERF)) { bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF); error = NFSERR_STALEWRITEVERF; } } nfsm_reqdone; return (error); } /* * Kludge City.. * - make nfs_bmap() essentially a no-op that does no translation * - do nfs_strategy() by doing I/O with nfs_readrpc/nfs_writerpc * (Maybe I could use the process's page mapping, but I was concerned that * Kernel Write might not be enabled and also figured copyout() would do * a lot more work than bcopy() and also it currently happens in the * context of the swapper process (2). */ static int nfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { register struct vnode *vp = ap->a_vp; if (ap->a_vpp != NULL) *ap->a_vpp = vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize); if (ap->a_runp != NULL) *ap->a_runp = 0; if (ap->a_runb != NULL) *ap->a_runb = 0; return (0); } /* * Strategy routine. * For async requests when nfsiod(s) are running, queue the request by * calling nfs_asyncio(), otherwise just all nfs_doio() to do the * request. */ static int nfs_strategy(ap) struct vop_strategy_args *ap; { register struct buf *bp = ap->a_bp; struct ucred *cr; struct proc *p; int error = 0; if (bp->b_flags & B_PHYS) panic("nfs physio"); if (bp->b_flags & B_ASYNC) p = (struct proc *)0; else p = curproc; /* XXX */ if (bp->b_flags & B_READ) cr = bp->b_rcred; else cr = bp->b_wcred; /* * If the op is asynchronous and an i/o daemon is waiting * queue the request, wake it up and wait for completion * otherwise just do it ourselves. */ if ((bp->b_flags & B_ASYNC) == 0 || nfs_asyncio(bp, NOCRED)) error = nfs_doio(bp, cr, p); return (error); } /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ static int nfs_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (EINVAL); } /* * fsync vnode op. Just call nfs_flush() with commit == 1. */ /* ARGSUSED */ static int nfs_fsync(ap) struct vop_fsync_args /* { struct vnodeop_desc *a_desc; struct vnode * a_vp; struct ucred * a_cred; int a_waitfor; struct proc * a_p; } */ *ap; { return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1)); } /* * Flush all the blocks associated with a vnode. * Walk through the buffer pool and push any dirty pages * associated with the vnode. */ static int nfs_flush(vp, cred, waitfor, p, commit) register struct vnode *vp; struct ucred *cred; int waitfor; struct proc *p; int commit; { register struct nfsnode *np = VTONFS(vp); register struct buf *bp; register int i; struct buf *nbp; struct nfsmount *nmp = VFSTONFS(vp->v_mount); int s, error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos; int passone = 1; u_quad_t off, endoff, toff; struct ucred* wcred = NULL; struct buf **bvec = NULL; #ifndef NFS_COMMITBVECSIZ #define NFS_COMMITBVECSIZ 20 #endif struct buf *bvec_on_stack[NFS_COMMITBVECSIZ]; int bvecsize = 0, bveccount; if (nmp->nm_flag & NFSMNT_INT) slpflag = PCATCH; if (!commit) passone = 0; /* * A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the * server, but nas not been committed to stable storage on the server * yet. On the first pass, the byte range is worked out and the commit * rpc is done. On the second pass, nfs_writebp() is called to do the * job. */ again: off = (u_quad_t)-1; endoff = 0; bvecpos = 0; if (NFS_ISV3(vp) && commit) { s = splbio(); /* * Count up how many buffers waiting for a commit. */ bveccount = 0; for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT)) == (B_DELWRI | B_NEEDCOMMIT)) bveccount++; } /* * Allocate space to remember the list of bufs to commit. It is * important to use M_NOWAIT here to avoid a race with nfs_write. * If we can't get memory (for whatever reason), we will end up * committing the buffers one-by-one in the loop below. */ if (bveccount > NFS_COMMITBVECSIZ) { if (bvec != NULL && bvec != bvec_on_stack) free(bvec, M_TEMP); bvec = (struct buf **) malloc(bveccount * sizeof(struct buf *), M_TEMP, M_NOWAIT); if (bvec == NULL) { bvec = bvec_on_stack; bvecsize = NFS_COMMITBVECSIZ; } else bvecsize = bveccount; } else { bvec = bvec_on_stack; bvecsize = NFS_COMMITBVECSIZ; } for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if (bvecpos >= bvecsize) break; if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT)) != (B_DELWRI | B_NEEDCOMMIT)) continue; bremfree(bp); /* * Work out if all buffers are using the same cred * so we can deal with them all with one commit. */ if (wcred == NULL) wcred = bp->b_wcred; else if (wcred != bp->b_wcred) wcred = NOCRED; bp->b_flags |= (B_BUSY | B_WRITEINPROG); vfs_busy_pages(bp, 1); /* * A list of these buffers is kept so that the * second loop knows which buffers have actually * been committed. This is necessary, since there * may be a race between the commit rpc and new * uncommitted writes on the file. */ bvec[bvecpos++] = bp; toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; if (toff < off) off = toff; toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff); if (toff > endoff) endoff = toff; } splx(s); } if (bvecpos > 0) { /* * Commit data on the server, as required. * If all bufs are using the same wcred, then use that with * one call for all of them, otherwise commit each one * separately. */ if (wcred != NOCRED) retv = nfs_commit(vp, off, (int)(endoff - off), wcred, p); else { retv = 0; for (i = 0; i < bvecpos; i++) { off_t off, size; bp = bvec[i]; off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; size = (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff); retv = nfs_commit(vp, off, (int)size, bp->b_wcred, p); if (retv) break; } } if (retv == NFSERR_STALEWRITEVERF) nfs_clearcommit(vp->v_mount); /* * Now, either mark the blocks I/O done or mark the * blocks dirty, depending on whether the commit * succeeded. */ for (i = 0; i < bvecpos; i++) { bp = bvec[i]; bp->b_flags &= ~(B_NEEDCOMMIT | B_WRITEINPROG); if (retv) { vfs_unbusy_pages(bp); brelse(bp); } else { vp->v_numoutput++; bp->b_flags |= B_ASYNC; if (bp->b_flags & B_DELWRI) { --numdirtybuffers; if (needsbuffer) { vfs_bio_need_satisfy(); } } bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI); bp->b_dirtyoff = bp->b_dirtyend = 0; reassignbuf(bp, vp); biodone(bp); } } } /* * Start/do any write(s) that are required. */ loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if (bp->b_flags & B_BUSY) { if (waitfor != MNT_WAIT || passone) continue; bp->b_flags |= B_WANTED; error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); splx(s); if (error) { if (nfs_sigintr(nmp, (struct nfsreq *)0, p)) { error = EINTR; goto done; } if (slpflag == PCATCH) { slpflag = 0; slptimeo = 2 * hz; } } goto loop; } if ((bp->b_flags & B_DELWRI) == 0) panic("nfs_fsync: not dirty"); if ((passone || !commit) && (bp->b_flags & B_NEEDCOMMIT)) continue; bremfree(bp); if (passone || !commit) bp->b_flags |= (B_BUSY|B_ASYNC); else bp->b_flags |= (B_BUSY|B_ASYNC|B_WRITEINPROG|B_NEEDCOMMIT); splx(s); VOP_BWRITE(bp); goto loop; } splx(s); if (passone) { passone = 0; goto again; } if (waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; error = tsleep((caddr_t)&vp->v_numoutput, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); if (error) { if (nfs_sigintr(nmp, (struct nfsreq *)0, p)) { error = EINTR; goto done; } if (slpflag == PCATCH) { slpflag = 0; slptimeo = 2 * hz; } } } if (vp->v_dirtyblkhd.lh_first && commit) { goto loop; } } if (np->n_flag & NWRITEERR) { error = np->n_error; np->n_flag &= ~NWRITEERR; } done: if (bvec != NULL && bvec != bvec_on_stack) free(bvec, M_TEMP); return (error); } /* * Return POSIX pathconf information applicable to nfs. * * The NFS V2 protocol doesn't support this, so just return EINVAL * for V2. */ /* ARGSUSED */ static int nfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { return (EINVAL); } /* * NFS advisory byte-level locks. * Currently unsupported. */ static int nfs_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); /* * The following kludge is to allow diskless support to work * until a real NFS lockd is implemented. Basically, just pretend * that this is a local lock. */ return (lf_advlock(ap, &(np->n_lockf), np->n_size)); } /* * Print out the contents of an nfsnode. */ static int nfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); printf("tag VT_NFS, fileid %ld fsid 0x%lx", np->n_vattr.va_fileid, np->n_vattr.va_fsid); if (vp->v_type == VFIFO) fifo_printinfo(vp); printf("\n"); return (0); } /* * NFS directory offset lookup. * Currently unsupported. */ static int nfs_blkatoff(ap) struct vop_blkatoff_args /* { struct vnode *a_vp; off_t a_offset; char **a_res; struct buf **a_bpp; } */ *ap; { return (EOPNOTSUPP); } /* * NFS flat namespace allocation. * Currently unsupported. */ static int nfs_valloc(ap) struct vop_valloc_args /* { struct vnode *a_pvp; int a_mode; struct ucred *a_cred; struct vnode **a_vpp; } */ *ap; { return (EOPNOTSUPP); } /* * NFS flat namespace free. * Currently unsupported. */ static int nfs_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (EOPNOTSUPP); } /* * NFS file truncation. */ static int nfs_truncate(ap) struct vop_truncate_args /* { struct vnode *a_vp; off_t a_length; int a_flags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* Use nfs_setattr */ printf("nfs_truncate: need to implement!!"); return (EOPNOTSUPP); } /* * NFS update. */ static int nfs_update(ap) struct vop_update_args /* { struct vnode *a_vp; struct timeval *a_ta; struct timeval *a_tm; int a_waitfor; } */ *ap; { #if 0 /* Use nfs_setattr */ printf("nfs_update: need to implement!!"); #endif return (EOPNOTSUPP); } /* * Just call nfs_writebp() with the force argument set to 1. */ static int nfs_bwrite(ap) struct vop_bwrite_args /* { struct vnode *a_bp; } */ *ap; { return (nfs_writebp(ap->a_bp, 1)); } /* * This is a clone of vn_bwrite(), except that B_WRITEINPROG isn't set unless * the force flag is one and it also handles the B_NEEDCOMMIT flag. */ int nfs_writebp(bp, force) register struct buf *bp; int force; { register int oldflags = bp->b_flags, retv = 1; off_t off; if(!(bp->b_flags & B_BUSY)) panic("bwrite: buffer is not busy???"); if (bp->b_flags & B_DELWRI) { --numdirtybuffers; if (needsbuffer) vfs_bio_need_satisfy(); } bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI); if ((oldflags & (B_ASYNC|B_DELWRI)) == (B_ASYNC|B_DELWRI)) { reassignbuf(bp, bp->b_vp); } bp->b_vp->v_numoutput++; curproc->p_stats->p_ru.ru_oublock++; /* * If B_NEEDCOMMIT is set, a commit rpc may do the trick. If not * an actual write will have to be scheduled via. VOP_STRATEGY(). * If B_WRITEINPROG is already set, then push it with a write anyhow. */ vfs_busy_pages(bp, 1); if ((oldflags & (B_NEEDCOMMIT | B_WRITEINPROG)) == B_NEEDCOMMIT) { off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; bp->b_flags |= B_WRITEINPROG; retv = nfs_commit(bp->b_vp, off, bp->b_dirtyend-bp->b_dirtyoff, bp->b_wcred, bp->b_proc); bp->b_flags &= ~B_WRITEINPROG; if (!retv) { bp->b_dirtyoff = bp->b_dirtyend = 0; bp->b_flags &= ~B_NEEDCOMMIT; biodone(bp); } else if (retv == NFSERR_STALEWRITEVERF) nfs_clearcommit(bp->b_vp->v_mount); } if (retv) { if (force) bp->b_flags |= B_WRITEINPROG; VOP_STRATEGY(bp); } if( (oldflags & B_ASYNC) == 0) { int rtval = biowait(bp); if (oldflags & B_DELWRI) { reassignbuf(bp, bp->b_vp); } brelse(bp); return (rtval); } return (0); } /* * nfs special file access vnode op. * Essentially just get vattr and then imitate iaccess() since the device is * local to the client. */ static int nfsspec_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vattr *vap; register gid_t *gp; register struct ucred *cred = ap->a_cred; struct vnode *vp = ap->a_vp; mode_t mode = ap->a_mode; struct vattr vattr; register int i; int error; /* * Disallow write attempts on filesystems mounted read-only; * unless the file is a socket, fifo, or a block or character * device resident on the filesystem. */ if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); } } /* * If you're the super-user, * you always get access. */ if (cred->cr_uid == 0) return (0); vap = &vattr; error = VOP_GETATTR(vp, vap, cred, ap->a_p); if (error) return (error); /* * Access check is based on only one of owner, group, public. * If not owner, then check group. If not a member of the * group, then check public access. */ if (cred->cr_uid != vap->va_uid) { mode >>= 3; gp = cred->cr_groups; for (i = 0; i < cred->cr_ngroups; i++, gp++) if (vap->va_gid == *gp) goto found; mode >>= 3; found: ; } error = (vap->va_mode & mode) == mode ? 0 : EACCES; return (error); } /* * Read wrapper for special devices. */ static int nfsspec_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); struct timeval tv; /* * Set access flag. */ np->n_flag |= NACC; gettime(&tv); np->n_atim.tv_sec = tv.tv_sec; np->n_atim.tv_nsec = tv.tv_usec * 1000; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for special devices. */ static int nfsspec_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); struct timeval tv; /* * Set update flag. */ np->n_flag |= NUPD; gettime(&tv); np->n_mtim.tv_sec = tv.tv_sec; np->n_mtim.tv_nsec = tv.tv_usec * 1000; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for special devices. * * Update the times on the nfsnode then do device close. */ static int nfsspec_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); struct vattr vattr; if (np->n_flag & (NACC | NUPD)) { np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); if (np->n_flag & NACC) vattr.va_atime = np->n_atim; if (np->n_flag & NUPD) vattr.va_mtime = np->n_mtim; (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap)); } /* * Read wrapper for fifos. */ static int nfsfifo_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); struct timeval tv; /* * Set access flag. */ np->n_flag |= NACC; gettime(&tv); np->n_atim.tv_sec = tv.tv_sec; np->n_atim.tv_nsec = tv.tv_usec * 1000; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for fifos. */ static int nfsfifo_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); struct timeval tv; /* * Set update flag. */ np->n_flag |= NUPD; gettime(&tv); np->n_mtim.tv_sec = tv.tv_sec; np->n_mtim.tv_nsec = tv.tv_usec * 1000; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for fifos. * * Update the times on the nfsnode then do fifo close. */ static int nfsfifo_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); struct timeval tv; struct vattr vattr; if (np->n_flag & (NACC | NUPD)) { gettime(&tv); if (np->n_flag & NACC) { np->n_atim.tv_sec = tv.tv_sec; np->n_atim.tv_nsec = tv.tv_usec * 1000; } if (np->n_flag & NUPD) { np->n_mtim.tv_sec = tv.tv_sec; np->n_mtim.tv_nsec = tv.tv_usec * 1000; } np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); if (np->n_flag & NACC) vattr.va_atime = np->n_atim; if (np->n_flag & NUPD) vattr.va_mtime = np->n_mtim; (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap)); } static int nfs_ioctl(ap) struct vop_ioctl_args *ap; { /* * XXX we were once bogusly enoictl() which returned this (ENOTTY). * Probably we should return ENODEV. */ return (ENOTTY); } static int nfs_select(ap) struct vop_select_args *ap; { /* * We were once bogusly seltrue() which returns 1. Is this right? */ return (1); } Index: head/sys/nfsclient/nfs_vnops.c =================================================================== --- head/sys/nfsclient/nfs_vnops.c (revision 29290) +++ head/sys/nfsclient/nfs_vnops.c (revision 29291) @@ -1,3459 +1,3450 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)nfs_vnops.c 8.16 (Berkeley) 5/27/95 - * $Id: nfs_vnops.c,v 1.56 1997/08/26 07:32:43 phk Exp $ + * $Id: nfs_vnops.c,v 1.57 1997/09/10 19:52:27 phk Exp $ */ /* * vnode op calls for Sun NFS version 2 and 3 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Defs */ #define TRUE 1 #define FALSE 0 /* * Ifdef for FreeBSD-current merged buffer cache. It is unfortunate that these * calls are not in getblk() and brelse() so that they would not be necessary * here. */ #ifndef B_VMIO #define vfs_busy_pages(bp, f) #endif static int nfsspec_read __P((struct vop_read_args *)); static int nfsspec_write __P((struct vop_write_args *)); static int nfsfifo_read __P((struct vop_read_args *)); static int nfsfifo_write __P((struct vop_write_args *)); static int nfsspec_close __P((struct vop_close_args *)); static int nfsfifo_close __P((struct vop_close_args *)); static int nfs_ioctl __P((struct vop_ioctl_args *)); static int nfs_select __P((struct vop_select_args *)); static int nfs_flush __P((struct vnode *,struct ucred *,int,struct proc *,int)); static int nfs_setattrrpc __P((struct vnode *,struct vattr *,struct ucred *,struct proc *)); static int nfs_lookup __P((struct vop_cachedlookup_args *)); static int nfs_create __P((struct vop_create_args *)); static int nfs_mknod __P((struct vop_mknod_args *)); static int nfs_open __P((struct vop_open_args *)); static int nfs_close __P((struct vop_close_args *)); static int nfs_access __P((struct vop_access_args *)); static int nfs_getattr __P((struct vop_getattr_args *)); static int nfs_setattr __P((struct vop_setattr_args *)); static int nfs_read __P((struct vop_read_args *)); static int nfs_mmap __P((struct vop_mmap_args *)); static int nfs_fsync __P((struct vop_fsync_args *)); static int nfs_remove __P((struct vop_remove_args *)); static int nfs_link __P((struct vop_link_args *)); static int nfs_rename __P((struct vop_rename_args *)); static int nfs_mkdir __P((struct vop_mkdir_args *)); static int nfs_rmdir __P((struct vop_rmdir_args *)); static int nfs_symlink __P((struct vop_symlink_args *)); static int nfs_readdir __P((struct vop_readdir_args *)); static int nfs_bmap __P((struct vop_bmap_args *)); static int nfs_strategy __P((struct vop_strategy_args *)); static int nfs_lookitup __P((struct vnode *,char *,int,struct ucred *,struct proc *,struct nfsnode **)); static int nfs_sillyrename __P((struct vnode *,struct vnode *,struct componentname *)); static int nfsspec_access __P((struct vop_access_args *)); static int nfs_readlink __P((struct vop_readlink_args *)); static int nfs_print __P((struct vop_print_args *)); static int nfs_pathconf __P((struct vop_pathconf_args *)); static int nfs_advlock __P((struct vop_advlock_args *)); static int nfs_blkatoff __P((struct vop_blkatoff_args *)); static int nfs_bwrite __P((struct vop_bwrite_args *)); static int nfs_valloc __P((struct vop_valloc_args *)); static int nfs_vfree __P((struct vop_vfree_args *)); static int nfs_truncate __P((struct vop_truncate_args *)); static int nfs_update __P((struct vop_update_args *)); /* * Global vfs data structures for nfs */ vop_t **nfsv2_vnodeop_p; static struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, /* lookup */ { &vop_cachedlookup_desc, (vop_t *)nfs_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)nfs_create }, /* create */ { &vop_mknod_desc, (vop_t *)nfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)nfs_open }, /* open */ { &vop_close_desc, (vop_t *)nfs_close }, /* close */ { &vop_access_desc, (vop_t *)nfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)nfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)nfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)nfs_read }, /* read */ { &vop_write_desc, (vop_t *)nfs_write }, /* write */ { &vop_lease_desc, (vop_t *)nfs_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)nfs_ioctl }, /* ioctl */ { &vop_select_desc, (vop_t *)nfs_select }, /* select */ { &vop_revoke_desc, (vop_t *)nfs_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)nfs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)nfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)nfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)nfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)nfs_link }, /* link */ { &vop_rename_desc, (vop_t *)nfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)nfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)nfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)nfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)nfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)nfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)nfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)nfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)nfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)nfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)nfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)nfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)nfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)nfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)nfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)nfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)nfs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)nfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)nfs_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)nfs_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)nfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)nfs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)nfs_update }, /* update */ { &vop_bwrite_desc, (vop_t *)nfs_bwrite }, /* bwrite */ { &vop_getpages_desc, (vop_t *)nfs_getpages }, /* getpages */ { NULL, NULL } }; static struct vnodeopv_desc nfsv2_vnodeop_opv_desc = { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries }; VNODEOP_SET(nfsv2_vnodeop_opv_desc); /* * Special device vnode ops */ vop_t **spec_nfsv2nodeop_p; static struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)nfsspec_close }, /* close */ { &vop_access_desc, (vop_t *)nfsspec_access }, /* access */ { &vop_getattr_desc, (vop_t *)nfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)nfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)nfsspec_read }, /* read */ { &vop_write_desc, (vop_t *)nfsspec_write }, /* write */ { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ { &vop_select_desc, (vop_t *)spec_select }, /* select */ { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)nfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)nfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)nfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)nfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)nfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)nfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)nfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)spec_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)spec_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)nfs_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc spec_nfsv2nodeop_opv_desc = { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries }; VNODEOP_SET(spec_nfsv2nodeop_opv_desc); vop_t **fifo_nfsv2nodeop_p; static struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)nfsfifo_close }, /* close */ { &vop_access_desc, (vop_t *)nfsspec_access }, /* access */ { &vop_getattr_desc, (vop_t *)nfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)nfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)nfsfifo_read }, /* read */ { &vop_write_desc, (vop_t *)nfsfifo_write }, /* write */ { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ { &vop_select_desc, (vop_t *)fifo_select }, /* select */ { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)nfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link }, /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)nfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)nfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)nfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)nfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_badop }, /* strategy */ { &vop_print_desc, (vop_t *)nfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)nfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)fifo_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)fifo_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)nfs_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc = { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries }; VNODEOP_SET(fifo_nfsv2nodeop_opv_desc); static int nfs_commit __P((struct vnode *vp, u_quad_t offset, int cnt, struct ucred *cred, struct proc *procp)); static int nfs_mknodrpc __P((struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, struct vattr *vap)); static int nfs_removerpc __P((struct vnode *dvp, char *name, int namelen, struct ucred *cred, struct proc *proc)); static int nfs_renamerpc __P((struct vnode *fdvp, char *fnameptr, int fnamelen, struct vnode *tdvp, char *tnameptr, int tnamelen, struct ucred *cred, struct proc *proc)); static int nfs_renameit __P((struct vnode *sdvp, struct componentname *scnp, struct sillyrename *sp)); /* * Global variables */ extern u_long nfs_true, nfs_false; extern struct nfsstats nfsstats; extern nfstype nfsv3_type[9]; struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; struct nfsmount *nfs_iodmount[NFS_MAXASYNCDAEMON]; int nfs_numasync = 0; #define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1)) /* * nfs access vnode op. * For nfs version 2, just return ok. File accesses may fail later. * For nfs version 3, use the access rpc to check accessibility. If file modes * are changed on the server, accesses might still fail later. */ static int nfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register u_long *tl; register caddr_t cp; register int t1, t2; caddr_t bpos, dpos, cp2; int error = 0, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; u_long mode, rmode; int v3 = NFS_ISV3(vp); /* * Disallow write attempts on filesystems mounted read-only; * unless the file is a socket, fifo, or a block or character * device resident on the filesystem. */ if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); } } /* * For nfs v3, do an access rpc, otherwise you are stuck emulating * ufs_access() locally using the vattr. This may not be correct, * since the server may apply other access criteria such as * client uid-->server uid mapping that we do not know about, but * this is better than just returning anything that is lying about * in the cache. */ if (v3) { nfsstats.rpccnt[NFSPROC_ACCESS]++; nfsm_reqhead(vp, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED); nfsm_fhtom(vp, v3); nfsm_build(tl, u_long *, NFSX_UNSIGNED); if (ap->a_mode & VREAD) mode = NFSV3ACCESS_READ; else mode = 0; if (vp->v_type == VDIR) { if (ap->a_mode & VWRITE) mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND | NFSV3ACCESS_DELETE); if (ap->a_mode & VEXEC) mode |= NFSV3ACCESS_LOOKUP; } else { if (ap->a_mode & VWRITE) mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND); if (ap->a_mode & VEXEC) mode |= NFSV3ACCESS_EXECUTE; } *tl = txdr_unsigned(mode); nfsm_request(vp, NFSPROC_ACCESS, ap->a_p, ap->a_cred); nfsm_postop_attr(vp, attrflag); if (!error) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); rmode = fxdr_unsigned(u_long, *tl); /* * The NFS V3 spec does not clarify whether or not * the returned access bits can be a superset of * the ones requested, so... */ if ((rmode & mode) != mode) error = EACCES; } nfsm_reqdone; return (error); } else { if (error = nfsspec_access(ap)) return (error); /* * Attempt to prevent a mapped root from accessing a file * which it shouldn't. We try to read a byte from the file * if the user is root and the file is not zero length. * After calling nfsspec_access, we should have the correct * file size cached. */ if (ap->a_cred->cr_uid == 0 && (ap->a_mode & VREAD) && VTONFS(vp)->n_size > 0) { struct iovec aiov; struct uio auio; char buf[1]; aiov.iov_base = buf; aiov.iov_len = 1; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_resid = 1; auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_procp = ap->a_p; if (vp->v_type == VREG) error = nfs_readrpc(vp, &auio, ap->a_cred); else if (vp->v_type == VDIR) { char* buf; buf = malloc(NFS_DIRBLKSIZ, M_TEMP, M_WAITOK); aiov.iov_base = buf; aiov.iov_len = auio.uio_resid = NFS_DIRBLKSIZ; error = nfs_readdirrpc(vp, &auio, ap->a_cred); free(buf, M_TEMP); } else if (vp->v_type = VLNK) error = nfs_readlinkrpc(vp, &auio, ap->a_cred); else error = EACCES; } return (error); } } /* * nfs open vnode op * Check to see if the type is ok * and that deletion is not in progress. * For paged in text files, you will need to flush the page cache * if consistency is lost. */ /* ARGSUSED */ static int nfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; struct nfsnode *np = VTONFS(vp); struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct vattr vattr; int error; if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) { printf("open eacces vtyp=%d\n",vp->v_type); return (EACCES); } /* * Get a valid lease. If cached data is stale, flush it. */ if (nmp->nm_flag & NFSMNT_NQNFS) { if (NQNFS_CKINVALID(vp, np, ND_READ)) { do { error = nqnfs_getlease(vp, ND_READ, ap->a_cred, ap->a_p); } while (error == NQNFS_EXPIRED); if (error) return (error); if (np->n_lrev != np->n_brev || (np->n_flag & NQNFSNONCACHE)) { if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); (void) vnode_pager_uncache(vp, ap->a_p); np->n_brev = np->n_lrev; } } } else { if (np->n_flag & NMODIFIED) { if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_attrstamp = 0; if (vp->v_type == VDIR) np->n_direofoffset = 0; error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); if (error) return (error); np->n_mtime = vattr.va_mtime.tv_sec; } else { error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); if (error) return (error); if (np->n_mtime != vattr.va_mtime.tv_sec) { if (vp->v_type == VDIR) np->n_direofoffset = 0; if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_mtime = vattr.va_mtime.tv_sec; } } } if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) np->n_attrstamp = 0; /* For Open/Close consistency */ return (0); } /* * nfs close vnode op * What an NFS client should do upon close after writing is a debatable issue. * Most NFS clients push delayed writes to the server upon close, basically for * two reasons: * 1 - So that any write errors may be reported back to the client process * doing the close system call. By far the two most likely errors are * NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure. * 2 - To put a worst case upper bound on cache inconsistency between * multiple clients for the file. * There is also a consistency problem for Version 2 of the protocol w.r.t. * not being able to tell if other clients are writing a file concurrently, * since there is no way of knowing if the changed modify time in the reply * is only due to the write for this client. * (NFS Version 3 provides weak cache consistency data in the reply that * should be sufficient to detect and handle this case.) * * The current code does the following: * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers * for NFS Version 3 - flush dirty buffers to the server but don't invalidate * or commit them (this satisfies 1 and 2 except for the * case where the server crashes after this close but * before the commit RPC, which is felt to be "good * enough". Changing the last argument to nfs_flush() to * a 1 would force a commit operation, if it is felt a * commit is necessary now. * for NQNFS - do nothing now, since 2 is dealt with via leases and * 1 should be dealt with via an fsync() system call for * cases where write errors are important. */ /* ARGSUSED */ static int nfs_close(ap) struct vop_close_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); int error = 0; if (vp->v_type == VREG) { if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 && (np->n_flag & NMODIFIED)) { if (NFS_ISV3(vp)) { error = nfs_flush(vp, ap->a_cred, MNT_WAIT, ap->a_p, 0); np->n_flag &= ~NMODIFIED; } else error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); np->n_attrstamp = 0; } if (np->n_flag & NWRITEERR) { np->n_flag &= ~NWRITEERR; error = np->n_error; } } return (error); } /* * nfs getattr call from vfs. */ static int nfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register caddr_t cp; register u_long *tl; register int t1, t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(vp); /* * Update local times for special files. */ if (np->n_flag & (NACC | NUPD)) np->n_flag |= NCHG; /* * First look in the cache. */ if (nfs_getattrcache(vp, ap->a_vap) == 0) return (0); nfsstats.rpccnt[NFSPROC_GETATTR]++; nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH(v3)); nfsm_fhtom(vp, v3); nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred); if (!error) nfsm_loadattr(vp, ap->a_vap); nfsm_reqdone; return (error); } /* * nfs setattr call. */ static int nfs_setattr(ap) struct vop_setattr_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct vattr *vap = ap->a_vap; int error = 0; u_quad_t tsize; #ifndef nolint tsize = (u_quad_t)0; #endif /* * Disallow write attempts if the filesystem is mounted read-only. */ if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) && (vp->v_mount->mnt_flag & MNT_RDONLY)) return (EROFS); if (vap->va_size != VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VCHR: case VBLK: case VSOCK: case VFIFO: if (vap->va_mtime.tv_sec == VNOVAL && vap->va_atime.tv_sec == VNOVAL && vap->va_mode == (u_short)VNOVAL && vap->va_uid == (uid_t)VNOVAL && vap->va_gid == (gid_t)VNOVAL) return (0); vap->va_size = VNOVAL; break; default: /* * Disallow write attempts if the filesystem is * mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (np->n_flag & NMODIFIED) { if (vap->va_size == 0) error = nfs_vinvalbuf(vp, 0, ap->a_cred, ap->a_p, 1); else error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); if (error) return (error); } tsize = np->n_size; np->n_size = np->n_vattr.va_size = vap->va_size; vnode_pager_setsize(vp, (u_long)np->n_size); }; } else if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) && (np->n_flag & NMODIFIED) && vp->v_type == VREG && (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); error = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_p); if (error && vap->va_size != VNOVAL) { np->n_size = np->n_vattr.va_size = tsize; vnode_pager_setsize(vp, (u_long)np->n_size); } return (error); } /* * Do an nfs setattr rpc. */ static int nfs_setattrrpc(vp, vap, cred, procp) register struct vnode *vp; register struct vattr *vap; struct ucred *cred; struct proc *procp; { register struct nfsv2_sattr *sp; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; u_long *tl; int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(vp); nfsstats.rpccnt[NFSPROC_SETATTR]++; nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH(v3) + NFSX_SATTR(v3)); nfsm_fhtom(vp, v3); if (v3) { if (vap->va_mode != (u_short)VNOVAL) { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_true; *tl = txdr_unsigned(vap->va_mode); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } if (vap->va_uid != (uid_t)VNOVAL) { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_true; *tl = txdr_unsigned(vap->va_uid); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } if (vap->va_gid != (gid_t)VNOVAL) { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_true; *tl = txdr_unsigned(vap->va_gid); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } if (vap->va_size != VNOVAL) { nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); *tl++ = nfs_true; txdr_hyper(&vap->va_size, tl); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } if (vap->va_atime.tv_sec != VNOVAL) { if (vap->va_atime.tv_sec != time.tv_sec) { nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); txdr_nfsv3time(&vap->va_atime, tl); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER); } } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE); } if (vap->va_mtime.tv_sec != VNOVAL) { if (vap->va_mtime.tv_sec != time.tv_sec) { nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); txdr_nfsv3time(&vap->va_mtime, tl); } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER); } } else { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE); } nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = nfs_false; } else { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); if (vap->va_mode == (u_short)VNOVAL) sp->sa_mode = VNOVAL; else sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode); if (vap->va_uid == (uid_t)VNOVAL) sp->sa_uid = VNOVAL; else sp->sa_uid = txdr_unsigned(vap->va_uid); if (vap->va_gid == (gid_t)VNOVAL) sp->sa_gid = VNOVAL; else sp->sa_gid = txdr_unsigned(vap->va_gid); sp->sa_size = txdr_unsigned(vap->va_size); txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(vp, NFSPROC_SETATTR, procp, cred); if (v3) { nfsm_wcc_data(vp, wccflag); } else nfsm_loadattr(vp, (struct vattr *)0); nfsm_reqdone; return (error); } /* * nfs lookup call, one step at a time... * First look in cache * If not found, unlock the directory nfsnode and do the rpc */ static int nfs_lookup(ap) struct vop_cachedlookup_args /* { struct vnodeop_desc *a_desc; struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { register struct componentname *cnp = ap->a_cnp; register struct vnode *dvp = ap->a_dvp; register struct vnode **vpp = ap->a_vpp; register int flags = cnp->cn_flags; register struct vnode *newvp; register u_long *tl; register caddr_t cp; register long t1, t2; struct nfsmount *nmp; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; long len; nfsfh_t *fhp; struct nfsnode *np; int lockparent, wantparent, error = 0, attrflag, fhsize; int v3 = NFS_ISV3(dvp); struct proc *p = cnp->cn_proc; if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); *vpp = NULLVP; if (dvp->v_type != VDIR) return (ENOTDIR); lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT|WANTPARENT); nmp = VFSTONFS(dvp->v_mount); np = VTONFS(dvp); error = 0; newvp = NULLVP; nfsstats.lookupcache_misses++; nfsstats.rpccnt[NFSPROC_LOOKUP]++; len = cnp->cn_namelen; nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred); if (error) { nfsm_postop_attr(dvp, attrflag); m_freem(mrep); goto nfsmout; } nfsm_getfh(fhp, fhsize, v3); /* * Handle RENAME case... */ if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) { if (NFS_CMPFH(np, fhp, fhsize)) { m_freem(mrep); return (EISDIR); } if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) { m_freem(mrep); return (error); } newvp = NFSTOV(np); if (v3) { nfsm_postop_attr(newvp, attrflag); nfsm_postop_attr(dvp, attrflag); } else nfsm_loadattr(newvp, (struct vattr *)0); *vpp = newvp; m_freem(mrep); cnp->cn_flags |= SAVENAME; if (!lockparent) VOP_UNLOCK(dvp, 0, p); return (0); } if (flags & ISDOTDOT) { VOP_UNLOCK(dvp, 0, p); error = nfs_nget(dvp->v_mount, fhp, fhsize, &np); if (error) { vn_lock(dvp, LK_EXCLUSIVE + LK_RETRY, p); return (error); } newvp = NFSTOV(np); if (lockparent && (flags & ISLASTCN) && (error = vn_lock(dvp, LK_EXCLUSIVE, p))) { vput(newvp); return (error); } } else if (NFS_CMPFH(np, fhp, fhsize)) { VREF(dvp); newvp = dvp; } else { if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) { m_freem(mrep); return (error); } if (!lockparent || !(flags & ISLASTCN)) VOP_UNLOCK(dvp, 0, p); newvp = NFSTOV(np); } if (v3) { nfsm_postop_attr(newvp, attrflag); nfsm_postop_attr(dvp, attrflag); } else nfsm_loadattr(newvp, (struct vattr *)0); if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) { np->n_ctime = np->n_vattr.va_ctime.tv_sec; cache_enter(dvp, newvp, cnp); } *vpp = newvp; nfsm_reqdone; if (error) { if (newvp != NULLVP) { vrele(newvp); *vpp = NULLVP; } if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && (flags & ISLASTCN) && error == ENOENT) { if (!lockparent) VOP_UNLOCK(dvp, 0, p); if (dvp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else error = EJUSTRETURN; } if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; } return (error); } /* * nfs read call. * Just call nfs_bioread() to do the work. */ static int nfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; if (vp->v_type != VREG) return (EPERM); return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred, 0)); } /* * nfs readlink call */ static int nfs_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; if (vp->v_type != VLNK) return (EPERM); return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred, 0)); } /* * Do a readlink rpc. * Called by nfs_doio() from below the buffer cache. */ int nfs_readlinkrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, len, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(vp); nfsstats.rpccnt[NFSPROC_READLINK]++; nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH(v3)); nfsm_fhtom(vp, v3); nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred); if (v3) nfsm_postop_attr(vp, attrflag); if (!error) { nfsm_strsiz(len, NFS_MAXPATHLEN); nfsm_mtouio(uiop, len); } nfsm_reqdone; return (error); } /* * nfs read rpc call * Ditto above */ int nfs_readrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; int error = 0, len, retlen, tsiz, eof, attrflag; int v3 = NFS_ISV3(vp); #ifndef nolint eof = 0; #endif nmp = VFSTONFS(vp->v_mount); tsiz = uiop->uio_resid; if (uiop->uio_offset + tsiz > 0xffffffff && !v3) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_READ]++; len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz; nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH(v3) + NFSX_UNSIGNED * 3); nfsm_fhtom(vp, v3); nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3); if (v3) { txdr_hyper(&uiop->uio_offset, tl); *(tl + 2) = txdr_unsigned(len); } else { *tl++ = txdr_unsigned(uiop->uio_offset); *tl++ = txdr_unsigned(len); *tl = 0; } nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred); if (v3) { nfsm_postop_attr(vp, attrflag); if (error) { m_freem(mrep); goto nfsmout; } nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); eof = fxdr_unsigned(int, *(tl + 1)); } else nfsm_loadattr(vp, (struct vattr *)0); nfsm_strsiz(retlen, nmp->nm_rsize); nfsm_mtouio(uiop, retlen); m_freem(mrep); tsiz -= retlen; if (v3) { if (eof || retlen == 0) tsiz = 0; } else if (retlen < len) tsiz = 0; } nfsmout: return (error); } /* * nfs write call */ int nfs_writerpc(vp, uiop, cred, iomode, must_commit) register struct vnode *vp; register struct uio *uiop; struct ucred *cred; int *iomode, *must_commit; { register u_long *tl; register caddr_t cp; register int t1, t2, backup; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp = VFSTONFS(vp->v_mount); int error = 0, len, tsiz, wccflag = NFSV3_WCCRATTR, rlen, commit; int v3 = NFS_ISV3(vp), committed = NFSV3WRITE_FILESYNC; #ifndef DIAGNOSTIC if (uiop->uio_iovcnt != 1) panic("nfs: writerpc iovcnt > 1"); #endif *must_commit = 0; tsiz = uiop->uio_resid; if (uiop->uio_offset + tsiz > 0xffffffff && !v3) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_WRITE]++; len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz; nfsm_reqhead(vp, NFSPROC_WRITE, NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len)); nfsm_fhtom(vp, v3); if (v3) { nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED); txdr_hyper(&uiop->uio_offset, tl); tl += 2; *tl++ = txdr_unsigned(len); *tl++ = txdr_unsigned(*iomode); } else { nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); *++tl = txdr_unsigned(uiop->uio_offset); tl += 2; } *tl = txdr_unsigned(len); nfsm_uiotom(uiop, len); nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred); if (v3) { wccflag = NFSV3_WCCCHK; nfsm_wcc_data(vp, wccflag); if (!error) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED + NFSX_V3WRITEVERF); rlen = fxdr_unsigned(int, *tl++); if (rlen == 0) { error = NFSERR_IO; break; } else if (rlen < len) { backup = len - rlen; uiop->uio_iov->iov_base -= backup; uiop->uio_iov->iov_len += backup; uiop->uio_offset -= backup; uiop->uio_resid += backup; len = rlen; } commit = fxdr_unsigned(int, *tl++); /* * Return the lowest committment level * obtained by any of the RPCs. */ if (committed == NFSV3WRITE_FILESYNC) committed = commit; else if (committed == NFSV3WRITE_DATASYNC && commit == NFSV3WRITE_UNSTABLE) committed = commit; if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0) { bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF); nmp->nm_flag |= NFSMNT_HASWRITEVERF; } else if (bcmp((caddr_t)tl, (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) { *must_commit = 1; bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF); } } } else nfsm_loadattr(vp, (struct vattr *)0); if (wccflag) VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.tv_sec; m_freem(mrep); tsiz -= len; } nfsmout: if (vp->v_mount->mnt_flag & MNT_ASYNC) committed = NFSV3WRITE_FILESYNC; *iomode = committed; if (error) uiop->uio_resid = tsiz; return (error); } /* * nfs mknod rpc * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the * mode set to specify the file type and the size field for rdev. */ static int nfs_mknodrpc(dvp, vpp, cnp, vap) register struct vnode *dvp; register struct vnode **vpp; register struct componentname *cnp; register struct vattr *vap; { register struct nfsv2_sattr *sp; register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; struct vnode *newvp = (struct vnode *)0; struct nfsnode *np = (struct nfsnode *)0; struct vattr vattr; char *cp2; caddr_t bpos, dpos; int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; u_long rdev; int v3 = NFS_ISV3(dvp); if (vap->va_type == VCHR || vap->va_type == VBLK) rdev = txdr_unsigned(vap->va_rdev); else if (vap->va_type == VFIFO || vap->va_type == VSOCK) rdev = 0xffffffff; else { VOP_ABORTOP(dvp, cnp); vput(dvp); return (EOPNOTSUPP); } if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } nfsstats.rpccnt[NFSPROC_MKNOD]++; nfsm_reqhead(dvp, NFSPROC_MKNOD, NFSX_FH(v3) + 4 * NFSX_UNSIGNED + + nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); if (v3) { nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3SRVSATTR); *tl++ = vtonfsv3_type(vap->va_type); sp3 = (struct nfsv3_sattr *)tl; nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); if (vap->va_type == VCHR || vap->va_type == VBLK) { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(major(vap->va_rdev)); *tl = txdr_unsigned(minor(vap->va_rdev)); } } else { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); sp->sa_size = rdev; txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_MKNOD, cnp->cn_proc, cnp->cn_cred); if (!error) { nfsm_mtofh(dvp, newvp, v3, gotvp); if (!gotvp) { if (newvp) { vput(newvp); newvp = (struct vnode *)0; } error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np); if (!error) newvp = NFSTOV(np); } } if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; if (error) { if (newvp) vput(newvp); } else { if (cnp->cn_flags & MAKEENTRY) cache_enter(dvp, newvp, cnp); *vpp = newvp; } FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; vput(dvp); return (error); } /* * nfs mknod vop * just call nfs_mknodrpc() to do the work. */ /* ARGSUSED */ static int nfs_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct vnode *newvp; int error; error = nfs_mknodrpc(ap->a_dvp, &newvp, ap->a_cnp, ap->a_vap); if (!error) vput(newvp); return (error); } static u_long create_verf; /* * nfs file create call */ static int nfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; struct nfsnode *np = (struct nfsnode *)0; struct vnode *newvp = (struct vnode *)0; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0, fmode = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; int v3 = NFS_ISV3(dvp); /* * Oops, not for me.. */ if (vap->va_type == VSOCK) return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap)); if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } if (vap->va_vaflags & VA_EXCLUSIVE) fmode |= O_EXCL; again: nfsstats.rpccnt[NFSPROC_CREATE]++; nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH(v3) + 2 * NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); if (v3) { nfsm_build(tl, u_long *, NFSX_UNSIGNED); if (fmode & O_EXCL) { *tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE); nfsm_build(tl, u_long *, NFSX_V3CREATEVERF); if (!TAILQ_EMPTY(&in_ifaddrhead)) *tl++ = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr.s_addr; else *tl++ = create_verf; *tl = ++create_verf; } else { *tl = txdr_unsigned(NFSV3CREATE_UNCHECKED); nfsm_build(tl, u_long *, NFSX_V3SRVSATTR); sp3 = (struct nfsv3_sattr *)tl; nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); } } else { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); sp->sa_size = 0; txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred); if (!error) { nfsm_mtofh(dvp, newvp, v3, gotvp); if (!gotvp) { if (newvp) { vput(newvp); newvp = (struct vnode *)0; } error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np); if (!error) newvp = NFSTOV(np); } } if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; if (error) { if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) { fmode &= ~O_EXCL; goto again; } if (newvp) vput(newvp); } else if (v3 && (fmode & O_EXCL)) error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_proc); if (!error) { if (cnp->cn_flags & MAKEENTRY) cache_enter(dvp, newvp, cnp); *ap->a_vpp = newvp; } FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; vput(dvp); return (error); } /* * nfs file remove call * To try and make nfs semantics closer to ufs semantics, a file that has * other processes using the vnode is renamed instead of removed and then * removed later on the last close. * - If v_usecount > 1 * If a rename is not already in the works * call nfs_sillyrename() to set it up * else * do the remove rpc */ static int nfs_remove(ap) struct vop_remove_args /* { struct vnodeop_desc *a_desc; struct vnode * a_dvp; struct vnode * a_vp; struct componentname * a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register struct nfsnode *np = VTONFS(vp); int error = 0; struct vattr vattr; #ifndef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("nfs_remove: no name"); if (vp->v_usecount < 1) panic("nfs_remove: bad v_usecount"); #endif if (vp->v_usecount == 1 || (np->n_sillyrename && VOP_GETATTR(vp, &vattr, cnp->cn_cred, cnp->cn_proc) == 0 && vattr.va_nlink > 1)) { /* * Purge the name cache so that the chance of a lookup for * the name succeeding while the remove is in progress is * minimized. Without node locking it can still happen, such * that an I/O op returns ESTALE, but since you get this if * another host removes the file.. */ cache_purge(vp); /* * throw away biocache buffers, mainly to avoid * unnecessary delayed writes later. */ error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1); /* Do the rpc */ if (error != EINTR) error = nfs_removerpc(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc); /* * Kludge City: If the first reply to the remove rpc is lost.. * the reply to the retransmitted request will be ENOENT * since the file was in fact removed * Therefore, we cheat and return success. */ if (error == ENOENT) error = 0; } else if (!np->n_sillyrename) error = nfs_sillyrename(dvp, vp, cnp); FREE(cnp->cn_pnbuf, M_NAMEI); np->n_attrstamp = 0; vput(dvp); if (vp == dvp) vrele(vp); else vput(vp); return (error); } /* * nfs file remove rpc called from nfs_inactive */ int nfs_removeit(sp) register struct sillyrename *sp; { return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred, (struct proc *)0)); } /* * Nfs remove rpc, called from nfs_remove() and nfs_removeit(). */ static int nfs_removerpc(dvp, name, namelen, cred, proc) register struct vnode *dvp; char *name; int namelen; struct ucred *cred; struct proc *proc; { register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_REMOVE]++; nfsm_reqhead(dvp, NFSPROC_REMOVE, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen)); nfsm_fhtom(dvp, v3); nfsm_strtom(name, namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_REMOVE, proc, cred); if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; return (error); } /* * nfs file rename call */ static int nfs_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { register struct vnode *fvp = ap->a_fvp; register struct vnode *tvp = ap->a_tvp; register struct vnode *fdvp = ap->a_fdvp; register struct vnode *tdvp = ap->a_tdvp; register struct componentname *tcnp = ap->a_tcnp; register struct componentname *fcnp = ap->a_fcnp; int error; #ifndef DIAGNOSTIC if ((tcnp->cn_flags & HASBUF) == 0 || (fcnp->cn_flags & HASBUF) == 0) panic("nfs_rename: no name"); #endif /* Check for cross-device rename */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto out; } /* * If the tvp exists and is in use, sillyrename it before doing the * rename of the new file over it. * XXX Can't sillyrename a directory. */ if (tvp && tvp->v_usecount > 1 && !VTONFS(tvp)->n_sillyrename && tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp)) { vput(tvp); tvp = NULL; } error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen, tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred, tcnp->cn_proc); if (fvp->v_type == VDIR) { if (tvp != NULL && tvp->v_type == VDIR) cache_purge(tdvp); cache_purge(fdvp); } out: if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); /* * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nfs file rename rpc called from nfs_remove() above */ static int nfs_renameit(sdvp, scnp, sp) struct vnode *sdvp; struct componentname *scnp; register struct sillyrename *sp; { return (nfs_renamerpc(sdvp, scnp->cn_nameptr, scnp->cn_namelen, sdvp, sp->s_name, sp->s_namlen, scnp->cn_cred, scnp->cn_proc)); } /* * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit(). */ static int nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc) register struct vnode *fdvp; char *fnameptr; int fnamelen; register struct vnode *tdvp; char *tnameptr; int tnamelen; struct ucred *cred; struct proc *proc; { register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, fwccflag = NFSV3_WCCRATTR, twccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(fdvp); nfsstats.rpccnt[NFSPROC_RENAME]++; nfsm_reqhead(fdvp, NFSPROC_RENAME, (NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) + nfsm_rndup(tnamelen)); nfsm_fhtom(fdvp, v3); nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN); nfsm_fhtom(tdvp, v3); nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN); nfsm_request(fdvp, NFSPROC_RENAME, proc, cred); if (v3) { nfsm_wcc_data(fdvp, fwccflag); nfsm_wcc_data(tdvp, twccflag); } nfsm_reqdone; VTONFS(fdvp)->n_flag |= NMODIFIED; VTONFS(tdvp)->n_flag |= NMODIFIED; if (!fwccflag) VTONFS(fdvp)->n_attrstamp = 0; if (!twccflag) VTONFS(tdvp)->n_attrstamp = 0; return (error); } /* * nfs hard link create call */ static int nfs_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { -#if defined(__NetBSD__) - /* - * Since the args are reversed in the VOP_LINK() calls, - * switch them back. Argh! - */ - register struct vnode *vp = ap->a_tdvp; - register struct vnode *tdvp = ap->a_vp; -#else register struct vnode *vp = ap->a_vp; register struct vnode *tdvp = ap->a_tdvp; -#endif register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR, attrflag = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(vp); if (vp->v_mount != tdvp->v_mount) { VOP_ABORTOP(vp, cnp); if (tdvp == vp) vrele(tdvp); else vput(tdvp); return (EXDEV); } /* * Push all writes to the server, so that the attribute cache * doesn't get "out of sync" with the server. * XXX There should be a better way! */ VOP_FSYNC(vp, cnp->cn_cred, MNT_WAIT, cnp->cn_proc); nfsstats.rpccnt[NFSPROC_LINK]++; nfsm_reqhead(vp, NFSPROC_LINK, NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(vp, v3); nfsm_fhtom(tdvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(vp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred); if (v3) { nfsm_postop_attr(vp, attrflag); nfsm_wcc_data(tdvp, wccflag); } nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(tdvp)->n_flag |= NMODIFIED; if (!attrflag) VTONFS(vp)->n_attrstamp = 0; if (!wccflag) VTONFS(tdvp)->n_attrstamp = 0; vput(tdvp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ if (error == EEXIST) error = 0; return (error); } /* * nfs symbolic link create call */ static int nfs_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int slen, error = 0, wccflag = NFSV3_WCCRATTR, gotvp; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vnode *newvp = (struct vnode *)0; int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_SYMLINK]++; slen = strlen(ap->a_target); nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH(v3) + 2*NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); if (v3) { nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR); nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, cnp->cn_cred->cr_gid); } nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN); if (!v3) { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); sp->sa_mode = vtonfsv2_mode(VLNK, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid); sp->sa_size = -1; txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred); if (v3) { if (!error) nfsm_mtofh(dvp, newvp, v3, gotvp); nfsm_wcc_data(dvp, wccflag); } nfsm_reqdone; if (newvp) vput(newvp); FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; vput(dvp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ if (error == EEXIST) error = 0; return (error); } /* * nfs make dir call */ static int nfs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; register int len; struct nfsnode *np = (struct nfsnode *)0; struct vnode *newvp = (struct vnode *)0; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR; int gotvp = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; int v3 = NFS_ISV3(dvp); if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } len = cnp->cn_namelen; nfsstats.rpccnt[NFSPROC_MKDIR]++; nfsm_reqhead(dvp, NFSPROC_MKDIR, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); if (v3) { nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR); nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); } else { nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); sp->sa_mode = vtonfsv2_mode(VDIR, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); sp->sa_size = -1; txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred); if (!error) nfsm_mtofh(dvp, newvp, v3, gotvp); if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; /* * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry * if we can succeed in looking up the directory. */ if (error == EEXIST || (!error && !gotvp)) { if (newvp) { vrele(newvp); newvp = (struct vnode *)0; } error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred, cnp->cn_proc, &np); if (!error) { newvp = NFSTOV(np); if (newvp->v_type != VDIR) error = EEXIST; } } if (error) { if (newvp) vrele(newvp); } else *ap->a_vpp = newvp; FREE(cnp->cn_pnbuf, M_NAMEI); vput(dvp); return (error); } /* * nfs remove directory call */ static int nfs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_RMDIR]++; nfsm_reqhead(dvp, NFSPROC_RMDIR, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred); if (v3) nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) VTONFS(dvp)->n_attrstamp = 0; cache_purge(dvp); cache_purge(vp); vput(vp); vput(dvp); /* * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nfs readdir call */ static int nfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct uio *uio = ap->a_uio; int tresid, error; struct vattr vattr; if (vp->v_type != VDIR) return (EPERM); /* * First, check for hit on the EOF offset cache */ if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset && (np->n_flag & NMODIFIED) == 0) { if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) { if (NQNFS_CKCACHABLE(vp, ND_READ)) { nfsstats.direofcache_hits++; return (0); } } else if (VOP_GETATTR(vp, &vattr, ap->a_cred, uio->uio_procp) == 0 && np->n_mtime == vattr.va_mtime.tv_sec) { nfsstats.direofcache_hits++; return (0); } } /* * Call nfs_bioread() to do the real work. */ tresid = uio->uio_resid; error = nfs_bioread(vp, uio, 0, ap->a_cred, 0); if (!error && uio->uio_resid == tresid) nfsstats.direofcache_misses++; return (error); } /* * Readdir rpc call. * Called from below the buffer cache by nfs_doio(). */ int nfs_readdirrpc(vp, uiop, cred) struct vnode *vp; register struct uio *uiop; struct ucred *cred; { register int len, left; register struct dirent *dp; register u_long *tl; register caddr_t cp; register long t1, t2; register nfsuint64 *cookiep; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsuint64 cookie; struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct nfsnode *dnp = VTONFS(vp); u_quad_t fileno; int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1; int attrflag; int v3 = NFS_ISV3(vp); #ifndef nolint dp = (struct dirent *)0; #endif #ifndef DIAGNOSTIC if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) || (uiop->uio_resid & (NFS_DIRBLKSIZ - 1))) panic("nfs readdirrpc bad uio"); #endif /* * If there is no cookie, assume directory was stale. */ cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0); if (cookiep) cookie = *cookiep; else return (NFSERR_BAD_COOKIE); /* * Loop around doing readdir rpc's of size nm_readdirsize * truncated to a multiple of DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ while (more_dirs && bigenough) { nfsstats.rpccnt[NFSPROC_READDIR]++; nfsm_reqhead(vp, NFSPROC_READDIR, NFSX_FH(v3) + NFSX_READDIR(v3)); nfsm_fhtom(vp, v3); if (v3) { nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED); *tl++ = cookie.nfsuquad[0]; *tl++ = cookie.nfsuquad[1]; *tl++ = dnp->n_cookieverf.nfsuquad[0]; *tl++ = dnp->n_cookieverf.nfsuquad[1]; } else { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = cookie.nfsuquad[0]; } *tl = txdr_unsigned(nmp->nm_readdirsize); nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred); if (v3) { nfsm_postop_attr(vp, attrflag); if (!error) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); dnp->n_cookieverf.nfsuquad[0] = *tl++; dnp->n_cookieverf.nfsuquad[1] = *tl; } else { m_freem(mrep); goto nfsmout; } } nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); /* loop thru the dir entries, doctoring them to 4bsd form */ while (more_dirs && bigenough) { if (v3) { nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); fxdr_hyper(tl, &fileno); len = fxdr_unsigned(int, *(tl + 2)); } else { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fileno = fxdr_unsigned(u_quad_t, *tl++); len = fxdr_unsigned(int, *tl); } if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } tlen = nfsm_rndup(len); if (tlen == len) tlen += 4; /* To ensure null termination */ left = DIRBLKSIZ - blksiz; if ((tlen + DIRHDSIZ) > left) { dp->d_reclen += left; uiop->uio_iov->iov_base += left; uiop->uio_iov->iov_len -= left; uiop->uio_offset += left; uiop->uio_resid -= left; blksiz = 0; } if ((tlen + DIRHDSIZ) > uiop->uio_resid) bigenough = 0; if (bigenough) { dp = (struct dirent *)uiop->uio_iov->iov_base; dp->d_fileno = (int)fileno; dp->d_namlen = len; dp->d_reclen = tlen + DIRHDSIZ; dp->d_type = DT_UNKNOWN; blksiz += dp->d_reclen; if (blksiz == DIRBLKSIZ) blksiz = 0; uiop->uio_offset += DIRHDSIZ; uiop->uio_resid -= DIRHDSIZ; uiop->uio_iov->iov_base += DIRHDSIZ; uiop->uio_iov->iov_len -= DIRHDSIZ; nfsm_mtouio(uiop, len); cp = uiop->uio_iov->iov_base; tlen -= len; *cp = '\0'; /* null terminate */ uiop->uio_iov->iov_base += tlen; uiop->uio_iov->iov_len -= tlen; uiop->uio_offset += tlen; uiop->uio_resid -= tlen; } else nfsm_adv(nfsm_rndup(len)); if (v3) { nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); } else { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); } if (bigenough) { cookie.nfsuquad[0] = *tl++; if (v3) cookie.nfsuquad[1] = *tl++; } else if (v3) tl += 2; else tl++; more_dirs = fxdr_unsigned(int, *tl); } /* * If at end of rpc data, get the eof boolean */ if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); } m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of DIRBLKSIZ * by increasing d_reclen for the last record. */ if (blksiz > 0) { left = DIRBLKSIZ - blksiz; dp->d_reclen += left; uiop->uio_iov->iov_base += left; uiop->uio_iov->iov_len -= left; uiop->uio_offset += left; uiop->uio_resid -= left; } /* * We are now either at the end of the directory or have filled the * block. */ if (bigenough) dnp->n_direofoffset = uiop->uio_offset; else { if (uiop->uio_resid > 0) printf("EEK! readdirrpc resid > 0\n"); cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1); *cookiep = cookie; } nfsmout: return (error); } /* * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc(). */ int nfs_readdirplusrpc(vp, uiop, cred) struct vnode *vp; register struct uio *uiop; struct ucred *cred; { register int len, left; register struct dirent *dp; register u_long *tl; register caddr_t cp; register long t1, t2; register struct vnode *newvp; register nfsuint64 *cookiep; caddr_t bpos, dpos, cp2, dpossav1, dpossav2; struct mbuf *mreq, *mrep, *md, *mb, *mb2, *mdsav1, *mdsav2; struct nameidata nami, *ndp = &nami; struct componentname *cnp = &ndp->ni_cnd; nfsuint64 cookie; struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct nfsnode *dnp = VTONFS(vp), *np; nfsfh_t *fhp; u_quad_t fileno; int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i; int attrflag, fhsize; #ifndef nolint dp = (struct dirent *)0; #endif #ifndef DIAGNOSTIC if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) || (uiop->uio_resid & (DIRBLKSIZ - 1))) panic("nfs readdirplusrpc bad uio"); #endif ndp->ni_dvp = vp; newvp = NULLVP; /* * If there is no cookie, assume directory was stale. */ cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0); if (cookiep) cookie = *cookiep; else return (NFSERR_BAD_COOKIE); /* * Loop around doing readdir rpc's of size nm_readdirsize * truncated to a multiple of DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ while (more_dirs && bigenough) { nfsstats.rpccnt[NFSPROC_READDIRPLUS]++; nfsm_reqhead(vp, NFSPROC_READDIRPLUS, NFSX_FH(1) + 6 * NFSX_UNSIGNED); nfsm_fhtom(vp, 1); nfsm_build(tl, u_long *, 6 * NFSX_UNSIGNED); *tl++ = cookie.nfsuquad[0]; *tl++ = cookie.nfsuquad[1]; *tl++ = dnp->n_cookieverf.nfsuquad[0]; *tl++ = dnp->n_cookieverf.nfsuquad[1]; *tl++ = txdr_unsigned(nmp->nm_readdirsize); *tl = txdr_unsigned(nmp->nm_rsize); nfsm_request(vp, NFSPROC_READDIRPLUS, uiop->uio_procp, cred); nfsm_postop_attr(vp, attrflag); if (error) { m_freem(mrep); goto nfsmout; } nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); dnp->n_cookieverf.nfsuquad[0] = *tl++; dnp->n_cookieverf.nfsuquad[1] = *tl++; more_dirs = fxdr_unsigned(int, *tl); /* loop thru the dir entries, doctoring them to 4bsd form */ while (more_dirs && bigenough) { nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); fxdr_hyper(tl, &fileno); len = fxdr_unsigned(int, *(tl + 2)); if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } tlen = nfsm_rndup(len); if (tlen == len) tlen += 4; /* To ensure null termination*/ left = DIRBLKSIZ - blksiz; if ((tlen + DIRHDSIZ) > left) { dp->d_reclen += left; uiop->uio_iov->iov_base += left; uiop->uio_iov->iov_len -= left; uiop->uio_offset += left; uiop->uio_resid -= left; blksiz = 0; } if ((tlen + DIRHDSIZ) > uiop->uio_resid) bigenough = 0; if (bigenough) { dp = (struct dirent *)uiop->uio_iov->iov_base; dp->d_fileno = (int)fileno; dp->d_namlen = len; dp->d_reclen = tlen + DIRHDSIZ; dp->d_type = DT_UNKNOWN; blksiz += dp->d_reclen; if (blksiz == DIRBLKSIZ) blksiz = 0; uiop->uio_offset += DIRHDSIZ; uiop->uio_resid -= DIRHDSIZ; uiop->uio_iov->iov_base += DIRHDSIZ; uiop->uio_iov->iov_len -= DIRHDSIZ; cnp->cn_nameptr = uiop->uio_iov->iov_base; cnp->cn_namelen = len; nfsm_mtouio(uiop, len); cp = uiop->uio_iov->iov_base; tlen -= len; *cp = '\0'; uiop->uio_iov->iov_base += tlen; uiop->uio_iov->iov_len -= tlen; uiop->uio_offset += tlen; uiop->uio_resid -= tlen; } else nfsm_adv(nfsm_rndup(len)); nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); if (bigenough) { cookie.nfsuquad[0] = *tl++; cookie.nfsuquad[1] = *tl++; } else tl += 2; /* * Since the attributes are before the file handle * (sigh), we must skip over the attributes and then * come back and get them. */ attrflag = fxdr_unsigned(int, *tl); if (attrflag) { dpossav1 = dpos; mdsav1 = md; nfsm_adv(NFSX_V3FATTR); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); doit = fxdr_unsigned(int, *tl); if (doit) { nfsm_getfh(fhp, fhsize, 1); if (NFS_CMPFH(dnp, fhp, fhsize)) { VREF(vp); newvp = vp; np = dnp; } else { if (error = nfs_nget(vp->v_mount, fhp, fhsize, &np)) doit = 0; else newvp = NFSTOV(np); } } if (doit) { dpossav2 = dpos; dpos = dpossav1; mdsav2 = md; md = mdsav1; nfsm_loadattr(newvp, (struct vattr *)0); dpos = dpossav2; md = mdsav2; dp->d_type = IFTODT(VTTOIF(np->n_vattr.va_type)); ndp->ni_vp = newvp; cnp->cn_hash = 0; for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++) cnp->cn_hash += (unsigned char)*cp * i; cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp); } } else { /* Just skip over the file handle */ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); i = fxdr_unsigned(int, *tl); nfsm_adv(nfsm_rndup(i)); } if (newvp != NULLVP) { vrele(newvp); newvp = NULLVP; } nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); } /* * If at end of rpc data, get the eof boolean */ if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); } m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ * by increasing d_reclen for the last record. */ if (blksiz > 0) { left = DIRBLKSIZ - blksiz; dp->d_reclen += left; uiop->uio_iov->iov_base += left; uiop->uio_iov->iov_len -= left; uiop->uio_offset += left; uiop->uio_resid -= left; } /* * We are now either at the end of the directory or have filled the * block. */ if (bigenough) dnp->n_direofoffset = uiop->uio_offset; else { if (uiop->uio_resid > 0) printf("EEK! readdirplusrpc resid > 0\n"); cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1); *cookiep = cookie; } nfsmout: if (newvp != NULLVP) { if (newvp == vp) vrele(newvp); else vput(newvp); newvp = NULLVP; } return (error); } /* * Silly rename. To make the NFS filesystem that is stateless look a little * more like the "ufs" a remove of an active vnode is translated to a rename * to a funny looking filename that is removed by nfs_inactive on the * nfsnode. There is the potential for another process on a different client * to create the same funny name between the nfs_lookitup() fails and the * nfs_rename() completes, but... */ static int nfs_sillyrename(dvp, vp, cnp) struct vnode *dvp, *vp; struct componentname *cnp; { register struct sillyrename *sp; struct nfsnode *np; int error; short pid; cache_purge(dvp); np = VTONFS(vp); #ifndef DIAGNOSTIC if (vp->v_type == VDIR) panic("nfs: sillyrename dir"); #endif MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename), M_NFSREQ, M_WAITOK); sp->s_cred = crdup(cnp->cn_cred); sp->s_dvp = dvp; VREF(dvp); /* Fudge together a funny name */ pid = cnp->cn_proc->p_pid; sp->s_namlen = sprintf(sp->s_name, ".nfsA%04x4.4", pid); /* Try lookitups until we get one that isn't there */ while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, cnp->cn_proc, (struct nfsnode **)0) == 0) { sp->s_name[4]++; if (sp->s_name[4] > 'z') { error = EINVAL; goto bad; } } if (error = nfs_renameit(dvp, cnp, sp)) goto bad; error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, cnp->cn_proc, &np); np->n_sillyrename = sp; return (0); bad: vrele(sp->s_dvp); crfree(sp->s_cred); free((caddr_t)sp, M_NFSREQ); return (error); } /* * Look up a file name and optionally either update the file handle or * allocate an nfsnode, depending on the value of npp. * npp == NULL --> just do the lookup * *npp == NULL --> allocate a new nfsnode and make sure attributes are * handled too * *npp != NULL --> update the file handle in the vnode */ static int nfs_lookitup(dvp, name, len, cred, procp, npp) register struct vnode *dvp; char *name; int len; struct ucred *cred; struct proc *procp; struct nfsnode **npp; { register u_long *tl; register caddr_t cp; register long t1, t2; struct vnode *newvp = (struct vnode *)0; struct nfsnode *np, *dnp = VTONFS(dvp); caddr_t bpos, dpos, cp2; int error = 0, fhlen, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsfh_t *nfhp; int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_LOOKUP]++; nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len)); nfsm_fhtom(dvp, v3); nfsm_strtom(name, len, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred); if (npp && !error) { nfsm_getfh(nfhp, fhlen, v3); if (*npp) { np = *npp; if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) { free((caddr_t)np->n_fhp, M_NFSBIGFH); np->n_fhp = &np->n_fh; } else if (np->n_fhsize <= NFS_SMALLFH && fhlen>NFS_SMALLFH) np->n_fhp =(nfsfh_t *)malloc(fhlen,M_NFSBIGFH,M_WAITOK); bcopy((caddr_t)nfhp, (caddr_t)np->n_fhp, fhlen); np->n_fhsize = fhlen; newvp = NFSTOV(np); } else if (NFS_CMPFH(dnp, nfhp, fhlen)) { VREF(dvp); newvp = dvp; } else { error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np); if (error) { m_freem(mrep); return (error); } newvp = NFSTOV(np); } if (v3) { nfsm_postop_attr(newvp, attrflag); if (!attrflag && *npp == NULL) { m_freem(mrep); if (newvp == dvp) vrele(newvp); else vput(newvp); return (ENOENT); } } else nfsm_loadattr(newvp, (struct vattr *)0); } nfsm_reqdone; if (npp && *npp == NULL) { if (error) { if (newvp) if (newvp == dvp) vrele(newvp); else vput(newvp); } else *npp = np; } return (error); } /* * Nfs Version 3 commit rpc */ static int nfs_commit(vp, offset, cnt, cred, procp) register struct vnode *vp; u_quad_t offset; int cnt; struct ucred *cred; struct proc *procp; { register caddr_t cp; register u_long *tl; register int t1, t2; register struct nfsmount *nmp = VFSTONFS(vp->v_mount); caddr_t bpos, dpos, cp2; int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0) return (0); nfsstats.rpccnt[NFSPROC_COMMIT]++; nfsm_reqhead(vp, NFSPROC_COMMIT, NFSX_FH(1)); nfsm_fhtom(vp, 1); nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); txdr_hyper(&offset, tl); tl += 2; *tl = txdr_unsigned(cnt); nfsm_request(vp, NFSPROC_COMMIT, procp, cred); nfsm_wcc_data(vp, wccflag); if (!error) { nfsm_dissect(tl, u_long *, NFSX_V3WRITEVERF); if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl, NFSX_V3WRITEVERF)) { bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF); error = NFSERR_STALEWRITEVERF; } } nfsm_reqdone; return (error); } /* * Kludge City.. * - make nfs_bmap() essentially a no-op that does no translation * - do nfs_strategy() by doing I/O with nfs_readrpc/nfs_writerpc * (Maybe I could use the process's page mapping, but I was concerned that * Kernel Write might not be enabled and also figured copyout() would do * a lot more work than bcopy() and also it currently happens in the * context of the swapper process (2). */ static int nfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { register struct vnode *vp = ap->a_vp; if (ap->a_vpp != NULL) *ap->a_vpp = vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize); if (ap->a_runp != NULL) *ap->a_runp = 0; if (ap->a_runb != NULL) *ap->a_runb = 0; return (0); } /* * Strategy routine. * For async requests when nfsiod(s) are running, queue the request by * calling nfs_asyncio(), otherwise just all nfs_doio() to do the * request. */ static int nfs_strategy(ap) struct vop_strategy_args *ap; { register struct buf *bp = ap->a_bp; struct ucred *cr; struct proc *p; int error = 0; if (bp->b_flags & B_PHYS) panic("nfs physio"); if (bp->b_flags & B_ASYNC) p = (struct proc *)0; else p = curproc; /* XXX */ if (bp->b_flags & B_READ) cr = bp->b_rcred; else cr = bp->b_wcred; /* * If the op is asynchronous and an i/o daemon is waiting * queue the request, wake it up and wait for completion * otherwise just do it ourselves. */ if ((bp->b_flags & B_ASYNC) == 0 || nfs_asyncio(bp, NOCRED)) error = nfs_doio(bp, cr, p); return (error); } /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ static int nfs_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (EINVAL); } /* * fsync vnode op. Just call nfs_flush() with commit == 1. */ /* ARGSUSED */ static int nfs_fsync(ap) struct vop_fsync_args /* { struct vnodeop_desc *a_desc; struct vnode * a_vp; struct ucred * a_cred; int a_waitfor; struct proc * a_p; } */ *ap; { return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1)); } /* * Flush all the blocks associated with a vnode. * Walk through the buffer pool and push any dirty pages * associated with the vnode. */ static int nfs_flush(vp, cred, waitfor, p, commit) register struct vnode *vp; struct ucred *cred; int waitfor; struct proc *p; int commit; { register struct nfsnode *np = VTONFS(vp); register struct buf *bp; register int i; struct buf *nbp; struct nfsmount *nmp = VFSTONFS(vp->v_mount); int s, error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos; int passone = 1; u_quad_t off, endoff, toff; struct ucred* wcred = NULL; struct buf **bvec = NULL; #ifndef NFS_COMMITBVECSIZ #define NFS_COMMITBVECSIZ 20 #endif struct buf *bvec_on_stack[NFS_COMMITBVECSIZ]; int bvecsize = 0, bveccount; if (nmp->nm_flag & NFSMNT_INT) slpflag = PCATCH; if (!commit) passone = 0; /* * A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the * server, but nas not been committed to stable storage on the server * yet. On the first pass, the byte range is worked out and the commit * rpc is done. On the second pass, nfs_writebp() is called to do the * job. */ again: off = (u_quad_t)-1; endoff = 0; bvecpos = 0; if (NFS_ISV3(vp) && commit) { s = splbio(); /* * Count up how many buffers waiting for a commit. */ bveccount = 0; for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT)) == (B_DELWRI | B_NEEDCOMMIT)) bveccount++; } /* * Allocate space to remember the list of bufs to commit. It is * important to use M_NOWAIT here to avoid a race with nfs_write. * If we can't get memory (for whatever reason), we will end up * committing the buffers one-by-one in the loop below. */ if (bveccount > NFS_COMMITBVECSIZ) { if (bvec != NULL && bvec != bvec_on_stack) free(bvec, M_TEMP); bvec = (struct buf **) malloc(bveccount * sizeof(struct buf *), M_TEMP, M_NOWAIT); if (bvec == NULL) { bvec = bvec_on_stack; bvecsize = NFS_COMMITBVECSIZ; } else bvecsize = bveccount; } else { bvec = bvec_on_stack; bvecsize = NFS_COMMITBVECSIZ; } for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if (bvecpos >= bvecsize) break; if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT)) != (B_DELWRI | B_NEEDCOMMIT)) continue; bremfree(bp); /* * Work out if all buffers are using the same cred * so we can deal with them all with one commit. */ if (wcred == NULL) wcred = bp->b_wcred; else if (wcred != bp->b_wcred) wcred = NOCRED; bp->b_flags |= (B_BUSY | B_WRITEINPROG); vfs_busy_pages(bp, 1); /* * A list of these buffers is kept so that the * second loop knows which buffers have actually * been committed. This is necessary, since there * may be a race between the commit rpc and new * uncommitted writes on the file. */ bvec[bvecpos++] = bp; toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; if (toff < off) off = toff; toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff); if (toff > endoff) endoff = toff; } splx(s); } if (bvecpos > 0) { /* * Commit data on the server, as required. * If all bufs are using the same wcred, then use that with * one call for all of them, otherwise commit each one * separately. */ if (wcred != NOCRED) retv = nfs_commit(vp, off, (int)(endoff - off), wcred, p); else { retv = 0; for (i = 0; i < bvecpos; i++) { off_t off, size; bp = bvec[i]; off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; size = (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff); retv = nfs_commit(vp, off, (int)size, bp->b_wcred, p); if (retv) break; } } if (retv == NFSERR_STALEWRITEVERF) nfs_clearcommit(vp->v_mount); /* * Now, either mark the blocks I/O done or mark the * blocks dirty, depending on whether the commit * succeeded. */ for (i = 0; i < bvecpos; i++) { bp = bvec[i]; bp->b_flags &= ~(B_NEEDCOMMIT | B_WRITEINPROG); if (retv) { vfs_unbusy_pages(bp); brelse(bp); } else { vp->v_numoutput++; bp->b_flags |= B_ASYNC; if (bp->b_flags & B_DELWRI) { --numdirtybuffers; if (needsbuffer) { vfs_bio_need_satisfy(); } } bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI); bp->b_dirtyoff = bp->b_dirtyend = 0; reassignbuf(bp, vp); biodone(bp); } } } /* * Start/do any write(s) that are required. */ loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if (bp->b_flags & B_BUSY) { if (waitfor != MNT_WAIT || passone) continue; bp->b_flags |= B_WANTED; error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); splx(s); if (error) { if (nfs_sigintr(nmp, (struct nfsreq *)0, p)) { error = EINTR; goto done; } if (slpflag == PCATCH) { slpflag = 0; slptimeo = 2 * hz; } } goto loop; } if ((bp->b_flags & B_DELWRI) == 0) panic("nfs_fsync: not dirty"); if ((passone || !commit) && (bp->b_flags & B_NEEDCOMMIT)) continue; bremfree(bp); if (passone || !commit) bp->b_flags |= (B_BUSY|B_ASYNC); else bp->b_flags |= (B_BUSY|B_ASYNC|B_WRITEINPROG|B_NEEDCOMMIT); splx(s); VOP_BWRITE(bp); goto loop; } splx(s); if (passone) { passone = 0; goto again; } if (waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; error = tsleep((caddr_t)&vp->v_numoutput, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); if (error) { if (nfs_sigintr(nmp, (struct nfsreq *)0, p)) { error = EINTR; goto done; } if (slpflag == PCATCH) { slpflag = 0; slptimeo = 2 * hz; } } } if (vp->v_dirtyblkhd.lh_first && commit) { goto loop; } } if (np->n_flag & NWRITEERR) { error = np->n_error; np->n_flag &= ~NWRITEERR; } done: if (bvec != NULL && bvec != bvec_on_stack) free(bvec, M_TEMP); return (error); } /* * Return POSIX pathconf information applicable to nfs. * * The NFS V2 protocol doesn't support this, so just return EINVAL * for V2. */ /* ARGSUSED */ static int nfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { return (EINVAL); } /* * NFS advisory byte-level locks. * Currently unsupported. */ static int nfs_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); /* * The following kludge is to allow diskless support to work * until a real NFS lockd is implemented. Basically, just pretend * that this is a local lock. */ return (lf_advlock(ap, &(np->n_lockf), np->n_size)); } /* * Print out the contents of an nfsnode. */ static int nfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); printf("tag VT_NFS, fileid %ld fsid 0x%lx", np->n_vattr.va_fileid, np->n_vattr.va_fsid); if (vp->v_type == VFIFO) fifo_printinfo(vp); printf("\n"); return (0); } /* * NFS directory offset lookup. * Currently unsupported. */ static int nfs_blkatoff(ap) struct vop_blkatoff_args /* { struct vnode *a_vp; off_t a_offset; char **a_res; struct buf **a_bpp; } */ *ap; { return (EOPNOTSUPP); } /* * NFS flat namespace allocation. * Currently unsupported. */ static int nfs_valloc(ap) struct vop_valloc_args /* { struct vnode *a_pvp; int a_mode; struct ucred *a_cred; struct vnode **a_vpp; } */ *ap; { return (EOPNOTSUPP); } /* * NFS flat namespace free. * Currently unsupported. */ static int nfs_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (EOPNOTSUPP); } /* * NFS file truncation. */ static int nfs_truncate(ap) struct vop_truncate_args /* { struct vnode *a_vp; off_t a_length; int a_flags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* Use nfs_setattr */ printf("nfs_truncate: need to implement!!"); return (EOPNOTSUPP); } /* * NFS update. */ static int nfs_update(ap) struct vop_update_args /* { struct vnode *a_vp; struct timeval *a_ta; struct timeval *a_tm; int a_waitfor; } */ *ap; { #if 0 /* Use nfs_setattr */ printf("nfs_update: need to implement!!"); #endif return (EOPNOTSUPP); } /* * Just call nfs_writebp() with the force argument set to 1. */ static int nfs_bwrite(ap) struct vop_bwrite_args /* { struct vnode *a_bp; } */ *ap; { return (nfs_writebp(ap->a_bp, 1)); } /* * This is a clone of vn_bwrite(), except that B_WRITEINPROG isn't set unless * the force flag is one and it also handles the B_NEEDCOMMIT flag. */ int nfs_writebp(bp, force) register struct buf *bp; int force; { register int oldflags = bp->b_flags, retv = 1; off_t off; if(!(bp->b_flags & B_BUSY)) panic("bwrite: buffer is not busy???"); if (bp->b_flags & B_DELWRI) { --numdirtybuffers; if (needsbuffer) vfs_bio_need_satisfy(); } bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI); if ((oldflags & (B_ASYNC|B_DELWRI)) == (B_ASYNC|B_DELWRI)) { reassignbuf(bp, bp->b_vp); } bp->b_vp->v_numoutput++; curproc->p_stats->p_ru.ru_oublock++; /* * If B_NEEDCOMMIT is set, a commit rpc may do the trick. If not * an actual write will have to be scheduled via. VOP_STRATEGY(). * If B_WRITEINPROG is already set, then push it with a write anyhow. */ vfs_busy_pages(bp, 1); if ((oldflags & (B_NEEDCOMMIT | B_WRITEINPROG)) == B_NEEDCOMMIT) { off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; bp->b_flags |= B_WRITEINPROG; retv = nfs_commit(bp->b_vp, off, bp->b_dirtyend-bp->b_dirtyoff, bp->b_wcred, bp->b_proc); bp->b_flags &= ~B_WRITEINPROG; if (!retv) { bp->b_dirtyoff = bp->b_dirtyend = 0; bp->b_flags &= ~B_NEEDCOMMIT; biodone(bp); } else if (retv == NFSERR_STALEWRITEVERF) nfs_clearcommit(bp->b_vp->v_mount); } if (retv) { if (force) bp->b_flags |= B_WRITEINPROG; VOP_STRATEGY(bp); } if( (oldflags & B_ASYNC) == 0) { int rtval = biowait(bp); if (oldflags & B_DELWRI) { reassignbuf(bp, bp->b_vp); } brelse(bp); return (rtval); } return (0); } /* * nfs special file access vnode op. * Essentially just get vattr and then imitate iaccess() since the device is * local to the client. */ static int nfsspec_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vattr *vap; register gid_t *gp; register struct ucred *cred = ap->a_cred; struct vnode *vp = ap->a_vp; mode_t mode = ap->a_mode; struct vattr vattr; register int i; int error; /* * Disallow write attempts on filesystems mounted read-only; * unless the file is a socket, fifo, or a block or character * device resident on the filesystem. */ if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); } } /* * If you're the super-user, * you always get access. */ if (cred->cr_uid == 0) return (0); vap = &vattr; error = VOP_GETATTR(vp, vap, cred, ap->a_p); if (error) return (error); /* * Access check is based on only one of owner, group, public. * If not owner, then check group. If not a member of the * group, then check public access. */ if (cred->cr_uid != vap->va_uid) { mode >>= 3; gp = cred->cr_groups; for (i = 0; i < cred->cr_ngroups; i++, gp++) if (vap->va_gid == *gp) goto found; mode >>= 3; found: ; } error = (vap->va_mode & mode) == mode ? 0 : EACCES; return (error); } /* * Read wrapper for special devices. */ static int nfsspec_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); struct timeval tv; /* * Set access flag. */ np->n_flag |= NACC; gettime(&tv); np->n_atim.tv_sec = tv.tv_sec; np->n_atim.tv_nsec = tv.tv_usec * 1000; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for special devices. */ static int nfsspec_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); struct timeval tv; /* * Set update flag. */ np->n_flag |= NUPD; gettime(&tv); np->n_mtim.tv_sec = tv.tv_sec; np->n_mtim.tv_nsec = tv.tv_usec * 1000; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for special devices. * * Update the times on the nfsnode then do device close. */ static int nfsspec_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); struct vattr vattr; if (np->n_flag & (NACC | NUPD)) { np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); if (np->n_flag & NACC) vattr.va_atime = np->n_atim; if (np->n_flag & NUPD) vattr.va_mtime = np->n_mtim; (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap)); } /* * Read wrapper for fifos. */ static int nfsfifo_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); struct timeval tv; /* * Set access flag. */ np->n_flag |= NACC; gettime(&tv); np->n_atim.tv_sec = tv.tv_sec; np->n_atim.tv_nsec = tv.tv_usec * 1000; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for fifos. */ static int nfsfifo_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); struct timeval tv; /* * Set update flag. */ np->n_flag |= NUPD; gettime(&tv); np->n_mtim.tv_sec = tv.tv_sec; np->n_mtim.tv_nsec = tv.tv_usec * 1000; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for fifos. * * Update the times on the nfsnode then do fifo close. */ static int nfsfifo_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); struct timeval tv; struct vattr vattr; if (np->n_flag & (NACC | NUPD)) { gettime(&tv); if (np->n_flag & NACC) { np->n_atim.tv_sec = tv.tv_sec; np->n_atim.tv_nsec = tv.tv_usec * 1000; } if (np->n_flag & NUPD) { np->n_mtim.tv_sec = tv.tv_sec; np->n_mtim.tv_nsec = tv.tv_usec * 1000; } np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); if (np->n_flag & NACC) vattr.va_atime = np->n_atim; if (np->n_flag & NUPD) vattr.va_mtime = np->n_mtim; (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap)); } static int nfs_ioctl(ap) struct vop_ioctl_args *ap; { /* * XXX we were once bogusly enoictl() which returned this (ENOTTY). * Probably we should return ENODEV. */ return (ENOTTY); } static int nfs_select(ap) struct vop_select_args *ap; { /* * We were once bogusly seltrue() which returns 1. Is this right? */ return (1); } Index: head/sys/nfsserver/nfs_serv.c =================================================================== --- head/sys/nfsserver/nfs_serv.c (revision 29290) +++ head/sys/nfsserver/nfs_serv.c (revision 29291) @@ -1,3464 +1,3460 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)nfs_serv.c 8.3 (Berkeley) 1/12/94 - * $Id: nfs_serv.c,v 1.48 1997/09/02 01:19:33 bde Exp $ + * $Id: nfs_serv.c,v 1.49 1997/09/10 19:52:25 phk Exp $ */ /* * nfs version 2 and 3 server calls to vnode ops * - these routines generally have 3 phases * 1 - break down and validate rpc request in mbuf list * 2 - do the vnode ops for the request * (surprisingly ?? many are very similar to syscalls in vfs_syscalls.c) * 3 - build the rpc reply in an mbuf list * nb: * - do not mix the phases, since the nfsm_?? macros can return failures * 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 * 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. * For Version 3, nfsm_reply() does not return for the error case, since * most version 3 rpcs return more than the status for error cases. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON }; #ifndef NFS_NOSERVER nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON, NFCHR, NFNON }; /* Global vars */ extern u_long nfs_xdrneg1; extern u_long nfs_false, nfs_true; extern enum vtype nv3tov_type[8]; extern struct nfsstats nfsstats; int nfsrvw_procrastinate = NFS_GATHERDELAY * 1000; int nfsrvw_procrastinate_v3 = 0; int nfs_async; SYSCTL_INT(_vfs_nfs, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0, ""); static int nfsrv_access __P((struct vnode *,int,struct ucred *,int, struct proc *)); static void nfsrvw_coalesce __P((struct nfsrv_descript *, struct nfsrv_descript *)); /* * nfs v3 access service */ int nfsrv3_access(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, getret; char *cp2; struct mbuf *mb, *mreq, *mb2; struct vattr vattr, *vap = &vattr; u_long testmode, nfsmode; u_quad_t frev; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(1, (struct vattr *)0); return (0); } nfsmode = fxdr_unsigned(u_long, *tl); if ((nfsmode & NFSV3ACCESS_READ) && nfsrv_access(vp, VREAD, cred, rdonly, procp)) nfsmode &= ~NFSV3ACCESS_READ; if (vp->v_type == VDIR) testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND | NFSV3ACCESS_DELETE); else testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND); if ((nfsmode & testmode) && nfsrv_access(vp, VWRITE, cred, rdonly, procp)) nfsmode &= ~testmode; if (vp->v_type == VDIR) testmode = NFSV3ACCESS_LOOKUP; else testmode = NFSV3ACCESS_EXECUTE; if ((nfsmode & testmode) && nfsrv_access(vp, VEXEC, cred, rdonly, procp)) nfsmode &= ~testmode; getret = VOP_GETATTR(vp, vap, cred, procp); vput(vp); nfsm_reply(NFSX_POSTOPATTR(1) + NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, vap); nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(nfsmode); nfsm_srvdone; } /* * nfs getattr service */ int nfsrv_getattr(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct nfs_fattr *fp; struct vattr va; register struct vattr *vap = &va; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache; char *cp2; struct mbuf *mb, *mb2, *mreq; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(0); return (0); } nqsrv_getl(vp, ND_READ); error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); nfsm_reply(NFSX_FATTR(nfsd->nd_flag & ND_NFSV3)); if (error) return (0); nfsm_build(fp, struct nfs_fattr *, NFSX_FATTR(nfsd->nd_flag & ND_NFSV3)); nfsm_srvfillattr(vap, fp); nfsm_srvdone; } /* * nfs setattr service */ int nfsrv_setattr(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr va, preat; register struct vattr *vap = &va; register struct nfsv2_sattr *sp; register struct nfs_fattr *fp; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, preat_ret = 1, postat_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3), gcheck = 0; char *cp2; struct mbuf *mb, *mb2, *mreq; u_quad_t frev; struct timespec guard; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); VATTR_NULL(vap); if (v3) { nfsm_srvsattr(vap); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); gcheck = fxdr_unsigned(int, *tl); if (gcheck) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fxdr_nfsv3time(tl, &guard); } } else { nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); /* * Nah nah nah nah na nah * There is a bug in the Sun client that puts 0xffff in the mode * field of sattr when it should put in 0xffffffff. The u_short * doesn't sign extend. * --> check the low order 2 bytes for 0xffff */ if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff) vap->va_mode = nfstov_mode(sp->sa_mode); if (sp->sa_uid != nfs_xdrneg1) vap->va_uid = fxdr_unsigned(uid_t, sp->sa_uid); if (sp->sa_gid != nfs_xdrneg1) vap->va_gid = fxdr_unsigned(gid_t, sp->sa_gid); if (sp->sa_size != nfs_xdrneg1) vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_size); if (sp->sa_atime.nfsv2_sec != nfs_xdrneg1) { #ifdef notyet fxdr_nfsv2time(&sp->sa_atime, &vap->va_atime); #else vap->va_atime.tv_sec = fxdr_unsigned(long, sp->sa_atime.nfsv2_sec); vap->va_atime.tv_nsec = 0; #endif } if (sp->sa_mtime.nfsv2_sec != nfs_xdrneg1) fxdr_nfsv2time(&sp->sa_mtime, &vap->va_mtime); } /* * Now that we have all the fields, lets do it. */ if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); return (0); } nqsrv_getl(vp, ND_WRITE); if (v3) { error = preat_ret = VOP_GETATTR(vp, &preat, cred, procp); if (!error && gcheck && (preat.va_ctime.tv_sec != guard.tv_sec || preat.va_ctime.tv_nsec != guard.tv_nsec)) error = NFSERR_NOT_SYNC; if (error) { vput(vp); nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); return (0); } } /* * If the size is being changed write acces is required, otherwise * just check for a read only file system. */ if (vap->va_size == ((u_quad_t)((quad_t) -1))) { if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) { error = EROFS; goto out; } } else { if (vp->v_type == VDIR) { error = EISDIR; goto out; } else if (error = nfsrv_access(vp, VWRITE, cred, rdonly, procp)) goto out; } error = VOP_SETATTR(vp, vap, cred, procp); postat_ret = VOP_GETATTR(vp, vap, cred, procp); if (!error) error = postat_ret; out: vput(vp); nfsm_reply(NFSX_WCCORFATTR(v3)); if (v3) { nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); return (0); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } nfsm_srvdone; } /* * nfs lookup rpc */ int nfsrv_lookup(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct nfs_fattr *fp; struct nameidata nd, ind, *ndp = &nd; struct vnode *vp, *dirp; nfsfh_t nfh; fhandle_t *fhp; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, dirattr_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3), pubflag; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vattr va, dirattr, *vap = &va; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); pubflag = nfs_ispublicfh(fhp); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), pubflag); if (!error && pubflag) { if (nd.ni_vp->v_type == VDIR && nfs_pub.np_index != NULL) { /* * Setup call to lookup() to see if we can find * the index file. Arguably, this doesn't belong * in a kernel.. Ugh. */ ind = nd; VOP_UNLOCK(nd.ni_vp, 0, procp); ind.ni_pathlen = strlen(nfs_pub.np_index); ind.ni_cnd.cn_nameptr = ind.ni_cnd.cn_pnbuf = nfs_pub.np_index; ind.ni_startdir = nd.ni_vp; VREF(ind.ni_startdir); error = lookup(&ind); if (!error) { /* * Found an index file. Get rid of * the old references. */ if (dirp) vrele(dirp); dirp = nd.ni_vp; vrele(nd.ni_startdir); ndp = &ind; } else error = 0; } /* * If the public filehandle was used, check that this lookup * didn't result in a filehandle outside the publicly exported * filesystem. */ if (!error && ndp->ni_vp->v_mount != nfs_pub.np_mount) { vput(nd.ni_vp); error = EPERM; } } if (dirp) { if (v3) dirattr_ret = VOP_GETATTR(dirp, &dirattr, cred, procp); vrele(dirp); } if (error) { nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(dirattr_ret, &dirattr); return (0); } nqsrv_getl(ndp->ni_startdir, ND_READ); vrele(ndp->ni_startdir); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); vp = ndp->ni_vp; bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPORFATTR(v3) + NFSX_POSTOPATTR(v3)); if (error) { nfsm_srvpostop_attr(dirattr_ret, &dirattr); return (0); } nfsm_srvfhtom(fhp, v3); if (v3) { nfsm_srvpostop_attr(0, vap); nfsm_srvpostop_attr(dirattr_ret, &dirattr); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } nfsm_srvdone; } /* * nfs readlink service */ int nfsrv_readlink(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN]; register struct iovec *ivp = iv; register struct mbuf *mp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, i, tlen, len, getret; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mp2, *mp3, *mreq; struct vnode *vp; struct vattr attr; nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; u_quad_t frev; #ifndef nolint mp2 = mp3 = (struct mbuf *)0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); len = 0; i = 0; while (len < NFS_MAXPATHLEN) { MGET(mp, M_WAIT, MT_DATA); MCLGET(mp, M_WAIT); mp->m_len = NFSMSIZ(mp); if (len == 0) mp3 = mp2 = mp; else { mp2->m_next = mp; mp2 = mp; } if ((len+mp->m_len) > NFS_MAXPATHLEN) { mp->m_len = NFS_MAXPATHLEN-len; len = NFS_MAXPATHLEN; } else len += mp->m_len; ivp->iov_base = mtod(mp, caddr_t); ivp->iov_len = mp->m_len; i++; ivp++; } uiop->uio_iov = iv; uiop->uio_iovcnt = i; uiop->uio_offset = 0; uiop->uio_resid = len; uiop->uio_rw = UIO_READ; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_procp = (struct proc *)0; if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { m_freem(mp3); nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvpostop_attr(1, (struct vattr *)0); return (0); } if (vp->v_type != VLNK) { if (v3) error = EINVAL; else error = ENXIO; goto out; } nqsrv_getl(vp, ND_READ); error = VOP_READLINK(vp, uiop, cred); out: getret = VOP_GETATTR(vp, &attr, cred, procp); vput(vp); if (error) m_freem(mp3); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_UNSIGNED); if (v3) { nfsm_srvpostop_attr(getret, &attr); if (error) return (0); } if (uiop->uio_resid > 0) { len -= uiop->uio_resid; tlen = nfsm_rndup(len); nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len); } nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = txdr_unsigned(len); mb->m_next = mp3; nfsm_srvdone; } /* * nfs read service */ int nfsrv_read(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct iovec *iv; struct iovec *iv2; register struct mbuf *m; register struct nfs_fattr *fp; register u_long *tl; register long t1; register int i; caddr_t bpos; int error = 0, rdonly, cache, cnt, len, left, siz, tlen, getret; int v3 = (nfsd->nd_flag & ND_NFSV3), reqlen; char *cp2; struct mbuf *mb, *mb2, *mreq; struct mbuf *m2; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; struct vattr va, *vap = &va; off_t off; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (v3) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fxdr_hyper(tl, &off); } else { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); off = (off_t)fxdr_unsigned(u_long, *tl); } nfsm_srvstrsiz(reqlen, NFS_SRVMAXDATA(nfsd)); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvpostop_attr(1, (struct vattr *)0); return (0); } if (vp->v_type != VREG) { if (v3) error = EINVAL; else error = (vp->v_type == VDIR) ? EISDIR : EACCES; } if (!error) { nqsrv_getl(vp, ND_READ); if (error = nfsrv_access(vp, VREAD, cred, rdonly, procp)) error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); } getret = VOP_GETATTR(vp, vap, cred, procp); if (!error) error = getret; if (error) { vput(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, vap); return (0); } if (off >= vap->va_size) cnt = 0; else if ((off + reqlen) > vap->va_size) cnt = nfsm_rndup(vap->va_size - off); else cnt = reqlen; nfsm_reply(NFSX_POSTOPORFATTR(v3) + 3 * NFSX_UNSIGNED+nfsm_rndup(cnt)); if (v3) { nfsm_build(tl, u_long *, NFSX_V3FATTR + 4 * NFSX_UNSIGNED); *tl++ = nfs_true; fp = (struct nfs_fattr *)tl; tl += (NFSX_V3FATTR / sizeof (u_long)); } else { nfsm_build(tl, u_long *, NFSX_V2FATTR + NFSX_UNSIGNED); fp = (struct nfs_fattr *)tl; tl += (NFSX_V2FATTR / sizeof (u_long)); } len = left = cnt; if (cnt > 0) { /* * Generate the mbuf list with the uio_iov ref. to it. */ i = 0; m = m2 = mb; while (left > 0) { siz = min(M_TRAILINGSPACE(m), left); if (siz > 0) { left -= siz; i++; } if (left > 0) { MGET(m, M_WAIT, MT_DATA); MCLGET(m, M_WAIT); m->m_len = 0; m2->m_next = m; m2 = m; } } MALLOC(iv, struct iovec *, i * sizeof (struct iovec), M_TEMP, M_WAITOK); uiop->uio_iov = iv2 = iv; m = mb; left = cnt; i = 0; while (left > 0) { if (m == NULL) panic("nfsrv_read iov"); siz = min(M_TRAILINGSPACE(m), left); if (siz > 0) { iv->iov_base = mtod(m, caddr_t) + m->m_len; iv->iov_len = siz; m->m_len += siz; left -= siz; iv++; i++; } m = m->m_next; } uiop->uio_iovcnt = i; uiop->uio_offset = off; uiop->uio_resid = cnt; uiop->uio_rw = UIO_READ; uiop->uio_segflg = UIO_SYSSPACE; error = VOP_READ(vp, uiop, IO_NODELOCKED, cred); off = uiop->uio_offset; FREE((caddr_t)iv2, M_TEMP); if (error || (getret = VOP_GETATTR(vp, vap, cred, procp))) { if (!error) error = getret; m_freem(mreq); vput(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, vap); return (0); } } else uiop->uio_resid = 0; vput(vp); nfsm_srvfillattr(vap, fp); len -= uiop->uio_resid; tlen = nfsm_rndup(len); if (cnt != tlen || tlen != len) nfsm_adj(mb, cnt - tlen, tlen - len); if (v3) { *tl++ = txdr_unsigned(len); if (len < reqlen) *tl++ = nfs_true; else *tl++ = nfs_false; } *tl = txdr_unsigned(len); nfsm_srvdone; } /* * nfs write service */ int nfsrv_write(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct iovec *ivp; register int i, cnt; register struct mbuf *mp; register struct nfs_fattr *fp; struct iovec *iv; struct vattr va, forat; register struct vattr *vap = &va; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, len, forat_ret = 1; int ioflags, aftat_ret = 1, retlen, zeroing, adjust; int stable = NFSV3WRITE_FILESYNC; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; off_t off; u_quad_t frev; if (mrep == NULL) { *mrq = NULL; return (0); } fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &off); tl += 3; stable = fxdr_unsigned(int, *tl++); } else { nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); off = (off_t)fxdr_unsigned(u_long, *++tl); tl += 2; if (nfs_async) stable = NFSV3WRITE_UNSTABLE; } retlen = len = fxdr_unsigned(long, *tl); cnt = i = 0; /* * For NFS Version 2, it is not obvious what a write of zero length * should do, but I might as well be consistent with Version 3, * which is to return ok so long as there are no permission problems. */ if (len > 0) { zeroing = 1; mp = mrep; while (mp) { if (mp == md) { zeroing = 0; adjust = dpos - mtod(mp, caddr_t); mp->m_len -= adjust; if (mp->m_len > 0 && adjust > 0) NFSMADV(mp, adjust); } if (zeroing) mp->m_len = 0; else if (mp->m_len > 0) { i += mp->m_len; if (i > len) { mp->m_len -= (i - len); zeroing = 1; } if (mp->m_len > 0) cnt++; } mp = mp->m_next; } } if (len > NFS_MAXDATA || len < 0 || i < len) { error = EIO; nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); return (0); } if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); return (0); } if (v3) forat_ret = VOP_GETATTR(vp, &forat, cred, procp); if (vp->v_type != VREG) { if (v3) error = EINVAL; else error = (vp->v_type == VDIR) ? EISDIR : EACCES; } if (!error) { nqsrv_getl(vp, ND_WRITE); error = nfsrv_access(vp, VWRITE, cred, rdonly, procp); } if (error) { vput(vp); nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); return (0); } if (len > 0) { MALLOC(ivp, struct iovec *, cnt * sizeof (struct iovec), M_TEMP, M_WAITOK); uiop->uio_iov = iv = ivp; uiop->uio_iovcnt = cnt; mp = mrep; while (mp) { if (mp->m_len > 0) { ivp->iov_base = mtod(mp, caddr_t); ivp->iov_len = mp->m_len; ivp++; } mp = mp->m_next; } /* * XXX * The IO_METASYNC flag indicates that all metadata (and not just * enough to ensure data integrity) mus be written to stable storage * synchronously. * (IO_METASYNC is not yet implemented in 4.4BSD-Lite.) */ if (stable == NFSV3WRITE_UNSTABLE) ioflags = IO_NODELOCKED; else if (stable == NFSV3WRITE_DATASYNC) ioflags = (IO_SYNC | IO_NODELOCKED); else ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); uiop->uio_resid = len; uiop->uio_rw = UIO_WRITE; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_procp = (struct proc *)0; uiop->uio_offset = off; error = VOP_WRITE(vp, uiop, ioflags, cred); nfsstats.srvvop_writes++; FREE((caddr_t)iv, M_TEMP); } aftat_ret = VOP_GETATTR(vp, vap, cred, procp); vput(vp); if (!error) error = aftat_ret; nfsm_reply(NFSX_PREOPATTR(v3) + NFSX_POSTOPORFATTR(v3) + 2 * NFSX_UNSIGNED + NFSX_WRITEVERF(v3)); if (v3) { nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); if (error) return (0); nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(retlen); /* * If nfs_async is set, then pretend the write was FILESYNC. */ if (stable == NFSV3WRITE_UNSTABLE && !nfs_async) *tl++ = txdr_unsigned(stable); else *tl++ = txdr_unsigned(NFSV3WRITE_FILESYNC); /* * Actually, there is no need to txdr these fields, * but it may make the values more human readable, * for debugging purposes. */ *tl++ = txdr_unsigned(boottime.tv_sec); *tl = txdr_unsigned(boottime.tv_usec); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } nfsm_srvdone; } /* * NFS write service with write gathering support. Called when * nfsrvw_procrastinate > 0. * See: Chet Juszczak, "Improving the Write Performance of an NFS Server", * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco, * Jan. 1994. */ int nfsrv_writegather(ndp, slp, procp, mrq) struct nfsrv_descript **ndp; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { register struct iovec *ivp; register struct mbuf *mp; register struct nfsrv_descript *wp, *nfsd, *owp, *swp; register struct nfs_fattr *fp; register int i; struct iovec *iov; struct nfsrvw_delayhash *wpp; struct ucred *cred; struct vattr va, forat; register u_long *tl; register long t1; caddr_t bpos, dpos; int error = 0, rdonly, cache, len, forat_ret = 1; int ioflags, aftat_ret = 1, s, adjust, v3, zeroing; char *cp2; struct mbuf *mb, *mb2, *mreq, *mrep, *md; struct vnode *vp; struct uio io, *uiop = &io; u_quad_t frev, cur_usec; #ifndef nolint i = 0; len = 0; #endif *mrq = NULL; if (*ndp) { nfsd = *ndp; *ndp = NULL; mrep = nfsd->nd_mrep; md = nfsd->nd_md; dpos = nfsd->nd_dpos; cred = &nfsd->nd_cr; v3 = (nfsd->nd_flag & ND_NFSV3); LIST_INIT(&nfsd->nd_coalesce); nfsd->nd_mreq = NULL; nfsd->nd_stable = NFSV3WRITE_FILESYNC; cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec; nfsd->nd_time = cur_usec + (v3 ? nfsrvw_procrastinate_v3 : nfsrvw_procrastinate); /* * Now, get the write header.. */ nfsm_srvmtofh(&nfsd->nd_fh); if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &nfsd->nd_off); tl += 3; nfsd->nd_stable = fxdr_unsigned(int, *tl++); } else { nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); nfsd->nd_off = (off_t)fxdr_unsigned(u_long, *++tl); tl += 2; if (nfs_async) nfsd->nd_stable = NFSV3WRITE_UNSTABLE; } len = fxdr_unsigned(long, *tl); nfsd->nd_len = len; nfsd->nd_eoff = nfsd->nd_off + len; /* * Trim the header out of the mbuf list and trim off any trailing * junk so that the mbuf list has only the write data. */ zeroing = 1; i = 0; mp = mrep; while (mp) { if (mp == md) { zeroing = 0; adjust = dpos - mtod(mp, caddr_t); mp->m_len -= adjust; if (mp->m_len > 0 && adjust > 0) NFSMADV(mp, adjust); } if (zeroing) mp->m_len = 0; else { i += mp->m_len; if (i > len) { mp->m_len -= (i - len); zeroing = 1; } } mp = mp->m_next; } if (len > NFS_MAXDATA || len < 0 || i < len) { nfsmout: m_freem(mrep); error = EIO; nfsm_writereply(2 * NFSX_UNSIGNED, v3); if (v3) nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); nfsd->nd_mreq = mreq; nfsd->nd_mrep = NULL; nfsd->nd_time = 0; } /* * Add this entry to the hash and time queues. */ s = splsoftclock(); owp = NULL; wp = slp->ns_tq.lh_first; while (wp && wp->nd_time < nfsd->nd_time) { owp = wp; wp = wp->nd_tq.le_next; } NFS_DPF(WG, ("Q%03x", nfsd->nd_retxid & 0xfff)); if (owp) { LIST_INSERT_AFTER(owp, nfsd, nd_tq); } else { LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq); } if (nfsd->nd_mrep) { wpp = NWDELAYHASH(slp, nfsd->nd_fh.fh_fid.fid_data); owp = NULL; wp = wpp->lh_first; while (wp && bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) { owp = wp; wp = wp->nd_hash.le_next; } while (wp && wp->nd_off < nfsd->nd_off && !bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) { owp = wp; wp = wp->nd_hash.le_next; } if (owp) { LIST_INSERT_AFTER(owp, nfsd, nd_hash); /* * Search the hash list for overlapping entries and * coalesce. */ for(; nfsd && NFSW_CONTIG(owp, nfsd); nfsd = wp) { wp = nfsd->nd_hash.le_next; if (NFSW_SAMECRED(owp, nfsd)) nfsrvw_coalesce(owp, nfsd); } } else { LIST_INSERT_HEAD(wpp, nfsd, nd_hash); } } splx(s); } /* * Now, do VOP_WRITE()s for any one(s) that need to be done now * and generate the associated reply mbuf list(s). */ loop1: cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec; s = splsoftclock(); for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = owp) { owp = nfsd->nd_tq.le_next; if (nfsd->nd_time > cur_usec) break; if (nfsd->nd_mreq) continue; NFS_DPF(WG, ("P%03x", nfsd->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_tq); LIST_REMOVE(nfsd, nd_hash); splx(s); mrep = nfsd->nd_mrep; nfsd->nd_mrep = NULL; cred = &nfsd->nd_cr; v3 = (nfsd->nd_flag & ND_NFSV3); forat_ret = aftat_ret = 1; error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, cred, slp, nfsd->nd_nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE); if (!error) { if (v3) forat_ret = VOP_GETATTR(vp, &forat, cred, procp); if (vp->v_type != VREG) { if (v3) error = EINVAL; else error = (vp->v_type == VDIR) ? EISDIR : EACCES; } } else vp = NULL; if (!error) { nqsrv_getl(vp, ND_WRITE); error = nfsrv_access(vp, VWRITE, cred, rdonly, procp); } if (nfsd->nd_stable == NFSV3WRITE_UNSTABLE) ioflags = IO_NODELOCKED; else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC) ioflags = (IO_SYNC | IO_NODELOCKED); else ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); uiop->uio_rw = UIO_WRITE; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_procp = (struct proc *)0; uiop->uio_offset = nfsd->nd_off; uiop->uio_resid = nfsd->nd_eoff - nfsd->nd_off; if (uiop->uio_resid > 0) { mp = mrep; i = 0; while (mp) { if (mp->m_len > 0) i++; mp = mp->m_next; } uiop->uio_iovcnt = i; MALLOC(iov, struct iovec *, i * sizeof (struct iovec), M_TEMP, M_WAITOK); uiop->uio_iov = ivp = iov; mp = mrep; while (mp) { if (mp->m_len > 0) { ivp->iov_base = mtod(mp, caddr_t); ivp->iov_len = mp->m_len; ivp++; } mp = mp->m_next; } if (!error) { error = VOP_WRITE(vp, uiop, ioflags, cred); nfsstats.srvvop_writes++; } FREE((caddr_t)iov, M_TEMP); } m_freem(mrep); if (vp) { aftat_ret = VOP_GETATTR(vp, &va, cred, procp); vput(vp); } /* * Loop around generating replies for all write rpcs that have * now been completed. */ swp = nfsd; do { NFS_DPF(WG, ("R%03x", nfsd->nd_retxid & 0xfff)); if (error) { nfsm_writereply(NFSX_WCCDATA(v3), v3); if (v3) { nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); } } else { nfsm_writereply(NFSX_PREOPATTR(v3) + NFSX_POSTOPORFATTR(v3) + 2 * NFSX_UNSIGNED + NFSX_WRITEVERF(v3), v3); if (v3) { nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(nfsd->nd_len); *tl++ = txdr_unsigned(swp->nd_stable); /* * Actually, there is no need to txdr these fields, * but it may make the values more human readable, * for debugging purposes. */ *tl++ = txdr_unsigned(boottime.tv_sec); *tl = txdr_unsigned(boottime.tv_usec); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(&va, fp); } } nfsd->nd_mreq = mreq; if (nfsd->nd_mrep) panic("nfsrv_write: nd_mrep not free"); /* * Done. Put it at the head of the timer queue so that * the final phase can return the reply. */ s = splsoftclock(); if (nfsd != swp) { nfsd->nd_time = 0; LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq); } nfsd = swp->nd_coalesce.lh_first; if (nfsd) { LIST_REMOVE(nfsd, nd_tq); } splx(s); } while (nfsd); s = splsoftclock(); swp->nd_time = 0; LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq); splx(s); goto loop1; } splx(s); /* * Search for a reply to return. */ s = splsoftclock(); for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = nfsd->nd_tq.le_next) if (nfsd->nd_mreq) { NFS_DPF(WG, ("X%03x", nfsd->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_tq); *mrq = nfsd->nd_mreq; *ndp = nfsd; break; } splx(s); return (0); } /* * Coalesce the write request nfsd into owp. To do this we must: * - remove nfsd from the queues * - merge nfsd->nd_mrep into owp->nd_mrep * - update the nd_eoff and nd_stable for owp * - put nfsd on owp's nd_coalesce list * NB: Must be called at splsoftclock(). */ static void nfsrvw_coalesce(owp, nfsd) register struct nfsrv_descript *owp; register struct nfsrv_descript *nfsd; { register int overlap; register struct mbuf *mp; struct nfsrv_descript *p; NFS_DPF(WG, ("C%03x-%03x", nfsd->nd_retxid & 0xfff, owp->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_hash); LIST_REMOVE(nfsd, nd_tq); if (owp->nd_eoff < nfsd->nd_eoff) { overlap = owp->nd_eoff - nfsd->nd_off; if (overlap < 0) panic("nfsrv_coalesce: bad off"); if (overlap > 0) m_adj(nfsd->nd_mrep, overlap); mp = owp->nd_mrep; while (mp->m_next) mp = mp->m_next; mp->m_next = nfsd->nd_mrep; owp->nd_eoff = nfsd->nd_eoff; } else m_freem(nfsd->nd_mrep); nfsd->nd_mrep = NULL; if (nfsd->nd_stable == NFSV3WRITE_FILESYNC) owp->nd_stable = NFSV3WRITE_FILESYNC; else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC && owp->nd_stable == NFSV3WRITE_UNSTABLE) owp->nd_stable = NFSV3WRITE_DATASYNC; LIST_INSERT_HEAD(&owp->nd_coalesce, nfsd, nd_tq); /* * If nfsd had anything else coalesced into it, transfer them * to owp, otherwise their replies will never get sent. */ for (p = nfsd->nd_coalesce.lh_first; p; p = nfsd->nd_coalesce.lh_first) { LIST_REMOVE(p, nd_tq); LIST_INSERT_HEAD(&owp->nd_coalesce, p, nd_tq); } } /* * Sort the group list in increasing numerical order. * (Insertion sort by Chris Torek, who was grossed out by the bubble sort * that used to be here.) */ void nfsrvw_sort(list, num) register gid_t *list; register int num; { register int i, j; gid_t v; /* Insertion sort. */ for (i = 1; i < num; i++) { v = list[i]; /* find correct slot for value v, moving others up */ for (j = i; --j >= 0 && v < list[j];) list[j + 1] = list[j]; list[j + 1] = v; } } /* * copy credentials making sure that the result can be compared with bcmp(). */ void nfsrv_setcred(incred, outcred) register struct ucred *incred, *outcred; { register int i; bzero((caddr_t)outcred, sizeof (struct ucred)); outcred->cr_ref = 1; outcred->cr_uid = incred->cr_uid; outcred->cr_ngroups = incred->cr_ngroups; for (i = 0; i < incred->cr_ngroups; i++) outcred->cr_groups[i] = incred->cr_groups[i]; nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups); } /* * nfs create service * now does a truncate to 0 length via. setattr if it already exists */ int nfsrv_create(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct nfs_fattr *fp; struct vattr va, dirfor, diraft; register struct vattr *vap = &va; register struct nfsv2_sattr *sp; register u_long *tl; struct nameidata nd; register caddr_t cp; register long t1; caddr_t bpos; int error = 0, rdev, cache, len, tsize, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3), how, exclusive_flag = 0; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp, *dirp = (struct vnode *)0; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev, tempsize; u_char cverf[NFSX_V3CREATEVERF]; #ifndef nolint rdev = 0; #endif nd.ni_cnd.cn_nameiop = 0; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) { nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); if (dirp) vrele(dirp); return (0); } VATTR_NULL(vap); if (v3) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); how = fxdr_unsigned(int, *tl); switch (how) { case NFSV3CREATE_GUARDED: if (nd.ni_vp) { error = EEXIST; break; } case NFSV3CREATE_UNCHECKED: nfsm_srvsattr(vap); break; case NFSV3CREATE_EXCLUSIVE: nfsm_dissect(cp, caddr_t, NFSX_V3CREATEVERF); bcopy(cp, cverf, NFSX_V3CREATEVERF); exclusive_flag = 1; if (nd.ni_vp == NULL) vap->va_mode = 0; break; }; vap->va_type = VREG; } else { nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); vap->va_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode)); if (vap->va_type == VNON) vap->va_type = VREG; vap->va_mode = nfstov_mode(sp->sa_mode); switch (vap->va_type) { case VREG: tsize = fxdr_unsigned(long, sp->sa_size); if (tsize != -1) vap->va_size = (u_quad_t)tsize; break; case VCHR: case VBLK: case VFIFO: rdev = fxdr_unsigned(long, sp->sa_size); break; }; } /* * Iff doesn't exist, create it * otherwise just truncate to 0 length * should I set the mode too ?? */ if (nd.ni_vp == NULL) { if (vap->va_type == VREG || vap->va_type == VSOCK) { vrele(nd.ni_startdir); nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); if (!error) { nfsrv_object_create(nd.ni_vp); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); if (exclusive_flag) { exclusive_flag = 0; VATTR_NULL(vap); bcopy(cverf, (caddr_t)&vap->va_atime, NFSX_V3CREATEVERF); error = VOP_SETATTR(nd.ni_vp, vap, cred, procp); } } } 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 && (error = suser(cred, (u_short *)0))) { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); nfsm_reply(0); return (error); } else vap->va_rdev = (dev_t)rdev; nqsrv_getl(nd.ni_dvp, ND_WRITE); if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) { vrele(nd.ni_startdir); nfsm_reply(0); } nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART); nd.ni_cnd.cn_proc = procp; nd.ni_cnd.cn_cred = cred; if (error = lookup(&nd)) { free(nd.ni_cnd.cn_pnbuf, M_NAMEI); nfsm_reply(0); } nfsrv_object_create(nd.ni_vp); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); if (nd.ni_cnd.cn_flags & ISSYMLINK) { vrele(nd.ni_dvp); vput(nd.ni_vp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); error = EINVAL; nfsm_reply(0); } } else { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); error = ENXIO; } vp = nd.ni_vp; } else { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); vp = nd.ni_vp; if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (vap->va_size != -1) { error = nfsrv_access(vp, VWRITE, cred, (nd.ni_cnd.cn_flags & RDONLY), procp); if (!error) { nqsrv_getl(vp, ND_WRITE); tempsize = vap->va_size; VATTR_NULL(vap); vap->va_size = tempsize; error = VOP_SETATTR(vp, vap, cred, procp); } if (error) vput(vp); } } if (!error) { bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); } if (v3) { if (exclusive_flag && !error && bcmp(cverf, (caddr_t)&vap->va_atime, NFSX_V3CREATEVERF)) error = EEXIST; diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_SRVFH(v3) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { if (!error) { nfsm_srvpostop_fh(fhp); nfsm_srvpostop_attr(0, vap); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); } else { nfsm_srvfhtom(fhp, v3); nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } return (0); nfsmout: if (dirp) vrele(dirp); if (nd.ni_cnd.cn_nameiop) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); } VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vput(nd.ni_vp); return (error); } /* * nfs v3 mknod service */ int nfsrv_mknod(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr va, dirfor, diraft; register struct vattr *vap = &va; register u_long *tl; struct nameidata nd; register long t1; caddr_t bpos; int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; u_long major, minor; enum vtype vtyp; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp, *dirp = (struct vnode *)0; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; nd.ni_cnd.cn_nameiop = 0; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); if (error) { nfsm_reply(NFSX_WCCDATA(1)); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); if (dirp) vrele(dirp); return (0); } nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); vtyp = nfsv3tov_type(*tl); if (vtyp != VCHR && vtyp != VBLK && vtyp != VSOCK && vtyp != VFIFO) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); error = NFSERR_BADTYPE; VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); goto out; } VATTR_NULL(vap); nfsm_srvsattr(vap); if (vtyp == VCHR || vtyp == VBLK) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); major = fxdr_unsigned(u_long, *tl++); minor = fxdr_unsigned(u_long, *tl); vap->va_rdev = makedev(major, minor); } /* * Iff doesn't exist, create it. */ if (nd.ni_vp) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); error = EEXIST; VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); goto out; } vap->va_type = vtyp; if (vtyp == VSOCK) { vrele(nd.ni_startdir); nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); if (!error) FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); } else { if (vtyp != VFIFO && (error = suser(cred, (u_short *)0))) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); vput(nd.ni_dvp); goto out; } nqsrv_getl(nd.ni_dvp, ND_WRITE); if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) { vrele(nd.ni_startdir); goto out; } nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART); nd.ni_cnd.cn_proc = procp; nd.ni_cnd.cn_cred = procp->p_ucred; error = lookup(&nd); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); if (error) goto out; if (nd.ni_cnd.cn_flags & ISSYMLINK) { vrele(nd.ni_dvp); vput(nd.ni_vp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); error = EINVAL; } } out: vp = nd.ni_vp; if (!error) { bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); } diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); nfsm_reply(NFSX_SRVFH(1) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1)); if (!error) { nfsm_srvpostop_fh(fhp); nfsm_srvpostop_attr(0, vap); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); nfsmout: if (dirp) vrele(dirp); if (nd.ni_cnd.cn_nameiop) { vrele(nd.ni_startdir); free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); } VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vput(nd.ni_vp); return (error); } /* * nfs remove service */ int nfsrv_remove(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct nameidata nd; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mreq; struct vnode *vp, *dirp; struct vattr dirfor, diraft; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; #ifndef nolint vp = (struct vnode *)0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = DELETE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else vrele(dirp); } if (!error) { vp = nd.ni_vp; if (vp->v_type == VDIR) { error = EPERM; /* POSIX */ goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) { error = EBUSY; goto out; } out: if (!error) { vnode_pager_uncache(vp, procp); nqsrv_getl(nd.ni_dvp, ND_WRITE); nqsrv_getl(vp, ND_WRITE); error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); } } if (dirp && v3) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_WCCDATA(v3)); if (v3) { nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); } nfsm_srvdone; } /* * nfs rename service */ int nfsrv_rename(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, len2, fdirfor_ret = 1, fdiraft_ret = 1; int tdirfor_ret = 1, tdiraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mreq; struct nameidata fromnd, tond; struct vnode *fvp, *tvp, *tdvp, *fdirp = (struct vnode *)0; struct vnode *tdirp = (struct vnode *)0; struct vattr fdirfor, fdiraft, tdirfor, tdiraft; nfsfh_t fnfh, tnfh; fhandle_t *ffhp, *tfhp; u_quad_t frev; uid_t saved_uid; #ifndef nolint fvp = (struct vnode *)0; #endif ffhp = &fnfh.fh_generic; tfhp = &tnfh.fh_generic; fromnd.ni_cnd.cn_nameiop = 0; tond.ni_cnd.cn_nameiop = 0; nfsm_srvmtofh(ffhp); nfsm_srvnamesiz(len); /* * Remember our original uid so that we can reset cr_uid before * the second nfs_namei() call, in case it is remapped. */ saved_uid = cred->cr_uid; fromnd.ni_cnd.cn_cred = cred; fromnd.ni_cnd.cn_nameiop = DELETE; fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART; error = nfs_namei(&fromnd, ffhp, len, slp, nam, &md, &dpos, &fdirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (fdirp) { if (v3) fdirfor_ret = VOP_GETATTR(fdirp, &fdirfor, cred, procp); else { vrele(fdirp); fdirp = (struct vnode *)0; } } if (error) { nfsm_reply(2 * NFSX_WCCDATA(v3)); nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); if (fdirp) vrele(fdirp); return (0); } fvp = fromnd.ni_vp; nfsm_srvmtofh(tfhp); nfsm_strsiz(len2, NFS_MAXNAMLEN); cred->cr_uid = saved_uid; tond.ni_cnd.cn_cred = cred; tond.ni_cnd.cn_nameiop = RENAME; tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART; error = nfs_namei(&tond, tfhp, len2, slp, nam, &md, &dpos, &tdirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (tdirp) { if (v3) tdirfor_ret = VOP_GETATTR(tdirp, &tdirfor, cred, procp); else { vrele(tdirp); tdirp = (struct vnode *)0; } } if (error) { VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { if (v3) error = EEXIST; else error = EISDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { if (v3) error = EEXIST; else error = ENOTDIR; goto out; } if (tvp->v_type == VDIR && tvp->v_mountedhere) { if (v3) error = EXDEV; else error = ENOTEMPTY; goto out; } } if (fvp->v_type == VDIR && fvp->v_mountedhere) { if (v3) error = EXDEV; else error = ENOTEMPTY; goto out; } if (fvp->v_mount != tdvp->v_mount) { if (v3) error = EXDEV; else error = ENOTEMPTY; goto out; } if (fvp == tdvp) if (v3) error = EINVAL; else error = ENOTEMPTY; /* * If source is the same as the destination (that is the * same vnode with the same name in the same directory), * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, fromnd.ni_cnd.cn_namelen)) error = -1; out: if (!error) { nqsrv_getl(fromnd.ni_dvp, ND_WRITE); nqsrv_getl(tdvp, ND_WRITE); if (tvp) { nqsrv_getl(tvp, ND_WRITE); (void) vnode_pager_uncache(tvp, procp); } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); } else { VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); if (error == -1) error = 0; } vrele(tond.ni_startdir); FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); out1: if (fdirp) { fdiraft_ret = VOP_GETATTR(fdirp, &fdiraft, cred, procp); vrele(fdirp); } if (tdirp) { tdiraft_ret = VOP_GETATTR(tdirp, &tdiraft, cred, procp); vrele(tdirp); } vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); nfsm_reply(2 * NFSX_WCCDATA(v3)); if (v3) { nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); } return (0); nfsmout: if (fdirp) vrele(fdirp); if (tdirp) vrele(tdirp); if (tond.ni_cnd.cn_nameiop) { vrele(tond.ni_startdir); FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); } if (fromnd.ni_cnd.cn_nameiop) { vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); } return (error); } /* * nfs link service */ int nfsrv_link(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct nameidata nd; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, len, dirfor_ret = 1, diraft_ret = 1; int getret = 1, v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mreq; struct vnode *vp, *xp, *dirp = (struct vnode *)0; struct vattr dirfor, diraft, at; nfsfh_t nfh, dnfh; fhandle_t *fhp, *dfhp; u_quad_t frev; fhp = &nfh.fh_generic; dfhp = &dnfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvmtofh(dfhp); nfsm_srvnamesiz(len); if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); nfsm_srvpostop_attr(getret, &at); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); } if (vp->v_type == VDIR) { error = EPERM; /* POSIX */ goto out1; } nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT; error = nfs_namei(&nd, dfhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) goto out1; xp = nd.ni_vp; if (xp != NULL) { error = EEXIST; goto out; } xp = nd.ni_dvp; if (vp->v_mount != xp->v_mount) error = EXDEV; out: if (!error) { nqsrv_getl(vp, ND_WRITE); nqsrv_getl(xp, ND_WRITE); -#if defined(__NetBSD__) || defined(__FreeBSD__) error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); -#else /* Lite2 version */ - error = VOP_LINK(vp, nd.ni_dvp, &nd.ni_cnd); -#endif } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); } out1: if (v3) getret = VOP_GETATTR(vp, &at, cred, procp); if (dirp) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } vrele(vp); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { nfsm_srvpostop_attr(getret, &at); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); } nfsm_srvdone; } /* * nfs symbolic link service */ int nfsrv_symlink(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr va, dirfor, diraft; struct nameidata nd; register struct vattr *vap = &va; register u_long *tl; register long t1; struct nfsv2_sattr *sp; char *bpos, *pathcp = (char *)0, *cp2; struct uio io; struct iovec iv; int error = 0, cache, len, len2, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); struct mbuf *mb, *mreq, *mb2; struct vnode *dirp = (struct vnode *)0; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; nd.ni_cnd.cn_nameiop = 0; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT | SAVESTART; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) goto out; VATTR_NULL(vap); if (v3) nfsm_srvsattr(vap); 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; io.uio_procp = (struct proc *)0; nfsm_mtouio(&io, len2); if (!v3) { nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode); } *(pathcp + len2) = '\0'; if (nd.ni_vp) { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); error = EEXIST; goto out; } nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap, pathcp); if (error) vrele(nd.ni_startdir); else { if (v3) { nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART | FOLLOW); nd.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF); nd.ni_cnd.cn_proc = procp; nd.ni_cnd.cn_cred = cred; error = lookup(&nd); if (!error) { bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = nd.ni_vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(nd.ni_vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(nd.ni_vp, vap, cred, procp); vput(nd.ni_vp); } } else vrele(nd.ni_startdir); FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); } out: if (pathcp) FREE(pathcp, M_TEMP); if (dirp) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { if (!error) { nfsm_srvpostop_fh(fhp); nfsm_srvpostop_attr(0, vap); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); } return (0); nfsmout: if (nd.ni_cnd.cn_nameiop) { vrele(nd.ni_startdir); free(nd.ni_cnd.cn_pnbuf, M_NAMEI); } if (dirp) vrele(dirp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); if (pathcp) FREE(pathcp, M_TEMP); return (error); } /* * nfs mkdir service */ int nfsrv_mkdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr va, dirfor, diraft; register struct vattr *vap = &va; register struct nfs_fattr *fp; struct nameidata nd; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp, *dirp = (struct vnode *)0; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) { nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); if (dirp) vrele(dirp); return (0); } VATTR_NULL(vap); if (v3) { nfsm_srvsattr(vap); } else { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); vap->va_mode = nfstov_mode(*tl++); } vap->va_type = VDIR; vp = nd.ni_vp; if (vp != NULL) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(vp); error = EEXIST; goto out; } nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); if (!error) { vp = nd.ni_vp; bzero((caddr_t)fhp, sizeof(nfh)); fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fhp->fh_fid); if (!error) error = VOP_GETATTR(vp, vap, cred, procp); vput(vp); } out: if (dirp) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { if (!error) { nfsm_srvpostop_fh(fhp); nfsm_srvpostop_attr(0, vap); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); } else { nfsm_srvfhtom(fhp, v3); nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } return (0); nfsmout: if (dirp) vrele(dirp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); return (error); } /* * nfs rmdir service */ int nfsrv_rmdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register long t1; caddr_t bpos; int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mreq; struct vnode *vp, *dirp = (struct vnode *)0; struct vattr dirfor, diraft; nfsfh_t nfh; fhandle_t *fhp; struct nameidata nd; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = DELETE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); if (dirp) { if (v3) dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); else { vrele(dirp); dirp = (struct vnode *)0; } } if (error) { nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); if (dirp) vrele(dirp); return (0); } vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) error = EBUSY; out: if (!error) { nqsrv_getl(nd.ni_dvp, ND_WRITE); nqsrv_getl(vp, ND_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); } if (dirp) { diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); vrele(dirp); } nfsm_reply(NFSX_WCCDATA(v3)); if (v3) { nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); } nfsm_srvdone; } /* * nfs readdir service * - mallocs what it thinks is enough to read * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR * - calls VOP_READDIR() * - loops around building the reply * if the output generated exceeds count break out of loop * The nfsm_clget macro is used here so that the reply will be packed * tightly in mbuf clusters. * - it only knows that it has encountered eof when the VOP_READDIR() * reads nothing * - as such one readdir rpc will return eof false although you are there * and then the next will return eof * - it trims out records with d_fileno == 0 * this doesn't matter for Unix clients, but they might confuse clients * for other os'. * NB: It is tempting to set eof to true if the VOP_READDIR() reads less * than requested, but this may not apply to all filesystems. For * example, client NFS does not { although it is never remote mounted * anyhow } * The alternate call nfsrv_readdirplus() does lookups as well. * PS: The NFS protocol spec. does not clarify what the "count" byte * argument is a count of.. just name strings and file id's or the * entire reply rpc or ... * I tried just file name and id sizes and it confused the Sun client, * so I am using the full rpc size now. The "paranoia.." comment refers * to including the status longwords that are not a part of the dir. * "entry" structures, but are in the rpc. */ struct flrep { nfsuint64 fl_off; u_long fl_postopok; u_long fl_fattr[NFSX_V3FATTR / sizeof (u_long)]; u_long fl_fhok; u_long fl_fhsize; u_long fl_nfh[NFSX_V3FH / sizeof (u_long)]; }; int nfsrv_readdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register char *bp, *be; register struct mbuf *mp; register struct dirent *dp; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; struct mbuf *mb, *mb2, *mreq, *mp2; char *cpos, *cend, *cp2, *rbuf; struct vnode *vp; struct vattr at; nfsfh_t nfh; fhandle_t *fhp; struct uio io; struct iovec iv; int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1; int siz, cnt, fullsiz, eofflag, rdonly, cache, ncookies; int v3 = (nfsd->nd_flag & ND_NFSV3); u_quad_t frev, off, toff, verf; u_long *cookies = NULL, *cookiep; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &toff); tl += 2; fxdr_hyper(tl, &verf); tl += 2; } else { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); toff = fxdr_unsigned(u_quad_t, *tl++); } off = toff; cnt = fxdr_unsigned(int, *tl); siz = ((cnt + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); xfer = NFS_SRVMAXDATA(nfsd); if (siz > xfer) siz = xfer; fullsiz = siz; if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } nqsrv_getl(vp, ND_READ); if (v3) { error = getret = VOP_GETATTR(vp, &at, cred, procp); if (!error && toff && verf && verf != at.va_filerev) error = NFSERR_BAD_COOKIE; } if (!error) error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); if (error) { vput(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, &at); return (0); } VOP_UNLOCK(vp, 0, procp); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); again: iv.iov_base = rbuf; iv.iov_len = fullsiz; io.uio_iov = &iv; io.uio_iovcnt = 1; io.uio_offset = (off_t)off; io.uio_resid = fullsiz; io.uio_segflg = UIO_SYSSPACE; io.uio_rw = UIO_READ; io.uio_procp = (struct proc *)0; eofflag = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, procp); if (cookies) { free((caddr_t)cookies, M_TEMP); cookies = NULL; } error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies); off = (off_t)io.uio_offset; if (!cookies && !error) error = NFSERR_PERM; if (v3) { getret = VOP_GETATTR(vp, &at, cred, procp); if (!error) error = getret; } VOP_UNLOCK(vp, 0, procp); if (error) { vrele(vp); free((caddr_t)rbuf, M_TEMP); if (cookies) free((caddr_t)cookies, M_TEMP); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, &at); return (0); } if (io.uio_resid) { siz -= io.uio_resid; /* * If nothing read, return eof * rpc reply */ if (siz == 0) { vrele(vp); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + 2 * NFSX_UNSIGNED); if (v3) { nfsm_srvpostop_attr(getret, &at); nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); txdr_hyper(&at.va_filerev, tl); tl += 2; } else nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_false; *tl = nfs_true; FREE((caddr_t)rbuf, M_TEMP); FREE((caddr_t)cookies, M_TEMP); return (0); } } /* * Check for degenerate cases of nothing useful read. * If so go try again */ cpos = rbuf; cend = rbuf + siz; dp = (struct dirent *)cpos; cookiep = cookies; /* * For some reason FreeBSD's ufs_readdir() chooses to back the * directory offset up to a block boundary, so it is necessary to * skip over the records that preceed the requested offset. This * requires the assumption that file offset cookies monotonically * increase. */ while (cpos < cend && ncookies > 0 && (dp->d_fileno == 0 || ((u_quad_t)(*cookiep)) <= toff)) { cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } if (cpos >= cend || ncookies == 0) { toff = off; siz = fullsiz; goto again; } len = 3 * NFSX_UNSIGNED; /* paranoia, probably can be 0 */ nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + siz); if (v3) { nfsm_srvpostop_attr(getret, &at); nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); txdr_hyper(&at.va_filerev, tl); } mp = mp2 = mb; bp = bpos; be = bp + M_TRAILINGSPACE(mp); /* Loop through the records and build reply */ while (cpos < cend && ncookies > 0) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; rem = nfsm_rndup(nlen)-nlen; len += (4 * NFSX_UNSIGNED + nlen + rem); if (v3) len += 2 * NFSX_UNSIGNED; if (len > cnt) { eofflag = 0; break; } /* * Build the directory record xdr from * the dirent entry. */ nfsm_clget; *tl = nfs_true; bp += NFSX_UNSIGNED; if (v3) { nfsm_clget; *tl = 0; bp += NFSX_UNSIGNED; } nfsm_clget; *tl = txdr_unsigned(dp->d_fileno); bp += NFSX_UNSIGNED; nfsm_clget; *tl = txdr_unsigned(nlen); bp += NFSX_UNSIGNED; /* And loop around copying the name */ xfer = nlen; cp = dp->d_name; while (xfer > 0) { nfsm_clget; if ((bp+xfer) > be) tsiz = be-bp; else tsiz = xfer; bcopy(cp, bp, tsiz); bp += tsiz; xfer -= tsiz; if (xfer > 0) cp += tsiz; } /* And null pad to a long boundary */ for (i = 0; i < rem; i++) *bp++ = '\0'; nfsm_clget; /* Finish off the record */ if (v3) { *tl = 0; bp += NFSX_UNSIGNED; nfsm_clget; } *tl = txdr_unsigned(*cookiep); bp += NFSX_UNSIGNED; } cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } vrele(vp); nfsm_clget; *tl = nfs_false; bp += NFSX_UNSIGNED; nfsm_clget; if (eofflag) *tl = nfs_true; else *tl = nfs_false; bp += NFSX_UNSIGNED; if (mp != mb) { if (bp < be) mp->m_len = bp - mtod(mp, caddr_t); } else mp->m_len += bp - bpos; FREE((caddr_t)rbuf, M_TEMP); FREE((caddr_t)cookies, M_TEMP); nfsm_srvdone; } int nfsrv_readdirplus(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register char *bp, *be; register struct mbuf *mp; register struct dirent *dp; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; struct mbuf *mb, *mb2, *mreq, *mp2; char *cpos, *cend, *cp2, *rbuf; struct vnode *vp, *nvp; struct flrep fl; nfsfh_t nfh; fhandle_t *fhp, *nfhp = (fhandle_t *)fl.fl_nfh; struct uio io; struct iovec iv; struct vattr va, at, *vap = &va; struct nfs_fattr *fp; int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1; int siz, cnt, fullsiz, eofflag, rdonly, cache, dirlen, ncookies; u_quad_t frev, off, toff, verf; u_long *cookies = NULL, *cookiep; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_dissect(tl, u_long *, 6 * NFSX_UNSIGNED); fxdr_hyper(tl, &toff); tl += 2; fxdr_hyper(tl, &verf); tl += 2; siz = fxdr_unsigned(int, *tl++); cnt = fxdr_unsigned(int, *tl); off = toff; siz = ((siz + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); xfer = NFS_SRVMAXDATA(nfsd); if (siz > xfer) siz = xfer; fullsiz = siz; if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } error = getret = VOP_GETATTR(vp, &at, cred, procp); if (!error && toff && verf && verf != at.va_filerev) error = NFSERR_BAD_COOKIE; if (!error) { nqsrv_getl(vp, ND_READ); error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); } if (error) { vput(vp); nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } VOP_UNLOCK(vp, 0, procp); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); again: iv.iov_base = rbuf; iv.iov_len = fullsiz; io.uio_iov = &iv; io.uio_iovcnt = 1; io.uio_offset = (off_t)off; io.uio_resid = fullsiz; io.uio_segflg = UIO_SYSSPACE; io.uio_rw = UIO_READ; io.uio_procp = (struct proc *)0; eofflag = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, procp); if (cookies) { free((caddr_t)cookies, M_TEMP); cookies = NULL; } error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies); off = (u_quad_t)io.uio_offset; getret = VOP_GETATTR(vp, &at, cred, procp); VOP_UNLOCK(vp, 0, procp); if (!cookies && !error) error = NFSERR_PERM; if (!error) error = getret; if (error) { vrele(vp); if (cookies) free((caddr_t)cookies, M_TEMP); free((caddr_t)rbuf, M_TEMP); nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } if (io.uio_resid) { siz -= io.uio_resid; /* * If nothing read, return eof * rpc reply */ if (siz == 0) { vrele(vp); nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); txdr_hyper(&at.va_filerev, tl); tl += 2; *tl++ = nfs_false; *tl = nfs_true; FREE((caddr_t)cookies, M_TEMP); FREE((caddr_t)rbuf, M_TEMP); return (0); } } /* * Check for degenerate cases of nothing useful read. * If so go try again */ cpos = rbuf; cend = rbuf + siz; dp = (struct dirent *)cpos; cookiep = cookies; /* * For some reason FreeBSD's ufs_readdir() chooses to back the * directory offset up to a block boundary, so it is necessary to * skip over the records that preceed the requested offset. This * requires the assumption that file offset cookies monotonically * increase. */ while (cpos < cend && ncookies > 0 && (dp->d_fileno == 0 || ((u_quad_t)(*cookiep)) <= toff)) { cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } if (cpos >= cend || ncookies == 0) { toff = off; siz = fullsiz; goto again; } /* * Probe one of the directory entries to see if the filesystem * supports VGET. */ if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp) == EOPNOTSUPP) { error = NFSERR_NOTSUPP; vrele(vp); free((caddr_t)cookies, M_TEMP); free((caddr_t)rbuf, M_TEMP); nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } vput(nvp); dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED; nfsm_reply(cnt); nfsm_srvpostop_attr(getret, &at); nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); txdr_hyper(&at.va_filerev, tl); mp = mp2 = mb; bp = bpos; be = bp + M_TRAILINGSPACE(mp); /* Loop through the records and build reply */ while (cpos < cend && ncookies > 0) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; rem = nfsm_rndup(nlen)-nlen; /* * For readdir_and_lookup get the vnode using * the file number. */ if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp)) goto invalid; bzero((caddr_t)nfhp, NFSX_V3FH); nfhp->fh_fsid = nvp->v_mount->mnt_stat.f_fsid; if (VFS_VPTOFH(nvp, &nfhp->fh_fid)) { vput(nvp); goto invalid; } if (VOP_GETATTR(nvp, vap, cred, procp)) { vput(nvp); goto invalid; } vput(nvp); /* * If either the dircount or maxcount will be * exceeded, get out now. Both of these lengths * are calculated conservatively, including all * XDR overheads. */ len += (7 * NFSX_UNSIGNED + nlen + rem + NFSX_V3FH + NFSX_V3POSTOPATTR); dirlen += (6 * NFSX_UNSIGNED + nlen + rem); if (len > cnt || dirlen > fullsiz) { eofflag = 0; break; } /* * Build the directory record xdr from * the dirent entry. */ fp = (struct nfs_fattr *)&fl.fl_fattr; nfsm_srvfillattr(vap, fp); fl.fl_fhsize = txdr_unsigned(NFSX_V3FH); fl.fl_fhok = nfs_true; fl.fl_postopok = nfs_true; fl.fl_off.nfsuquad[0] = 0; fl.fl_off.nfsuquad[1] = txdr_unsigned(*cookiep); nfsm_clget; *tl = nfs_true; bp += NFSX_UNSIGNED; nfsm_clget; *tl = 0; bp += NFSX_UNSIGNED; nfsm_clget; *tl = txdr_unsigned(dp->d_fileno); bp += NFSX_UNSIGNED; nfsm_clget; *tl = txdr_unsigned(nlen); bp += NFSX_UNSIGNED; /* And loop around copying the name */ xfer = nlen; cp = dp->d_name; while (xfer > 0) { nfsm_clget; if ((bp + xfer) > be) tsiz = be - bp; else tsiz = xfer; bcopy(cp, bp, tsiz); bp += tsiz; xfer -= tsiz; if (xfer > 0) cp += tsiz; } /* And null pad to a long boundary */ for (i = 0; i < rem; i++) *bp++ = '\0'; /* * Now copy the flrep structure out. */ xfer = sizeof (struct flrep); cp = (caddr_t)&fl; while (xfer > 0) { nfsm_clget; if ((bp + xfer) > be) tsiz = be - bp; else tsiz = xfer; bcopy(cp, bp, tsiz); bp += tsiz; xfer -= tsiz; if (xfer > 0) cp += tsiz; } } invalid: cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } vrele(vp); nfsm_clget; *tl = nfs_false; bp += NFSX_UNSIGNED; nfsm_clget; if (eofflag) *tl = nfs_true; else *tl = nfs_false; bp += NFSX_UNSIGNED; if (mp != mb) { if (bp < be) mp->m_len = bp - mtod(mp, caddr_t); } else mp->m_len += bp - bpos; FREE((caddr_t)cookies, M_TEMP); FREE((caddr_t)rbuf, M_TEMP); nfsm_srvdone; } /* * nfs commit service */ int nfsrv_commit(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; struct vattr bfor, aft; struct vnode *vp; nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, for_ret = 1, aft_ret = 1, cnt, cache; char *cp2; struct mbuf *mb, *mb2, *mreq; u_quad_t frev, off; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); /* * XXX At this time VOP_FSYNC() does not accept offset and byte * count parameters, so these arguments are useless (someday maybe). */ fxdr_hyper(tl, &off); tl += 2; cnt = fxdr_unsigned(int, *tl); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(2 * NFSX_UNSIGNED); nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft); return (0); } for_ret = VOP_GETATTR(vp, &bfor, cred, procp); error = VOP_FSYNC(vp, cred, MNT_WAIT, procp); aft_ret = VOP_GETATTR(vp, &aft, cred, procp); vput(vp); nfsm_reply(NFSX_V3WCCDATA + NFSX_V3WRITEVERF); nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft); if (!error) { nfsm_build(tl, u_long *, NFSX_V3WRITEVERF); *tl++ = txdr_unsigned(boottime.tv_sec); *tl = txdr_unsigned(boottime.tv_usec); } else return (0); nfsm_srvdone; } /* * nfs statfs service */ int nfsrv_statfs(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register struct statfs *sf; register struct nfs_statfs *sfp; register u_long *tl; register long t1; caddr_t bpos; int error = 0, rdonly, cache, getret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; struct vattr at; nfsfh_t nfh; fhandle_t *fhp; struct statfs statfs; u_quad_t frev, tval; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } sf = &statfs; error = VFS_STATFS(vp->v_mount, sf, procp); getret = VOP_GETATTR(vp, &at, cred, procp); vput(vp); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_STATFS(v3)); if (v3) nfsm_srvpostop_attr(getret, &at); if (error) return (0); nfsm_build(sfp, struct nfs_statfs *, NFSX_STATFS(v3)); if (v3) { tval = (u_quad_t)sf->f_blocks; tval *= (u_quad_t)sf->f_bsize; txdr_hyper(&tval, &sfp->sf_tbytes); tval = (u_quad_t)sf->f_bfree; tval *= (u_quad_t)sf->f_bsize; txdr_hyper(&tval, &sfp->sf_fbytes); tval = (u_quad_t)sf->f_bavail; tval *= (u_quad_t)sf->f_bsize; txdr_hyper(&tval, &sfp->sf_abytes); sfp->sf_tfiles.nfsuquad[0] = 0; sfp->sf_tfiles.nfsuquad[1] = txdr_unsigned(sf->f_files); sfp->sf_ffiles.nfsuquad[0] = 0; sfp->sf_ffiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree); sfp->sf_afiles.nfsuquad[0] = 0; sfp->sf_afiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree); sfp->sf_invarsec = 0; } else { sfp->sf_tsize = txdr_unsigned(NFS_MAXDGRAMDATA); sfp->sf_bsize = txdr_unsigned(sf->f_bsize); sfp->sf_blocks = txdr_unsigned(sf->f_blocks); sfp->sf_bfree = txdr_unsigned(sf->f_bfree); sfp->sf_bavail = txdr_unsigned(sf->f_bavail); } nfsm_srvdone; } /* * nfs fsinfo service */ int nfsrv_fsinfo(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register struct nfsv3_fsinfo *sip; register long t1; caddr_t bpos; int error = 0, rdonly, cache, getret = 1, pref; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; struct vattr at; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } getret = VOP_GETATTR(vp, &at, cred, procp); vput(vp); nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3FSINFO); nfsm_srvpostop_attr(getret, &at); nfsm_build(sip, struct nfsv3_fsinfo *, NFSX_V3FSINFO); /* * XXX * There should be file system VFS OP(s) to get this information. * For now, assume ufs. */ if (slp->ns_so->so_type == SOCK_DGRAM) pref = NFS_MAXDGRAMDATA; else pref = NFS_MAXDATA; sip->fs_rtmax = txdr_unsigned(NFS_MAXDATA); sip->fs_rtpref = txdr_unsigned(pref); sip->fs_rtmult = txdr_unsigned(NFS_FABLKSIZE); sip->fs_wtmax = txdr_unsigned(NFS_MAXDATA); sip->fs_wtpref = txdr_unsigned(pref); sip->fs_wtmult = txdr_unsigned(NFS_FABLKSIZE); sip->fs_dtpref = txdr_unsigned(pref); sip->fs_maxfilesize.nfsuquad[0] = 0xffffffff; sip->fs_maxfilesize.nfsuquad[1] = 0xffffffff; sip->fs_timedelta.nfsv3_sec = 0; sip->fs_timedelta.nfsv3_nsec = txdr_unsigned(1); sip->fs_properties = txdr_unsigned(NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK | NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME); nfsm_srvdone; } /* * nfs pathconf service */ int nfsrv_pathconf(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; struct sockaddr *nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register struct nfsv3_pathconf *pc; register long t1; caddr_t bpos; int error = 0, rdonly, cache, getret = 1, linkmax, namemax; int chownres, notrunc; char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; struct vattr at; nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; #ifndef nolint cache = 0; #endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE)) { nfsm_reply(NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); return (0); } error = VOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax); if (!error) error = VOP_PATHCONF(vp, _PC_NAME_MAX, &namemax); if (!error) error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres); if (!error) error = VOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc); getret = VOP_GETATTR(vp, &at, cred, procp); vput(vp); nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3PATHCONF); nfsm_srvpostop_attr(getret, &at); if (error) return (0); nfsm_build(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF); pc->pc_linkmax = txdr_unsigned(linkmax); pc->pc_namemax = txdr_unsigned(namemax); pc->pc_notrunc = txdr_unsigned(notrunc); pc->pc_chownrestricted = txdr_unsigned(chownres); /* * These should probably be supported by VOP_PATHCONF(), but * until msdosfs is exportable (why would you want to?), the * Unix defaults should be ok. */ pc->pc_caseinsensitive = nfs_false; pc->pc_casepreserving = nfs_true; nfsm_srvdone; } /* * Null operation, used by clients to ping server */ /* ARGSUSED */ int nfsrv_null(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep; caddr_t bpos; int error = NFSERR_RETVOID, cache; struct mbuf *mb, *mreq; u_quad_t frev; #ifndef nolint cache = 0; #endif nfsm_reply(0); return (0); } /* * No operation, used for obsolete procedures */ /* ARGSUSED */ int nfsrv_noop(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; struct proc *procp; struct mbuf **mrq; { struct mbuf *mrep = nfsd->nd_mrep; caddr_t bpos; int error, cache; struct mbuf *mb, *mreq; u_quad_t frev; #ifndef nolint cache = 0; #endif if (nfsd->nd_repstat) error = nfsd->nd_repstat; else error = EPROCUNAVAIL; nfsm_reply(0); return (0); } /* * 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 exported rdonly 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 * a security hole the size of a barn door anyhow, what the heck. */ static int nfsrv_access(vp, flags, cred, rdonly, p) register struct vnode *vp; int flags; register struct ucred *cred; int rdonly; struct proc *p; { struct vattr vattr; int error; if (flags & VWRITE) { /* Just vn_writechk() changed to check rdonly */ /* * 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 (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); } } /* * If there's shared text associated with * the inode, we can't allow writing. */ if (vp->v_flag & VTEXT) return (ETXTBSY); } if (error = VOP_GETATTR(vp, &vattr, cred, p)) return (error); if ((error = VOP_ACCESS(vp, flags, cred, p)) && cred->cr_uid != vattr.va_uid) return (error); return (0); } #endif /* NFS_NOSERVER */