diff options
| author | pgoyette <pgoyette@NetBSD.org> | 2016-11-18 08:31:29 +0000 |
|---|---|---|
| committer | pgoyette <pgoyette@NetBSD.org> | 2016-11-18 08:31:29 +0000 |
| commit | 7e7dbfb2f85561fa20fb14d9d035512b3d278dbf (patch) | |
| tree | 6a1d27a2a42f526f04ce9140c2ed91b631f2659d /sys | |
| parent | 6afb5203449c3ae6695530ceeb045f7165f1252f (diff) | |
Resolve conflicts
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/fs/nfs/client/nfs_clbio.c | 176 | ||||
| -rw-r--r-- | sys/fs/nfs/client/nfs_clstate.c | 125 | ||||
| -rw-r--r-- | sys/fs/nfs/client/nfs_clvnops.c | 142 | ||||
| -rw-r--r-- | sys/fs/nfs/common/krpc_subr.c | 8 | ||||
| -rw-r--r-- | sys/fs/nfs/common/nfs_fha.h | 22 | ||||
| -rw-r--r-- | sys/fs/nfs/common/nfsm_subs.h | 16 | ||||
| -rw-r--r-- | sys/fs/nfs/files.newnfs | 13 | ||||
| -rw-r--r-- | sys/fs/nfs/nlm/nlm_prot_impl.c | 26 | ||||
| -rw-r--r-- | sys/fs/nfs/server/nfs_nfsdcache.c | 204 | ||||
| -rw-r--r-- | sys/fs/nfs/server/nfs_nfsdkrpc.c | 112 | ||||
| -rw-r--r-- | sys/fs/nfs/server/nfs_nfsdserv.c | 804 | ||||
| -rw-r--r-- | sys/fs/nfs/server/nfs_nfsdstate.c | 1275 | ||||
| -rw-r--r-- | sys/fs/nfs/server/nfs_nfsdsubs.c | 179 |
13 files changed, 2294 insertions, 808 deletions
diff --git a/sys/fs/nfs/client/nfs_clbio.c b/sys/fs/nfs/client/nfs_clbio.c index d50d910493f..ab79b38fd6d 100644 --- a/sys/fs/nfs/client/nfs_clbio.c +++ b/sys/fs/nfs/client/nfs_clbio.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_clbio.c,v 1.2 2013/12/09 09:35:17 wiz Exp $ */ +/* $NetBSD: nfs_clbio.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -34,10 +34,8 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/fs/nfsclient/nfs_clbio.c 252072 2013-06-21 22:26:18Z rmacklem "); */ -__RCSID("$NetBSD: nfs_clbio.c,v 1.2 2013/12/09 09:35:17 wiz Exp $"); - -#include "opt_kdtrace.h" +/* __FBSDID("FreeBSD: head/sys/fs/nfsclient/nfs_clbio.c 304026 2016-08-12 22:44:59Z rmacklem "); */ +__RCSID("$NetBSD: nfs_clbio.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -64,7 +62,7 @@ __RCSID("$NetBSD: nfs_clbio.c,v 1.2 2013/12/09 09:35:17 wiz Exp $"); #include <fs/nfsclient/nfs_kdtrace.h> extern int newnfs_directio_allow_mmap; -extern struct nfsstats newnfsstats; +extern struct nfsstatsv1 nfsstatsv1; extern struct mtx ncl_iod_mutex; extern int ncl_numasync; extern enum nfsiod_state ncl_iodwant[NFS_MAXASYNCDAEMON]; @@ -104,10 +102,10 @@ ncl_getpages(struct vop_getpages_args *ap) cred = curthread->td_ucred; /* XXX */ nmp = VFSTONFS(vp->v_mount); pages = ap->a_m; - count = ap->a_count; + npages = ap->a_count; if ((object = vp->v_object) == NULL) { - ncl_printf("nfs_getpages: called with non-merged cache vnode??\n"); + printf("ncl_getpages: called with non-merged cache vnode\n"); return (VM_PAGER_ERROR); } @@ -115,7 +113,7 @@ ncl_getpages(struct vop_getpages_args *ap) mtx_lock(&np->n_mtx); if ((np->n_flag & NNONCACHE) && (vp->v_type == VREG)) { mtx_unlock(&np->n_mtx); - ncl_printf("nfs_getpages: called on non-cacheable vnode??\n"); + printf("ncl_getpages: called on non-cacheable vnode\n"); return (VM_PAGER_ERROR); } else mtx_unlock(&np->n_mtx); @@ -130,30 +128,21 @@ ncl_getpages(struct vop_getpages_args *ap) } else mtx_unlock(&nmp->nm_mtx); - npages = btoc(count); - /* * If the requested page is partially valid, just return it and * allow the pager to zero-out the blanks. Partially valid pages * can only occur at the file EOF. + * + * XXXGL: is that true for NFS, where short read can occur??? */ VM_OBJECT_WLOCK(object); - if (pages[ap->a_reqpage]->valid != 0) { - for (i = 0; i < npages; ++i) { - if (i != ap->a_reqpage) { - vm_page_lock(pages[i]); - vm_page_free(pages[i]); - vm_page_unlock(pages[i]); - } - } - VM_OBJECT_WUNLOCK(object); - return (0); - } + if (pages[npages - 1]->valid != 0 && --npages == 0) + goto out; VM_OBJECT_WUNLOCK(object); /* * We use only the kva address for the buffer, but this is extremely - * convienient and fast. + * convenient and fast. */ bp = getpbuf(&ncl_pbuf_freecnt); @@ -162,6 +151,7 @@ ncl_getpages(struct vop_getpages_args *ap) PCPU_INC(cnt.v_vnodein); PCPU_ADD(cnt.v_vnodepgsin, npages); + count = npages << PAGE_SHIFT; iov.iov_base = (caddr_t) kva; iov.iov_len = count; uio.uio_iov = &iov; @@ -178,16 +168,7 @@ ncl_getpages(struct vop_getpages_args *ap) relpbuf(bp, &ncl_pbuf_freecnt); if (error && (uio.uio_resid == count)) { - ncl_printf("nfs_getpages: error %d\n", error); - VM_OBJECT_WLOCK(object); - for (i = 0; i < npages; ++i) { - if (i != ap->a_reqpage) { - vm_page_lock(pages[i]); - vm_page_free(pages[i]); - vm_page_unlock(pages[i]); - } - } - VM_OBJECT_WUNLOCK(object); + printf("ncl_getpages: error %d\n", error); return (VM_PAGER_ERROR); } @@ -222,7 +203,7 @@ ncl_getpages(struct vop_getpages_args *ap) } else { /* * Read operation was short. If no error - * occured we may have hit a zero-fill + * occurred we may have hit a zero-fill * section. We leave valid set to 0, and page * is freed by vm_page_readahead_finish() if * its index is not equal to requested, or @@ -231,11 +212,14 @@ ncl_getpages(struct vop_getpages_args *ap) */ ; } - if (i != ap->a_reqpage) - vm_page_readahead_finish(m); } +out: VM_OBJECT_WUNLOCK(object); - return (0); + if (ap->a_rbehind) + *ap->a_rbehind = 0; + if (ap->a_rahead) + *ap->a_rahead = 0; + return (VM_PAGER_OK); } /* @@ -285,7 +269,7 @@ ncl_putpages(struct vop_putpages_args *ap) if (newnfs_directio_enable && !newnfs_directio_allow_mmap && (np->n_flag & NNONCACHE) && (vp->v_type == VREG)) { mtx_unlock(&np->n_mtx); - ncl_printf("ncl_putpages: called on noncache-able vnode??\n"); + printf("ncl_putpages: called on noncache-able vnode\n"); mtx_lock(&np->n_mtx); } @@ -304,7 +288,7 @@ ncl_putpages(struct vop_putpages_args *ap) /* * We use only the kva address for the buffer, but this is extremely - * convienient and fast. + * convenient and fast. */ bp = getpbuf(&ncl_pbuf_freecnt); @@ -484,7 +468,7 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred) switch (vp->v_type) { case VREG: - NFSINCRGLOBAL(newnfsstats.biocache_reads); + NFSINCRGLOBAL(nfsstatsv1.biocache_reads); lbn = uio->uio_offset / biosize; on = uio->uio_offset - (lbn * biosize); @@ -561,7 +545,7 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred) n = MIN((unsigned)(bcount - on), uio->uio_resid); break; case VLNK: - NFSINCRGLOBAL(newnfsstats.biocache_readlinks); + NFSINCRGLOBAL(nfsstatsv1.biocache_readlinks); bp = nfs_getcacheblk(vp, (daddr_t)0, NFS_MAXPATHLEN, td); if (!bp) { error = newnfs_sigintr(nmp, td); @@ -581,7 +565,7 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred) on = 0; break; case VDIR: - NFSINCRGLOBAL(newnfsstats.biocache_readdirs); + NFSINCRGLOBAL(nfsstatsv1.biocache_readdirs); if (np->n_direofoffset && uio->uio_offset >= np->n_direofoffset) { return (0); @@ -696,10 +680,10 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred) n = np->n_direofoffset - uio->uio_offset; break; default: - ncl_printf(" ncl_bioread: type %x unexpected\n", vp->v_type); + printf(" ncl_bioread: type %x unexpected\n", vp->v_type); bp = NULL; break; - }; + } if (n > 0) { error = vn_io_fault_uiomove(bp->b_data + on, (int)n, uio); @@ -876,8 +860,8 @@ ncl_write(struct vop_write_args *ap) struct vattr vattr; struct nfsmount *nmp = VFSTONFS(vp->v_mount); daddr_t lbn; - int bcount; - int bp_cached, n, on, error = 0, error1; + int bcount, noncontig_write, obcount; + int bp_cached, n, on, error = 0, error1, wouldcommit; size_t orig_resid, local_resid; off_t orig_size, tmp_off; @@ -921,7 +905,6 @@ ncl_write(struct vop_write_args *ap) if (ioflag & IO_NDELAY) return (EAGAIN); #endif -flush_and_restart: np->n_attrstamp = 0; KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); error = ncl_vinvalbuf(vp, V_SAVE, td, 1); @@ -978,27 +961,14 @@ flush_and_restart: * IO_UNIT -- we just make all writes atomic anyway, as there's * no point optimizing for something that really won't ever happen. */ + wouldcommit = 0; if (!(ioflag & IO_SYNC)) { int nflag; mtx_lock(&np->n_mtx); nflag = np->n_flag; mtx_unlock(&np->n_mtx); - int needrestart = 0; - if (nmp->nm_wcommitsize < uio->uio_resid) { - /* - * If this request could not possibly be completed - * without exceeding the maximum outstanding write - * commit size, see if we can convert it into a - * synchronous write operation. - */ - if (ioflag & IO_NDELAY) - return (EAGAIN); - ioflag |= IO_SYNC; - if (nflag & NMODIFIED) - needrestart = 1; - } else if (nflag & NMODIFIED) { - int wouldcommit = 0; + if (nflag & NMODIFIED) { BO_LOCK(&vp->v_bufobj); if (vp->v_bufobj.bo_dirty.bv_cnt != 0) { TAILQ_FOREACH(bp, &vp->v_bufobj.bo_dirty.bv_hd, @@ -1008,28 +978,23 @@ flush_and_restart: } } BO_UNLOCK(&vp->v_bufobj); - /* - * Since we're not operating synchronously and - * bypassing the buffer cache, we are in a commit - * and holding all of these buffers whether - * transmitted or not. If not limited, this - * will lead to the buffer cache deadlocking, - * as no one else can flush our uncommitted buffers. - */ - wouldcommit += uio->uio_resid; - /* - * If we would initially exceed the maximum - * outstanding write commit size, flush and restart. - */ - if (wouldcommit > nmp->nm_wcommitsize) - needrestart = 1; } - if (needrestart) - goto flush_and_restart; } do { - NFSINCRGLOBAL(newnfsstats.biocache_writes); + if (!(ioflag & IO_SYNC)) { + wouldcommit += biosize; + if (wouldcommit > nmp->nm_wcommitsize) { + np->n_attrstamp = 0; + KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); + error = ncl_vinvalbuf(vp, V_SAVE, td, 1); + if (error) + return (error); + wouldcommit = biosize; + } + } + + NFSINCRGLOBAL(nfsstatsv1.biocache_writes); lbn = uio->uio_offset / biosize; on = uio->uio_offset - (lbn * biosize); n = MIN((unsigned)(biosize - on), uio->uio_resid); @@ -1039,7 +1004,15 @@ again: * unaligned buffer size. */ mtx_lock(&np->n_mtx); - if (uio->uio_offset == np->n_size && n) { + if ((np->n_flag & NHASBEENLOCKED) == 0 && + (nmp->nm_flag & NFSMNT_NONCONTIGWR) != 0) + noncontig_write = 1; + else + noncontig_write = 0; + if ((uio->uio_offset == np->n_size || + (noncontig_write != 0 && + lbn == (np->n_size / biosize) && + uio->uio_offset + n > np->n_size)) && n) { mtx_unlock(&np->n_mtx); /* * Get the buffer (in its pre-append state to maintain @@ -1047,8 +1020,8 @@ again: * nfsnode after we have locked the buffer to prevent * readers from reading garbage. */ - bcount = on; - bp = nfs_getcacheblk(vp, lbn, bcount, td); + obcount = np->n_size - (lbn * biosize); + bp = nfs_getcacheblk(vp, lbn, obcount, td); if (bp != NULL) { long save; @@ -1060,9 +1033,12 @@ again: mtx_unlock(&np->n_mtx); save = bp->b_flags & B_CACHE; - bcount += n; + bcount = on + n; allocbuf(bp, bcount); bp->b_flags |= save; + if (noncontig_write != 0 && on > obcount) + vfs_bio_bzero_buf(bp, obcount, on - + obcount); } } else { /* @@ -1147,7 +1123,7 @@ again: */ if (bp->b_dirtyend > bcount) { - ncl_printf("NFS append race @%lx:%d\n", + printf("NFS append race @%lx:%d\n", (long)bp->b_blkno * DEV_BSIZE, bp->b_dirtyend - bcount); bp->b_dirtyend = bcount; @@ -1161,19 +1137,23 @@ again: * area, just update the b_dirtyoff and b_dirtyend, * otherwise force a write rpc of the old dirty area. * + * If there has been a file lock applied to this file + * or vfs.nfs.old_noncontig_writing is set, do the following: * While it is possible to merge discontiguous writes due to * our having a B_CACHE buffer ( and thus valid read data * for the hole), we don't because it could lead to * significant cache coherency problems with multiple clients, * especially if locking is implemented later on. * - * As an optimization we could theoretically maintain - * a linked list of discontinuous areas, but we would still - * have to commit them separately so there isn't much - * advantage to it except perhaps a bit of asynchronization. + * If vfs.nfs.old_noncontig_writing is not set and there has + * not been file locking done on this file: + * Relax coherency a bit for the sake of performance and + * expand the current dirty region to contain the new + * write even if it means we mark some non-dirty data as + * dirty. */ - if (bp->b_dirtyend > 0 && + if (noncontig_write == 0 && bp->b_dirtyend > 0 && (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) { if (bwrite(bp) == EINTR) { error = EINTR; @@ -1210,7 +1190,7 @@ again: /* * Get the partial update on the progress made from - * uiomove, if an error occured. + * uiomove, if an error occurred. */ if (error != 0) n = local_resid - uio->uio_resid; @@ -1502,7 +1482,7 @@ again: } /* * We might have lost our iod while sleeping, - * so check and loop if nescessary. + * so check and loop if necessary. */ goto again; } @@ -1628,7 +1608,7 @@ ncl_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td, switch (vp->v_type) { case VREG: uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; - NFSINCRGLOBAL(newnfsstats.read_bios); + NFSINCRGLOBAL(nfsstatsv1.read_bios); error = ncl_readrpc(vp, uiop, cr); if (!error) { @@ -1663,11 +1643,11 @@ ncl_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td, break; case VLNK: uiop->uio_offset = (off_t)0; - NFSINCRGLOBAL(newnfsstats.readlink_bios); + NFSINCRGLOBAL(nfsstatsv1.readlink_bios); error = ncl_readlinkrpc(vp, uiop, cr); break; case VDIR: - NFSINCRGLOBAL(newnfsstats.readdir_bios); + NFSINCRGLOBAL(nfsstatsv1.readdir_bios); uiop->uio_offset = ((u_quad_t)bp->b_lblkno) * NFS_DIRBLKSIZ; if ((nmp->nm_flag & NFSMNT_RDIRPLUS) != 0) { error = ncl_readdirplusrpc(vp, uiop, cr, td); @@ -1684,9 +1664,9 @@ ncl_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td, bp->b_flags |= B_INVAL; break; default: - ncl_printf("ncl_doio: type %x unexpected\n", vp->v_type); + printf("ncl_doio: type %x unexpected\n", vp->v_type); break; - }; + } if (error) { bp->b_ioflags |= BIO_ERROR; bp->b_error = error; @@ -1729,7 +1709,7 @@ ncl_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td, + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; - NFSINCRGLOBAL(newnfsstats.write_bios); + NFSINCRGLOBAL(nfsstatsv1.write_bios); if ((bp->b_flags & (B_ASYNC | B_NEEDCOMMIT | B_NOCACHE | B_CLUSTER)) == B_ASYNC) iomode = NFSWRITE_UNSTABLE; diff --git a/sys/fs/nfs/client/nfs_clstate.c b/sys/fs/nfs/client/nfs_clstate.c index b4c9e0936d6..ac4911d57bb 100644 --- a/sys/fs/nfs/client/nfs_clstate.c +++ b/sys/fs/nfs/client/nfs_clstate.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_clstate.c,v 1.2 2014/10/18 08:33:28 snj Exp $ */ +/* $NetBSD: nfs_clstate.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 2009 Rick Macklem, University of Guelph * All rights reserved. @@ -27,8 +27,8 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/fs/nfsclient/nfs_clstate.c 252100 2013-06-22 21:58:21Z rmacklem "); */ -__RCSID("$NetBSD: nfs_clstate.c,v 1.2 2014/10/18 08:33:28 snj Exp $"); +/* __FBSDID("FreeBSD: head/sys/fs/nfsclient/nfs_clstate.c 304026 2016-08-12 22:44:59Z rmacklem "); */ +__RCSID("$NetBSD: nfs_clstate.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); /* * These functions implement the client side state handling for NFSv4. @@ -86,7 +86,7 @@ __RCSID("$NetBSD: nfs_clstate.c,v 1.2 2014/10/18 08:33:28 snj Exp $"); /* * Global variables */ -extern struct nfsstats newnfsstats; +extern struct nfsstatsv1 nfsstatsv1; extern struct nfsreqhead nfsd_reqq; extern u_int32_t newnfs_false, newnfs_true; extern int nfscl_debuglevel; @@ -148,7 +148,7 @@ static int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *, static int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t, struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *); static void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *); -static int nfscl_errmap(struct nfsrv_descript *); +static int nfscl_errmap(struct nfsrv_descript *, u_int32_t); static void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *); static int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *, struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int); @@ -283,6 +283,23 @@ nfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg, newonep); /* + * Now, check the mode on the open and return the appropriate + * value. + */ + if (retp != NULL) { + if (nfhp != NULL && dp != NULL && nop == NULL) + /* new local open on delegation */ + *retp = NFSCLOPEN_SETCRED; + else + *retp = NFSCLOPEN_OK; + } + if (op != NULL && (amode & ~(op->nfso_mode))) { + op->nfso_mode |= amode; + if (retp != NULL && dp == NULL) + *retp = NFSCLOPEN_DOOPEN; + } + + /* * Serialize modifications to the open owner for multiple threads * within the same process using a read/write sleep lock. */ @@ -297,23 +314,6 @@ nfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg, *owpp = owp; if (opp != NULL) *opp = op; - if (retp != NULL) { - if (nfhp != NULL && dp != NULL && nop == NULL) - /* new local open on delegation */ - *retp = NFSCLOPEN_SETCRED; - else - *retp = NFSCLOPEN_OK; - } - - /* - * Now, check the mode on the open and return the appropriate - * value. - */ - if (op != NULL && (amode & ~(op->nfso_mode))) { - op->nfso_mode |= amode; - if (retp != NULL && dp == NULL) - *retp = NFSCLOPEN_DOOPEN; - } return (0); } @@ -345,10 +345,10 @@ nfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp, nowp->nfsow_defunct = 0; nfscl_lockinit(&nowp->nfsow_rwlock); if (dp != NULL) { - newnfsstats.cllocalopenowners++; + nfsstatsv1.cllocalopenowners++; LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list); } else { - newnfsstats.clopenowners++; + nfsstatsv1.clopenowners++; LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list); } owp = *owpp = nowp; @@ -382,9 +382,9 @@ nfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp, TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list); dp->nfsdl_timestamp = NFSD_MONOSEC + 120; - newnfsstats.cllocalopens++; + nfsstatsv1.cllocalopens++; } else { - newnfsstats.clopens++; + nfsstatsv1.clopens++; } LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list); *opp = nop; @@ -432,7 +432,7 @@ nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp, LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp, nfsdl_hash); dp->nfsdl_timestamp = NFSD_MONOSEC + 120; - newnfsstats.cldelegates++; + nfsstatsv1.cldelegates++; nfscl_delegcnt++; } else { /* @@ -718,7 +718,7 @@ nfscl_openrelease(struct nfsclopen *op, int error, int candelete) /* * Called to get a clientid structure. It will optionally lock the * client data structures to do the SetClientId/SetClientId_confirm, - * but will release that lock and return the clientid with a refernce + * but will release that lock and return the clientid with a reference * count on it. * If the "cred" argument is NULL, a new clientid should not be created. * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot @@ -1073,10 +1073,10 @@ nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len, LIST_INIT(&nlp->nfsl_lock); if (donelocally) { nlp->nfsl_open = NULL; - newnfsstats.cllocallockowners++; + nfsstatsv1.cllocallockowners++; } else { nlp->nfsl_open = op; - newnfsstats.cllockowners++; + nfsstatsv1.cllockowners++; } LIST_INSERT_HEAD(lhp, nlp, nfsl_list); lp = nlp; @@ -1299,7 +1299,7 @@ nfscl_checkwritelocked(vnode_t vp, struct flock *fl, break; default: return (1); - }; + } if (fl->l_len != 0) { end = off + fl->l_len; if (end < off) @@ -1404,9 +1404,9 @@ nfscl_freeopen(struct nfsclopen *op, int local) nfscl_freealllocks(&op->nfso_lock, local); FREE((caddr_t)op, M_NFSCLOPEN); if (local) - newnfsstats.cllocalopens--; + nfsstatsv1.cllocalopens--; else - newnfsstats.clopens--; + nfsstatsv1.clopens--; } /* @@ -1485,9 +1485,9 @@ nfscl_freeopenowner(struct nfsclowner *owp, int local) LIST_REMOVE(owp, nfsow_list); FREE((caddr_t)owp, M_NFSCLOWNER); if (local) - newnfsstats.cllocalopenowners--; + nfsstatsv1.cllocalopenowners--; else - newnfsstats.clopenowners--; + nfsstatsv1.clopenowners--; } /* @@ -1504,9 +1504,9 @@ nfscl_freelockowner(struct nfscllockowner *lp, int local) } FREE((caddr_t)lp, M_NFSCLLOCKOWNER); if (local) - newnfsstats.cllocallockowners--; + nfsstatsv1.cllocallockowners--; else - newnfsstats.cllockowners--; + nfsstatsv1.cllockowners--; } /* @@ -1519,9 +1519,9 @@ nfscl_freelock(struct nfscllock *lop, int local) LIST_REMOVE(lop, nfslo_list); FREE((caddr_t)lop, M_NFSCLLOCK); if (local) - newnfsstats.cllocallocks--; + nfsstatsv1.cllocallocks--; else - newnfsstats.cllocks--; + nfsstatsv1.cllocks--; } /* @@ -1555,7 +1555,7 @@ nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp) TAILQ_REMOVE(hdp, dp, nfsdl_list); LIST_REMOVE(dp, nfsdl_hash); FREE((caddr_t)dp, M_NFSCLDELEG); - newnfsstats.cldelegates--; + nfsstatsv1.cldelegates--; nfscl_delegcnt--; } @@ -1623,18 +1623,18 @@ nfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp, LIST_REMOVE(op, nfso_list); op->nfso_own = towp; LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list); - newnfsstats.cllocalopens--; - newnfsstats.clopens++; + nfsstatsv1.cllocalopens--; + nfsstatsv1.clopens++; } } else { /* Just add the openowner to the client list */ LIST_REMOVE(owp, nfsow_list); owp->nfsow_clp = clp; LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list); - newnfsstats.cllocalopenowners--; - newnfsstats.clopenowners++; - newnfsstats.cllocalopens--; - newnfsstats.clopens++; + nfsstatsv1.cllocalopenowners--; + nfsstatsv1.clopenowners++; + nfsstatsv1.cllocalopens--; + nfsstatsv1.clopens++; } } owp = nowp; @@ -2284,9 +2284,9 @@ nfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop, else LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list); if (local) - newnfsstats.cllocallocks++; + nfsstatsv1.cllocallocks++; else - newnfsstats.cllocks++; + nfsstatsv1.cllocks++; } /* @@ -2573,7 +2573,7 @@ tryagain: LIST_REMOVE(dp, nfsdl_hash); TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list); nfscl_delegcnt--; - newnfsstats.cldelegates--; + nfsstatsv1.cldelegates--; } NFSLOCKCLSTATE(); } @@ -2614,7 +2614,7 @@ tryagain: LIST_REMOVE(dp, nfsdl_hash); TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list); nfscl_delegcnt--; - newnfsstats.cldelegates--; + nfsstatsv1.cldelegates--; } } dp = ndp; @@ -3148,7 +3148,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) struct nfsclclient *clp; struct nfscldeleg *dp = NULL; int numops, taglen = -1, error = 0, trunc; - u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident; + u_int32_t minorvers = 0, retops = 0, *retopsp = NULL, *repp, cbident; u_char tag[NFSV4_SMALLSTR + 1], *tagstr; vnode_t vp = NULL; struct nfsnode *np; @@ -3212,13 +3212,13 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) (op > NFSV4OP_CBNOTIFYDEVID && minorvers == NFSV41_MINORVERSION)) { nd->nd_repstat = NFSERR_OPILLEGAL; - *repp = nfscl_errmap(nd); + *repp = nfscl_errmap(nd, minorvers); retops++; break; } nd->nd_procnum = op; - if (op < NFSV4OP_CBNOPS) - newnfsstats.cbrpccnt[nd->nd_procnum]++; + if (op < NFSV41_CBNOPS) + nfsstatsv1.cbrpccnt[nd->nd_procnum]++; switch (op) { case NFSV4OP_CBGETATTR: NFSCL_DEBUG(4, "cbgetattr\n"); @@ -3281,7 +3281,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) FREE((caddr_t)nfhp, M_NFSFH); if (!error) (void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va, - NULL, 0, &rattrbits, NULL, NULL, 0, 0, 0, 0, + NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0, (uint64_t)0); break; case NFSV4OP_CBRECALL: @@ -3509,7 +3509,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) error = NFSERR_NOTSUPP; } break; - }; + } if (error) { if (error == EBADRPC || error == NFSERR_BADXDR) { nd->nd_repstat = NFSERR_BADXDR; @@ -3520,7 +3520,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) } retops++; if (nd->nd_repstat) { - *repp = nfscl_errmap(nd); + *repp = nfscl_errmap(nd, minorvers); break; } else *repp = 0; /* NFS4_OK */ @@ -3541,7 +3541,7 @@ nfsmout: } else { *retopsp = txdr_unsigned(retops); } - *nd->nd_errp = nfscl_errmap(nd); + *nd->nd_errp = nfscl_errmap(nd, minorvers); out: if (gotseq_ok != 0) { rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK); @@ -3550,7 +3550,7 @@ out: if (clp != NULL) { nfsv4_seqsess_cacherep(slotid, NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_cbslots, - rep); + NFSERR_OK, &rep); NFSUNLOCKCLSTATE(); } else { NFSUNLOCKCLSTATE(); @@ -4647,7 +4647,7 @@ nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime) } static int -nfscl_errmap(struct nfsrv_descript *nd) +nfscl_errmap(struct nfsrv_descript *nd, u_int32_t minorvers) { short *defaulterrp, *errp; @@ -4660,6 +4660,11 @@ nfscl_errmap(struct nfsrv_descript *nd) if (nd->nd_repstat == NFSERR_MINORVERMISMATCH || nd->nd_repstat == NFSERR_OPILLEGAL) return (txdr_unsigned(nd->nd_repstat)); + if (nd->nd_repstat >= NFSERR_BADIOMODE && nd->nd_repstat < 20000 && + minorvers > NFSV4_MINORVERSION) { + /* NFSv4.n error. */ + return (txdr_unsigned(nd->nd_repstat)); + } if (nd->nd_procnum < NFSV4OP_CBNOPS) errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum]; else diff --git a/sys/fs/nfs/client/nfs_clvnops.c b/sys/fs/nfs/client/nfs_clvnops.c index 000ced7781b..1906fe9407c 100644 --- a/sys/fs/nfs/client/nfs_clvnops.c +++ b/sys/fs/nfs/client/nfs_clvnops.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_clvnops.c,v 1.2 2014/03/25 16:30:28 christos Exp $ */ +/* $NetBSD: nfs_clvnops.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -34,14 +34,13 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/fs/nfsclient/nfs_clvnops.c 252072 2013-06-21 22:26:18Z rmacklem "); */ -__RCSID("$NetBSD: nfs_clvnops.c,v 1.2 2014/03/25 16:30:28 christos Exp $"); +/* __FBSDID("FreeBSD: head/sys/fs/nfsclient/nfs_clvnops.c 304026 2016-08-12 22:44:59Z rmacklem "); */ +__RCSID("$NetBSD: nfs_clvnops.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); /* * vnode op calls for Sun NFS version 2, 3 and 4 */ -#include "opt_kdtrace.h" #include "opt_inet.h" #include <sys/param.h> @@ -103,7 +102,7 @@ uint32_t nfscl_accesscache_load_done_id; #define TRUE 1 #define FALSE 0 -extern struct nfsstats newnfsstats; +extern struct nfsstatsv1 nfsstatsv1; extern int nfsrv_useacl; extern int nfscl_debuglevel; MALLOC_DECLARE(M_NEWNFSREQ); @@ -141,7 +140,6 @@ static vop_rmdir_t nfs_rmdir; static vop_symlink_t nfs_symlink; static vop_readdir_t nfs_readdir; static vop_strategy_t nfs_strategy; -static vop_lock1_t nfs_lock1; static int nfs_lookitup(struct vnode *, char *, int, struct ucred *, struct thread *, struct nfsnode **); static int nfs_sillyrename(struct vnode *, struct vnode *, @@ -170,7 +168,6 @@ struct vop_vector newnfs_vnodeops = { .vop_putpages = ncl_putpages, .vop_inactive = ncl_inactive, .vop_link = nfs_link, - .vop_lock1 = nfs_lock1, .vop_lookup = nfs_lookup, .vop_mkdir = nfs_mkdir, .vop_mknod = nfs_mknod, @@ -263,14 +260,6 @@ int newnfs_directio_allow_mmap = 1; SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_directio_allow_mmap, CTLFLAG_RW, &newnfs_directio_allow_mmap, 0, "Enable mmaped IO on file with O_DIRECT opens"); -#if 0 -SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_hits, CTLFLAG_RD, - &newnfsstats.accesscache_hits, 0, "NFS ACCESS cache hit count"); - -SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_misses, CTLFLAG_RD, - &newnfsstats.accesscache_misses, 0, "NFS ACCESS cache miss count"); -#endif - #define NFSACCESS_ALL (NFSACCESS_READ | NFSACCESS_MODIFY \ | NFSACCESS_EXTEND | NFSACCESS_EXECUTE \ | NFSACCESS_DELETE | NFSACCESS_LOOKUP) @@ -423,7 +412,7 @@ nfs_access(struct vop_access_args *ap) if (time_second < (np->n_accesscache[i].stamp + nfsaccess_cache_timeout) && (np->n_accesscache[i].mode & mode) == mode) { - NFSINCRGLOBAL(newnfsstats.accesscache_hits); + NFSINCRGLOBAL(nfsstatsv1.accesscache_hits); gotahit = 1; } break; @@ -442,7 +431,7 @@ nfs_access(struct vop_access_args *ap) /* * Either a no, or a don't know. Go to the wire. */ - NFSINCRGLOBAL(newnfsstats.accesscache_misses); + NFSINCRGLOBAL(nfsstatsv1.accesscache_misses); error = nfs34_access_otw(vp, wmode, ap->a_td, ap->a_cred, &rmode); if (!error && @@ -770,7 +759,9 @@ nfs_close(struct vop_close_args *ap) /* * Get attributes so "change" is up to date. */ - if (error == 0 && nfscl_mustflush(vp) != 0) { + if (error == 0 && nfscl_mustflush(vp) != 0 && + vp->v_type == VREG && + (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOCTO) == 0) { ret = nfsrpc_getattr(vp, cred, ap->a_td, &nfsva, NULL); if (!ret) { @@ -860,7 +851,7 @@ nfs_getattr(struct vop_getattr_args *ap) if (NFS_ISV34(vp) && nfs_prime_access_cache && nfsaccess_cache_timeout > 0) { - NFSINCRGLOBAL(newnfsstats.accesscache_misses); + NFSINCRGLOBAL(nfsstatsv1.accesscache_misses); nfs34_access_otw(vp, NFSACCESS_ALL, td, ap->a_cred, NULL); if (ncl_getattrcache(vp, ap->a_vap) == 0) { nfscl_deleggetmodtime(vp, &ap->a_vap->va_mtime); @@ -975,7 +966,7 @@ nfs_setattr(struct vop_setattr_args *ap) mtx_lock(&np->n_mtx); np->n_vattr.na_size = np->n_size = vap->va_size; mtx_unlock(&np->n_mtx); - }; + } } else { mtx_lock(&np->n_mtx); if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) && @@ -1117,7 +1108,7 @@ nfs_lookup(struct vop_lookup_args *ap) ((u_int)(ticks - ncticks) < (nmp->nm_nametimeo * hz) && VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 && timespeccmp(&vattr.va_ctime, &nctime, ==))) { - NFSINCRGLOBAL(newnfsstats.lookupcache_hits); + NFSINCRGLOBAL(nfsstatsv1.lookupcache_hits); if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; @@ -1144,7 +1135,7 @@ nfs_lookup(struct vop_lookup_args *ap) if ((u_int)(ticks - ncticks) < (nmp->nm_negnametimeo * hz) && VOP_GETATTR(dvp, &vattr, cnp->cn_cred) == 0 && timespeccmp(&vattr.va_mtime, &nctime, ==)) { - NFSINCRGLOBAL(newnfsstats.lookupcache_hits); + NFSINCRGLOBAL(nfsstatsv1.lookupcache_hits); return (ENOENT); } cache_purge_negative(dvp); @@ -1152,7 +1143,7 @@ nfs_lookup(struct vop_lookup_args *ap) error = 0; newvp = NULLVP; - NFSINCRGLOBAL(newnfsstats.lookupcache_misses); + NFSINCRGLOBAL(nfsstatsv1.lookupcache_misses); error = nfsrpc_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, td, &dnfsva, &nfsva, &nfhp, &attrflag, &dattrflag, NULL); @@ -1185,8 +1176,7 @@ nfs_lookup(struct vop_lookup_args *ap) return (EJUSTRETURN); } - if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE && - dattrflag) { + if ((cnp->cn_flags & MAKEENTRY) != 0 && dattrflag) { /* * Cache the modification time of the parent * directory from the post-op attributes in @@ -1608,20 +1598,6 @@ again: } } else if (NFS_ISV34(dvp) && (fmode & O_EXCL)) { if (nfscl_checksattr(vap, &nfsva)) { - /* - * We are normally called with only a partially - * initialized VAP. Since the NFSv3 spec says that - * the server may use the file attributes to - * store the verifier, the spec requires us to do a - * SETATTR RPC. FreeBSD servers store the verifier in - * atime, but we can't really assume that all servers - * will so we ensure that our SETATTR sets both atime - * and mtime. - */ - if (vap->va_mtime.tv_sec == VNOVAL) - vfs_timestamp(&vap->va_mtime); - if (vap->va_atime.tv_sec == VNOVAL) - vap->va_atime = vap->va_mtime; error = nfsrpc_setattr(newvp, vap, NULL, cnp->cn_cred, cnp->cn_thread, &nfsva, &attrflag, NULL); if (error && (vap->va_uid != (uid_t)VNOVAL || @@ -1803,7 +1779,7 @@ nfs_rename(struct vop_rename_args *ap) } if (fvp == tvp) { - ncl_printf("nfs_rename: fvp == tvp (can't happen)\n"); + printf("nfs_rename: fvp == tvp (can't happen)\n"); error = 0; goto out; } @@ -1977,10 +1953,6 @@ nfs_link(struct vop_link_args *ap) struct nfsvattr nfsva, dnfsva; int error = 0, attrflag, dattrflag; - if (vp->v_mount != tdvp->v_mount) { - return (EXDEV); - } - /* * Push all writes to the server, so that the attribute cache * doesn't get "out of sync" with the server. @@ -2230,7 +2202,7 @@ nfs_readdir(struct vop_readdir_args *ap) struct vnode *vp = ap->a_vp; struct nfsnode *np = VTONFS(vp); struct uio *uio = ap->a_uio; - ssize_t tresid; + ssize_t tresid, left; int error = 0; struct vattr vattr; @@ -2249,7 +2221,7 @@ nfs_readdir(struct vop_readdir_args *ap) if ((NFS_ISV4(vp) && np->n_change == vattr.va_filerev) || !NFS_TIMESPEC_COMPARE(&np->n_mtime, &vattr.va_mtime)) { mtx_unlock(&np->n_mtx); - NFSINCRGLOBAL(newnfsstats.direofcache_hits); + NFSINCRGLOBAL(nfsstatsv1.direofcache_hits); if (ap->a_eofflag != NULL) *ap->a_eofflag = 1; return (0); @@ -2259,16 +2231,30 @@ nfs_readdir(struct vop_readdir_args *ap) } /* + * NFS always guarantees that directory entries don't straddle + * DIRBLKSIZ boundaries. As such, we need to limit the size + * to an exact multiple of DIRBLKSIZ, to avoid copying a partial + * directory entry. + */ + left = uio->uio_resid % DIRBLKSIZ; + if (left == uio->uio_resid) + return (EINVAL); + uio->uio_resid -= left; + + /* * Call ncl_bioread() to do the real work. */ tresid = uio->uio_resid; error = ncl_bioread(vp, uio, 0, ap->a_cred); if (!error && uio->uio_resid == tresid) { - NFSINCRGLOBAL(newnfsstats.direofcache_misses); + NFSINCRGLOBAL(nfsstatsv1.direofcache_misses); if (ap->a_eofflag != NULL) *ap->a_eofflag = 1; } + + /* Add the partial DIRBLKSIZ (left) back in. */ + uio->uio_resid += left; return (error); } @@ -2321,7 +2307,7 @@ ncl_readdirrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred, dnp->n_direofoffset = uiop->uio_offset; else { if (uiop->uio_resid > 0) - ncl_printf("EEK! readdirrpc resid > 0\n"); + printf("EEK! readdirrpc resid > 0\n"); ncl_dircookie_lock(dnp); cookiep = ncl_getcookie(dnp, uiop->uio_offset, 1); *cookiep = cookie; @@ -2380,7 +2366,7 @@ ncl_readdirplusrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred, dnp->n_direofoffset = uiop->uio_offset; else { if (uiop->uio_resid > 0) - ncl_printf("EEK! readdirplusrpc resid > 0\n"); + printf("EEK! readdirplusrpc resid > 0\n"); ncl_dircookie_lock(dnp); cookiep = ncl_getcookie(dnp, uiop->uio_offset, 1); *cookiep = cookie; @@ -2420,7 +2406,7 @@ nfs_sillyrename(struct vnode *dvp, struct vnode *vp, struct componentname *cnp) /* * Fudge together a funny name. - * Changing the format of the funny name to accomodate more + * Changing the format of the funny name to accommodate more * sillynames per directory. * The name is now changed to .nfs.<ticks>.<pid>.4, where ticks is * CPU ticks since boot. @@ -3081,6 +3067,10 @@ nfs_advlock(struct vop_advlock_args *ap) np->n_change = va.va_filerev; } } + /* Mark that a file lock has been acquired. */ + mtx_lock(&np->n_mtx); + np->n_flag |= NHASBEENLOCKED; + mtx_unlock(&np->n_mtx); } NFSVOPUNLOCK(vp, 0); return (0); @@ -3100,6 +3090,16 @@ nfs_advlock(struct vop_advlock_args *ap) error = ENOLCK; } } + if (error == 0 && ap->a_op == F_SETLK) { + error = NFSVOPLOCK(vp, LK_SHARED); + if (error == 0) { + /* Mark that a file lock has been acquired. */ + mtx_lock(&np->n_mtx); + np->n_flag |= NHASBEENLOCKED; + mtx_unlock(&np->n_mtx); + NFSVOPUNLOCK(vp, 0); + } + } } return (error); } @@ -3139,8 +3139,8 @@ nfs_print(struct vop_print_args *ap) struct vnode *vp = ap->a_vp; struct nfsnode *np = VTONFS(vp); - ncl_printf("\tfileid %ld fsid 0x%x", - np->n_vattr.na_fileid, np->n_vattr.na_fsid); + printf("\tfileid %ld fsid 0x%x", np->n_vattr.na_fileid, + np->n_vattr.na_fsid); if (vp->v_type == VFIFO) fifo_printinfo(vp); printf("\n"); @@ -3345,37 +3345,6 @@ struct buf_ops buf_ops_newnfs = { .bop_bdflush = bufbdflush, }; -/* - * Cloned from vop_stdlock(), and then the ugly hack added. - */ -static int -nfs_lock1(struct vop_lock1_args *ap) -{ - struct vnode *vp = ap->a_vp; - int error = 0; - - /* - * Since vfs_hash_get() calls vget() and it will no longer work - * for FreeBSD8 with flags == 0, I can only think of this horrible - * hack to work around it. I call vfs_hash_get() with LK_EXCLOTHER - * and then handle it here. All I want for this case is a v_usecount - * on the vnode to use for recovery, while another thread might - * hold a lock on the vnode. I have the other threads blocked, so - * there isn't any race problem. - */ - if ((ap->a_flags & LK_TYPE_MASK) == LK_EXCLOTHER) { - if ((ap->a_flags & LK_INTERLOCK) == 0) - panic("ncllock1"); - if ((vp->v_iflag & VI_DOOMED)) - error = ENOENT; - VI_UNLOCK(vp); - return (error); - } - return (_lockmgr_args(vp->v_vnlock, ap->a_flags, VI_MTX(vp), - LK_WMESG_DEFAULT, LK_PRIO_DEFAULT, LK_TIMO_DEFAULT, ap->a_file, - ap->a_line)); -} - static int nfs_getacl(struct vop_getacl_args *ap) { @@ -3420,12 +3389,15 @@ nfs_pathconf(struct vop_pathconf_args *ap) struct thread *td = curthread; int attrflag, error; - if (NFS_ISV4(vp) || (NFS_ISV3(vp) && (ap->a_name == _PC_LINK_MAX || + if ((NFS_ISV34(vp) && (ap->a_name == _PC_LINK_MAX || ap->a_name == _PC_NAME_MAX || ap->a_name == _PC_CHOWN_RESTRICTED || - ap->a_name == _PC_NO_TRUNC))) { + ap->a_name == _PC_NO_TRUNC)) || + (NFS_ISV4(vp) && ap->a_name == _PC_ACL_NFS4)) { /* * Since only the above 4 a_names are returned by the NFSv3 * Pathconf RPC, there is no point in doing it for others. + * For NFSv4, the Pathconf RPC (actually a Getattr Op.) can + * be used for _PC_NFS4_ACL as well. */ error = nfsrpc_pathconf(vp, &pc, td->td_ucred, td, &nfsva, &attrflag, NULL); diff --git a/sys/fs/nfs/common/krpc_subr.c b/sys/fs/nfs/common/krpc_subr.c index 3caec4a9ac2..e54ad8ce1f9 100644 --- a/sys/fs/nfs/common/krpc_subr.c +++ b/sys/fs/nfs/common/krpc_subr.c @@ -43,8 +43,8 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/nfs/krpc_subr.c 248207 2013-03-12 13:42:47Z glebius "); */ -__RCSID("$NetBSD: krpc_subr.c,v 1.4 2016/06/10 13:27:15 ozaki-r Exp $"); +/* __FBSDID("FreeBSD: head/sys/nfs/krpc_subr.c 298788 2016-04-29 16:07:25Z pfg "); */ +__RCSID("$NetBSD: krpc_subr.c,v 1.5 2016/11/18 08:31:30 pgoyette Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -219,10 +219,10 @@ krpc_call(struct sockaddr_in *sa, u_int prog, u_int vers, u_int func, from = NULL; /* - * Create socket and set its recieve timeout. + * Create socket and set its receive timeout. */ if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0, td->td_ucred, td))) - goto out; + return error; tv.tv_sec = 1; tv.tv_usec = 0; diff --git a/sys/fs/nfs/common/nfs_fha.h b/sys/fs/nfs/common/nfs_fha.h index 9f47df52c74..a64c5ea379b 100644 --- a/sys/fs/nfs/common/nfs_fha.h +++ b/sys/fs/nfs/common/nfs_fha.h @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_fha.h,v 1.2 2016/07/07 06:55:42 msaitoh Exp $ */ +/* $NetBSD: nfs_fha.h,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -/* FreeBSD: head/sys/nfs/nfs_fha.h 249596 2013-04-17 22:42:43Z ken */ -/* $NetBSD: nfs_fha.h,v 1.2 2016/07/07 06:55:42 msaitoh Exp $ */ +/* FreeBSD: head/sys/nfs/nfs_fha.h 260097 2013-12-30 20:23:15Z mav */ +/* $NetBSD: nfs_fha.h,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ #ifndef _NFS_FHA_H #define _NFS_FHA_H 1 @@ -37,11 +37,7 @@ #define FHA_DEF_MAX_NFSDS_PER_FH 8 #define FHA_DEF_MAX_REQS_PER_NFSD 0 /* Unlimited */ -/* This is the global structure that represents the state of the fha system. */ -struct fha_global { - struct fha_hash_entry_list *hashtable; - u_long hashmask; -}; +#define FHA_HASH_SIZE 251 struct fha_ctls { int enable; @@ -64,6 +60,7 @@ struct fha_ctls { * avoid contention between threads over single files. */ struct fha_hash_entry { + struct mtx *mtx; LIST_ENTRY(fha_hash_entry) link; u_int64_t fh; u_int32_t num_rw; @@ -74,6 +71,11 @@ struct fha_hash_entry { LIST_HEAD(fha_hash_entry_list, fha_hash_entry); +struct fha_hash_slot { + struct fha_hash_entry_list list; + struct mtx mtx; +}; + /* A structure used for passing around data internally. */ struct fha_info { u_int64_t fh; @@ -84,7 +86,7 @@ struct fha_info { struct fha_callbacks { rpcproc_t (*get_procnum)(rpcproc_t procnum); int (*realign)(struct mbuf **mb, int malloc_flags); - int (*get_fh)(fhandle_t *fh, int v3, struct mbuf **md, caddr_t *dpos); + int (*get_fh)(uint64_t *fh, int v3, struct mbuf **md, caddr_t *dpos); int (*is_read)(rpcproc_t procnum); int (*is_write)(rpcproc_t procnum); int (*get_offset)(struct mbuf **md, caddr_t *dpos, int v3, struct @@ -95,7 +97,7 @@ struct fha_callbacks { }; struct fha_params { - struct fha_global g_fha; + struct fha_hash_slot fha_hash[FHA_HASH_SIZE]; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; struct fha_ctls ctls; diff --git a/sys/fs/nfs/common/nfsm_subs.h b/sys/fs/nfs/common/nfsm_subs.h index 11893a26c38..15a83f28f62 100644 --- a/sys/fs/nfs/common/nfsm_subs.h +++ b/sys/fs/nfs/common/nfsm_subs.h @@ -1,4 +1,4 @@ -/* $NetBSD: nfsm_subs.h,v 1.2 2016/07/07 06:55:42 msaitoh Exp $ */ +/* $NetBSD: nfsm_subs.h,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -30,8 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * FreeBSD: head/sys/fs/nfs/nfsm_subs.h 249592 2013-04-17 21:00:22Z ken - * $NetBSD: nfsm_subs.h,v 1.2 2016/07/07 06:55:42 msaitoh Exp $ + * FreeBSD: head/sys/fs/nfs/nfsm_subs.h 276780 2015-01-07 17:22:56Z rwatson + * $NetBSD: nfsm_subs.h,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ #ifndef _NFS_NFSM_SUBS_H_ @@ -48,16 +48,6 @@ /* * First define what the actual subs. return */ -#define M_HASCL(m) ((m)->m_flags & M_EXT) -#define NFSMINOFF(m) \ - if (M_HASCL(m)) \ - (m)->m_data = (m)->m_ext.ext_buf; \ - else if ((m)->m_flags & M_PKTHDR) \ - (m)->m_data = (m)->m_pktdat; \ - else \ - (m)->m_data = (m)->m_dat -#define NFSMSIZ(m) ((M_HASCL(m))?MCLBYTES: \ - (((m)->m_flags & M_PKTHDR)?MHLEN:MLEN)) #define NFSM_DATAP(m, s) (m)->m_data += (s) /* diff --git a/sys/fs/nfs/files.newnfs b/sys/fs/nfs/files.newnfs index 7236ce3b47d..05af49698d3 100644 --- a/sys/fs/nfs/files.newnfs +++ b/sys/fs/nfs/files.newnfs @@ -1,15 +1,20 @@ -# $NetBSD: files.newnfs,v 1.3 2016/09/22 15:57:05 christos Exp $ +# $NetBSD: files.newnfs,v 1.4 2016/11/18 08:31:29 pgoyette Exp $ deffs NEW_NFSCLIENT -defflag opt_newnfs.h NEW_NFS_BOOT_BOOTP +defflag opt_newnfs.h DTRACEALL + NEW_NFS_BOOT_BOOTP NEW_NFSSERVER + COMPILE-WITH + "${CDDL_C}" NEW_NFSLOCKD + DTNFSCL NEW_NFS_BOOT # XXX define new_nfsclient: vfs, net file fs/nfs/client/nfs_clbio.c new_nfsclient file fs/nfs/client/nfs_clcomsubs.c new_nfsclient +file fs/nfs/client/nfs_clkdtrace.c dtnfscl & new_nfsclient | dtraceall & new_nfsclient compile-with & "${CDDL_C}" file fs/nfs/client/nfs_clkrpc.c new_nfsclient file fs/nfs/client/nfs_clnfsiod.c new_nfsclient file fs/nfs/client/nfs_clnode.c new_nfsclient @@ -19,8 +24,8 @@ file fs/nfs/client/nfs_clstate.c new_nfsclient file fs/nfs/client/nfs_clsubs.c new_nfsclient file fs/nfs/client/nfs_clvfsops.c new_nfsclient file fs/nfs/client/nfs_clvnops.c new_nfsclient -file fs/nfs/common/bootp_subr.c new_nfs_boot_bootp & new_nfs_boot_bootp & new_nfsclient -file fs/nfs/common/krpc_subr.c new_nfs_boot_bootp & new_nfs_boot_bootp & new_nfsclient +file fs/nfs/common/bootp_subr.c new_nfs_boot_bootp & new_nfsclient +file fs/nfs/common/krpc_subr.c new_nfs_boot_bootp & new_nfsclient file fs/nfs/common/nfs_commonacl.c new_nfsclient | new_nfsserver file fs/nfs/common/nfs_commonkrpc.c new_nfsclient | new_nfsserver file fs/nfs/common/nfs_commonport.c new_nfsclient | new_nfsserver diff --git a/sys/fs/nfs/nlm/nlm_prot_impl.c b/sys/fs/nfs/nlm/nlm_prot_impl.c index 603d77bacfc..9df0b55cd43 100644 --- a/sys/fs/nfs/nlm/nlm_prot_impl.c +++ b/sys/fs/nfs/nlm/nlm_prot_impl.c @@ -1,4 +1,4 @@ -/* $NetBSD: nlm_prot_impl.c,v 1.2 2015/12/13 18:31:09 christos Exp $ */ +/* $NetBSD: nlm_prot_impl.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ * Authors: Doug Rabson <dfr@rabson.org> @@ -29,8 +29,8 @@ #include "opt_inet6.h" #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/nlm/nlm_prot_impl.c 255333 2013-09-06 23:14:31Z rmacklem "); */ -__RCSID("$NetBSD: nlm_prot_impl.c,v 1.2 2015/12/13 18:31:09 christos Exp $"); +/* __FBSDID("FreeBSD: head/sys/nlm/nlm_prot_impl.c 302216 2016-06-26 20:08:42Z kib "); */ +__RCSID("$NetBSD: nlm_prot_impl.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); #include <sys/param.h> #include <sys/fail.h> @@ -297,7 +297,7 @@ nlm_init(void *dummy) TAILQ_INIT(&nlm_hosts); error = syscall_register(&nlm_syscall_offset, &nlm_syscall_sysent, - &nlm_syscall_prev_sysent); + &nlm_syscall_prev_sysent, SY_THR_STATIC_KLD); if (error) NLM_ERR("Can't register NLM syscall\n"); else @@ -1074,7 +1074,7 @@ nlm_find_host_by_addr(const struct sockaddr *addr, int vers) break; #endif default: - strcpy(tmp, "<unknown>"); + strlcpy(tmp, "<unknown>", sizeof(tmp)); } @@ -1358,7 +1358,7 @@ int nlm_wait_lock(void *handle, int timo) { struct nlm_waiting_lock *nw = handle; - int error; + int error, stops_deferred; /* * If the granted message arrived before we got here, @@ -1366,8 +1366,11 @@ nlm_wait_lock(void *handle, int timo) */ mtx_lock(&nlm_global_lock); error = 0; - if (nw->nw_waiting) + if (nw->nw_waiting) { + stops_deferred = sigdeferstop(SIGDEFERSTOP_ERESTART); error = msleep(nw, &nlm_global_lock, PCATCH, "nlmlock", timo); + sigallowstop(stops_deferred); + } TAILQ_REMOVE(&nlm_waiting_locks, nw, nw_link); if (error) { /* @@ -1428,7 +1431,6 @@ nlm_register_services(SVCPOOL *pool, int addr_count, char **addrs) static void (*dispatchers[])(struct svc_req *, SVCXPRT *) = { nlm_prog_0, nlm_prog_1, nlm_prog_3, nlm_prog_4 }; - static const int version_count = sizeof(versions) / sizeof(versions[0]); SVCXPRT **xprts; char netid[16]; @@ -1441,8 +1443,14 @@ nlm_register_services(SVCPOOL *pool, int addr_count, char **addrs) return (EINVAL); } + if (addr_count < 0 || addr_count > 256 ) { + NLM_ERR("NLM: too many service addresses (%d) given, " + "max 256 - can't start server\n", addr_count); + return (EINVAL); + } + xprts = malloc(addr_count * sizeof(SVCXPRT *), M_NLM, M_WAITOK|M_ZERO); - for (i = 0; i < version_count; i++) { + for (i = 0; i < nitems(versions); i++) { for (j = 0; j < addr_count; j++) { /* * Create transports for the first version and diff --git a/sys/fs/nfs/server/nfs_nfsdcache.c b/sys/fs/nfs/server/nfs_nfsdcache.c index 484e2ad9565..e8c0a0d9b82 100644 --- a/sys/fs/nfs/server/nfs_nfsdcache.c +++ b/sys/fs/nfs/server/nfs_nfsdcache.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_nfsdcache.c,v 1.2 2013/11/27 17:24:44 christos Exp $ */ +/* $NetBSD: nfs_nfsdcache.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -33,8 +33,8 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdcache.c 254337 2013-08-14 21:11:26Z rmacklem "); */ -__RCSID("$NetBSD: nfs_nfsdcache.c,v 1.2 2013/11/27 17:24:44 christos Exp $"); +/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdcache.c 304026 2016-08-12 22:44:59Z rmacklem "); */ +__RCSID("$NetBSD: nfs_nfsdcache.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); /* * Here is the basic algorithm: @@ -161,9 +161,10 @@ __RCSID("$NetBSD: nfs_nfsdcache.c,v 1.2 2013/11/27 17:24:44 christos Exp $"); #ifndef APPLEKEXT #include <fs/nfs/nfsport.h> -extern struct nfsstats newnfsstats; +extern struct nfsstatsv1 nfsstatsv1; extern struct mtx nfsrc_udpmtx; extern struct nfsrchash_bucket nfsrchash_table[NFSRVCACHE_HASHSIZE]; +extern struct nfsrchash_bucket nfsrcahash_table[NFSRVCACHE_HASHSIZE]; int nfsrc_floodlevel = NFSRVCACHE_FLOODLEVEL, nfsrc_tcpsavedreplies = 0; #endif /* !APPLEKEXT */ @@ -240,6 +241,7 @@ static int newnfsv2_procid[NFS_V3NPROCS] = { (&nfsrvudphashtbl[nfsrc_hash(xid)]) #define NFSRCHASH(xid) \ (&nfsrchash_table[nfsrc_hash(xid)].tbl) +#define NFSRCAHASH(xid) (&nfsrcahash_table[nfsrc_hash(xid)]) #define TRUE 1 #define FALSE 0 #define NFSRVCACHE_CHECKLEN 100 @@ -283,9 +285,6 @@ static void nfsrc_lock(struct nfsrvcache *rp); static void nfsrc_unlock(struct nfsrvcache *rp); static void nfsrc_wanted(struct nfsrvcache *rp); static void nfsrc_freecache(struct nfsrvcache *rp); -static void nfsrc_trimcache(u_int64_t, struct socket *); -static int nfsrc_activesocket(struct nfsrvcache *rp, u_int64_t, - struct socket *); static int nfsrc_getlenandcksum(mbuf_t m1, u_int16_t *cksum); static void nfsrc_marksametcpconn(u_int64_t); @@ -316,21 +315,21 @@ nfsrvd_initcache(void) for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { LIST_INIT(&nfsrvudphashtbl[i]); LIST_INIT(&nfsrchash_table[i].tbl); + LIST_INIT(&nfsrcahash_table[i].tbl); } TAILQ_INIT(&nfsrvudplru); nfsrc_tcpsavedreplies = 0; nfsrc_udpcachesize = 0; - newnfsstats.srvcache_tcppeak = 0; - newnfsstats.srvcache_size = 0; + nfsstatsv1.srvcache_tcppeak = 0; + nfsstatsv1.srvcache_size = 0; } /* * Get a cache entry for this request. Basically just malloc a new one * and then call nfsrc_getudp() or nfsrc_gettcp() to do the rest. - * Call nfsrc_trimcache() to clean up the cache before returning. */ APPLESTATIC int -nfsrvd_getcache(struct nfsrv_descript *nd, struct socket *so) +nfsrvd_getcache(struct nfsrv_descript *nd) { struct nfsrvcache *newrp; int ret; @@ -358,7 +357,6 @@ nfsrvd_getcache(struct nfsrv_descript *nd, struct socket *so) } else { ret = nfsrc_gettcp(nd, newrp); } - nfsrc_trimcache(nd->nd_sockref, so); NFSEXITCODE2(0, nd); return (ret); } @@ -399,14 +397,14 @@ loop: TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru); TAILQ_INSERT_TAIL(&nfsrvudplru, rp, rc_lru); if (rp->rc_flag & RC_INPROG) { - newnfsstats.srvcache_inproghits++; + nfsstatsv1.srvcache_inproghits++; mtx_unlock(mutex); ret = RC_DROPIT; } else if (rp->rc_flag & RC_REPSTATUS) { /* * V2 only. */ - newnfsstats.srvcache_nonidemdonehits++; + nfsstatsv1.srvcache_nonidemdonehits++; mtx_unlock(mutex); nfsrvd_rephead(nd); *(nd->nd_errp) = rp->rc_status; @@ -414,7 +412,7 @@ loop: rp->rc_timestamp = NFSD_MONOSEC + NFSRVCACHE_UDPTIMEOUT; } else if (rp->rc_flag & RC_REPMBUF) { - newnfsstats.srvcache_nonidemdonehits++; + nfsstatsv1.srvcache_nonidemdonehits++; mtx_unlock(mutex); nd->nd_mreq = m_copym(rp->rc_reply, 0, M_COPYALL, M_WAITOK); @@ -429,8 +427,8 @@ loop: goto out; } } - newnfsstats.srvcache_misses++; - atomic_add_int(&newnfsstats.srvcache_size, 1); + nfsstatsv1.srvcache_misses++; + atomic_add_int(&nfsstatsv1.srvcache_size, 1); nfsrc_udpcachesize++; newrp->rc_flag |= RC_INPROG; @@ -458,7 +456,7 @@ out: * Update a request cache entry after the rpc has been done */ APPLESTATIC struct nfsrvcache * -nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) +nfsrvd_updatecache(struct nfsrv_descript *nd) { struct nfsrvcache *rp; struct nfsrvcache *retrp = NULL; @@ -484,7 +482,7 @@ nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) * Reply from cache is a special case returned by nfsrv_checkseqid(). */ if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) { - newnfsstats.srvcache_nonidemdonehits++; + nfsstatsv1.srvcache_nonidemdonehits++; mtx_unlock(mutex); nd->nd_repstat = 0; if (nd->nd_mreq) @@ -523,8 +521,8 @@ nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) if (!(rp->rc_flag & RC_UDP)) { atomic_add_int(&nfsrc_tcpsavedreplies, 1); if (nfsrc_tcpsavedreplies > - newnfsstats.srvcache_tcppeak) - newnfsstats.srvcache_tcppeak = + nfsstatsv1.srvcache_tcppeak) + nfsstatsv1.srvcache_tcppeak = nfsrc_tcpsavedreplies; } mtx_unlock(mutex); @@ -551,7 +549,6 @@ nfsrvd_updatecache(struct nfsrv_descript *nd, struct socket *so) } out: - nfsrc_trimcache(nd->nd_sockref, so); NFSEXITCODE2(0, nd); return (retrp); } @@ -577,29 +574,23 @@ nfsrvd_delcache(struct nfsrvcache *rp) /* * Called after nfsrvd_updatecache() once the reply is sent, to update - * the entry for nfsrc_activesocket() and unlock it. The argument is + * the entry's sequence number and unlock it. The argument is * the pointer returned by nfsrvd_updatecache(). */ APPLESTATIC void -nfsrvd_sentcache(struct nfsrvcache *rp, struct socket *so, int err) +nfsrvd_sentcache(struct nfsrvcache *rp, int have_seq, uint32_t seq) { - tcp_seq tmp_seq; - struct mtx *mutex; + struct nfsrchash_bucket *hbp; - mutex = nfsrc_cachemutex(rp); - if (!(rp->rc_flag & RC_LOCKED)) - panic("nfsrvd_sentcache not locked"); - if (!err) { - if ((so->so_proto->pr_domain->dom_family != AF_INET && - so->so_proto->pr_domain->dom_family != AF_INET6) || - so->so_proto->pr_protocol != IPPROTO_TCP) - panic("nfs sent cache"); - if (nfsrv_getsockseqnum(so, &tmp_seq)) { - mtx_lock(mutex); - rp->rc_tcpseq = tmp_seq; - rp->rc_flag |= RC_TCPSEQ; - mtx_unlock(mutex); - } + KASSERT(rp->rc_flag & RC_LOCKED, ("nfsrvd_sentcache not locked")); + if (have_seq) { + hbp = NFSRCAHASH(rp->rc_sockref); + mtx_lock(&hbp->mtx); + rp->rc_tcpseq = seq; + if (rp->rc_acked != RC_NO_ACK) + LIST_INSERT_HEAD(&hbp->tbl, rp, rc_ahash); + rp->rc_acked = RC_NO_ACK; + mtx_unlock(&hbp->mtx); } nfsrc_unlock(rp); } @@ -689,7 +680,7 @@ tryagain: panic("nfs tcp cache0"); rp->rc_flag |= RC_LOCKED; if (rp->rc_flag & RC_INPROG) { - newnfsstats.srvcache_inproghits++; + nfsstatsv1.srvcache_inproghits++; mtx_unlock(mutex); if (newrp->rc_sockref == rp->rc_sockref) nfsrc_marksametcpconn(rp->rc_sockref); @@ -698,7 +689,7 @@ tryagain: /* * V2 only. */ - newnfsstats.srvcache_nonidemdonehits++; + nfsstatsv1.srvcache_nonidemdonehits++; mtx_unlock(mutex); if (newrp->rc_sockref == rp->rc_sockref) nfsrc_marksametcpconn(rp->rc_sockref); @@ -707,7 +698,7 @@ tryagain: *(nd->nd_errp) = rp->rc_status; rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout; } else if (rp->rc_flag & RC_REPMBUF) { - newnfsstats.srvcache_nonidemdonehits++; + nfsstatsv1.srvcache_nonidemdonehits++; mtx_unlock(mutex); if (newrp->rc_sockref == rp->rc_sockref) nfsrc_marksametcpconn(rp->rc_sockref); @@ -722,8 +713,8 @@ tryagain: free((caddr_t)newrp, M_NFSRVCACHE); goto out; } - newnfsstats.srvcache_misses++; - atomic_add_int(&newnfsstats.srvcache_size, 1); + nfsstatsv1.srvcache_misses++; + atomic_add_int(&nfsstatsv1.srvcache_size, 1); /* * For TCP, multiple entries for a key are allowed, so don't @@ -792,11 +783,18 @@ nfsrc_wanted(struct nfsrvcache *rp) static void nfsrc_freecache(struct nfsrvcache *rp) { + struct nfsrchash_bucket *hbp; LIST_REMOVE(rp, rc_hash); if (rp->rc_flag & RC_UDP) { TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru); nfsrc_udpcachesize--; + } else if (rp->rc_acked != RC_NO_SEQ) { + hbp = NFSRCAHASH(rp->rc_sockref); + mtx_lock(&hbp->mtx); + if (rp->rc_acked == RC_NO_ACK) + LIST_REMOVE(rp, rc_ahash); + mtx_unlock(&hbp->mtx); } nfsrc_wanted(rp); if (rp->rc_flag & RC_REPMBUF) { @@ -805,7 +803,7 @@ nfsrc_freecache(struct nfsrvcache *rp) atomic_add_int(&nfsrc_tcpsavedreplies, -1); } FREE((caddr_t)rp, M_NFSRVCACHE); - atomic_add_int(&newnfsstats.srvcache_size, -1); + atomic_add_int(&nfsstatsv1.srvcache_size, -1); } /* @@ -829,22 +827,41 @@ nfsrvd_cleancache(void) nfsrc_freecache(rp); } } - newnfsstats.srvcache_size = 0; + nfsstatsv1.srvcache_size = 0; mtx_unlock(&nfsrc_udpmtx); nfsrc_tcpsavedreplies = 0; } +#define HISTSIZE 16 /* * The basic rule is to get rid of entries that are expired. */ -static void -nfsrc_trimcache(u_int64_t sockref, struct socket *so) +void +nfsrc_trimcache(u_int64_t sockref, uint32_t snd_una, int final) { + struct nfsrchash_bucket *hbp; struct nfsrvcache *rp, *nextrp; - int i, j, k, time_histo[10]; + int force, lastslot, i, j, k, tto, time_histo[HISTSIZE]; time_t thisstamp; static time_t udp_lasttrim = 0, tcp_lasttrim = 0; - static int onethread = 0; + static int onethread = 0, oneslot = 0; + + if (sockref != 0) { + hbp = NFSRCAHASH(sockref); + mtx_lock(&hbp->mtx); + LIST_FOREACH_SAFE(rp, &hbp->tbl, rc_ahash, nextrp) { + if (sockref == rp->rc_sockref) { + if (SEQ_GEQ(snd_una, rp->rc_tcpseq)) { + rp->rc_acked = RC_ACK; + LIST_REMOVE(rp, rc_ahash); + } else if (final) { + rp->rc_acked = RC_NACK; + LIST_REMOVE(rp, rc_ahash); + } + } + } + mtx_unlock(&hbp->mtx); + } if (atomic_cmpset_acq_int(&onethread, 0, 1) == 0) return; @@ -865,17 +882,42 @@ nfsrc_trimcache(u_int64_t sockref, struct socket *so) } if (NFSD_MONOSEC != tcp_lasttrim || nfsrc_tcpsavedreplies >= nfsrc_tcphighwater) { - for (i = 0; i < 10; i++) - time_histo[i] = 0; - for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) { + force = nfsrc_tcphighwater / 4; + if (force > 0 && + nfsrc_tcpsavedreplies + force >= nfsrc_tcphighwater) { + for (i = 0; i < HISTSIZE; i++) + time_histo[i] = 0; + i = 0; + lastslot = NFSRVCACHE_HASHSIZE - 1; + } else { + force = 0; + if (NFSD_MONOSEC != tcp_lasttrim) { + i = 0; + lastslot = NFSRVCACHE_HASHSIZE - 1; + } else { + lastslot = i = oneslot; + if (++oneslot >= NFSRVCACHE_HASHSIZE) + oneslot = 0; + } + } + tto = nfsrc_tcptimeout; + tcp_lasttrim = NFSD_MONOSEC; + for (; i <= lastslot; i++) { mtx_lock(&nfsrchash_table[i].mtx); - if (i == 0) - tcp_lasttrim = NFSD_MONOSEC; LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, rc_hash, nextrp) { if (!(rp->rc_flag & (RC_INPROG|RC_LOCKED|RC_WANTED)) && rp->rc_refcnt == 0) { + if ((rp->rc_flag & RC_REFCNT) || + tcp_lasttrim > rp->rc_timestamp || + rp->rc_acked == RC_ACK) { + nfsrc_freecache(rp); + continue; + } + + if (force == 0) + continue; /* * The timestamps range from roughly the * present (tcp_lasttrim) to the present @@ -883,34 +925,30 @@ nfsrc_trimcache(u_int64_t sockref, struct socket *so) * histogram of where the timeouts fall. */ j = rp->rc_timestamp - tcp_lasttrim; - if (j >= nfsrc_tcptimeout) - j = nfsrc_tcptimeout - 1; - if (j < 0) + if (j >= tto) + j = HISTSIZE - 1; + else if (j < 0) j = 0; - j = (j * 10 / nfsrc_tcptimeout) % 10; + else + j = j * HISTSIZE / tto; time_histo[j]++; - if ((rp->rc_flag & RC_REFCNT) || - tcp_lasttrim > rp->rc_timestamp || - nfsrc_activesocket(rp, sockref, so)) - nfsrc_freecache(rp); } } mtx_unlock(&nfsrchash_table[i].mtx); } - j = nfsrc_tcphighwater / 5; /* 20% of it */ - if (j > 0 && (nfsrc_tcpsavedreplies + j) > nfsrc_tcphighwater) { + if (force) { /* * Trim some more with a smaller timeout of as little * as 20% of nfsrc_tcptimeout to try and get below * 80% of the nfsrc_tcphighwater. */ k = 0; - for (i = 0; i < 8; i++) { + for (i = 0; i < (HISTSIZE - 2); i++) { k += time_histo[i]; - if (k > j) + if (k > force) break; } - k = nfsrc_tcptimeout * (i + 1) / 10; + k = tto * (i + 1) / HISTSIZE; if (k < 1) k = 1; thisstamp = tcp_lasttrim + k; @@ -923,8 +961,7 @@ nfsrc_trimcache(u_int64_t sockref, struct socket *so) && rp->rc_refcnt == 0 && ((rp->rc_flag & RC_REFCNT) || thisstamp > rp->rc_timestamp || - nfsrc_activesocket(rp, sockref, - so))) + rp->rc_acked == RC_ACK)) nfsrc_freecache(rp); } mtx_unlock(&nfsrchash_table[i].mtx); @@ -942,6 +979,9 @@ nfsrvd_refcache(struct nfsrvcache *rp) { struct mtx *mutex; + if (rp == NULL) + /* For NFSv4.1, there is no cache entry. */ + return; mutex = nfsrc_cachemutex(rp); mtx_lock(mutex); if (rp->rc_refcnt < 0) @@ -969,28 +1009,6 @@ nfsrvd_derefcache(struct nfsrvcache *rp) } /* - * Check to see if the socket is active. - * Return 1 if the reply has been received/acknowledged by the client, - * 0 otherwise. - * XXX - Uses tcp internals. - */ -static int -nfsrc_activesocket(struct nfsrvcache *rp, u_int64_t cur_sockref, - struct socket *cur_so) -{ - int ret = 0; - - if (!(rp->rc_flag & RC_TCPSEQ)) - return (ret); - /* - * If the sockref is the same, it is the same TCP connection. - */ - if (cur_sockref == rp->rc_sockref) - ret = nfsrv_checksockseqnum(cur_so, rp->rc_tcpseq); - return (ret); -} - -/* * Calculate the length of the mbuf list and a checksum on the first up to * NFSRVCACHE_CHECKLEN bytes. */ diff --git a/sys/fs/nfs/server/nfs_nfsdkrpc.c b/sys/fs/nfs/server/nfs_nfsdkrpc.c index 07d54ba08c5..5c2b8e82872 100644 --- a/sys/fs/nfs/server/nfs_nfsdkrpc.c +++ b/sys/fs/nfs/server/nfs_nfsdkrpc.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_nfsdkrpc.c,v 1.2 2014/03/25 16:30:28 christos Exp $ */ +/* $NetBSD: nfs_nfsdkrpc.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -33,8 +33,8 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdkrpc.c 249596 2013-04-17 22:42:43Z ken "); */ -__RCSID("$NetBSD: nfs_nfsdkrpc.c,v 1.2 2014/03/25 16:30:28 christos Exp $"); +/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdkrpc.c 299203 2016-05-06 23:40:37Z pfg "); */ +__RCSID("$NetBSD: nfs_nfsdkrpc.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); #include "opt_inet6.h" #include "opt_kgssapi.h" @@ -87,20 +87,20 @@ SYSCTL_DECL(_vfs_nfsd); SVCPOOL *nfsrvd_pool; static int nfs_privport = 0; -SYSCTL_INT(_vfs_nfsd, OID_AUTO, nfs_privport, CTLFLAG_RW, +SYSCTL_INT(_vfs_nfsd, OID_AUTO, nfs_privport, CTLFLAG_RWTUN, &nfs_privport, 0, "Only allow clients using a privileged port for NFSv2 and 3"); static int nfs_minvers = NFS_VER2; -SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_min_nfsvers, CTLFLAG_RW, +SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_min_nfsvers, CTLFLAG_RWTUN, &nfs_minvers, 0, "The lowest version of NFS handled by the server"); static int nfs_maxvers = NFS_VER4; -SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_max_nfsvers, CTLFLAG_RW, +SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_max_nfsvers, CTLFLAG_RWTUN, &nfs_maxvers, 0, "The highest version of NFS handled by the server"); -static int nfs_proc(struct nfsrv_descript *, u_int32_t, struct socket *, - u_int64_t, struct nfsrvcache **); +static int nfs_proc(struct nfsrv_descript *, u_int32_t, SVCXPRT *xprt, + struct nfsrvcache **); extern u_long sb_max_adj; extern int newnfs_numnfsd; @@ -119,7 +119,8 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) memset(&nd, 0, sizeof(nd)); if (rqst->rq_vers == NFS_VER2) { - if (rqst->rq_proc > NFSV2PROC_STATFS) { + if (rqst->rq_proc > NFSV2PROC_STATFS || + newnfs_nfsv3_procid[rqst->rq_proc] == NFSPROC_NOOP) { svcerr_noproc(rqst); svc_freereq(rqst); goto out; @@ -232,10 +233,16 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) * Get a refcnt (shared lock) on nfsd_suspend_lock. * NFSSVC_SUSPENDNFSD will take an exclusive lock on * nfsd_suspend_lock to suspend these threads. + * The call to nfsv4_lock() that precedes nfsv4_getref() + * ensures that the acquisition of the exclusive lock + * takes priority over acquisition of the shared lock by + * waiting for any exclusive lock request to complete. * This must be done here, before the check of * nfsv4root exports by nfsvno_v4rootexport(). */ NFSLOCKV4ROOTMUTEX(); + nfsv4_lock(&nfsd_suspend_lock, 0, NULL, NFSV4ROOTLOCKMUTEXPTR, + NULL); nfsv4_getref(&nfsd_suspend_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); NFSUNLOCKV4ROOTMUTEX(); @@ -253,8 +260,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) } } - cacherep = nfs_proc(&nd, rqst->rq_xid, xprt->xp_socket, - xprt->xp_sockref, &rp); + cacherep = nfs_proc(&nd, rqst->rq_xid, xprt, &rp); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsd_suspend_lock); NFSUNLOCKV4ROOTMUTEX(); @@ -289,11 +295,15 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) } else if (!svc_sendreply_mbuf(rqst, nd.nd_mreq)) { svcerr_systemerr(rqst); } - if (rp != NULL) - nfsrvd_sentcache(rp, xprt->xp_socket, 0); + if (rp != NULL) { + nfsrvd_sentcache(rp, (rqst->rq_reply_seq != 0 || + SVC_ACK(xprt, NULL)), rqst->rq_reply_seq); + } svc_freereq(rqst); out: + if (softdep_ast_cleanup != NULL) + softdep_ast_cleanup(); NFSEXITCODE(0); } @@ -302,11 +312,15 @@ out: * Return the appropriate cache response. */ static int -nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, struct socket *so, - u_int64_t sockref, struct nfsrvcache **rpp) +nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, SVCXPRT *xprt, + struct nfsrvcache **rpp) { struct thread *td = curthread; - int cacherep = RC_DOIT, isdgram; + int cacherep = RC_DOIT, isdgram, taglen = -1; + struct mbuf *m; + u_char tag[NFSV4_SMALLSTR + 1], *tagstr = NULL; + u_int32_t minorvers = 0; + uint32_t ack; *rpp = NULL; if (nd->nd_nam2 == NULL) { @@ -338,8 +352,19 @@ nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, struct socket *so, nd->nd_flag |= ND_SAMETCPCONN; nd->nd_retxid = xid; nd->nd_tcpconntime = NFSD_MONOSEC; - nd->nd_sockref = sockref; - cacherep = nfsrvd_getcache(nd, so); + nd->nd_sockref = xprt->xp_sockref; + if ((nd->nd_flag & ND_NFSV4) != 0) + nfsd_getminorvers(nd, tag, &tagstr, &taglen, + &minorvers); + if ((nd->nd_flag & ND_NFSV41) != 0) + /* NFSv4.1 caches replies in the session slots. */ + cacherep = RC_DOIT; + else { + cacherep = nfsrvd_getcache(nd); + ack = 0; + SVC_ACK(xprt, &ack); + nfsrc_trimcache(xprt->xp_sockref, ack, 0); + } } /* @@ -349,18 +374,48 @@ nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, struct socket *so, * RC_DROPIT - just throw the request away */ if (cacherep == RC_DOIT) { - nfsrvd_dorpc(nd, isdgram, td); - if (nd->nd_repstat == NFSERR_DONTREPLY) - cacherep = RC_DROPIT; - else + if ((nd->nd_flag & ND_NFSV41) != 0) + nd->nd_xprt = xprt; + nfsrvd_dorpc(nd, isdgram, tagstr, taglen, minorvers, td); + if ((nd->nd_flag & ND_NFSV41) != 0) { + if (nd->nd_repstat != NFSERR_REPLYFROMCACHE && + (nd->nd_flag & ND_SAVEREPLY) != 0) { + /* Cache a copy of the reply. */ + m = m_copym(nd->nd_mreq, 0, M_COPYALL, + M_WAITOK); + } else + m = NULL; + if ((nd->nd_flag & ND_HASSEQUENCE) != 0) + nfsrv_cache_session(nd->nd_sessionid, + nd->nd_slotid, nd->nd_repstat, &m); + if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) + nd->nd_repstat = 0; cacherep = RC_REPLY; - *rpp = nfsrvd_updatecache(nd, so); + } else { + if (nd->nd_repstat == NFSERR_DONTREPLY) + cacherep = RC_DROPIT; + else + cacherep = RC_REPLY; + *rpp = nfsrvd_updatecache(nd); + } } + if (tagstr != NULL && taglen > NFSV4_SMALLSTR) + free(tagstr, M_TEMP); NFSEXITCODE2(0, nd); return (cacherep); } +static void +nfssvc_loss(SVCXPRT *xprt) +{ + uint32_t ack; + + ack = 0; + SVC_ACK(xprt, &ack); + nfsrc_trimcache(xprt->xp_sockref, ack, 1); +} + /* * Adds a socket to the list for servicing by nfsds. */ @@ -401,6 +456,8 @@ nfsrvd_addsock(struct file *fp) if (nfs_maxvers >= NFS_VER4) svc_reg(xprt, NFS_PROG, NFS_VER4, nfssvc_program, NULL); + if (so->so_type == SOCK_STREAM) + svc_loss_reg(xprt, nfssvc_loss); SVC_RELEASE(xprt); } @@ -417,6 +474,7 @@ int nfsrvd_nfsd(struct thread *td, struct nfsd_nfsd_args *args) { char principal[MAXHOSTNAMELEN + 5]; + struct proc *p; int error = 0; bool_t ret2, ret3, ret4; @@ -434,6 +492,10 @@ nfsrvd_nfsd(struct thread *td, struct nfsd_nfsd_args *args) */ NFSD_LOCK(); if (newnfs_numnfsd == 0) { + p = td->td_proc; + PROC_LOCK(p); + p->p_flag2 |= P2_AST_SU; + PROC_UNLOCK(p); newnfs_numnfsd++; NFSD_UNLOCK(); @@ -465,6 +527,9 @@ nfsrvd_nfsd(struct thread *td, struct nfsd_nfsd_args *args) NFSD_LOCK(); newnfs_numnfsd--; nfsrvd_init(1); + PROC_LOCK(p); + p->p_flag2 &= ~P2_AST_SU; + PROC_UNLOCK(p); } NFSD_UNLOCK(); @@ -487,6 +552,7 @@ nfsrvd_init(int terminating) if (terminating) { nfsd_master_proc = NULL; NFSD_UNLOCK(); + nfsrv_freeallbackchannel_xprts(); svcpool_destroy(nfsrvd_pool); nfsrvd_pool = NULL; NFSD_LOCK(); diff --git a/sys/fs/nfs/server/nfs_nfsdserv.c b/sys/fs/nfs/server/nfs_nfsdserv.c index 0b26b98192f..a465fd02f83 100644 --- a/sys/fs/nfs/server/nfs_nfsdserv.c +++ b/sys/fs/nfs/server/nfs_nfsdserv.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_nfsdserv.c,v 1.2 2014/03/25 16:30:28 christos Exp $ */ +/* $NetBSD: nfs_nfsdserv.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -33,8 +33,8 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdserv.c 245613 2013-01-18 19:42:08Z delphij "); */ -__RCSID("$NetBSD: nfs_nfsdserv.c,v 1.2 2014/03/25 16:30:28 christos Exp $"); +/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdserv.c 299514 2016-05-12 05:03:12Z cem "); */ +__RCSID("$NetBSD: nfs_nfsdserv.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); /* * nfs version 2, 3 and 4 server calls to vnode ops @@ -55,6 +55,7 @@ extern enum vtype nv34tov_type[8]; extern struct timeval nfsboottime; extern int nfs_rootfhset; extern int nfsrv_enable_crossmntpt; +extern int nfsrv_statehashsize; #endif /* !APPLEKEXT */ static int nfs_async = 0; @@ -212,6 +213,17 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram, if (nd->nd_repstat == 0) { accmode = 0; NFSSET_ATTRBIT(&tmpbits, &attrbits); + + /* + * GETATTR with write-only attr time_access_set and time_modify_set + * should return NFS4ERR_INVAL. + */ + if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEACCESSSET) || + NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEMODIFYSET)){ + error = NFSERR_INVAL; + vput(vp); + goto out; + } if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) { NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL); accmode |= VREAD_ACL; @@ -317,7 +329,7 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram, stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER); } - error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p); + error = nfsrv_sattr(nd, vp, &nva, &attrbits, aclp, p); if (error) goto nfsmout; preat_ret = nfsvno_getattr(vp, &nva2, nd->nd_cred, p, 1); @@ -668,10 +680,14 @@ nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); clientid.lval[0] = stp->ls_stateid.other[0] = *tl++; clientid.lval[1] = stp->ls_stateid.other[1] = *tl++; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK1 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -820,10 +836,14 @@ nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); clientid.lval[0] = stp->ls_stateid.other[0] = *tl++; clientid.lval[1] = stp->ls_stateid.other[1] = *tl++; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK2 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -864,7 +884,7 @@ nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram, i = mbuf_len(mp); } - if (retlen > NFS_MAXDATA || retlen < 0) + if (retlen > NFS_SRVMAXIO || retlen < 0) nd->nd_repstat = EIO; if (vnode_vtype(vp) != VREG && !nd->nd_repstat) { if (nd->nd_flag & ND_NFSV3) @@ -977,7 +997,7 @@ nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram, goto out; } NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, - LOCKPARENT | LOCKLEAF | SAVESTART); + LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE); nfsvno_setpathbuf(&named, &bufp, &hashp); error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); if (error) @@ -1006,14 +1026,14 @@ nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram, break; default: break; - }; + } } else { NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); how = fxdr_unsigned(int, *tl); switch (how) { case NFSCREATE_GUARDED: case NFSCREATE_UNCHECKED: - error = nfsrv_sattr(nd, &nva, NULL, NULL, p); + error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p); if (error) goto nfsmout; break; @@ -1023,7 +1043,7 @@ nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram, cverf[1] = *tl; exclusive_flag = 1; break; - }; + } NFSVNO_SETATTRVAL(&nva, type, VREG); } } @@ -1070,7 +1090,7 @@ nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram, if (named.ni_vp == NULL) NFSVNO_SETATTRVAL(&nva, mode, 0); break; - }; + } } /* @@ -1188,7 +1208,7 @@ nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram, goto out; } } - NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags); + NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE); nfsvno_setpathbuf(&named, &bufp, &hashp); error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); if (error) @@ -1198,7 +1218,7 @@ nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram, NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); vtyp = nfsv34tov_type(*tl); } - error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p); + error = nfsrv_sattr(nd, NULL, &nva, &attrbits, aclp, p); if (error) goto nfsmout; nva.na_type = vtyp; @@ -1459,10 +1479,23 @@ nfsrvd_rename(struct nfsrv_descript *nd, int isdgram, nfsvno_relpathbuf(&fromnd); goto out; } + /* + * Unlock dp in this code section, so it is unlocked before + * tdp gets locked. This avoids a potential LOR if tdp is the + * parent directory of dp. + */ if (nd->nd_flag & ND_NFSV4) { tdp = todp; tnes = *toexp; - tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, p, 0); + if (dp != tdp) { + NFSVOPUNLOCK(dp, 0); + tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, + p, 0); /* Might lock tdp. */ + } else { + tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, + p, 1); + NFSVOPUNLOCK(dp, 0); + } } else { tfh.nfsrvfh_len = 0; error = nfsrv_mtofh(nd, &tfh); @@ -1483,10 +1516,12 @@ nfsrvd_rename(struct nfsrv_descript *nd, int isdgram, tnes = *exp; tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, p, 1); + NFSVOPUNLOCK(dp, 0); } else { + NFSVOPUNLOCK(dp, 0); nd->nd_cred->cr_uid = nd->nd_saveduid; nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL, - 0, p); + 0, p); /* Locks tdp. */ if (tdp) { tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, p, 1); @@ -1501,7 +1536,7 @@ nfsrvd_rename(struct nfsrv_descript *nd, int isdgram, if (error) { if (tdp) vrele(tdp); - vput(dp); + vrele(dp); nfsvno_relpathbuf(&fromnd); nfsvno_relpathbuf(&tond); goto out; @@ -1516,7 +1551,7 @@ nfsrvd_rename(struct nfsrv_descript *nd, int isdgram, } if (tdp) vrele(tdp); - vput(dp); + vrele(dp); nfsvno_relpathbuf(&fromnd); nfsvno_relpathbuf(&tond); goto out; @@ -1525,7 +1560,7 @@ nfsrvd_rename(struct nfsrv_descript *nd, int isdgram, /* * Done parsing, now down to business. */ - nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 1, exp, p, &fdirp); + nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, p, &fdirp); if (nd->nd_repstat) { if (nd->nd_flag & ND_NFSV3) { nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, @@ -1607,13 +1642,6 @@ nfsrvd_link(struct nfsrv_descript *nd, int isdgram, nd->nd_repstat = NFSERR_INVAL; if (tovp) vrele(tovp); - } else if (vnode_vtype(vp) == VLNK) { - if (nd->nd_flag & ND_NFSV2) - nd->nd_repstat = NFSERR_INVAL; - else - nd->nd_repstat = NFSERR_NOTSUPP; - if (tovp) - vrele(tovp); } if (!nd->nd_repstat) { if (nd->nd_flag & ND_NFSV4) { @@ -1633,7 +1661,7 @@ nfsrvd_link(struct nfsrv_descript *nd, int isdgram, } } NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, - LOCKPARENT | SAVENAME); + LOCKPARENT | SAVENAME | NOCACHE); if (!nd->nd_repstat) { nfsvno_setpathbuf(&named, &bufp, &hashp); error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); @@ -1710,7 +1738,7 @@ nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram, *vpp = NULL; NFSVNO_ATTRINIT(&nva); NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, - LOCKPARENT | SAVESTART); + LOCKPARENT | SAVESTART | NOCACHE); nfsvno_setpathbuf(&named, &bufp, &hashp); error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); if (!error && !nd->nd_repstat) @@ -1828,7 +1856,7 @@ nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram, goto out; } NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, - LOCKPARENT | SAVENAME); + LOCKPARENT | SAVENAME | NOCACHE); nfsvno_setpathbuf(&named, &bufp, &hashp); error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); if (error) @@ -1836,7 +1864,7 @@ nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram, if (!nd->nd_repstat) { NFSVNO_ATTRINIT(&nva); if (nd->nd_flag & ND_NFSV3) { - error = nfsrv_sattr(nd, &nva, NULL, NULL, p); + error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p); if (error) goto nfsmout; } else { @@ -1953,11 +1981,21 @@ nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram, int error = 0, for_ret = 1, aft_ret = 1, cnt; u_int64_t off; - if (nd->nd_repstat) { + if (nd->nd_repstat) { nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft); goto out; } + + /* Return NFSERR_ISDIR in NFSv4 when commit on a directory. */ + if (vp->v_type != VREG) { + if (nd->nd_flag & ND_NFSV3) + error = NFSERR_NOTSUPP; + else + error = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_INVAL; + goto nfsmout; + } NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); + /* * XXX At this time VOP_FSYNC() does not accept offset and byte * count parameters, so these arguments are useless (someday maybe). @@ -2170,7 +2208,7 @@ nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram, default: nd->nd_repstat = NFSERR_BADXDR; goto nfsmout; - }; + } if (*tl++ == newnfs_true) flags |= NFSLCK_RECLAIM; offset = fxdr_hyper(tl); @@ -2198,10 +2236,14 @@ nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_opentolockseq = fxdr_unsigned(int, *tl++); clientid.lval[0] = *tl++; clientid.lval[1] = *tl++; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK3 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2221,10 +2263,14 @@ nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_seq = fxdr_unsigned(int, *tl); clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK4 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2356,7 +2402,7 @@ nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram, default: nd->nd_repstat = NFSERR_BADXDR; goto nfsmout; - }; + } lo.lo_first = fxdr_hyper(tl); tl += 2; len = fxdr_hyper(tl); @@ -2370,10 +2416,14 @@ nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram, tl += 2; clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK5 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2389,8 +2439,6 @@ nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram, if (!nd->nd_repstat) nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid, &stateid, exp, nd, p); - if (stp) - FREE((caddr_t)stp, M_NFSDSTATE); if (nd->nd_repstat) { if (nd->nd_repstat == NFSERR_DENIED) { NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED); @@ -2412,6 +2460,8 @@ nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram, } } vput(vp); + if (stp) + FREE((caddr_t)stp, M_NFSDSTATE); NFSEXITCODE2(0, nd); return (0); nfsmout: @@ -2461,7 +2511,7 @@ nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram, free(stp, M_NFSDSTATE); free(lop, M_NFSDLOCK); goto nfsmout; - }; + } stp->ls_ownerlen = 0; stp->ls_uid = nd->nd_cred->cr_uid; stp->ls_seq = fxdr_unsigned(int, *tl++); @@ -2481,10 +2531,14 @@ nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram, } clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK6 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2525,7 +2579,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, struct nfsexstuff *exp) { u_int32_t *tl; - int i; + int i, retext; struct nfsstate *stp = NULL; int error = 0, create, claim, exclusive_flag = 0; u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask; @@ -2562,6 +2616,39 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_uid = nd->nd_cred->cr_uid; stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++); i = fxdr_unsigned(int, *tl++); + retext = 0; + if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG | + NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) { + retext = 1; + /* For now, ignore these. */ + i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG); + switch (i & NFSV4OPEN_WANTDELEGMASK) { + case NFSV4OPEN_WANTANYDELEG: + stp->ls_flags |= (NFSLCK_WANTRDELEG | + NFSLCK_WANTWDELEG); + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + case NFSV4OPEN_WANTREADDELEG: + stp->ls_flags |= NFSLCK_WANTRDELEG; + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + case NFSV4OPEN_WANTWRITEDELEG: + stp->ls_flags |= NFSLCK_WANTWDELEG; + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + case NFSV4OPEN_WANTNODELEG: + stp->ls_flags |= NFSLCK_WANTNODELEG; + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + case NFSV4OPEN_WANTCANCEL: + printf("NFSv4: ignore Open WantCancel\n"); + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + default: + /* nd_repstat will be set to NFSERR_INVAL below. */ + break; + } + } switch (i) { case NFSV4OPEN_ACCESSREAD: stp->ls_flags |= NFSLCK_READACCESS; @@ -2574,7 +2661,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, break; default: nd->nd_repstat = NFSERR_INVAL; - }; + } i = fxdr_unsigned(int, *tl++); switch (i) { case NFSV4OPEN_DENYNONE: @@ -2590,13 +2677,17 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, break; default: nd->nd_repstat = NFSERR_INVAL; - }; + } clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK7 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2616,7 +2707,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, switch (how) { case NFSCREATE_UNCHECKED: case NFSCREATE_GUARDED: - error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p); + error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p); if (error) goto nfsmout; /* @@ -2636,10 +2727,32 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, cverf[0] = *tl++; cverf[1] = *tl; break; + case NFSCREATE_EXCLUSIVE41: + NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF); + cverf[0] = *tl++; + cverf[1] = *tl; + error = nfsv4_sattr(nd, vp, &nva, &attrbits, aclp, p); + if (error != 0) + goto nfsmout; + if (NFSISSET_ATTRBIT(&attrbits, + NFSATTRBIT_TIMEACCESSSET)) + nd->nd_repstat = NFSERR_INVAL; + /* + * If the na_gid being set is the same as that of + * the directory it is going in, clear it, since + * that is what will be set by default. This allows + * a user that isn't in that group to do the create. + */ + if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) && + nva.na_gid == dirfor.na_gid) + NFSVNO_UNSET(&nva, gid); + if (nd->nd_repstat == 0) + nd->nd_repstat = nfsrv_checkuidgid(nd, &nva); + break; default: nd->nd_repstat = NFSERR_BADXDR; goto nfsmout; - }; + } } else if (create != NFSV4OPEN_NOCREATE) { nd->nd_repstat = NFSERR_BADXDR; goto nfsmout; @@ -2672,7 +2785,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, } if (create == NFSV4OPEN_CREATE) NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, - LOCKPARENT | LOCKLEAF | SAVESTART); + LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE); else NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP, LOCKLEAF | SAVESTART); @@ -2717,27 +2830,38 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, exclusive_flag = 1; if (!named.ni_vp) nva.na_mode = 0; - }; + break; + case NFSCREATE_EXCLUSIVE41: + exclusive_flag = 1; + break; + } } nfsvno_open(nd, &named, clientid, &stateid, stp, &exclusive_flag, &nva, cverf, create, aclp, &attrbits, nd->nd_cred, p, exp, &vp); - } else if (claim == NFSV4OPEN_CLAIMPREVIOUS) { - NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); - i = fxdr_unsigned(int, *tl); - switch (i) { - case NFSV4OPEN_DELEGATEREAD: - stp->ls_flags |= NFSLCK_DELEGREAD; - break; - case NFSV4OPEN_DELEGATEWRITE: - stp->ls_flags |= NFSLCK_DELEGWRITE; - case NFSV4OPEN_DELEGATENONE: - break; - default: - nd->nd_repstat = NFSERR_BADXDR; - goto nfsmout; - }; - stp->ls_flags |= NFSLCK_RECLAIM; + } else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim == + NFSV4OPEN_CLAIMFH) { + if (claim == NFSV4OPEN_CLAIMPREVIOUS) { + NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); + i = fxdr_unsigned(int, *tl); + switch (i) { + case NFSV4OPEN_DELEGATEREAD: + stp->ls_flags |= NFSLCK_DELEGREAD; + break; + case NFSV4OPEN_DELEGATEWRITE: + stp->ls_flags |= NFSLCK_DELEGWRITE; + case NFSV4OPEN_DELEGATENONE: + break; + default: + nd->nd_repstat = NFSERR_BADXDR; + goto nfsmout; + } + stp->ls_flags |= NFSLCK_RECLAIM; + } else { + /* CLAIM_NULL_FH */ + if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE) + nd->nd_repstat = NFSERR_INVAL; + } vp = dp; NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); if ((vp->v_iflag & VI_DOOMED) == 0) @@ -2758,7 +2882,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, * The IETF working group decided that this is the correct * error return for all non-regular files. */ - nd->nd_repstat = NFSERR_SYMLINK; + nd->nd_repstat = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_SYMLINK; } if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS)) nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, @@ -2826,7 +2950,21 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, *tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD); else if (rflags & NFSV4OPEN_WRITEDELEGATE) *tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE); - else + else if (retext != 0) { + *tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT); + if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) { + NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION); + *tl = newnfs_false; + } else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) { + NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE); + *tl = newnfs_false; + } else { + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OPEN_NOTWANTED); + } + } else *tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE); if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) { NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED); @@ -2902,10 +3040,14 @@ nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_flags = NFSLCK_CLOSE; clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK8 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2942,14 +3084,18 @@ nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram, NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK9 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } - nd->nd_repstat = nfsrv_delegupdate(clientid, NULL, NULL, + nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL, NFSV4OP_DELEGPURGE, nd->nd_cred, p); nfsmout: NFSEXITCODE2(error, nd); @@ -2973,14 +3119,18 @@ nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram, NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER); clientid.lval[0] = stateid.other[0]; clientid.lval[1] = stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK10 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } - nd->nd_repstat = nfsrv_delegupdate(clientid, &stateid, vp, + nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp, NFSV4OP_DELEGRETURN, nd->nd_cred, p); nfsmout: vput(vp); @@ -3018,6 +3168,10 @@ nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram, nfsv4stateid_t stateid; nfsquad_t clientid; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED); stp->ls_ownerlen = 0; stp->ls_op = nd->nd_rp; @@ -3030,10 +3184,14 @@ nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_flags = NFSLCK_CONFIRM; clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK11 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -3063,6 +3221,11 @@ nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram, nfsv4stateid_t stateid; nfsquad_t clientid; + /* opendowngrade can only work on a file object.*/ + if (vp->v_type != VREG) { + error = NFSERR_INVAL; + goto nfsmout; + } NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED); stp->ls_ownerlen = 0; stp->ls_op = nd->nd_rp; @@ -3086,7 +3249,7 @@ nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram, break; default: nd->nd_repstat = NFSERR_BADXDR; - }; + } i = fxdr_unsigned(int, *tl); switch (i) { case NFSV4OPEN_DENYNONE: @@ -3102,14 +3265,18 @@ nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram, break; default: nd->nd_repstat = NFSERR_BADXDR; - }; + } clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK12 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -3138,6 +3305,10 @@ nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram, int error = 0; nfsquad_t clientid; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { nd->nd_repstat = NFSERR_WRONGSEC; goto nfsmout; @@ -3145,15 +3316,19 @@ nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram, NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER); clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK13 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW), - NULL, (nfsquad_t)((u_quad_t)0), nd, p); + NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); nfsmout: NFSEXITCODE2(error, nd); return (error); @@ -3277,6 +3452,10 @@ nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram, u_char *verf, *ucp, *ucp2, addrbuf[24]; nfsquad_t clientid, confirm; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { nd->nd_repstat = NFSERR_WRONGSEC; goto out; @@ -3292,9 +3471,10 @@ nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram, idlen = i; if (nd->nd_flag & ND_GSS) i += nd->nd_princlen; - MALLOC(clp, struct nfsclient *, sizeof (struct nfsclient) + i, - M_NFSDCLIENT, M_WAITOK); - NFSBZERO((caddr_t)clp, sizeof (struct nfsclient) + i); + clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK | + M_ZERO); + clp->lc_stateid = malloc(sizeof(struct nfsstatehead) * + nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK); NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx); NFSSOCKADDRALLOC(clp->lc_req.nr_nam); NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in)); @@ -3354,7 +3534,8 @@ nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram, if (clp) { NFSSOCKADDRFREE(clp->lc_req.nr_nam); NFSFREEMUTEX(&clp->lc_req.nr_mtx); - free((caddr_t)clp, M_NFSDCLIENT); + free(clp->lc_stateid, M_NFSDCLIENT); + free(clp, M_NFSDCLIENT); } if (!nd->nd_repstat) { NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER); @@ -3371,7 +3552,8 @@ nfsmout: if (clp) { NFSSOCKADDRFREE(clp->lc_req.nr_nam); NFSFREEMUTEX(&clp->lc_req.nr_mtx); - free((caddr_t)clp, M_NFSDCLIENT); + free(clp->lc_stateid, M_NFSDCLIENT); + free(clp, M_NFSDCLIENT); } NFSEXITCODE2(error, nd); return (error); @@ -3389,6 +3571,10 @@ nfsrvd_setclientidcfrm(struct nfsrv_descript *nd, int error = 0; nfsquad_t clientid, confirm; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { nd->nd_repstat = NFSERR_WRONGSEC; goto nfsmout; @@ -3404,7 +3590,7 @@ nfsrvd_setclientidcfrm(struct nfsrv_descript *nd, * returns the appropriate NFSERR status. */ nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW), - NULL, confirm, nd, p); + NULL, NULL, confirm, 0, nd, p); nfsmout: NFSEXITCODE2(error, nd); return (error); @@ -3479,6 +3665,10 @@ nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram, int error = 0, len; nfsquad_t clientid; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { nd->nd_repstat = NFSERR_WRONGSEC; goto nfsmout; @@ -3497,10 +3687,14 @@ nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_uid = nd->nd_cred->cr_uid; clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK14 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -3518,3 +3712,389 @@ nfsmout: NFSEXITCODE2(error, nd); return (error); } + +/* + * nfsv4 exchange_id service + */ +APPLESTATIC int +nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + int error = 0, i, idlen; + struct nfsclient *clp = NULL; + nfsquad_t clientid, confirm; + uint8_t *verf; + uint32_t sp4type, v41flags; + uint64_t owner_minor; + struct timespec verstime; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED); + verf = (uint8_t *)tl; + tl += (NFSX_VERF / NFSX_UNSIGNED); + i = fxdr_unsigned(int, *tl); + if (i > NFSV4_OPAQUELIMIT || i <= 0) { + nd->nd_repstat = NFSERR_BADXDR; + goto nfsmout; + } + idlen = i; + if (nd->nd_flag & ND_GSS) + i += nd->nd_princlen; + clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK | + M_ZERO); + clp->lc_stateid = malloc(sizeof(struct nfsstatehead) * + nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK); + NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx); + NFSSOCKADDRALLOC(clp->lc_req.nr_nam); + NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in)); + clp->lc_req.nr_cred = NULL; + NFSBCOPY(verf, clp->lc_verf, NFSX_VERF); + clp->lc_idlen = idlen; + error = nfsrv_mtostr(nd, clp->lc_id, idlen); + if (error != 0) + goto nfsmout; + if ((nd->nd_flag & ND_GSS) != 0) { + clp->lc_flags = LCL_GSS | LCL_NFSV41; + if ((nd->nd_flag & ND_GSSINTEGRITY) != 0) + clp->lc_flags |= LCL_GSSINTEGRITY; + else if ((nd->nd_flag & ND_GSSPRIVACY) != 0) + clp->lc_flags |= LCL_GSSPRIVACY; + } else + clp->lc_flags = LCL_NFSV41; + if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) { + clp->lc_flags |= LCL_NAME; + clp->lc_namelen = nd->nd_princlen; + clp->lc_name = &clp->lc_id[idlen]; + NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen); + } else { + clp->lc_uid = nd->nd_cred->cr_uid; + clp->lc_gid = nd->nd_cred->cr_gid; + } + NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + v41flags = fxdr_unsigned(uint32_t, *tl++); + if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR | + NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS | + NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) { + nd->nd_repstat = NFSERR_INVAL; + goto nfsmout; + } + if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0) + confirm.lval[1] = 1; + else + confirm.lval[1] = 0; + v41flags = NFSV4EXCH_USENONPNFS; + sp4type = fxdr_unsigned(uint32_t, *tl); + if (sp4type != NFSV4EXCH_SP4NONE) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } + + /* + * nfsrv_setclient() does the actual work of adding it to the + * client list. If there is no error, the structure has been + * linked into the client list and clp should no longer be used + * here. When an error is returned, it has not been linked in, + * so it should be free'd. + */ + nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p); + if (clp != NULL) { + NFSSOCKADDRFREE(clp->lc_req.nr_nam); + NFSFREEMUTEX(&clp->lc_req.nr_mtx); + free(clp->lc_stateid, M_NFSDCLIENT); + free(clp, M_NFSDCLIENT); + } + if (nd->nd_repstat == 0) { + if (confirm.lval[1] != 0) + v41flags |= NFSV4EXCH_CONFIRMEDR; + NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED); + *tl++ = clientid.lval[0]; /* ClientID */ + *tl++ = clientid.lval[1]; + *tl++ = txdr_unsigned(confirm.lval[0]); /* SequenceID */ + *tl++ = txdr_unsigned(v41flags); /* Exch flags */ + *tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE); /* No SSV */ + owner_minor = 0; /* Owner */ + txdr_hyper(owner_minor, tl); /* Minor */ + (void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid, + strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */ + NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(NFSX_UNSIGNED); + *tl++ = time_uptime; /* Make scope a unique value. */ + *tl = txdr_unsigned(1); + (void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org")); + (void)nfsm_strtom(nd, version, strlen(version)); + NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME); + verstime.tv_sec = 1293840000; /* Jan 1, 2011 */ + verstime.tv_nsec = 0; + txdr_nfsv4time(&verstime, tl); + } + NFSEXITCODE2(0, nd); + return (0); +nfsmout: + if (clp != NULL) { + NFSSOCKADDRFREE(clp->lc_req.nr_nam); + NFSFREEMUTEX(&clp->lc_req.nr_mtx); + free(clp->lc_stateid, M_NFSDCLIENT); + free(clp, M_NFSDCLIENT); + } + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 create session service + */ +APPLESTATIC int +nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + int error = 0; + nfsquad_t clientid, confirm; + struct nfsdsession *sep = NULL; + uint32_t rdmacnt; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession), + M_NFSDSESSION, M_WAITOK | M_ZERO); + sep->sess_refcnt = 1; + mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF); + NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED); + clientid.lval[0] = *tl++; + clientid.lval[1] = *tl++; + confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++); + sep->sess_crflags = fxdr_unsigned(uint32_t, *tl); + /* Persistent sessions and RDMA are not supported. */ + sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN; + + /* Fore channel attributes. */ + NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED); + tl++; /* Header pad always 0. */ + sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++); + sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++); + sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++); + sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++); + sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++); + if (sep->sess_maxslots > NFSV4_SLOTS) + sep->sess_maxslots = NFSV4_SLOTS; + rdmacnt = fxdr_unsigned(uint32_t, *tl); + if (rdmacnt > 1) { + nd->nd_repstat = NFSERR_BADXDR; + goto nfsmout; + } else if (rdmacnt == 1) + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + + /* Back channel attributes. */ + NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED); + tl++; /* Header pad always 0. */ + sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++); + sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++); + sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++); + sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++); + sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++); + rdmacnt = fxdr_unsigned(uint32_t, *tl); + if (rdmacnt > 1) { + nd->nd_repstat = NFSERR_BADXDR; + goto nfsmout; + } else if (rdmacnt == 1) + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl); + + /* + * nfsrv_getclient() searches the client list for a match and + * returns the appropriate NFSERR status. + */ + nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW, + NULL, sep, confirm, sep->sess_cbprogram, nd, p); + if (nd->nd_repstat == 0) { + NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID); + NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID); + NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(confirm.lval[0]); /* sequenceid */ + *tl++ = txdr_unsigned(sep->sess_crflags); + + /* Fore channel attributes. */ + *tl++ = 0; + *tl++ = txdr_unsigned(sep->sess_maxreq); + *tl++ = txdr_unsigned(sep->sess_maxresp); + *tl++ = txdr_unsigned(sep->sess_maxrespcached); + *tl++ = txdr_unsigned(sep->sess_maxops); + *tl++ = txdr_unsigned(sep->sess_maxslots); + *tl++ = txdr_unsigned(1); + *tl++ = txdr_unsigned(0); /* No RDMA. */ + + /* Back channel attributes. */ + *tl++ = 0; + *tl++ = txdr_unsigned(sep->sess_cbmaxreq); + *tl++ = txdr_unsigned(sep->sess_cbmaxresp); + *tl++ = txdr_unsigned(sep->sess_cbmaxrespcached); + *tl++ = txdr_unsigned(sep->sess_cbmaxops); + *tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots); + *tl++ = txdr_unsigned(1); + *tl = txdr_unsigned(0); /* No RDMA. */ + } +nfsmout: + if (nd->nd_repstat != 0 && sep != NULL) + free(sep, M_NFSDSESSION); + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 sequence service + */ +APPLESTATIC int +nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid; + int cache_this, error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID); + NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID); + NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED); + sequenceid = fxdr_unsigned(uint32_t, *tl++); + nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++); + highest_slotid = fxdr_unsigned(uint32_t, *tl++); + if (*tl == newnfs_true) + cache_this = 1; + else + cache_this = 0; + nd->nd_flag |= ND_HASSEQUENCE; + nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid, + &target_highest_slotid, cache_this, &sflags, p); + if (nd->nd_repstat == 0) { + NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID); + NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID); + NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(sequenceid); + *tl++ = txdr_unsigned(nd->nd_slotid); + *tl++ = txdr_unsigned(highest_slotid); + *tl++ = txdr_unsigned(target_highest_slotid); + *tl = txdr_unsigned(sflags); + } +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 reclaim complete service + */ +APPLESTATIC int +nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + int error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + if (*tl == newnfs_true) + nd->nd_repstat = NFSERR_NOTSUPP; + else + nd->nd_repstat = nfsrv_checkreclaimcomplete(nd); +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 destroy clientid service + */ +APPLESTATIC int +nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + nfsquad_t clientid; + int error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + clientid.lval[0] = *tl++; + clientid.lval[1] = *tl; + nd->nd_repstat = nfsrv_destroyclient(clientid, p); +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 destroy session service + */ +APPLESTATIC int +nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint8_t *cp, sessid[NFSX_V4SESSIONID]; + int error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID); + NFSBCOPY(cp, sessid, NFSX_V4SESSIONID); + nd->nd_repstat = nfsrv_destroysession(nd, sessid); +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 free stateid service + */ +APPLESTATIC int +nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + nfsv4stateid_t stateid; + int error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID); + stateid.seqid = fxdr_unsigned(uint32_t, *tl++); + NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER); + nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p); +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 service not supported + */ +APPLESTATIC int +nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + + nd->nd_repstat = NFSERR_NOTSUPP; + NFSEXITCODE2(0, nd); + return (0); +} + diff --git a/sys/fs/nfs/server/nfs_nfsdstate.c b/sys/fs/nfs/server/nfs_nfsdstate.c index f8260f026fc..24b360bbeff 100644 --- a/sys/fs/nfs/server/nfs_nfsdstate.c +++ b/sys/fs/nfs/server/nfs_nfsdstate.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_nfsdstate.c,v 1.2 2013/11/27 17:24:44 christos Exp $ */ +/* $NetBSD: nfs_nfsdstate.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 2009 Rick Macklem, University of Guelph * All rights reserved. @@ -27,8 +27,8 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdstate.c 245909 2013-01-25 15:25:24Z jhb "); */ -__RCSID("$NetBSD: nfs_nfsdstate.c,v 1.2 2013/11/27 17:24:44 christos Exp $"); +/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdstate.c 307694 2016-10-20 23:53:16Z rmacklem "); */ +__RCSID("$NetBSD: nfs_nfsdstate.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); #ifndef APPLEKEXT #include <fs/nfs/nfsport.h> @@ -39,25 +39,54 @@ int nfsrv_dolocallocks = 0; struct nfsv4lock nfsv4rootfs_lock; extern int newnfs_numnfsd; -extern struct nfsstats newnfsstats; +extern struct nfsstatsv1 nfsstatsv1; extern int nfsrv_lease; extern struct timeval nfsboottime; extern u_int32_t newnfs_true, newnfs_false; NFSV4ROOTLOCKMUTEX; NFSSTATESPINLOCK; +SYSCTL_DECL(_vfs_nfsd); +int nfsrv_statehashsize = NFSSTATEHASHSIZE; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, statehashsize, CTLFLAG_RDTUN, + &nfsrv_statehashsize, 0, + "Size of state hash table set via loader.conf"); + +int nfsrv_clienthashsize = NFSCLIENTHASHSIZE; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, clienthashsize, CTLFLAG_RDTUN, + &nfsrv_clienthashsize, 0, + "Size of client hash table set via loader.conf"); + +int nfsrv_lockhashsize = NFSLOCKHASHSIZE; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, fhhashsize, CTLFLAG_RDTUN, + &nfsrv_lockhashsize, 0, + "Size of file handle hash table set via loader.conf"); + +int nfsrv_sessionhashsize = NFSSESSIONHASHSIZE; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, sessionhashsize, CTLFLAG_RDTUN, + &nfsrv_sessionhashsize, 0, + "Size of session hash table set via loader.conf"); + +static int nfsrv_v4statelimit = NFSRV_V4STATELIMIT; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, v4statelimit, CTLFLAG_RWTUN, + &nfsrv_v4statelimit, 0, + "High water limit for NFSv4 opens+locks+delegations"); + +static int nfsrv_writedelegifpos = 0; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, writedelegifpos, CTLFLAG_RW, + &nfsrv_writedelegifpos, 0, + "Issue a write delegation for read opens if possible"); + /* * Hash lists for nfs V4. - * (Some would put them in the .h file, but I don't like declaring storage - * in a .h) */ -struct nfsclienthashhead nfsclienthash[NFSCLIENTHASHSIZE]; -struct nfslockhashhead nfslockhash[NFSLOCKHASHSIZE]; +struct nfsclienthashhead *nfsclienthash; +struct nfslockhashhead *nfslockhash; +struct nfssessionhash *nfssessionhash; #endif /* !APPLEKEXT */ static u_int32_t nfsrv_openpluslock = 0, nfsrv_delegatecnt = 0; static time_t nfsrvboottime; -static int nfsrv_writedelegifpos = 1; static int nfsrv_returnoldstateid = 0, nfsrv_clients = 0; static int nfsrv_clienthighwater = NFSRV_CLIENTHIGHWATER; static int nfsrv_nogsscallback = 0; @@ -81,7 +110,7 @@ static int nfsrv_getstate(struct nfsclient *clp, nfsv4stateid_t *stateidp, static void nfsrv_getowner(struct nfsstatehead *hp, struct nfsstate *new_stp, struct nfsstate **stpp); static int nfsrv_getlockfh(vnode_t vp, u_short flags, - struct nfslockfile **new_lfpp, fhandle_t *nfhp, NFSPROC_T *p); + struct nfslockfile *new_lfp, fhandle_t *nfhp, NFSPROC_T *p); static int nfsrv_getlockfile(u_short flags, struct nfslockfile **new_lfpp, struct nfslockfile **lfpp, fhandle_t *nfhp, int lockit); static void nfsrv_insertlock(struct nfslock *new_lop, @@ -91,10 +120,13 @@ static void nfsrv_updatelock(struct nfsstate *stp, struct nfslock **new_lopp, static int nfsrv_getipnumber(u_char *cp); static int nfsrv_checkrestart(nfsquad_t clientid, u_int32_t flags, nfsv4stateid_t *stateidp, int specialid); -static int nfsrv_checkgrace(u_int32_t flags); +static int nfsrv_checkgrace(struct nfsrv_descript *nd, struct nfsclient *clp, + u_int32_t flags); static int nfsrv_docallback(struct nfsclient *clp, int procnum, nfsv4stateid_t *stateidp, int trunc, fhandle_t *fhp, struct nfsvattr *nap, nfsattrbit_t *attrbitp, NFSPROC_T *p); +static int nfsrv_cbcallargs(struct nfsrv_descript *nd, struct nfsclient *clp, + uint32_t callback, int op, const char *optag, struct nfsdsession **sepp); static u_int32_t nfsrv_nextclientindex(void); static u_int32_t nfsrv_nextstateindex(struct nfsclient *clp); static void nfsrv_markstable(struct nfsclient *clp); @@ -125,6 +157,11 @@ static void nfsrv_locallock_commit(struct nfslockfile *lfp, int flags, uint64_t first, uint64_t end); static void nfsrv_locklf(struct nfslockfile *lfp); static void nfsrv_unlocklf(struct nfslockfile *lfp); +static struct nfsdsession *nfsrv_findsession(uint8_t *sessionid); +static int nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid); +static int nfsv4_setcbsequence(struct nfsrv_descript *nd, struct nfsclient *clp, + int dont_replycache, struct nfsdsession **sepp); +static int nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp); /* * Scan the client list for a match and either return the current one, @@ -146,7 +183,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, /* * Check for state resource limit exceeded. */ - if (nfsrv_openpluslock > NFSRV_V4STATELIMIT) { + if (nfsrv_openpluslock > nfsrv_v4statelimit) { error = NFSERR_RESOURCE; goto out; } @@ -181,7 +218,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, * Search for a match in the client list. */ gotit = i = 0; - while (i < NFSCLIENTHASHSIZE && !gotit) { + while (i < nfsrv_clienthashsize && !gotit) { LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) { if (new_clp->lc_idlen == clp->lc_idlen && !NFSBCMP(new_clp->lc_id, clp->lc_id, clp->lc_idlen)) { @@ -189,14 +226,27 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, break; } } - i++; + if (gotit == 0) + i++; } if (!gotit || (clp->lc_flags & (LCL_NEEDSCONFIRM | LCL_ADMINREVOKED))) { + if ((nd->nd_flag & ND_NFSV41) != 0 && confirmp->lval[1] != 0) { + /* + * For NFSv4.1, if confirmp->lval[1] is non-zero, the + * client is trying to update a confirmed clientid. + */ + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + confirmp->lval[1] = 0; + error = NFSERR_NOENT; + goto out; + } /* * Get rid of the old one. */ - if (i != NFSCLIENTHASHSIZE) { + if (i != nfsrv_clienthashsize) { LIST_REMOVE(clp, lc_hash); nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); @@ -207,7 +257,12 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, * Add it after assigning a client id to it. */ new_clp->lc_flags |= LCL_NEEDSCONFIRM; - confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; + if ((nd->nd_flag & ND_NFSV41) != 0) + new_clp->lc_confirm.lval[0] = confirmp->lval[0] = + ++confirm_index; + else + confirmp->qval = new_clp->lc_confirm.qval = + ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = (u_int32_t)nfsrvboottime; clientidp->lval[1] = new_clp->lc_clientid.lval[1] = @@ -219,11 +274,12 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, LIST_INIT(&new_clp->lc_open); LIST_INIT(&new_clp->lc_deleg); LIST_INIT(&new_clp->lc_olddeleg); - for (i = 0; i < NFSSTATEHASHSIZE; i++) + LIST_INIT(&new_clp->lc_session); + for (i = 0; i < nfsrv_statehashsize; i++) LIST_INIT(&new_clp->lc_stateid[i]); LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, lc_hash); - newnfsstats.srvclients++; + nfsstatsv1.srvclients++; nfsrv_openpluslock++; nfsrv_clients++; NFSLOCKV4ROOTMUTEX(); @@ -291,7 +347,12 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, */ LIST_REMOVE(clp, lc_hash); new_clp->lc_flags |= LCL_NEEDSCONFIRM; - confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; + if ((nd->nd_flag & ND_NFSV41) != 0) + new_clp->lc_confirm.lval[0] = confirmp->lval[0] = + ++confirm_index; + else + confirmp->qval = new_clp->lc_confirm.qval = + ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = nfsrvboottime; clientidp->lval[1] = new_clp->lc_clientid.lval[1] = @@ -314,7 +375,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, ls_list); LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) tstp->ls_clp = new_clp; - for (i = 0; i < NFSSTATEHASHSIZE; i++) { + for (i = 0; i < nfsrv_statehashsize; i++) { LIST_NEWHEAD(&new_clp->lc_stateid[i], &clp->lc_stateid[i], ls_hash); LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) @@ -322,7 +383,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, } LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, lc_hash); - newnfsstats.srvclients++; + nfsstatsv1.srvclients++; nfsrv_openpluslock++; nfsrv_clients++; NFSLOCKV4ROOTMUTEX(); @@ -344,60 +405,71 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, *new_clpp = NULL; goto out; } - /* - * id and verifier match, so update the net address info - * and get rid of any existing callback authentication - * handle, so a new one will be acquired. - */ - LIST_REMOVE(clp, lc_hash); - new_clp->lc_flags |= (LCL_NEEDSCONFIRM | LCL_DONTCLEAN); - new_clp->lc_expiry = nfsrv_leaseexpiry(); - confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; - clientidp->lval[0] = new_clp->lc_clientid.lval[0] = - clp->lc_clientid.lval[0]; - clientidp->lval[1] = new_clp->lc_clientid.lval[1] = - clp->lc_clientid.lval[1]; - new_clp->lc_delegtime = clp->lc_delegtime; - new_clp->lc_stateindex = clp->lc_stateindex; - new_clp->lc_statemaxindex = clp->lc_statemaxindex; - new_clp->lc_cbref = 0; - LIST_NEWHEAD(&new_clp->lc_open, &clp->lc_open, ls_list); - LIST_FOREACH(tstp, &new_clp->lc_open, ls_list) - tstp->ls_clp = new_clp; - LIST_NEWHEAD(&new_clp->lc_deleg, &clp->lc_deleg, ls_list); - LIST_FOREACH(tstp, &new_clp->lc_deleg, ls_list) - tstp->ls_clp = new_clp; - LIST_NEWHEAD(&new_clp->lc_olddeleg, &clp->lc_olddeleg, ls_list); - LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) - tstp->ls_clp = new_clp; - for (i = 0; i < NFSSTATEHASHSIZE; i++) { - LIST_NEWHEAD(&new_clp->lc_stateid[i], &clp->lc_stateid[i], - ls_hash); - LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) + + /* For NFSv4.1, mark that we found a confirmed clientid. */ + if ((nd->nd_flag & ND_NFSV41) != 0) { + clientidp->lval[0] = clp->lc_clientid.lval[0]; + clientidp->lval[1] = clp->lc_clientid.lval[1]; + confirmp->lval[0] = 0; /* Ignored by client */ + confirmp->lval[1] = 1; + } else { + /* + * id and verifier match, so update the net address info + * and get rid of any existing callback authentication + * handle, so a new one will be acquired. + */ + LIST_REMOVE(clp, lc_hash); + new_clp->lc_flags |= (LCL_NEEDSCONFIRM | LCL_DONTCLEAN); + new_clp->lc_expiry = nfsrv_leaseexpiry(); + confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; + clientidp->lval[0] = new_clp->lc_clientid.lval[0] = + clp->lc_clientid.lval[0]; + clientidp->lval[1] = new_clp->lc_clientid.lval[1] = + clp->lc_clientid.lval[1]; + new_clp->lc_delegtime = clp->lc_delegtime; + new_clp->lc_stateindex = clp->lc_stateindex; + new_clp->lc_statemaxindex = clp->lc_statemaxindex; + new_clp->lc_cbref = 0; + LIST_NEWHEAD(&new_clp->lc_open, &clp->lc_open, ls_list); + LIST_FOREACH(tstp, &new_clp->lc_open, ls_list) + tstp->ls_clp = new_clp; + LIST_NEWHEAD(&new_clp->lc_deleg, &clp->lc_deleg, ls_list); + LIST_FOREACH(tstp, &new_clp->lc_deleg, ls_list) + tstp->ls_clp = new_clp; + LIST_NEWHEAD(&new_clp->lc_olddeleg, &clp->lc_olddeleg, ls_list); + LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) tstp->ls_clp = new_clp; + for (i = 0; i < nfsrv_statehashsize; i++) { + LIST_NEWHEAD(&new_clp->lc_stateid[i], + &clp->lc_stateid[i], ls_hash); + LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) + tstp->ls_clp = new_clp; + } + LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, + lc_hash); + nfsstatsv1.srvclients++; + nfsrv_openpluslock++; + nfsrv_clients++; } - LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, - lc_hash); - newnfsstats.srvclients++; - nfsrv_openpluslock++; - nfsrv_clients++; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); - /* - * Must wait until any outstanding callback on the old clp - * completes. - */ - NFSLOCKSTATE(); - while (clp->lc_cbref) { - clp->lc_flags |= LCL_WAKEUPWANTED; - (void)mtx_sleep(clp, NFSSTATEMUTEXPTR, PZERO - 1, "nfsd clp", - 10 * hz); + if ((nd->nd_flag & ND_NFSV41) == 0) { + /* + * Must wait until any outstanding callback on the old clp + * completes. + */ + NFSLOCKSTATE(); + while (clp->lc_cbref) { + clp->lc_flags |= LCL_WAKEUPWANTED; + (void)mtx_sleep(clp, NFSSTATEMUTEXPTR, PZERO - 1, + "nfsdclp", 10 * hz); + } + NFSUNLOCKSTATE(); + nfsrv_zapclient(clp, p); + *new_clpp = NULL; } - NFSUNLOCKSTATE(); - nfsrv_zapclient(clp, p); - *new_clpp = NULL; out: NFSEXITCODE2(error, nd); @@ -409,17 +481,23 @@ out: */ APPLESTATIC int nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, - nfsquad_t confirm, struct nfsrv_descript *nd, NFSPROC_T *p) + struct nfsdsession *nsep, nfsquad_t confirm, uint32_t cbprogram, + struct nfsrv_descript *nd, NFSPROC_T *p) { struct nfsclient *clp; struct nfsstate *stp; int i; struct nfsclienthashhead *hp; int error = 0, igotlock, doneok; + struct nfssessionhash *shp; + struct nfsdsession *sep; + uint64_t sessid[2]; + static uint64_t next_sess = 0; if (clpp) *clpp = NULL; - if (nfsrvboottime != clientid.lval[0]) { + if ((nd == NULL || (nd->nd_flag & ND_NFSV41) == 0 || + opflags != CLOPS_RENEW) && nfsrvboottime != clientid.lval[0]) { error = NFSERR_STALECLIENTID; goto out; } @@ -436,15 +514,37 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); } while (!igotlock); + /* + * Create a new sessionid here, since we need to do it where + * there is a mutex held to serialize update of next_sess. + */ + if ((nd->nd_flag & ND_NFSV41) != 0) { + sessid[0] = ++next_sess; + sessid[1] = clientid.qval; + } NFSUNLOCKV4ROOTMUTEX(); } else if (opflags != CLOPS_RENEW) { NFSLOCKSTATE(); } - hp = NFSCLIENTHASH(clientid); - LIST_FOREACH(clp, hp, lc_hash) { - if (clp->lc_clientid.lval[1] == clientid.lval[1]) - break; + /* For NFSv4.1, the clp is acquired from the associated session. */ + if (nd != NULL && (nd->nd_flag & ND_NFSV41) != 0 && + opflags == CLOPS_RENEW) { + clp = NULL; + if ((nd->nd_flag & ND_HASSEQUENCE) != 0) { + shp = NFSSESSIONHASH(nd->nd_sessionid); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(nd->nd_sessionid); + if (sep != NULL) + clp = sep->sess_clp; + NFSUNLOCKSESSION(shp); + } + } else { + hp = NFSCLIENTHASH(clientid); + LIST_FOREACH(clp, hp, lc_hash) { + if (clp->lc_clientid.lval[1] == clientid.lval[1]) + break; + } } if (clp == NULL) { if (opflags & CLOPS_CONFIRM) @@ -472,7 +572,10 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, * Perform any operations specified by the opflags. */ if (opflags & CLOPS_CONFIRM) { - if (clp->lc_confirm.qval != confirm.qval) + if (((nd->nd_flag & ND_NFSV41) != 0 && + clp->lc_confirm.lval[0] != confirm.lval[0]) || + ((nd->nd_flag & ND_NFSV41) == 0 && + clp->lc_confirm.qval != confirm.qval)) error = NFSERR_STALECLIENTID; else if (nfsrv_notsamecredname(nd, clp)) error = NFSERR_CLIDINUSE; @@ -487,7 +590,7 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, */ nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_olddeleg); - if (nfsrv_checkgrace(0)) { + if (nfsrv_checkgrace(nd, clp, 0)) { /* In grace, so just delete delegations */ nfsrv_freedeleglist(&clp->lc_deleg); } else { @@ -498,10 +601,43 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, LIST_NEWHEAD(&clp->lc_olddeleg, &clp->lc_deleg, ls_list); } + if ((nd->nd_flag & ND_NFSV41) != 0) + clp->lc_program = cbprogram; } clp->lc_flags &= ~(LCL_NEEDSCONFIRM | LCL_DONTCLEAN); if (clp->lc_program) clp->lc_flags |= LCL_NEEDSCBNULL; + /* For NFSv4.1, link the session onto the client. */ + if (nsep != NULL) { + /* Hold a reference on the xprt for a backchannel. */ + if ((nsep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) + != 0 && clp->lc_req.nr_client == NULL) { + clp->lc_req.nr_client = (struct __rpc_client *) + clnt_bck_create(nd->nd_xprt->xp_socket, + cbprogram, NFSV4_CBVERS); + if (clp->lc_req.nr_client != NULL) { + SVC_ACQUIRE(nd->nd_xprt); + nd->nd_xprt->xp_p2 = + clp->lc_req.nr_client->cl_private; + /* Disable idle timeout. */ + nd->nd_xprt->xp_idletimeout = 0; + nsep->sess_cbsess.nfsess_xprt = nd->nd_xprt; + } else + nsep->sess_crflags &= ~NFSV4CRSESS_CONNBACKCHAN; + } + NFSBCOPY(sessid, nsep->sess_sessionid, + NFSX_V4SESSIONID); + NFSBCOPY(sessid, nsep->sess_cbsess.nfsess_sessionid, + NFSX_V4SESSIONID); + shp = NFSSESSIONHASH(nsep->sess_sessionid); + NFSLOCKSTATE(); + NFSLOCKSESSION(shp); + LIST_INSERT_HEAD(&shp->list, nsep, sess_hash); + LIST_INSERT_HEAD(&clp->lc_session, nsep, sess_list); + nsep->sess_clp = clp; + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + } } } else if (clp->lc_flags & LCL_NEEDSCONFIRM) { error = NFSERR_EXPIRED; @@ -513,7 +649,7 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, if (!error && (opflags & CLOPS_RENEWOP)) { if (nfsrv_notsamecredname(nd, clp)) { doneok = 0; - for (i = 0; i < NFSSTATEHASHSIZE && doneok == 0; i++) { + for (i = 0; i < nfsrv_statehashsize && doneok == 0; i++) { LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { if ((stp->ls_flags & NFSLCK_OPEN) && stp->ls_uid == nd->nd_cred->cr_uid) { @@ -548,6 +684,74 @@ out: } /* + * Perform the NFSv4.1 destroy clientid. + */ +int +nfsrv_destroyclient(nfsquad_t clientid, NFSPROC_T *p) +{ + struct nfsclient *clp; + struct nfsclienthashhead *hp; + int error = 0, i, igotlock; + + if (nfsrvboottime != clientid.lval[0]) { + error = NFSERR_STALECLIENTID; + goto out; + } + + /* Lock out other nfsd threads */ + NFSLOCKV4ROOTMUTEX(); + nfsv4_relref(&nfsv4rootfs_lock); + do { + igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, + NFSV4ROOTLOCKMUTEXPTR, NULL); + } while (igotlock == 0); + NFSUNLOCKV4ROOTMUTEX(); + + hp = NFSCLIENTHASH(clientid); + LIST_FOREACH(clp, hp, lc_hash) { + if (clp->lc_clientid.lval[1] == clientid.lval[1]) + break; + } + if (clp == NULL) { + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + /* Just return ok, since it is gone. */ + goto out; + } + + /* Scan for state on the clientid. */ + for (i = 0; i < nfsrv_statehashsize; i++) + if (!LIST_EMPTY(&clp->lc_stateid[i])) { + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + error = NFSERR_CLIENTIDBUSY; + goto out; + } + if (!LIST_EMPTY(&clp->lc_session) || !LIST_EMPTY(&clp->lc_deleg)) { + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + error = NFSERR_CLIENTIDBUSY; + goto out; + } + + /* Destroy the clientid and return ok. */ + nfsrv_cleanclient(clp, p); + nfsrv_freedeleglist(&clp->lc_deleg); + nfsrv_freedeleglist(&clp->lc_olddeleg); + LIST_REMOVE(clp, lc_hash); + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + nfsrv_zapclient(clp, p); +out: + NFSEXITCODE2(error, nd); + return (error); +} + +/* * Called from the new nfssvc syscall to admin revoke a clientid. * Returns 0 for success, error otherwise. */ @@ -574,7 +778,7 @@ nfsrv_adminrevoke(struct nfsd_clid *revokep, NFSPROC_T *p) * Search for a match in the client list. */ gotit = i = 0; - while (i < NFSCLIENTHASHSIZE && !gotit) { + while (i < nfsrv_clienthashsize && !gotit) { LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) { if (revokep->nclid_idlen == clp->lc_idlen && !NFSBCMP(revokep->nclid_id, clp->lc_id, clp->lc_idlen)) { @@ -617,7 +821,7 @@ out: /* * Dump out stats for all clients. Called from nfssvc(2), that is used - * newnfsstats. + * nfsstatsv1. */ APPLESTATIC void nfsrv_dumpclients(struct nfsd_dumpclients *dumpp, int maxcnt) @@ -636,7 +840,7 @@ nfsrv_dumpclients(struct nfsd_dumpclients *dumpp, int maxcnt) /* * Rattle through the client lists until done. */ - while (i < NFSCLIENTHASHSIZE && cnt < maxcnt) { + while (i < nfsrv_clienthashsize && cnt < maxcnt) { clp = LIST_FIRST(&nfsclienthash[i]); while (clp != NULL && cnt < maxcnt) { nfsrv_dumpaclient(clp, &dumpp[cnt]); @@ -904,7 +1108,7 @@ nfsrv_servertimer(void) /* * For each client... */ - for (i = 0; i < NFSCLIENTHASHSIZE; i++) { + for (i = 0; i < nfsrv_clienthashsize; i++) { clp = LIST_FIRST(&nfsclienthash[i]); while (clp != NULL) { nclp = LIST_NEXT(clp, lc_hash); @@ -915,7 +1119,7 @@ nfsrv_servertimer(void) nfsrv_clients > nfsrv_clienthighwater)) || (clp->lc_expiry + NFSRV_MOULDYLEASE) < NFSD_MONOSEC || (clp->lc_expiry < NFSD_MONOSEC && - (nfsrv_openpluslock * 10 / 9) > NFSRV_V4STATELIMIT)) { + (nfsrv_openpluslock * 10 / 9) > nfsrv_v4statelimit)) { /* * Lease has expired several nfsrv_lease times ago: * PLUS @@ -954,7 +1158,7 @@ nfsrv_servertimer(void) stp->ls_noopens++; if (stp->ls_noopens > NFSNOOPEN || (nfsrv_openpluslock * 2) > - NFSRV_V4STATELIMIT) + nfsrv_v4statelimit) nfsrv_stablefirst.nsf_flags |= NFSNSF_NOOPENS; } else { @@ -985,9 +1189,13 @@ APPLESTATIC void nfsrv_cleanclient(struct nfsclient *clp, NFSPROC_T *p) { struct nfsstate *stp, *nstp; + struct nfsdsession *sep, *nsep; LIST_FOREACH_SAFE(stp, &clp->lc_open, ls_list, nstp) nfsrv_freeopenowner(stp, 1, p); + if ((clp->lc_flags & LCL_ADMINREVOKED) == 0) + LIST_FOREACH_SAFE(sep, &clp->lc_session, sess_list, nsep) + (void)nfsrv_freesession(sep, NULL); } /* @@ -1014,9 +1222,10 @@ nfsrv_zapclient(struct nfsclient *clp, NFSPROC_T *p) newnfs_disconnect(&clp->lc_req); NFSSOCKADDRFREE(clp->lc_req.nr_nam); NFSFREEMUTEX(&clp->lc_req.nr_mtx); - free((caddr_t)clp, M_NFSDCLIENT); + free(clp->lc_stateid, M_NFSDCLIENT); + free(clp, M_NFSDCLIENT); NFSLOCKSTATE(); - newnfsstats.srvclients--; + nfsstatsv1.srvclients--; nfsrv_openpluslock--; nfsrv_clients--; NFSUNLOCKSTATE(); @@ -1057,7 +1266,7 @@ nfsrv_freedeleg(struct nfsstate *stp) nfsv4_testlock(&lfp->lf_locallock_lck) == 0) nfsrv_freenfslockfile(lfp); FREE((caddr_t)stp, M_NFSDSTATE); - newnfsstats.srvdelegates--; + nfsstatsv1.srvdelegates--; nfsrv_openpluslock--; nfsrv_delegatecnt--; } @@ -1083,7 +1292,7 @@ nfsrv_freeopenowner(struct nfsstate *stp, int cansleep, NFSPROC_T *p) if (stp->ls_op) nfsrvd_derefcache(stp->ls_op); FREE((caddr_t)stp, M_NFSDSTATE); - newnfsstats.srvopenowners--; + nfsstatsv1.srvopenowners--; nfsrv_openpluslock--; } @@ -1133,7 +1342,7 @@ nfsrv_freeopen(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p) if (cansleep != 0) NFSUNLOCKSTATE(); FREE((caddr_t)stp, M_NFSDSTATE); - newnfsstats.srvopens--; + nfsstatsv1.srvopens--; nfsrv_openpluslock--; return (ret); } @@ -1152,7 +1361,7 @@ nfsrv_freelockowner(struct nfsstate *stp, vnode_t vp, int cansleep, if (stp->ls_op) nfsrvd_derefcache(stp->ls_op); FREE((caddr_t)stp, M_NFSDSTATE); - newnfsstats.srvlockowners--; + nfsstatsv1.srvlockowners--; nfsrv_openpluslock--; } @@ -1170,6 +1379,8 @@ nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep, vnode_t tvp = NULL; uint64_t first, end; + if (vp != NULL) + ASSERT_VOP_UNLOCKED(vp, "nfsrv_freeallnfslocks: vnode locked"); lop = LIST_FIRST(&stp->ls_lock); while (lop != NULL) { nlop = LIST_NEXT(lop, lo_lckowner); @@ -1189,9 +1400,10 @@ nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep, if (gottvp == 0) { if (nfsrv_dolocallocks == 0) tvp = NULL; - else if (vp == NULL && cansleep != 0) + else if (vp == NULL && cansleep != 0) { tvp = nfsvno_getvp(&lfp->lf_fh); - else + NFSVOPUNLOCK(tvp, 0); + } else tvp = vp; gottvp = 1; } @@ -1212,7 +1424,7 @@ nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep, lop = nlop; } if (vp == NULL && tvp != NULL) - vput(tvp); + vrele(tvp); } /* @@ -1224,7 +1436,7 @@ nfsrv_freenfslock(struct nfslock *lop) if (lop->lo_lckfile.le_prev != NULL) { LIST_REMOVE(lop, lo_lckfile); - newnfsstats.srvlocks--; + nfsstatsv1.srvlocks--; nfsrv_openpluslock--; } LIST_REMOVE(lop, lo_lckowner); @@ -1323,7 +1535,7 @@ nfsrv_lockctrl(vnode_t vp, struct nfsstate **new_stpp, struct nfsclient *clp = NULL; u_int32_t bits; int error = 0, haslock = 0, ret, reterr; - int getlckret, delegation = 0, filestruct_locked; + int getlckret, delegation = 0, filestruct_locked, vnode_unlocked = 0; fhandle_t nfh; uint64_t first, end; uint32_t lock_flags; @@ -1357,7 +1569,7 @@ nfsrv_lockctrl(vnode_t vp, struct nfsstate **new_stpp, * Check for state resource limit exceeded. */ if ((new_stp->ls_flags & NFSLCK_LOCK) && - nfsrv_openpluslock > NFSRV_V4STATELIMIT) { + nfsrv_openpluslock > nfsrv_v4statelimit) { error = NFSERR_RESOURCE; goto out; } @@ -1413,6 +1625,11 @@ tryagain: * locking rolled back. */ NFSUNLOCKSTATE(); + if (vnode_unlocked == 0) { + ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl1"); + vnode_unlocked = 1; + NFSVOPUNLOCK(vp, 0); + } reterr = nfsrv_locallock(vp, lfp, (new_lop->lo_flags & (NFSLCK_READ | NFSLCK_WRITE)), new_lop->lo_first, new_lop->lo_end, cfp, p); @@ -1424,11 +1641,11 @@ tryagain: if (new_stp->ls_flags & NFSLCK_TEST) { /* * RFC 3530 does not list LockT as an op that renews a - * lease, but the concensus seems to be that it is ok + * lease, but the consensus seems to be that it is ok * for a server to do so. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); /* * Since NFSERR_EXPIRED, NFSERR_ADMINREVOKED are not valid @@ -1440,8 +1657,8 @@ tryagain: error = 0; lckstp = new_stp; } else { - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); if (error == 0) /* * Look up the stateid @@ -1454,9 +1671,17 @@ tryagain: */ if (error == 0 && (stp->ls_flags & NFSLCK_OPEN) && ((stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM) || - (getlckret == 0 && stp->ls_lfp != lfp))) - error = NFSERR_BADSTATEID; - if (error == 0 && + (getlckret == 0 && stp->ls_lfp != lfp))){ + /* + * NFSLCK_SETATTR should return OK rather than NFSERR_BADSTATEID + * The only exception is using SETATTR with SIZE. + * */ + if ((new_stp->ls_flags & + (NFSLCK_SETATTR | NFSLCK_CHECK)) != NFSLCK_SETATTR) + error = NFSERR_BADSTATEID; + } + + if (error == 0 && (stp->ls_flags & (NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) && getlckret == 0 && stp->ls_lfp != lfp) error = NFSERR_BADSTATEID; @@ -1523,15 +1748,18 @@ tryagain: * If the seqid part of the stateid isn't the same, return * NFSERR_OLDSTATEID for cases other than I/O Ops. * For I/O Ops, only return NFSERR_OLDSTATEID if - * nfsrv_returnoldstateid is set. (The concensus on the email + * nfsrv_returnoldstateid is set. (The consensus on the email * list was that most clients would prefer to not receive * NFSERR_OLDSTATEID for I/O Ops, but the RFC suggests that that * is what will happen, so I use the nfsrv_returnoldstateid to * allow for either server configuration.) */ if (!error && stp->ls_stateid.seqid!=new_stp->ls_stateid.seqid && - (!(new_stp->ls_flags & NFSLCK_CHECK) || - nfsrv_returnoldstateid)) + (((nd->nd_flag & ND_NFSV41) == 0 && + (!(new_stp->ls_flags & NFSLCK_CHECK) || + nfsrv_returnoldstateid)) || + ((nd->nd_flag & ND_NFSV41) != 0 && + new_stp->ls_stateid.seqid != 0))) error = NFSERR_OLDSTATEID; } } @@ -1540,7 +1768,7 @@ tryagain: * Now we can check for grace. */ if (!error) - error = nfsrv_checkgrace(new_stp->ls_flags); + error = nfsrv_checkgrace(nd, clp, new_stp->ls_flags); if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error && nfsrv_checkstable(clp)) error = NFSERR_NOGRACE; @@ -1571,6 +1799,11 @@ tryagain: if (filestruct_locked != 0) { /* Roll back local locks. */ NFSUNLOCKSTATE(); + if (vnode_unlocked == 0) { + ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl2"); + vnode_unlocked = 1; + NFSVOPUNLOCK(vp, 0); + } nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); @@ -1648,6 +1881,12 @@ tryagain: if (filestruct_locked != 0) { /* Roll back local locks. */ NFSUNLOCKSTATE(); + if (vnode_unlocked == 0) { + ASSERT_VOP_ELOCKED(vp, + "nfsrv_lockctrl3"); + vnode_unlocked = 1; + NFSVOPUNLOCK(vp, 0); + } nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); @@ -1667,6 +1906,8 @@ tryagain: bits = tstp->ls_flags; bits >>= NFSLCK_SHIFT; if (new_stp->ls_flags & bits & NFSLCK_ACCESSBITS) { + KASSERT(vnode_unlocked == 0, + ("nfsrv_lockctrl: vnode unlocked1")); ret = nfsrv_clientconflict(tstp->ls_clp, &haslock, vp, p); if (ret == 1) { @@ -1698,6 +1939,8 @@ tryagain: * For setattr, just get rid of all the Delegations for other clients. */ if (new_stp->ls_flags & NFSLCK_SETATTR) { + KASSERT(vnode_unlocked == 0, + ("nfsrv_lockctrl: vnode unlocked2")); ret = nfsrv_cleandeleg(vp, lfp, clp, &haslock, p); if (ret) { /* @@ -1734,7 +1977,7 @@ tryagain: * - there is a conflict if a different client has any delegation * - there is a conflict if the same client has a read delegation * (I don't understand why this isn't allowed, but that seems to be - * the current concensus?) + * the current consensus?) */ tstp = LIST_FIRST(&lfp->lf_deleg); while (tstp != NULL) { @@ -1748,14 +1991,26 @@ tryagain: (new_lop->lo_flags & NFSLCK_WRITE) && (clp != tstp->ls_clp || (tstp->ls_flags & NFSLCK_DELEGREAD)))) { + ret = 0; if (filestruct_locked != 0) { /* Roll back local locks. */ NFSUNLOCKSTATE(); + if (vnode_unlocked == 0) { + ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl4"); + NFSVOPUNLOCK(vp, 0); + } nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); + NFSUNLOCKSTATE(); + NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); + vnode_unlocked = 0; + if ((vp->v_iflag & VI_DOOMED) != 0) + ret = NFSERR_SERVERFAULT; + NFSLOCKSTATE(); } - ret = nfsrv_delegconflict(tstp, &haslock, p, vp); + if (ret == 0) + ret = nfsrv_delegconflict(tstp, &haslock, p, vp); if (ret) { /* * nfsrv_delegconflict unlocks state when it @@ -1787,11 +2042,18 @@ tryagain: end = new_lop->lo_end; nfsrv_updatelock(stp, new_lopp, &other_lop, lfp); stateidp->seqid = ++(stp->ls_stateid.seqid); + if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) + stateidp->seqid = stp->ls_stateid.seqid = 1; stateidp->other[0] = stp->ls_stateid.other[0]; stateidp->other[1] = stp->ls_stateid.other[1]; stateidp->other[2] = stp->ls_stateid.other[2]; if (filestruct_locked != 0) { NFSUNLOCKSTATE(); + if (vnode_unlocked == 0) { + ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl5"); + vnode_unlocked = 1; + NFSVOPUNLOCK(vp, 0); + } /* Update the local locks. */ nfsrv_localunlock(vp, lfp, first, end, p); NFSLOCKSTATE(); @@ -1822,14 +2084,29 @@ tryagain: FREE((caddr_t)other_lop, M_NFSDLOCK); other_lop = NULL; } - ret = nfsrv_clientconflict(lop->lo_stp->ls_clp,&haslock,vp,p); + if (vnode_unlocked != 0) + ret = nfsrv_clientconflict(lop->lo_stp->ls_clp, &haslock, + NULL, p); + else + ret = nfsrv_clientconflict(lop->lo_stp->ls_clp, &haslock, + vp, p); if (ret == 1) { if (filestruct_locked != 0) { + if (vnode_unlocked == 0) { + ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl6"); + NFSVOPUNLOCK(vp, 0); + } /* Roll back local locks. */ nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); NFSUNLOCKSTATE(); + NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); + vnode_unlocked = 0; + if ((vp->v_iflag & VI_DOOMED) != 0) { + error = NFSERR_SERVERFAULT; + goto out; + } } /* * nfsrv_clientconflict() unlocks state when it @@ -1863,6 +2140,11 @@ tryagain: if (filestruct_locked != 0 && ret == 0) { /* Roll back local locks. */ NFSUNLOCKSTATE(); + if (vnode_unlocked == 0) { + ASSERT_VOP_ELOCKED(vp, "nfsrv_lockctrl7"); + vnode_unlocked = 1; + NFSVOPUNLOCK(vp, 0); + } nfsrv_locallock_rollback(vp, lfp, p); NFSLOCKSTATE(); nfsrv_unlocklf(lfp); @@ -1894,6 +2176,8 @@ tryagain: if (!(new_stp->ls_flags & NFSLCK_OPENTOLOCK)) { nfsrv_updatelock(lckstp, new_lopp, &other_lop, lfp); stateidp->seqid = ++(lckstp->ls_stateid.seqid); + if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) + stateidp->seqid = lckstp->ls_stateid.seqid = 1; stateidp->other[0] = lckstp->ls_stateid.other[0]; stateidp->other[1] = lckstp->ls_stateid.other[1]; stateidp->other[2] = lckstp->ls_stateid.other[2]; @@ -1922,7 +2206,7 @@ tryagain: LIST_INSERT_HEAD(&stp->ls_open, new_stp, ls_list); *new_lopp = NULL; *new_stpp = NULL; - newnfsstats.srvlockowners++; + nfsstatsv1.srvlockowners++; nfsrv_openpluslock++; } if (filestruct_locked != 0) { @@ -1939,6 +2223,11 @@ out: nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); } + if (vnode_unlocked != 0) { + NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); + if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0) + error = NFSERR_SERVERFAULT; + } if (other_lop) FREE((caddr_t)other_lop, M_NFSDLOCK); NFSEXITCODE2(error, nd); @@ -1978,7 +2267,7 @@ nfsrv_opencheck(nfsquad_t clientid, nfsv4stateid_t *stateidp, * returns NFSERR_RESOURCE and the limit is just a rather * arbitrary high water mark, so no harm is done. */ - if (nfsrv_openpluslock > NFSRV_V4STATELIMIT) { + if (nfsrv_openpluslock > nfsrv_v4statelimit) { error = NFSERR_RESOURCE; goto out; } @@ -1987,14 +2276,14 @@ tryagain: MALLOC(new_lfp, struct nfslockfile *, sizeof (struct nfslockfile), M_NFSDLOCKFILE, M_WAITOK); if (vp) - getfhret = nfsrv_getlockfh(vp, new_stp->ls_flags, &new_lfp, + getfhret = nfsrv_getlockfh(vp, new_stp->ls_flags, new_lfp, NULL, p); NFSLOCKSTATE(); /* * Get the nfsclient structure. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); /* * Look up the open owner. See if it needs confirmation and @@ -2020,7 +2309,7 @@ tryagain: * Check for grace. */ if (!error) - error = nfsrv_checkgrace(new_stp->ls_flags); + error = nfsrv_checkgrace(nd, clp, new_stp->ls_flags); if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error && nfsrv_checkstable(clp)) error = NFSERR_NOGRACE; @@ -2086,7 +2375,9 @@ tryagain: */ LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { if (!(stp->ls_flags & NFSLCK_OLDDELEG) && - stateidp->seqid == stp->ls_stateid.seqid && + (((nd->nd_flag & ND_NFSV41) != 0 && + stateidp->seqid == 0) || + stateidp->seqid == stp->ls_stateid.seqid) && !NFSBCMP(stateidp->other, stp->ls_stateid.other, NFSX_STATEIDOTHER)) break; @@ -2153,7 +2444,7 @@ tryagain: * For Open with other Write Access or any Deny except None * - there is a conflict if a different client has any delegation * - there is a conflict if the same client has a read delegation - * (The current concensus is that this last case should be + * (The current consensus is that this last case should be * considered a conflict since the client with a read delegation * could have done an Open with ReadAccess and WriteDeny * locally and then not have checked for the WriteDeny.) @@ -2237,7 +2528,7 @@ tryagain: M_NFSDSTATE, M_WAITOK); MALLOC(new_deleg, struct nfsstate *, sizeof (struct nfsstate), M_NFSDSTATE, M_WAITOK); - getfhret = nfsrv_getlockfh(vp, new_stp->ls_flags, &new_lfp, + getfhret = nfsrv_getlockfh(vp, new_stp->ls_flags, new_lfp, NULL, p); NFSLOCKSTATE(); /* @@ -2254,8 +2545,8 @@ tryagain: * storage file must be written prior to completion of state * expiration. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); if (!error && (clp->lc_flags & LCL_NEEDSCBNULL) && clp->lc_program) { /* @@ -2343,7 +2634,9 @@ tryagain: */ LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { if (!(stp->ls_flags & NFSLCK_OLDDELEG) && - stateidp->seqid == stp->ls_stateid.seqid && + (((nd->nd_flag & ND_NFSV41) != 0 && + stateidp->seqid == 0) || + stateidp->seqid == stp->ls_stateid.seqid) && !NFSBCMP(stateidp->other, stp->ls_stateid.other, NFSX_STATEIDOTHER)) break; @@ -2444,7 +2737,7 @@ tryagain: * For Open with other Write Access or any Deny except None * - there is a conflict if a different client has any delegation * - there is a conflict if the same client has a read delegation - * (The current concensus is that this last case should be + * (The current consensus is that this last case should be * considered a conflict since the client with a read delegation * could have done an Open with ReadAccess and WriteDeny * locally and then not have checked for the WriteDeny.) @@ -2508,7 +2801,7 @@ tryagain: LIST_REMOVE(stp, ls_list); LIST_REMOVE(stp, ls_hash); stp->ls_flags &= ~NFSLCK_OLDDELEG; - stp->ls_stateid.seqid = delegstateidp->seqid = 0; + stp->ls_stateid.seqid = delegstateidp->seqid = 1; stp->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; stp->ls_stateid.other[1] = delegstateidp->other[1] = @@ -2529,7 +2822,7 @@ tryagain: /* * Now, do the associated open. */ - new_open->ls_stateid.seqid = 0; + new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); @@ -2562,12 +2855,12 @@ tryagain: LIST_INSERT_HEAD(&new_stp->ls_open, new_open, ls_list); LIST_INSERT_HEAD(&clp->lc_open, new_stp, ls_list); *new_stpp = NULL; - newnfsstats.srvopenowners++; + nfsstatsv1.srvopenowners++; nfsrv_openpluslock++; } openstp = new_open; new_open = NULL; - newnfsstats.srvopens++; + nfsstatsv1.srvopens++; nfsrv_openpluslock++; break; } @@ -2594,7 +2887,7 @@ tryagain: * First, add the delegation. (Although we must issue the * delegation, we can also ask for an immediate return.) */ - new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; + new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] = @@ -2626,14 +2919,14 @@ tryagain: NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) || !NFSVNO_DELEGOK(vp)) *rflagsp |= NFSV4OPEN_RECALL; - newnfsstats.srvdelegates++; + nfsstatsv1.srvdelegates++; nfsrv_openpluslock++; nfsrv_delegatecnt++; /* * Now, do the associated open. */ - new_open->ls_stateid.seqid = 0; + new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); @@ -2666,12 +2959,12 @@ tryagain: LIST_INSERT_HEAD(&new_stp->ls_open, new_open, ls_list); LIST_INSERT_HEAD(&clp->lc_open, new_stp, ls_list); *new_stpp = NULL; - newnfsstats.srvopenowners++; + nfsstatsv1.srvopenowners++; nfsrv_openpluslock++; } openstp = new_open; new_open = NULL; - newnfsstats.srvopens++; + nfsstatsv1.srvopens++; nfsrv_openpluslock++; } else { error = NFSERR_RECLAIMCONFLICT; @@ -2688,7 +2981,7 @@ tryagain: stp = LIST_FIRST(&ownerstp->ls_open); stp->ls_flags = (new_stp->ls_flags & NFSLCK_SHAREBITS) | NFSLCK_OPEN; - stp->ls_stateid.seqid = 0; + stp->ls_stateid.seqid = 1; stp->ls_uid = new_stp->ls_uid; if (lfp != stp->ls_lfp) { LIST_REMOVE(stp, ls_file); @@ -2699,18 +2992,28 @@ tryagain: } else if (openstp) { openstp->ls_flags |= (new_stp->ls_flags & NFSLCK_SHAREBITS); openstp->ls_stateid.seqid++; + if ((nd->nd_flag & ND_NFSV41) != 0 && + openstp->ls_stateid.seqid == 0) + openstp->ls_stateid.seqid = 1; /* * This is where we can choose to issue a delegation. */ - if (delegate && nfsrv_issuedelegs && - writedeleg && !NFSVNO_EXRDONLY(exp) && - (nfsrv_writedelegifpos || !readonly) && - (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == - LCL_CALLBACKSON && - !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && - NFSVNO_DELEGOK(vp)) { - new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; + if (delegate == 0 || writedeleg == 0 || + NFSVNO_EXRDONLY(exp) || (readonly != 0 && + nfsrv_writedelegifpos == 0) || + !NFSVNO_DELEGOK(vp) || + (new_stp->ls_flags & NFSLCK_WANTRDELEG) != 0 || + (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) != + LCL_CALLBACKSON) + *rflagsp |= NFSV4OPEN_WDCONTENTION; + else if (nfsrv_issuedelegs == 0 || + NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt)) + *rflagsp |= NFSV4OPEN_WDRESOURCE; + else if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0) + *rflagsp |= NFSV4OPEN_WDNOTWANTED; + else { + new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] @@ -2730,12 +3033,12 @@ tryagain: new_deleg->ls_stateid), new_deleg, ls_hash); LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); new_deleg = NULL; - newnfsstats.srvdelegates++; + nfsstatsv1.srvdelegates++; nfsrv_openpluslock++; nfsrv_delegatecnt++; } } else { - new_open->ls_stateid.seqid = 0; + new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); @@ -2752,19 +3055,24 @@ tryagain: new_open, ls_hash); openstp = new_open; new_open = NULL; - newnfsstats.srvopens++; + nfsstatsv1.srvopens++; nfsrv_openpluslock++; /* * This is where we can choose to issue a delegation. */ - if (delegate && nfsrv_issuedelegs && - (writedeleg || readonly) && - (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == - LCL_CALLBACKSON && - !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && - NFSVNO_DELEGOK(vp)) { - new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; + if (delegate == 0 || (writedeleg == 0 && readonly == 0) || + !NFSVNO_DELEGOK(vp) || + (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) != + LCL_CALLBACKSON) + *rflagsp |= NFSV4OPEN_WDCONTENTION; + else if (nfsrv_issuedelegs == 0 || + NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt)) + *rflagsp |= NFSV4OPEN_WDRESOURCE; + else if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0) + *rflagsp |= NFSV4OPEN_WDNOTWANTED; + else { + new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] @@ -2772,7 +3080,8 @@ tryagain: new_deleg->ls_stateid.other[2] = delegstateidp->other[2] = nfsrv_nextstateindex(clp); if (writedeleg && !NFSVNO_EXRDONLY(exp) && - (nfsrv_writedelegifpos || !readonly)) { + (nfsrv_writedelegifpos || !readonly) && + (new_stp->ls_flags & NFSLCK_WANTRDELEG) == 0) { new_deleg->ls_flags = (NFSLCK_DELEGWRITE | NFSLCK_READACCESS | NFSLCK_WRITEACCESS); *rflagsp |= NFSV4OPEN_WRITEDELEGATE; @@ -2791,7 +3100,7 @@ tryagain: new_deleg->ls_stateid), new_deleg, ls_hash); LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); new_deleg = NULL; - newnfsstats.srvdelegates++; + nfsstatsv1.srvdelegates++; nfsrv_openpluslock++; nfsrv_delegatecnt++; } @@ -2802,7 +3111,7 @@ tryagain: * Needs confirmation (unless a reclaim) and hang the * new open off it. */ - new_open->ls_stateid.seqid = 0; + new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); @@ -2816,6 +3125,64 @@ tryagain: LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); if (new_stp->ls_flags & NFSLCK_RECLAIM) { new_stp->ls_flags = 0; + } else if ((nd->nd_flag & ND_NFSV41) != 0) { + /* NFSv4.1 never needs confirmation. */ + new_stp->ls_flags = 0; + + /* + * This is where we can choose to issue a delegation. + */ + if (delegate && nfsrv_issuedelegs && + (writedeleg || readonly) && + (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == + LCL_CALLBACKSON && + !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && + NFSVNO_DELEGOK(vp) && + ((nd->nd_flag & ND_NFSV41) == 0 || + (new_stp->ls_flags & NFSLCK_WANTNODELEG) == 0)) { + new_deleg->ls_stateid.seqid = + delegstateidp->seqid = 1; + new_deleg->ls_stateid.other[0] = + delegstateidp->other[0] + = clp->lc_clientid.lval[0]; + new_deleg->ls_stateid.other[1] = + delegstateidp->other[1] + = clp->lc_clientid.lval[1]; + new_deleg->ls_stateid.other[2] = + delegstateidp->other[2] + = nfsrv_nextstateindex(clp); + if (writedeleg && !NFSVNO_EXRDONLY(exp) && + (nfsrv_writedelegifpos || !readonly) && + ((nd->nd_flag & ND_NFSV41) == 0 || + (new_stp->ls_flags & NFSLCK_WANTRDELEG) == + 0)) { + new_deleg->ls_flags = + (NFSLCK_DELEGWRITE | + NFSLCK_READACCESS | + NFSLCK_WRITEACCESS); + *rflagsp |= NFSV4OPEN_WRITEDELEGATE; + } else { + new_deleg->ls_flags = + (NFSLCK_DELEGREAD | + NFSLCK_READACCESS); + *rflagsp |= NFSV4OPEN_READDELEGATE; + } + new_deleg->ls_uid = new_stp->ls_uid; + new_deleg->ls_lfp = lfp; + new_deleg->ls_clp = clp; + new_deleg->ls_filerev = filerev; + new_deleg->ls_compref = nd->nd_compref; + LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, + ls_file); + LIST_INSERT_HEAD(NFSSTATEHASH(clp, + new_deleg->ls_stateid), new_deleg, ls_hash); + LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, + ls_list); + new_deleg = NULL; + nfsstatsv1.srvdelegates++; + nfsrv_openpluslock++; + nfsrv_delegatecnt++; + } } else { *rflagsp |= NFSV4OPEN_RESULTCONFIRM; new_stp->ls_flags = NFSLCK_NEEDSCONFIRM; @@ -2830,9 +3197,9 @@ tryagain: openstp = new_open; new_open = NULL; *new_stpp = NULL; - newnfsstats.srvopens++; + nfsstatsv1.srvopens++; nfsrv_openpluslock++; - newnfsstats.srvopenowners++; + nfsstatsv1.srvopenowners++; nfsrv_openpluslock++; } if (!error) { @@ -2883,8 +3250,8 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, /* * Get the open structure via clientid and stateid. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); if (!error) error = nfsrv_getstate(clp, &new_stp->ls_stateid, new_stp->ls_flags, &stp); @@ -2903,7 +3270,10 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, error = nfsrv_checkseqid(nd, new_stp->ls_seq, stp->ls_openowner, new_stp->ls_op); if (!error && stp->ls_stateid.seqid != new_stp->ls_stateid.seqid && - !(new_stp->ls_flags & NFSLCK_CONFIRM)) + (((nd->nd_flag & ND_NFSV41) == 0 && + !(new_stp->ls_flags & NFSLCK_CONFIRM)) || + ((nd->nd_flag & ND_NFSV41) != 0 && + new_stp->ls_stateid.seqid != 0))) error = NFSERR_OLDSTATEID; if (!error && vnode_vtype(vp) != VREG) { if (vnode_vtype(vp) == VDIR) @@ -2931,6 +3301,8 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, * Set the return stateid. */ stateidp->seqid = stp->ls_stateid.seqid + 1; + if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) + stateidp->seqid = 1; stateidp->other[0] = stp->ls_stateid.other[0]; stateidp->other[1] = stp->ls_stateid.other[1]; stateidp->other[2] = stp->ls_stateid.other[2]; @@ -2946,6 +3318,9 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, printf("Nfsv4d: stray open confirm\n"); stp->ls_openowner->ls_flags = 0; stp->ls_stateid.seqid++; + if ((nd->nd_flag & ND_NFSV41) != 0 && + stp->ls_stateid.seqid == 0) + stp->ls_stateid.seqid = 1; if (!(clp->lc_flags & LCL_STAMPEDSTABLE)) { clp->lc_flags |= LCL_STAMPEDSTABLE; len = clp->lc_idlen; @@ -2960,11 +3335,14 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, /* Get the lf lock */ nfsrv_locklf(lfp); NFSUNLOCKSTATE(); + ASSERT_VOP_ELOCKED(vp, "nfsrv_openupdate"); + NFSVOPUNLOCK(vp, 0); if (nfsrv_freeopen(stp, vp, 1, p) == 0) { NFSLOCKSTATE(); nfsrv_unlocklf(lfp); NFSUNLOCKSTATE(); } + NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); } else { (void) nfsrv_freeopen(stp, NULL, 0, p); NFSUNLOCKSTATE(); @@ -2982,6 +3360,9 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, } stp->ls_flags = (bits | NFSLCK_OPEN); stp->ls_stateid.seqid++; + if ((nd->nd_flag & ND_NFSV41) != 0 && + stp->ls_stateid.seqid == 0) + stp->ls_stateid.seqid = 1; NFSUNLOCKSTATE(); } @@ -3003,8 +3384,9 @@ out: * Delegation update. Does the purge and return. */ APPLESTATIC int -nfsrv_delegupdate(nfsquad_t clientid, nfsv4stateid_t *stateidp, - vnode_t vp, int op, struct ucred *cred, NFSPROC_T *p) +nfsrv_delegupdate(struct nfsrv_descript *nd, nfsquad_t clientid, + nfsv4stateid_t *stateidp, vnode_t vp, int op, struct ucred *cred, + NFSPROC_T *p) { struct nfsstate *stp; struct nfsclient *clp; @@ -3034,8 +3416,8 @@ nfsrv_delegupdate(nfsquad_t clientid, nfsv4stateid_t *stateidp, * Get the open structure via clientid and stateid. */ if (!error) - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); if (error) { if (error == NFSERR_CBPATHDOWN) error = 0; @@ -3044,7 +3426,8 @@ nfsrv_delegupdate(nfsquad_t clientid, nfsv4stateid_t *stateidp, } if (!error && op == NFSV4OP_DELEGRETURN) { error = nfsrv_getstate(clp, stateidp, NFSLCK_DELEGRETURN, &stp); - if (!error && stp->ls_stateid.seqid != stateidp->seqid) + if (!error && stp->ls_stateid.seqid != stateidp->seqid && + ((nd->nd_flag & ND_NFSV41) == 0 || stateidp->seqid != 0)) error = NFSERR_OLDSTATEID; } /* @@ -3103,8 +3486,8 @@ nfsrv_releaselckown(struct nfsstate *new_stp, nfsquad_t clientid, /* * Get the lock owner by name. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, NULL, p); if (error) { NFSUNLOCKSTATE(); goto out; @@ -3144,11 +3527,10 @@ out: * Get the file handle for a lock structure. */ static int -nfsrv_getlockfh(vnode_t vp, u_short flags, - struct nfslockfile **new_lfpp, fhandle_t *nfhp, NFSPROC_T *p) +nfsrv_getlockfh(vnode_t vp, u_short flags, struct nfslockfile *new_lfp, + fhandle_t *nfhp, NFSPROC_T *p) { fhandle_t *fhp = NULL; - struct nfslockfile *new_lfp; int error; /* @@ -3156,7 +3538,7 @@ nfsrv_getlockfh(vnode_t vp, u_short flags, * a fhandle_t on the stack. */ if (flags & NFSLCK_OPEN) { - new_lfp = *new_lfpp; + KASSERT(new_lfp != NULL, ("nfsrv_getlockfh: new_lfp NULL")); fhp = &new_lfp->lf_fh; } else if (nfhp) { fhp = nfhp; @@ -3269,7 +3651,7 @@ nfsrv_insertlock(struct nfslock *new_lop, struct nfslock *insert_lop, else LIST_INSERT_AFTER(insert_lop, new_lop, lo_lckowner); if (stp != NULL) { - newnfsstats.srvlocks++; + nfsstatsv1.srvlocks++; nfsrv_openpluslock++; } } @@ -3422,6 +3804,9 @@ nfsrv_checkseqid(struct nfsrv_descript *nd, u_int32_t seqid, { int error = 0; + if ((nd->nd_flag & ND_NFSV41) != 0) + /* NFSv4.1 ignores the open_seqid and lock_seqid. */ + goto out; if (op != nd->nd_rp) panic("nfsrvstate checkseqid"); if (!(op->rc_flag & RC_INPROG)) @@ -3464,7 +3849,7 @@ out: * just set lc_program to 0 to indicate no callbacks are possible. * (For cases where the address can't be parsed or is 0.0.0.0.0.0, set * the address to the client's transport address. This won't be used - * for callbacks, but can be printed out by newnfsstats for info.) + * for callbacks, but can be printed out by nfsstats for info.) * Return error if the xdr can't be parsed, 0 otherwise. */ APPLESTATIC int @@ -3640,7 +4025,7 @@ nfsrv_checkrestart(nfsquad_t clientid, u_int32_t flags, goto out; NFSLOCKSTATE(); - ret = nfsrv_checkgrace(flags); + ret = nfsrv_checkgrace(NULL, NULL, flags); NFSUNLOCKSTATE(); out: @@ -3652,11 +4037,12 @@ out: * Check for grace. */ static int -nfsrv_checkgrace(u_int32_t flags) +nfsrv_checkgrace(struct nfsrv_descript *nd, struct nfsclient *clp, + u_int32_t flags) { int error = 0; - if (nfsrv_stablefirst.nsf_flags & NFSNSF_GRACEOVER) { + if ((nfsrv_stablefirst.nsf_flags & NFSNSF_GRACEOVER) != 0) { if (flags & NFSLCK_RECLAIM) { error = NFSERR_NOGRACE; goto out; @@ -3666,6 +4052,12 @@ nfsrv_checkgrace(u_int32_t flags) error = NFSERR_GRACE; goto out; } + if (nd != NULL && clp != NULL && + (nd->nd_flag & ND_NFSV41) != 0 && + (clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0) { + error = NFSERR_NOGRACE; + goto out; + } /* * If grace is almost over and we are still getting Reclaims, @@ -3696,6 +4088,7 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, struct ucred *cred; int error = 0; u_int32_t callback; + struct nfsdsession *sep = NULL; cred = newnfs_getcred(); NFSLOCKSTATE(); /* mostly for lc_cbref++ */ @@ -3710,7 +4103,12 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, * structure for newnfs_connect() to use. */ clp->lc_req.nr_prog = clp->lc_program; - clp->lc_req.nr_vers = NFSV4_CBVERS; +#ifdef notnow + if ((clp->lc_flags & LCL_NFSV41) != 0) + clp->lc_req.nr_vers = NFSV41_CBVERS; + else +#endif + clp->lc_req.nr_vers = NFSV4_CBVERS; /* * First, fill in some of the fields of nd and cr. @@ -3718,6 +4116,8 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, nd->nd_flag = ND_NFSV4; if (clp->lc_flags & LCL_GSS) nd->nd_flag |= ND_KERBV; + if ((clp->lc_flags & LCL_NFSV41) != 0) + nd->nd_flag |= ND_NFSV41; nd->nd_repstat = 0; cred->cr_uid = clp->lc_uid; cred->cr_gid = clp->lc_gid; @@ -3738,22 +4138,23 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, */ if (procnum == NFSV4OP_CBGETATTR) { nd->nd_procnum = NFSV4PROC_CBCOMPOUND; - (void) nfsm_strtom(nd, "CB Getattr", 10); - NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED); - *tl++ = txdr_unsigned(NFSV4_MINORVERSION); - *tl++ = txdr_unsigned(callback); - *tl++ = txdr_unsigned(1); - *tl = txdr_unsigned(NFSV4OP_CBGETATTR); - (void) nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); - (void) nfsrv_putattrbit(nd, attrbitp); + error = nfsrv_cbcallargs(nd, clp, callback, NFSV4OP_CBGETATTR, + "CB Getattr", &sep); + if (error != 0) { + mbuf_freem(nd->nd_mreq); + goto errout; + } + (void)nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); + (void)nfsrv_putattrbit(nd, attrbitp); } else if (procnum == NFSV4OP_CBRECALL) { nd->nd_procnum = NFSV4PROC_CBCOMPOUND; - (void) nfsm_strtom(nd, "CB Recall", 9); - NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID); - *tl++ = txdr_unsigned(NFSV4_MINORVERSION); - *tl++ = txdr_unsigned(callback); - *tl++ = txdr_unsigned(1); - *tl++ = txdr_unsigned(NFSV4OP_CBRECALL); + error = nfsrv_cbcallargs(nd, clp, callback, NFSV4OP_CBRECALL, + "CB Recall", &sep); + if (error != 0) { + mbuf_freem(nd->nd_mreq); + goto errout; + } + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID); *tl++ = txdr_unsigned(stateidp->seqid); NFSBCOPY((caddr_t)stateidp->other, (caddr_t)tl, NFSX_STATEIDOTHER); @@ -3762,9 +4163,20 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, *tl = newnfs_true; else *tl = newnfs_false; - (void) nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); - } else { + (void)nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); + } else if (procnum == NFSV4PROC_CBNULL) { nd->nd_procnum = NFSV4PROC_CBNULL; + if ((clp->lc_flags & LCL_NFSV41) != 0) { + error = nfsv4_getcbsession(clp, &sep); + if (error != 0) { + mbuf_freem(nd->nd_mreq); + goto errout; + } + } + } else { + error = NFSERR_SERVERFAULT; + mbuf_freem(nd->nd_mreq); + goto errout; } /* @@ -3772,7 +4184,9 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, */ (void) newnfs_sndlock(&clp->lc_req.nr_lock); if (clp->lc_req.nr_client == NULL) { - if (nd->nd_procnum == NFSV4PROC_CBNULL) + if ((clp->lc_flags & LCL_NFSV41) != 0) + error = ECONNREFUSED; + else if (nd->nd_procnum == NFSV4PROC_CBNULL) error = newnfs_connect(NULL, &clp->lc_req, cred, NULL, 1); else @@ -3781,10 +4195,32 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, } newnfs_sndunlock(&clp->lc_req.nr_lock); if (!error) { - error = newnfs_request(nd, NULL, clp, &clp->lc_req, NULL, - NULL, cred, clp->lc_program, NFSV4_CBVERS, NULL, 1, NULL, - NULL); + if ((nd->nd_flag & ND_NFSV41) != 0) { + KASSERT(sep != NULL, ("sep NULL")); + if (sep->sess_cbsess.nfsess_xprt != NULL) + error = newnfs_request(nd, NULL, clp, + &clp->lc_req, NULL, NULL, cred, + clp->lc_program, clp->lc_req.nr_vers, NULL, + 1, NULL, &sep->sess_cbsess); + else { + /* + * This should probably never occur, but if a + * client somehow does an RPC without a + * SequenceID Op that causes a callback just + * after the nfsd threads have been terminated + * and restared we could conceivably get here + * without a backchannel xprt. + */ + printf("nfsrv_docallback: no xprt\n"); + error = ECONNREFUSED; + } + nfsrv_freesession(sep, NULL); + } else + error = newnfs_request(nd, NULL, clp, &clp->lc_req, + NULL, NULL, cred, clp->lc_program, + clp->lc_req.nr_vers, NULL, 1, NULL, NULL); } +errout: NFSFREECRED(cred); /* @@ -3814,7 +4250,7 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, NFSUNLOCKSTATE(); if (nd->nd_repstat) error = nd->nd_repstat; - else if (procnum == NFSV4OP_CBGETATTR) + else if (error == 0 && procnum == NFSV4OP_CBGETATTR) error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p, NULL); @@ -3833,6 +4269,38 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, } /* + * Set up the compound RPC for the callback. + */ +static int +nfsrv_cbcallargs(struct nfsrv_descript *nd, struct nfsclient *clp, + uint32_t callback, int op, const char *optag, struct nfsdsession **sepp) +{ + uint32_t *tl; + int error, len; + + len = strlen(optag); + (void)nfsm_strtom(nd, optag, len); + NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED); + if ((nd->nd_flag & ND_NFSV41) != 0) { + *tl++ = txdr_unsigned(NFSV41_MINORVERSION); + *tl++ = txdr_unsigned(callback); + *tl++ = txdr_unsigned(2); + *tl = txdr_unsigned(NFSV4OP_CBSEQUENCE); + error = nfsv4_setcbsequence(nd, clp, 1, sepp); + if (error != 0) + return (error); + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(op); + } else { + *tl++ = txdr_unsigned(NFSV4_MINORVERSION); + *tl++ = txdr_unsigned(callback); + *tl++ = txdr_unsigned(1); + *tl = txdr_unsigned(op); + } + return (0); +} + +/* * Return the next index# for a clientid. Mostly just increment and return * the next one, but... if the 32bit unsigned does actually wrap around, * it should be rebooted. @@ -3878,7 +4346,7 @@ nfsrv_nextstateindex(struct nfsclient *clp) */ min_index = 0; max_index = 0xffffffff; - for (i = 0; i < NFSSTATEHASHSIZE; i++) { + for (i = 0; i < nfsrv_statehashsize; i++) { LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { if (stp->ls_stateid.other[2] > 0x80000000) { if (stp->ls_stateid.other[2] < max_index) @@ -3902,7 +4370,7 @@ nfsrv_nextstateindex(struct nfsclient *clp) * cleanest way to code the loop.) */ tryagain: - for (i = 0; i < NFSSTATEHASHSIZE; i++) { + for (i = 0; i < nfsrv_statehashsize; i++) { LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { if (stp->ls_stateid.other[2] == canuse) { canuse++; @@ -3934,7 +4402,7 @@ tryagain: * nfsrvboottime does not, somehow, get set to a previous one. * (This is important so that Stale ClientIDs and StateIDs can * be recognized.) - * The number of previous nfsvrboottime values preceeds the list. + * The number of previous nfsvrboottime values precedes the list. * - followed by some number of appended records with: * - client id string * - flag that indicates it is a record revoking state via lease @@ -4275,7 +4743,7 @@ static int nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp, NFSPROC_T *p) { - int gotlock, lktype; + int gotlock, lktype = 0; /* * If lease hasn't expired, we can't fix it. @@ -4285,8 +4753,10 @@ nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp, return (0); if (*haslockp == 0) { NFSUNLOCKSTATE(); - lktype = NFSVOPISLOCKED(vp); - NFSVOPUNLOCK(vp, 0); + if (vp != NULL) { + lktype = NFSVOPISLOCKED(vp); + NFSVOPUNLOCK(vp, 0); + } NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); do { @@ -4295,11 +4765,12 @@ nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp, } while (!gotlock); NFSUNLOCKV4ROOTMUTEX(); *haslockp = 1; - NFSVOPLOCK(vp, lktype | LK_RETRY); - if ((vp->v_iflag & VI_DOOMED) != 0) - return (2); - else - return (1); + if (vp != NULL) { + NFSVOPLOCK(vp, lktype | LK_RETRY); + if ((vp->v_iflag & VI_DOOMED) != 0) + return (2); + } + return (1); } NFSUNLOCKSTATE(); @@ -4340,7 +4811,7 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p, vnode_t vp) { struct nfsclient *clp = stp->ls_clp; - int gotlock, error, lktype, retrycnt, zapped_clp; + int gotlock, error, lktype = 0, retrycnt, zapped_clp; nfsv4stateid_t tstateid; fhandle_t tfh; @@ -4457,8 +4928,10 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p, */ if (*haslockp == 0) { NFSUNLOCKSTATE(); - lktype = NFSVOPISLOCKED(vp); - NFSVOPUNLOCK(vp, 0); + if (vp != NULL) { + lktype = NFSVOPISLOCKED(vp); + NFSVOPUNLOCK(vp, 0); + } NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); do { @@ -4467,14 +4940,16 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p, } while (!gotlock); NFSUNLOCKV4ROOTMUTEX(); *haslockp = 1; - NFSVOPLOCK(vp, lktype | LK_RETRY); - if ((vp->v_iflag & VI_DOOMED) != 0) { - *haslockp = 0; - NFSLOCKV4ROOTMUTEX(); - nfsv4_unlock(&nfsv4rootfs_lock, 1); - NFSUNLOCKV4ROOTMUTEX(); - error = NFSERR_PERM; - goto out; + if (vp != NULL) { + NFSVOPLOCK(vp, lktype | LK_RETRY); + if ((vp->v_iflag & VI_DOOMED) != 0) { + *haslockp = 0; + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + error = NFSERR_PERM; + goto out; + } } error = -1; goto out; @@ -4565,12 +5040,17 @@ tryagain: * Now, look for a conflicting open share. */ if (remove) { - LIST_FOREACH(stp, &lfp->lf_open, ls_file) { - if (stp->ls_flags & NFSLCK_WRITEDENY) { - error = NFSERR_FILEOPEN; - break; + /* + * If the entry in the directory was the last reference to the + * corresponding filesystem object, the object can be destroyed + * */ + if(lfp->lf_usecount>1) + LIST_FOREACH(stp, &lfp->lf_open, ls_file) { + if (stp->ls_flags & NFSLCK_WRITEDENY) { + error = NFSERR_FILEOPEN; + break; + } } - } } NFSUNLOCKSTATE(); @@ -4855,15 +5335,15 @@ nfsrv_checkgetattr(struct nfsrv_descript *nd, vnode_t vp, nva.na_filerev > delegfilerev) || (NFSVNO_ISSETSIZE(&nva) && nva.na_size != nvap->na_size)) { - nfsvno_updfilerev(vp, nvap, cred, p); + error = nfsvno_updfilerev(vp, nvap, cred, p); if (NFSVNO_ISSETSIZE(&nva)) nvap->na_size = nva.na_size; } - } + } else + error = 0; /* Ignore callback errors for now. */ } else { NFSUNLOCKSTATE(); } - error = 0; out: NFSEXITCODE2(error, nd); @@ -4887,13 +5367,13 @@ nfsrv_throwawayopens(NFSPROC_T *p) /* * For each client... */ - for (i = 0; i < NFSCLIENTHASHSIZE; i++) { + for (i = 0; i < nfsrv_clienthashsize; i++) { LIST_FOREACH_SAFE(clp, &nfsclienthash[i], lc_hash, nclp) { LIST_FOREACH_SAFE(stp, &clp->lc_open, ls_list, nstp) { if (LIST_EMPTY(&stp->ls_open) && (stp->ls_noopens > NFSNOOPEN || (nfsrv_openpluslock * 2) > - NFSRV_V4STATELIMIT)) + nfsrv_v4statelimit)) nfsrv_freeopenowner(stp, 0, p); } } @@ -5264,11 +5744,12 @@ nfsrv_throwawayallstate(NFSPROC_T *p) /* * For each client, clean out the state and then free the structure. */ - for (i = 0; i < NFSCLIENTHASHSIZE; i++) { + for (i = 0; i < nfsrv_clienthashsize; i++) { LIST_FOREACH_SAFE(clp, &nfsclienthash[i], lc_hash, nclp) { nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_deleg); nfsrv_freedeleglist(&clp->lc_olddeleg); + free(clp->lc_stateid, M_NFSDCLIENT); free(clp, M_NFSDCLIENT); } } @@ -5276,7 +5757,7 @@ nfsrv_throwawayallstate(NFSPROC_T *p) /* * Also, free up any remaining lock file structures. */ - for (i = 0; i < NFSLOCKHASHSIZE; i++) { + for (i = 0; i < nfsrv_lockhashsize; i++) { LIST_FOREACH_SAFE(lfp, &nfslockhash[i], lf_hash, nlfp) { printf("nfsd unload: fnd a lock file struct\n"); nfsrv_freenfslockfile(lfp); @@ -5284,3 +5765,335 @@ nfsrv_throwawayallstate(NFSPROC_T *p) } } +/* + * Check the sequence# for the session and slot provided as an argument. + * Also, renew the lease if the session will return NFS_OK. + */ +int +nfsrv_checksequence(struct nfsrv_descript *nd, uint32_t sequenceid, + uint32_t *highest_slotidp, uint32_t *target_highest_slotidp, int cache_this, + uint32_t *sflagsp, NFSPROC_T *p) +{ + struct nfsdsession *sep; + struct nfssessionhash *shp; + int error; + SVCXPRT *savxprt; + + shp = NFSSESSIONHASH(nd->nd_sessionid); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(nd->nd_sessionid); + if (sep == NULL) { + NFSUNLOCKSESSION(shp); + return (NFSERR_BADSESSION); + } + error = nfsv4_seqsession(sequenceid, nd->nd_slotid, *highest_slotidp, + sep->sess_slots, NULL, NFSV4_SLOTS - 1); + if (error != 0) { + NFSUNLOCKSESSION(shp); + return (error); + } + if (cache_this != 0) + nd->nd_flag |= ND_SAVEREPLY; + /* Renew the lease. */ + sep->sess_clp->lc_expiry = nfsrv_leaseexpiry(); + nd->nd_clientid.qval = sep->sess_clp->lc_clientid.qval; + nd->nd_flag |= ND_IMPLIEDCLID; + + /* + * If this session handles the backchannel, save the nd_xprt for this + * RPC, since this is the one being used. + */ + if (sep->sess_clp->lc_req.nr_client != NULL && + (sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) { + savxprt = sep->sess_cbsess.nfsess_xprt; + SVC_ACQUIRE(nd->nd_xprt); + nd->nd_xprt->xp_p2 = + sep->sess_clp->lc_req.nr_client->cl_private; + nd->nd_xprt->xp_idletimeout = 0; /* Disable timeout. */ + sep->sess_cbsess.nfsess_xprt = nd->nd_xprt; + if (savxprt != NULL) + SVC_RELEASE(savxprt); + } + + *sflagsp = 0; + if (sep->sess_clp->lc_req.nr_client == NULL) + *sflagsp |= NFSV4SEQ_CBPATHDOWN; + NFSUNLOCKSESSION(shp); + if (error == NFSERR_EXPIRED) { + *sflagsp |= NFSV4SEQ_EXPIREDALLSTATEREVOKED; + error = 0; + } else if (error == NFSERR_ADMINREVOKED) { + *sflagsp |= NFSV4SEQ_ADMINSTATEREVOKED; + error = 0; + } + *highest_slotidp = *target_highest_slotidp = NFSV4_SLOTS - 1; + return (0); +} + +/* + * Check/set reclaim complete for this session/clientid. + */ +int +nfsrv_checkreclaimcomplete(struct nfsrv_descript *nd) +{ + struct nfsdsession *sep; + struct nfssessionhash *shp; + int error = 0; + + shp = NFSSESSIONHASH(nd->nd_sessionid); + NFSLOCKSTATE(); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(nd->nd_sessionid); + if (sep == NULL) { + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + return (NFSERR_BADSESSION); + } + + /* Check to see if reclaim complete has already happened. */ + if ((sep->sess_clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0) + error = NFSERR_COMPLETEALREADY; + else + sep->sess_clp->lc_flags |= LCL_RECLAIMCOMPLETE; + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + return (error); +} + +/* + * Cache the reply in a session slot. + */ +void +nfsrv_cache_session(uint8_t *sessionid, uint32_t slotid, int repstat, + struct mbuf **m) +{ + struct nfsdsession *sep; + struct nfssessionhash *shp; + + shp = NFSSESSIONHASH(sessionid); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(sessionid); + if (sep == NULL) { + NFSUNLOCKSESSION(shp); + printf("nfsrv_cache_session: no session\n"); + m_freem(*m); + return; + } + nfsv4_seqsess_cacherep(slotid, sep->sess_slots, repstat, m); + NFSUNLOCKSESSION(shp); +} + +/* + * Search for a session that matches the sessionid. + */ +static struct nfsdsession * +nfsrv_findsession(uint8_t *sessionid) +{ + struct nfsdsession *sep; + struct nfssessionhash *shp; + + shp = NFSSESSIONHASH(sessionid); + LIST_FOREACH(sep, &shp->list, sess_hash) { + if (!NFSBCMP(sessionid, sep->sess_sessionid, NFSX_V4SESSIONID)) + break; + } + return (sep); +} + +/* + * Destroy a session. + */ +int +nfsrv_destroysession(struct nfsrv_descript *nd, uint8_t *sessionid) +{ + int error, samesess; + + samesess = 0; + if (!NFSBCMP(sessionid, nd->nd_sessionid, NFSX_V4SESSIONID)) { + samesess = 1; + if ((nd->nd_flag & ND_LASTOP) == 0) + return (NFSERR_BADSESSION); + } + error = nfsrv_freesession(NULL, sessionid); + if (error == 0 && samesess != 0) + nd->nd_flag &= ~ND_HASSEQUENCE; + return (error); +} + +/* + * Free up a session structure. + */ +static int +nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid) +{ + struct nfssessionhash *shp; + int i; + + NFSLOCKSTATE(); + if (sep == NULL) { + shp = NFSSESSIONHASH(sessionid); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(sessionid); + } else { + shp = NFSSESSIONHASH(sep->sess_sessionid); + NFSLOCKSESSION(shp); + } + if (sep != NULL) { + sep->sess_refcnt--; + if (sep->sess_refcnt > 0) { + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + return (0); + } + LIST_REMOVE(sep, sess_hash); + LIST_REMOVE(sep, sess_list); + } + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + if (sep == NULL) + return (NFSERR_BADSESSION); + for (i = 0; i < NFSV4_SLOTS; i++) + if (sep->sess_slots[i].nfssl_reply != NULL) + m_freem(sep->sess_slots[i].nfssl_reply); + if (sep->sess_cbsess.nfsess_xprt != NULL) + SVC_RELEASE(sep->sess_cbsess.nfsess_xprt); + free(sep, M_NFSDSESSION); + return (0); +} + +/* + * Free a stateid. + * RFC5661 says that it should fail when there are associated opens, locks + * or delegations. Since stateids represent opens, I don't see how you can + * free an open stateid (it will be free'd when closed), so this function + * only works for lock stateids (freeing the lock_owner) or delegations. + */ +int +nfsrv_freestateid(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, + NFSPROC_T *p) +{ + struct nfsclient *clp; + struct nfsstate *stp; + int error; + + NFSLOCKSTATE(); + /* + * Look up the stateid + */ + error = nfsrv_getclient((nfsquad_t)((u_quad_t)0), CLOPS_RENEW, &clp, + NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); + if (error == 0) { + /* First, check for a delegation. */ + LIST_FOREACH(stp, &clp->lc_deleg, ls_list) { + if (!NFSBCMP(stp->ls_stateid.other, stateidp->other, + NFSX_STATEIDOTHER)) + break; + } + if (stp != NULL) { + nfsrv_freedeleg(stp); + NFSUNLOCKSTATE(); + return (error); + } + } + /* Not a delegation, try for a lock_owner. */ + if (error == 0) + error = nfsrv_getstate(clp, stateidp, 0, &stp); + if (error == 0 && ((stp->ls_flags & (NFSLCK_OPEN | NFSLCK_DELEGREAD | + NFSLCK_DELEGWRITE)) != 0 || (stp->ls_flags & NFSLCK_LOCK) == 0)) + /* Not a lock_owner stateid. */ + error = NFSERR_LOCKSHELD; + if (error == 0 && !LIST_EMPTY(&stp->ls_lock)) + error = NFSERR_LOCKSHELD; + if (error == 0) + nfsrv_freelockowner(stp, NULL, 0, p); + NFSUNLOCKSTATE(); + return (error); +} + +/* + * Generate the xdr for an NFSv4.1 CBSequence Operation. + */ +static int +nfsv4_setcbsequence(struct nfsrv_descript *nd, struct nfsclient *clp, + int dont_replycache, struct nfsdsession **sepp) +{ + struct nfsdsession *sep; + uint32_t *tl, slotseq = 0; + int maxslot, slotpos; + uint8_t sessionid[NFSX_V4SESSIONID]; + int error; + + error = nfsv4_getcbsession(clp, sepp); + if (error != 0) + return (error); + sep = *sepp; + (void)nfsv4_sequencelookup(NULL, &sep->sess_cbsess, &slotpos, &maxslot, + &slotseq, sessionid); + KASSERT(maxslot >= 0, ("nfsv4_setcbsequence neg maxslot")); + + /* Build the Sequence arguments. */ + NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 5 * NFSX_UNSIGNED); + bcopy(sessionid, tl, NFSX_V4SESSIONID); + tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; + nd->nd_slotseq = tl; + *tl++ = txdr_unsigned(slotseq); + *tl++ = txdr_unsigned(slotpos); + *tl++ = txdr_unsigned(maxslot); + if (dont_replycache == 0) + *tl++ = newnfs_true; + else + *tl++ = newnfs_false; + *tl = 0; /* No referring call list, for now. */ + nd->nd_flag |= ND_HASSEQUENCE; + return (0); +} + +/* + * Get a session for the callback. + */ +static int +nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp) +{ + struct nfsdsession *sep; + + NFSLOCKSTATE(); + LIST_FOREACH(sep, &clp->lc_session, sess_list) { + if ((sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) + break; + } + if (sep == NULL) { + NFSUNLOCKSTATE(); + return (NFSERR_BADSESSION); + } + sep->sess_refcnt++; + *sepp = sep; + NFSUNLOCKSTATE(); + return (0); +} + +/* + * Free up all backchannel xprts. This needs to be done when the nfsd threads + * exit, since those transports will all be going away. + * This is only called after all the nfsd threads are done performing RPCs, + * so locking shouldn't be an issue. + */ +APPLESTATIC void +nfsrv_freeallbackchannel_xprts(void) +{ + struct nfsdsession *sep; + struct nfsclient *clp; + SVCXPRT *xprt; + int i; + + for (i = 0; i < nfsrv_clienthashsize; i++) { + LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) { + LIST_FOREACH(sep, &clp->lc_session, sess_list) { + xprt = sep->sess_cbsess.nfsess_xprt; + sep->sess_cbsess.nfsess_xprt = NULL; + if (xprt != NULL) + SVC_RELEASE(xprt); + } + } + } +} + diff --git a/sys/fs/nfs/server/nfs_nfsdsubs.c b/sys/fs/nfs/server/nfs_nfsdsubs.c index c8958e1a63e..52b40ad9f01 100644 --- a/sys/fs/nfs/server/nfs_nfsdsubs.c +++ b/sys/fs/nfs/server/nfs_nfsdsubs.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_nfsdsubs.c,v 1.2 2014/10/18 08:33:29 snj Exp $ */ +/* $NetBSD: nfs_nfsdsubs.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -33,8 +33,8 @@ */ #include <sys/cdefs.h> -/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdsubs.c 228260 2011-12-04 16:33:04Z rmacklem "); */ -__RCSID("$NetBSD: nfs_nfsdsubs.c,v 1.2 2014/10/18 08:33:29 snj Exp $"); +/* __FBSDID("FreeBSD: head/sys/fs/nfsserver/nfs_nfsdsubs.c 298523 2016-04-23 21:18:45Z rmacklem "); */ +__RCSID("$NetBSD: nfs_nfsdsubs.c,v 1.3 2016/11/18 08:31:30 pgoyette Exp $"); #ifndef APPLEKEXT /* @@ -46,8 +46,12 @@ __RCSID("$NetBSD: nfs_nfsdsubs.c,v 1.2 2014/10/18 08:33:29 snj Exp $"); extern u_int32_t newnfs_true, newnfs_false; extern int nfs_pubfhset; -extern struct nfsclienthashhead nfsclienthash[NFSCLIENTHASHSIZE]; -extern struct nfslockhashhead nfslockhash[NFSLOCKHASHSIZE]; +extern struct nfsclienthashhead *nfsclienthash; +extern int nfsrv_clienthashsize; +extern struct nfslockhashhead *nfslockhash; +extern int nfsrv_lockhashsize; +extern struct nfssessionhash *nfssessionhash; +extern int nfsrv_sessionhashsize; extern int nfsrv_useacl; extern uid_t nfsrv_defaultuid; extern gid_t nfsrv_defaultgid; @@ -58,28 +62,40 @@ static nfstype newnfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, extern nfstype nfsv34_type[9]; #endif /* !APPLEKEXT */ +static u_int32_t nfsrv_isannfserr(u_int32_t); + SYSCTL_DECL(_vfs_nfsd); -static int disable_checkutf8 = 0; -SYSCTL_INT(_vfs_nfsd, OID_AUTO, disable_checkutf8, CTLFLAG_RW, - &disable_checkutf8, 0, - "Disable the NFSv4 check for a UTF8 compliant name"); +static int enable_checkutf8 = 1; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_checkutf8, CTLFLAG_RW, + &enable_checkutf8, 0, + "Enable the NFSv4 check for the UTF8 compliant name required by rfc3530"); + +static int enable_nobodycheck = 1; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_nobodycheck, CTLFLAG_RW, + &enable_nobodycheck, 0, + "Enable the NFSv4 check when setting user nobody as owner"); + +static int enable_nogroupcheck = 1; +SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_nogroupcheck, CTLFLAG_RW, + &enable_nogroupcheck, 0, + "Enable the NFSv4 check when setting group nogroup as owner"); static char nfsrv_hexdigit(char, int *); /* * Maps errno values to nfs error numbers. * Use NFSERR_IO as the catch all for ones not specifically defined in - * RFC 1094. + * RFC 1094. (It now includes the errors added for NFSv3.) */ -static u_char nfsrv_v2errmap[ELAST] = { +static u_char nfsrv_v2errmap[NFSERR_REMOTE] = { NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO, - NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR, - NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, + NFSERR_IO, NFSERR_EXIST, NFSERR_XDEV, NFSERR_NODEV, NFSERR_NOTDIR, + NFSERR_ISDIR, NFSERR_INVAL, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS, - NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, + NFSERR_MLINK, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, @@ -87,9 +103,7 @@ static u_char nfsrv_v2errmap[ELAST] = { NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO, NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE, - NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, - NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, - NFSERR_IO, + NFSERR_REMOTE, }; /* @@ -1132,6 +1146,7 @@ static short nfsv4err_setclientid[] = { NFSERR_INVAL, NFSERR_RESOURCE, NFSERR_SERVERFAULT, + NFSERR_WRONGSEC, 0, }; @@ -1495,19 +1510,41 @@ nfsd_errmap(struct nfsrv_descript *nd) else if (nd->nd_repstat == NFSERR_MINORVERMISMATCH || nd->nd_repstat == NFSERR_OPILLEGAL) return (txdr_unsigned(nd->nd_repstat)); - else + else if ((nd->nd_flag & ND_NFSV41) != 0) { + if (nd->nd_repstat == EOPNOTSUPP) + nd->nd_repstat = NFSERR_NOTSUPP; + nd->nd_repstat = nfsrv_isannfserr(nd->nd_repstat); + return (txdr_unsigned(nd->nd_repstat)); + } else errp = defaulterrp = nfsrv_v4errmap[nd->nd_procnum]; while (*++errp) if (*errp == nd->nd_repstat) return (txdr_unsigned(nd->nd_repstat)); return (txdr_unsigned(*defaulterrp)); } - if (nd->nd_repstat <= ELAST) + if (nd->nd_repstat <= NFSERR_REMOTE) return (txdr_unsigned(nfsrv_v2errmap[nd->nd_repstat - 1])); return (txdr_unsigned(NFSERR_IO)); } /* + * Check to see if the error is a valid NFS one. If not, replace it with + * NFSERR_IO. + */ +static u_int32_t +nfsrv_isannfserr(u_int32_t errval) +{ + + if (errval == NFSERR_OK) + return (errval); + if (errval >= NFSERR_BADHANDLE && errval <= NFSERR_DELEGREVOKED) + return (errval); + if (errval > 0 && errval <= NFSERR_REMOTE) + return (nfsrv_v2errmap[errval - 1]); + return (NFSERR_IO); +} + +/* * Check to see if setting a uid/gid is permitted when creating a new * file object. (Called when uid and/or gid is specified in the * settable attributes for V4. @@ -1522,8 +1559,10 @@ nfsrv_checkuidgid(struct nfsrv_descript *nd, struct nfsvattr *nvap) */ if (NFSVNO_NOTSETUID(nvap) && NFSVNO_NOTSETGID(nvap)) goto out; - if ((NFSVNO_ISSETUID(nvap) && nvap->na_uid == nfsrv_defaultuid) - || (NFSVNO_ISSETGID(nvap) && nvap->na_gid == nfsrv_defaultgid)) { + if ((NFSVNO_ISSETUID(nvap) && nvap->na_uid == nfsrv_defaultuid && + enable_nobodycheck == 1) + || (NFSVNO_ISSETGID(nvap) && nvap->na_gid == nfsrv_defaultgid && + enable_nogroupcheck == 1)) { error = NFSERR_BADOWNER; goto out; } @@ -1778,7 +1817,7 @@ nfsrv_putreferralattr(struct nfsrv_descript *nd, nfsattrbit_t *retbitp, break; default: printf("EEK! Bad V4 refattr bitpos=%d\n", bitpos); - }; + } } } *retnump = txdr_unsigned(retnum); @@ -1972,7 +2011,7 @@ nfsrv_parsename(struct nfsrv_descript *nd, char *bufp, u_long *hashp, error = 0; goto nfsmout; } - if (disable_checkutf8 == 0 && + if (enable_checkutf8 == 1 && nfsrv_checkutf8((u_int8_t *)bufp, outlen)) { nd->nd_repstat = NFSERR_INVAL; error = 0; @@ -1989,47 +2028,6 @@ nfsmout: return (error); } -/* - * Check the tcp socket sequence number has been acknowledged. - */ -int -nfsrv_checksockseqnum(struct socket *so, tcp_seq tcpseqval) -{ - tcp_seq maxseq, unaseq; - int error, ret; - - error = nfsrv_getsocksndseq(so, &maxseq, &unaseq); - if (error) - return (0); - ret = SEQ_GEQ(unaseq, tcpseqval); - return (ret); -} - -/* - * Get the tcp sequence number to be acknowledged. - */ -int -nfsrv_getsockseqnum(struct socket *so, tcp_seq *tcpseqp) -{ - tcp_seq maxseq, unaseq; - u_int sbcc; - int error; - - sbcc = so->so_snd.sb_cc; - error = nfsrv_getsocksndseq(so, &maxseq, &unaseq); - if (error) - return (0); - /* - * Set the seq# to a value that will - * be at least the end of the reply. - * When this sequence# is acknowledged - * by the client, the client has received - * the reply. - */ - *tcpseqp = sbcc + maxseq; - return (1); -} - void nfsd_init(void) { @@ -2044,10 +2042,20 @@ nfsd_init(void) * Initialize client queues. Don't free/reinitialize * them when nfsds are restarted. */ - for (i = 0; i < NFSCLIENTHASHSIZE; i++) + nfsclienthash = malloc(sizeof(struct nfsclienthashhead) * + nfsrv_clienthashsize, M_NFSDCLIENT, M_WAITOK | M_ZERO); + for (i = 0; i < nfsrv_clienthashsize; i++) LIST_INIT(&nfsclienthash[i]); - for (i = 0; i < NFSLOCKHASHSIZE; i++) + nfslockhash = malloc(sizeof(struct nfslockhashhead) * + nfsrv_lockhashsize, M_NFSDLOCKFILE, M_WAITOK | M_ZERO); + for (i = 0; i < nfsrv_lockhashsize; i++) LIST_INIT(&nfslockhash[i]); + nfssessionhash = malloc(sizeof(struct nfssessionhash) * + nfsrv_sessionhashsize, M_NFSDSESSION, M_WAITOK | M_ZERO); + for (i = 0; i < nfsrv_sessionhashsize; i++) { + mtx_init(&nfssessionhash[i].mtx, "nfssm", NULL, MTX_DEF); + LIST_INIT(&nfssessionhash[i].list); + } /* and the v2 pubfh should be all zeros */ NFSBZERO(nfs_v2pubfh, NFSX_V2FH); @@ -2075,3 +2083,42 @@ nfsd_checkrootexp(struct nfsrv_descript *nd) return (1); } +/* + * Parse the first part of an NFSv4 compound to find out what the minor + * version# is. + */ +void +nfsd_getminorvers(struct nfsrv_descript *nd, u_char *tag, u_char **tagstrp, + int *taglenp, u_int32_t *minversp) +{ + uint32_t *tl; + int error = 0, taglen = -1; + u_char *tagstr = NULL; + + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + taglen = fxdr_unsigned(int, *tl); + if (taglen < 0 || taglen > NFSV4_OPAQUELIMIT) { + error = EBADRPC; + goto nfsmout; + } + if (taglen <= NFSV4_SMALLSTR) + tagstr = tag; + else + tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK); + error = nfsrv_mtostr(nd, tagstr, taglen); + if (error != 0) + goto nfsmout; + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + *minversp = fxdr_unsigned(u_int32_t, *tl); + *tagstrp = tagstr; + if (*minversp == NFSV41_MINORVERSION) + nd->nd_flag |= ND_NFSV41; +nfsmout: + if (error != 0) { + if (tagstr != NULL && taglen > NFSV4_SMALLSTR) + free(tagstr, M_TEMP); + taglen = -1; + } + *taglenp = taglen; +} + |
