summaryrefslogtreecommitdiff
path: root/sys/fs
diff options
context:
space:
mode:
authorpgoyette <pgoyette@NetBSD.org>2016-11-18 08:31:29 +0000
committerpgoyette <pgoyette@NetBSD.org>2016-11-18 08:31:29 +0000
commit7e7dbfb2f85561fa20fb14d9d035512b3d278dbf (patch)
tree6a1d27a2a42f526f04ce9140c2ed91b631f2659d /sys/fs
parent6afb5203449c3ae6695530ceeb045f7165f1252f (diff)
Resolve conflicts
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/nfs/client/nfs_clbio.c176
-rw-r--r--sys/fs/nfs/client/nfs_clstate.c125
-rw-r--r--sys/fs/nfs/client/nfs_clvnops.c142
-rw-r--r--sys/fs/nfs/common/krpc_subr.c8
-rw-r--r--sys/fs/nfs/common/nfs_fha.h22
-rw-r--r--sys/fs/nfs/common/nfsm_subs.h16
-rw-r--r--sys/fs/nfs/files.newnfs13
-rw-r--r--sys/fs/nfs/nlm/nlm_prot_impl.c26
-rw-r--r--sys/fs/nfs/server/nfs_nfsdcache.c204
-rw-r--r--sys/fs/nfs/server/nfs_nfsdkrpc.c112
-rw-r--r--sys/fs/nfs/server/nfs_nfsdserv.c804
-rw-r--r--sys/fs/nfs/server/nfs_nfsdstate.c1275
-rw-r--r--sys/fs/nfs/server/nfs_nfsdsubs.c179
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;
+}
+