/*
- * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsnode.h>
+#include <nfs/nfs_gss.h>
#include <nfs/nfsmount.h>
-LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
-u_long nfsnodehash;
+#define NFSNOHASH(fhsum) \
+ (&nfsnodehashtbl[(fhsum) & nfsnodehash])
+static LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
+static u_long nfsnodehash;
-lck_grp_t * nfs_node_hash_lck_grp;
-lck_grp_attr_t * nfs_node_hash_lck_grp_attr;
-lck_attr_t * nfs_node_hash_lck_attr;
+static lck_grp_t *nfs_node_hash_lck_grp;
+static lck_grp_t *nfs_node_lck_grp;
lck_mtx_t *nfs_node_hash_mutex;
/*
void
nfs_nhinit(void)
{
- nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash);
-
- nfs_node_hash_lck_grp_attr = lck_grp_attr_alloc_init();
- lck_grp_attr_setstat(nfs_node_hash_lck_grp_attr);
- nfs_node_hash_lck_grp = lck_grp_alloc_init("nfs_node_hash", nfs_node_hash_lck_grp_attr);
-
- nfs_node_hash_lck_attr = lck_attr_alloc_init();
+ nfs_node_hash_lck_grp = lck_grp_alloc_init("nfs_node_hash", LCK_GRP_ATTR_NULL);
+ nfs_node_hash_mutex = lck_mtx_alloc_init(nfs_node_hash_lck_grp, LCK_ATTR_NULL);
+ nfs_node_lck_grp = lck_grp_alloc_init("nfs_node", LCK_GRP_ATTR_NULL);
+}
- nfs_node_hash_mutex = lck_mtx_alloc_init(nfs_node_hash_lck_grp, nfs_node_hash_lck_attr);
+void
+nfs_nhinit_finish(void)
+{
+ lck_mtx_lock(nfs_node_hash_mutex);
+ if (!nfsnodehashtbl)
+ nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash);
+ lck_mtx_unlock(nfs_node_hash_mutex);
}
/*
*/
int
nfs_nget(
- mount_t mntp,
- vnode_t dvp,
+ mount_t mp,
+ nfsnode_t dnp,
struct componentname *cnp,
u_char *fhp,
int fhsize,
struct nfs_vattr *nvap,
u_int64_t *xidp,
int flags,
- struct nfsnode **npp)
+ nfsnode_t *npp)
{
- struct nfsnode *np;
+ nfsnode_t np;
struct nfsnodehashhead *nhpp;
- vnode_t vp, nvp;
- int error;
- mount_t mp;
+ vnode_t vp;
+ int error, nfsvers;
+ mount_t mp2;
struct vnode_fsparam vfsp;
uint32_t vid;
+ FSDBG_TOP(263, mp, dnp, flags, npp);
+
/* Check for unmount in progress */
- if (!mntp || (mntp->mnt_kern_flag & MNTK_UNMOUNT)) {
- *npp = 0;
- return (!mntp ? ENXIO : EPERM);
+ if (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT)) {
+ *npp = NULL;
+ error = ENXIO;
+ FSDBG_BOT(263, mp, dnp, 0xd1e, error);
+ return (error);
}
+ nfsvers = VFSTONFS(mp)->nm_vers;
nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
loop:
lck_mtx_lock(nfs_node_hash_mutex);
for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
- mp = (np->n_flag & NINIT) ? np->n_mount : vnode_mount(NFSTOV(np));
- if (mntp != mp || np->n_fhsize != fhsize ||
+ mp2 = (np->n_hflag & NHINIT) ? np->n_mount : NFSTOMP(np);
+ if (mp != mp2 || np->n_fhsize != fhsize ||
bcmp(fhp, np->n_fhp, fhsize))
continue;
- /* if the node is still being initialized, sleep on it */
- if (np->n_flag & NINIT) {
- np->n_flag |= NWINIT;
- msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", 0);
+ FSDBG(263, dnp, np, np->n_flag, 0xcace0000);
+ /* if the node is locked, sleep on it */
+ if (np->n_hflag & NHLOCKED) {
+ np->n_hflag |= NHLOCKWANT;
+ FSDBG(263, dnp, np, np->n_flag, 0xcace2222);
+ msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", NULL);
+ FSDBG(263, dnp, np, np->n_flag, 0xcace3333);
goto loop;
}
vp = NFSTOV(np);
* If vnode is being reclaimed or has already
* changed identity, no need to wait.
*/
+ FSDBG_BOT(263, dnp, *npp, 0xcace0d1e, error);
return (error);
- }
+ }
+ if ((error = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE))) {
+ /* this only fails if the node is now unhashed */
+ /* so let's see if we can find/create it again */
+ FSDBG(263, dnp, *npp, 0xcaced1e2, error);
+ vnode_put(vp);
+ goto loop;
+ }
/* update attributes */
error = nfs_loadattrcache(np, nvap, xidp, 0);
if (error) {
+ nfs_unlock(np);
vnode_put(vp);
} else {
- if (dvp && cnp && (flags & NG_MAKEENTRY))
- cache_enter(dvp, vp, cnp);
+ if (dnp && cnp && (flags & NG_MAKEENTRY))
+ cache_enter(NFSTOV(dnp), vp, cnp);
*npp = np;
}
+ FSDBG_BOT(263, dnp, *npp, 0xcace0000, error);
return(error);
}
+ FSDBG(263, mp, dnp, npp, 0xaaaaaaaa);
+
/*
* allocate and initialize nfsnode and stick it in the hash
* before calling getnewvnode(). Anyone finding it in the
* hash before initialization is complete will wait for it.
*/
- MALLOC_ZONE(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK);
+ MALLOC_ZONE(np, nfsnode_t, sizeof *np, M_NFSNODE, M_WAITOK);
if (!np) {
lck_mtx_unlock(nfs_node_hash_mutex);
*npp = 0;
+ FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOMEM);
return (ENOMEM);
}
- bzero((caddr_t)np, sizeof *np);
- np->n_flag |= NINIT;
- np->n_mount = mntp;
+ bzero(np, sizeof *np);
+ np->n_hflag |= (NHINIT | NHLOCKED);
+ np->n_mount = mp;
+
+ if (dnp && cnp && ((cnp->cn_namelen != 2) ||
+ (cnp->cn_nameptr[0] != '.') || (cnp->cn_nameptr[1] != '.'))) {
+ vnode_t dvp = NFSTOV(dnp);
+ if (!vnode_get(dvp)) {
+ if (!vnode_ref(dvp))
+ np->n_parent = dvp;
+ vnode_put(dvp);
+ }
+ }
/* setup node's file handle */
if (fhsize > NFS_SMALLFH) {
lck_mtx_unlock(nfs_node_hash_mutex);
FREE_ZONE(np, sizeof *np, M_NFSNODE);
*npp = 0;
+ FSDBG_BOT(263, dnp, *npp, 0x80000002, ENOMEM);
return (ENOMEM);
}
} else {
np->n_fhsize = fhsize;
/* Insert the nfsnode in the hash queue for its new file handle */
- np->n_flag |= NHASHED;
LIST_INSERT_HEAD(nhpp, np, n_hash);
+ np->n_hflag |= NHHASHED;
+ FSDBG(266, 0, np, np->n_flag, np->n_hflag);
+
+ /* lock the new nfsnode */
+ lck_rw_init(&np->n_lock, nfs_node_lck_grp, LCK_ATTR_NULL);
+ lck_rw_init(&np->n_datalock, nfs_node_lck_grp, LCK_ATTR_NULL);
+ nfs_lock(np, NFS_NODE_LOCK_FORCE);
/* release lock on hash table */
lck_mtx_unlock(nfs_node_hash_mutex);
/* do initial loading of attributes */
error = nfs_loadattrcache(np, nvap, xidp, 1);
if (error) {
+ FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
+ nfs_unlock(np);
lck_mtx_lock(nfs_node_hash_mutex);
LIST_REMOVE(np, n_hash);
- np->n_flag &= ~(NHASHED|NINIT);
- if (np->n_flag & NWINIT) {
- np->n_flag &= ~NWINIT;
- wakeup((caddr_t)np);
+ np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED);
+ if (np->n_hflag & NHLOCKWANT) {
+ np->n_hflag &= ~NHLOCKWANT;
+ wakeup(np);
}
lck_mtx_unlock(nfs_node_hash_mutex);
+ if (np->n_parent) {
+ if (!vnode_get(np->n_parent)) {
+ vnode_rele(np->n_parent);
+ vnode_put(np->n_parent);
+ }
+ np->n_parent = NULL;
+ }
+ lck_rw_destroy(&np->n_lock, nfs_node_lck_grp);
+ lck_rw_destroy(&np->n_datalock, nfs_node_lck_grp);
if (np->n_fhsize > NFS_SMALLFH)
FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
FREE_ZONE(np, sizeof *np, M_NFSNODE);
*npp = 0;
+ FSDBG_BOT(263, dnp, *npp, 0x80000003, error);
return (error);
}
- np->n_mtime = nvap->nva_mtime;
+ NFS_CHANGED_UPDATE(nfsvers, np, nvap);
if (nvap->nva_type == VDIR)
- np->n_ncmtime = nvap->nva_mtime;
+ NFS_CHANGED_UPDATE_NC(nfsvers, np, nvap);
NMODEINVALIDATE(np);
/* now, attempt to get a new vnode */
- vfsp.vnfs_mp = mntp;
+ vfsp.vnfs_mp = mp;
vfsp.vnfs_vtype = nvap->nva_type;
vfsp.vnfs_str = "nfs";
- vfsp.vnfs_dvp = dvp;
+ vfsp.vnfs_dvp = dnp ? NFSTOV(dnp) : NULL;
vfsp.vnfs_fsnode = np;
- if (nvap->nva_type == VFIFO)
- vfsp.vnfs_vops = fifo_nfsv2nodeop_p;
- else if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
- vfsp.vnfs_vops = spec_nfsv2nodeop_p;
- else
- vfsp.vnfs_vops = nfsv2_vnodeop_p;
+ if (nfsvers == NFS_VER4) {
+#if FIFO
+ if (nvap->nva_type == VFIFO)
+ vfsp.vnfs_vops = fifo_nfsv4nodeop_p;
+ else
+#endif /* FIFO */
+ if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
+ vfsp.vnfs_vops = spec_nfsv4nodeop_p;
+ else
+ vfsp.vnfs_vops = nfsv4_vnodeop_p;
+ } else {
+#if FIFO
+ if (nvap->nva_type == VFIFO)
+ vfsp.vnfs_vops = fifo_nfsv2nodeop_p;
+ else
+#endif /* FIFO */
+ if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
+ vfsp.vnfs_vops = spec_nfsv2nodeop_p;
+ else
+ vfsp.vnfs_vops = nfsv2_vnodeop_p;
+ }
vfsp.vnfs_markroot = (flags & NG_MARKROOT) ? 1 : 0;
vfsp.vnfs_marksystem = 0;
vfsp.vnfs_rdev = 0;
vfsp.vnfs_filesize = nvap->nva_size;
vfsp.vnfs_cnp = cnp;
- if (dvp && cnp && (flags & NG_MAKEENTRY))
- vfsp.vnfs_flags = 0;
- else
- vfsp.vnfs_flags = VNFS_NOCACHE;
- error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &nvp);
+ vfsp.vnfs_flags = VNFS_ADDFSREF;
+ if (!dnp || !cnp || !(flags & NG_MAKEENTRY))
+ vfsp.vnfs_flags |= VNFS_NOCACHE;
+
+ error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &np->n_vnode);
if (error) {
+ FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
+ nfs_unlock(np);
lck_mtx_lock(nfs_node_hash_mutex);
LIST_REMOVE(np, n_hash);
- np->n_flag &= ~(NHASHED|NINIT);
- if (np->n_flag & NWINIT) {
- np->n_flag &= ~NWINIT;
- wakeup((caddr_t)np);
+ np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED);
+ if (np->n_hflag & NHLOCKWANT) {
+ np->n_hflag &= ~NHLOCKWANT;
+ wakeup(np);
}
lck_mtx_unlock(nfs_node_hash_mutex);
+ if (np->n_parent) {
+ if (!vnode_get(np->n_parent)) {
+ vnode_rele(np->n_parent);
+ vnode_put(np->n_parent);
+ }
+ np->n_parent = NULL;
+ }
+ lck_rw_destroy(&np->n_lock, nfs_node_lck_grp);
+ lck_rw_destroy(&np->n_datalock, nfs_node_lck_grp);
if (np->n_fhsize > NFS_SMALLFH)
FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
FREE_ZONE(np, sizeof *np, M_NFSNODE);
*npp = 0;
+ FSDBG_BOT(263, dnp, *npp, 0x80000004, error);
return (error);
}
- vp = nvp;
- np->n_vnode = vp;
- vnode_addfsref(vp);
- vnode_settag(vp, VT_NFS); // XXX shouldn't this be a vnode_create() parameter?
- *npp = np;
+ vp = np->n_vnode;
+ vnode_settag(vp, VT_NFS);
/* node is now initialized */
/* check if anyone's waiting on this node */
lck_mtx_lock(nfs_node_hash_mutex);
- np->n_flag &= ~NINIT;
- if (np->n_flag & NWINIT) {
- np->n_flag &= ~NWINIT;
- wakeup((caddr_t)np);
+ np->n_hflag &= ~(NHINIT|NHLOCKED);
+ if (np->n_hflag & NHLOCKWANT) {
+ np->n_hflag &= ~NHLOCKWANT;
+ wakeup(np);
}
lck_mtx_unlock(nfs_node_hash_mutex);
+ *npp = np;
+
+ FSDBG_BOT(263, dnp, vp, *npp, error);
return (error);
}
int
-nfs_inactive(ap)
+nfs_vnop_inactive(ap)
struct vnop_inactive_args /* {
struct vnodeop_desc *a_desc;
vnode_t a_vp;
vfs_context_t a_context;
} */ *ap;
{
- register struct nfsnode *np;
- register struct sillyrename *sp;
- kauth_cred_t cred;
+ vnode_t vp;
+ nfsnode_t np;
+ struct nfs_sillyrename *nsp;
+ struct nfs_vattr nvattr;
+ int unhash, attrerr;
+ vp = ap->a_vp;
np = VTONFS(ap->a_vp);
- if (vnode_vtype(ap->a_vp) != VDIR) {
- sp = np->n_sillyrename;
- np->n_sillyrename = (struct sillyrename *)0;
+
+ nfs_lock(np, NFS_NODE_LOCK_FORCE);
+
+ if (vnode_vtype(vp) != VDIR) {
+ nsp = np->n_sillyrename;
+ np->n_sillyrename = NULL;
} else
- sp = (struct sillyrename *)0;
+ nsp = NULL;
- if (sp) {
- /*
- * Remove the silly file that was rename'd earlier
- */
-#if DIAGNOSTIC
- kprintf("nfs_inactive removing %s, dvp=%x, a_vp=%x, ap=%x, np=%x, sp=%x\n",
- &sp->s_name[0], (unsigned)sp->s_dvp, (unsigned)ap->a_vp, (unsigned)ap,
- (unsigned)np, (unsigned)sp);
-#endif
- nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, vfs_context_proc(ap->a_context), 1);
- np->n_size = 0;
- ubc_setsize(ap->a_vp, (off_t)0);
- nfs_removeit(sp);
+ FSDBG_TOP(264, vp, np, np->n_flag, nsp);
+
+ if (!nsp) {
+ /* no silly file to clean up... */
+ /* clear all flags other than these */
+ np->n_flag &= (NMODIFIED);
+ nfs_unlock(np);
+ FSDBG_BOT(264, vp, np, np->n_flag, 0);
+ return (0);
+ }
+
+ /* Remove the silly file that was rename'd earlier */
+
+ /* flush all the buffers */
+ nfs_unlock(np);
+ nfs_vinvalbuf2(vp, V_SAVE, vfs_context_thread(ap->a_context), nsp->nsr_cred, 1);
+
+ /* purge the name cache to deter others from finding it */
+ cache_purge(vp);
+
+ /* try to get the latest attributes */
+ attrerr = nfs_getattr(np, &nvattr, ap->a_context, 0);
+
+ /* Check if we should remove it from the node hash. */
+ /* Leave it if inuse or it has multiple hard links. */
+ if (vnode_isinuse(vp, 0) || (!attrerr && (nvattr.nva_nlink > 1))) {
+ unhash = 0;
+ } else {
+ unhash = 1;
+ ubc_setsize(vp, 0);
+ }
+
+ /* grab node lock on this node and the directory */
+ nfs_lock2(nsp->nsr_dnp, np, NFS_NODE_LOCK_FORCE);
+
+ /* lock the node while we remove the silly file */
+ lck_mtx_lock(nfs_node_hash_mutex);
+ while (np->n_hflag & NHLOCKED) {
+ np->n_hflag |= NHLOCKWANT;
+ msleep(np, nfs_node_hash_mutex, PINOD, "nfs_inactive", NULL);
+ }
+ np->n_hflag |= NHLOCKED;
+ lck_mtx_unlock(nfs_node_hash_mutex);
+
+ /* purge again in case it was looked up while we were locking */
+ cache_purge(vp);
+
+ FSDBG(264, np, np->n_size, np->n_vattr.nva_size, 0xf00d00f1);
+
+ /* now remove the silly file */
+ nfs_removeit(nsp);
+
+ /* clear all flags other than these */
+ np->n_flag &= (NMODIFIED);
+ nfs_unlock2(nsp->nsr_dnp, np);
+
+ if (unhash && vnode_isinuse(vp, 0)) {
+ /* vnode now inuse after silly remove? */
+ unhash = 0;
+ ubc_setsize(vp, np->n_size);
+ }
+
+ lck_mtx_lock(nfs_node_hash_mutex);
+ if (unhash) {
/*
* remove nfsnode from hash now so we can't accidentally find it
* again if another object gets created with the same filehandle
* before this vnode gets reclaimed
*/
- lck_mtx_lock(nfs_node_hash_mutex);
- LIST_REMOVE(np, n_hash);
- np->n_flag &= ~NHASHED;
- lck_mtx_unlock(nfs_node_hash_mutex);
- cred = sp->s_cred;
- if (cred != NOCRED) {
- sp->s_cred = NOCRED;
- kauth_cred_rele(cred);
+ if (np->n_hflag & NHHASHED) {
+ LIST_REMOVE(np, n_hash);
+ np->n_hflag &= ~NHHASHED;
+ FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
}
- vnode_rele(sp->s_dvp);
- FREE_ZONE((caddr_t)sp, sizeof (struct sillyrename), M_NFSREQ);
- vnode_recycle(ap->a_vp);
+ vnode_recycle(vp);
}
- /* clear all flags other than these */
- np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NHASHED);
+ /* unlock the node */
+ np->n_hflag &= ~NHLOCKED;
+ if (np->n_hflag & NHLOCKWANT) {
+ np->n_hflag &= ~NHLOCKWANT;
+ wakeup(np);
+ }
+ lck_mtx_unlock(nfs_node_hash_mutex);
+
+ /* cleanup sillyrename info */
+ if (nsp->nsr_cred != NOCRED)
+ kauth_cred_unref(&nsp->nsr_cred);
+ vnode_rele(NFSTOV(nsp->nsr_dnp));
+ FREE_ZONE(nsp, sizeof(*nsp), M_NFSREQ);
+
+ FSDBG_BOT(264, vp, np, np->n_flag, 0);
return (0);
}
* Reclaim an nfsnode so that it can be used for other purposes.
*/
int
-nfs_reclaim(ap)
+nfs_vnop_reclaim(ap)
struct vnop_reclaim_args /* {
struct vnodeop_desc *a_desc;
vnode_t a_vp;
} */ *ap;
{
vnode_t vp = ap->a_vp;
- struct nfsnode *np = VTONFS(vp);
+ nfsnode_t np = VTONFS(vp);
struct nfsdmap *dp, *dp2;
+ FSDBG_TOP(265, vp, np, np->n_flag, 0);
+
+ lck_mtx_lock(nfs_node_hash_mutex);
+
+ if ((vnode_vtype(vp) != VDIR) && np->n_sillyrename)
+ printf("nfs_reclaim: leaving unlinked file %s\n", np->n_sillyrename->nsr_name);
+
vnode_removefsref(vp);
- if (np->n_flag & NHASHED) {
- lck_mtx_lock(nfs_node_hash_mutex);
+ if (np->n_hflag & NHHASHED) {
LIST_REMOVE(np, n_hash);
- np->n_flag &= ~NHASHED;
- lck_mtx_unlock(nfs_node_hash_mutex);
+ np->n_hflag &= ~NHHASHED;
+ FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
}
+ lck_mtx_unlock(nfs_node_hash_mutex);
/*
* Free up any directory cookie structures and
* large file handle structures that might be associated with
* this nfs node.
*/
+ nfs_lock(np, NFS_NODE_LOCK_FORCE);
if (vnode_vtype(vp) == VDIR) {
dp = np->n_cookies.lh_first;
while (dp) {
if (np->n_fhsize > NFS_SMALLFH) {
FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
}
+
+ nfs_unlock(np);
vnode_clearfsnode(vp);
+ if (np->n_parent) {
+ if (!vnode_get(np->n_parent)) {
+ vnode_rele(np->n_parent);
+ vnode_put(np->n_parent);
+ }
+ np->n_parent = NULL;
+ }
+
+ lck_rw_destroy(&np->n_lock, nfs_node_lck_grp);
+ lck_rw_destroy(&np->n_datalock, nfs_node_lck_grp);
+
+ FSDBG_BOT(265, vp, np, np->n_flag, 0xd1ed1e);
FREE_ZONE(np, sizeof(struct nfsnode), M_NFSNODE);
return (0);
}
+/*
+ * Acquire an NFS node lock
+ */
+int
+nfs_lock(nfsnode_t np, int locktype)
+{
+ FSDBG_TOP(268, np, locktype, np->n_lockowner, 0);
+ if (locktype == NFS_NODE_LOCK_SHARED) {
+ lck_rw_lock_shared(&np->n_lock);
+ } else {
+ lck_rw_lock_exclusive(&np->n_lock);
+ np->n_lockowner = current_thread();
+ }
+ if ((locktype != NFS_NODE_LOCK_FORCE) && !(np->n_hflag && NHHASHED)) {
+ FSDBG_BOT(268, np, 0xdead, np->n_lockowner, 0);
+ nfs_unlock(np);
+ return (ENOENT);
+ }
+ FSDBG_BOT(268, np, locktype, np->n_lockowner, 0);
+ return (0);
+}
+
+/*
+ * Release an NFS node lock
+ */
+void
+nfs_unlock(nfsnode_t np)
+{
+ FSDBG(269, np, np->n_lockowner, current_thread(), 0);
+ np->n_lockowner = NULL;
+ lck_rw_done(&np->n_lock);
+}
+
+/*
+ * Acquire 2 NFS node locks
+ * - locks taken in order given (assumed to be parent-child order)
+ * - both or neither of the locks are taken
+ * - only one lock taken per node (dup nodes are skipped)
+ */
+int
+nfs_lock2(nfsnode_t np1, nfsnode_t np2, int locktype)
+{
+ int error;
+
+ if ((error = nfs_lock(np1, locktype)))
+ return (error);
+ if (np1 == np2)
+ return (error);
+ if ((error = nfs_lock(np2, locktype)))
+ nfs_unlock(np1);
+ return (error);
+}
+
+/*
+ * Unlock a couple of NFS nodes
+ */
+void
+nfs_unlock2(nfsnode_t np1, nfsnode_t np2)
+{
+ nfs_unlock(np1);
+ if (np1 != np2)
+ nfs_unlock(np2);
+}
+
+/*
+ * Acquire 4 NFS node locks
+ * - fdnp/fnp and tdnp/tnp locks taken in order given
+ * - otherwise locks taken in node address order.
+ * - all or none of the locks are taken
+ * - only one lock taken per node (dup nodes are skipped)
+ * - some of the node pointers may be null
+ */
+int
+nfs_lock4(nfsnode_t fdnp, nfsnode_t fnp, nfsnode_t tdnp, nfsnode_t tnp, int locktype)
+{
+ nfsnode_t list[4];
+ int i, lcnt = 0, error;
+
+ if (fdnp == tdnp) {
+ list[lcnt++] = fdnp;
+ } else if (fdnp->n_parent && (tdnp == VTONFS(fdnp->n_parent))) {
+ list[lcnt++] = tdnp;
+ list[lcnt++] = fdnp;
+ } else if (tdnp->n_parent && (fdnp == VTONFS(tdnp->n_parent))) {
+ list[lcnt++] = fdnp;
+ list[lcnt++] = tdnp;
+ } else if (fdnp < tdnp) {
+ list[lcnt++] = fdnp;
+ list[lcnt++] = tdnp;
+ } else {
+ list[lcnt++] = tdnp;
+ list[lcnt++] = fdnp;
+ }
+
+ if (!tnp || (fnp == tnp) || (tnp == fdnp)) {
+ list[lcnt++] = fnp;
+ } else if (fnp < tnp) {
+ list[lcnt++] = fnp;
+ list[lcnt++] = tnp;
+ } else {
+ list[lcnt++] = tnp;
+ list[lcnt++] = fnp;
+ }
+
+ /* Now we can lock using list[0 - lcnt-1] */
+ for (i = 0; i < lcnt; ++i) {
+ if (list[i])
+ if ((error = nfs_lock(list[i], locktype))) {
+ /* Drop any locks we acquired. */
+ while (--i >= 0) {
+ if (list[i])
+ nfs_unlock(list[i]);
+ }
+ return (error);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Unlock a group of NFS nodes
+ */
+void
+nfs_unlock4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4)
+{
+ nfsnode_t list[4];
+ int i, k = 0;
+
+ if (np1) {
+ nfs_unlock(np1);
+ list[k++] = np1;
+ }
+ if (np2) {
+ for (i = 0; i < k; ++i)
+ if (list[i] == np2)
+ goto skip2;
+ nfs_unlock(np2);
+ list[k++] = np2;
+ }
+skip2:
+ if (np3) {
+ for (i = 0; i < k; ++i)
+ if (list[i] == np3)
+ goto skip3;
+ nfs_unlock(np3);
+ list[k++] = np3;
+ }
+skip3:
+ if (np4) {
+ for (i = 0; i < k; ++i)
+ if (list[i] == np4)
+ return;
+ nfs_unlock(np4);
+ }
+}
+
+/*
+ * Acquire an NFS node data lock
+ */
+void
+nfs_data_lock(nfsnode_t np, int locktype)
+{
+ nfs_data_lock2(np, locktype, 1);
+}
+void
+nfs_data_lock2(nfsnode_t np, int locktype, int updatesize)
+{
+ FSDBG_TOP(270, np, locktype, np->n_datalockowner, 0);
+ if (locktype == NFS_NODE_LOCK_SHARED) {
+ if (updatesize && ISSET(np->n_flag, NUPDATESIZE))
+ nfs_data_update_size(np, 0);
+ lck_rw_lock_shared(&np->n_datalock);
+ } else {
+ lck_rw_lock_exclusive(&np->n_datalock);
+ np->n_datalockowner = current_thread();
+ if (updatesize && ISSET(np->n_flag, NUPDATESIZE))
+ nfs_data_update_size(np, 1);
+ }
+ FSDBG_BOT(270, np, locktype, np->n_datalockowner, 0);
+}
+
+/*
+ * Release an NFS node data lock
+ */
+void
+nfs_data_unlock(nfsnode_t np)
+{
+ nfs_data_unlock2(np, 1);
+}
+void
+nfs_data_unlock2(nfsnode_t np, int updatesize)
+{
+ int mine = (np->n_datalockowner == current_thread());
+ FSDBG_TOP(271, np, np->n_datalockowner, current_thread(), 0);
+ if (updatesize && mine && ISSET(np->n_flag, NUPDATESIZE))
+ nfs_data_update_size(np, 1);
+ np->n_datalockowner = NULL;
+ lck_rw_done(&np->n_datalock);
+ if (updatesize && !mine && ISSET(np->n_flag, NUPDATESIZE))
+ nfs_data_update_size(np, 0);
+ FSDBG_BOT(271, np, np->n_datalockowner, current_thread(), 0);
+}
+
+
+/*
+ * update an NFS node's size
+ */
+void
+nfs_data_update_size(nfsnode_t np, int datalocked)
+{
+ int error;
+
+ FSDBG_TOP(272, np, np->n_flag, np->n_size, np->n_newsize);
+ if (!datalocked) {
+ nfs_data_lock(np, NFS_NODE_LOCK_EXCLUSIVE);
+ /* grabbing data lock will automatically update size */
+ nfs_data_unlock(np);
+ FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
+ return;
+ }
+ error = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE);
+ if (error || !ISSET(np->n_flag, NUPDATESIZE)) {
+ if (!error)
+ nfs_unlock(np);
+ FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
+ return;
+ }
+ CLR(np->n_flag, NUPDATESIZE);
+ np->n_size = np->n_newsize;
+ /* make sure we invalidate buffers the next chance we get */
+ SET(np->n_flag, NNEEDINVALIDATE);
+ nfs_unlock(np);
+ ubc_setsize(NFSTOV(np), (off_t)np->n_size); /* XXX error? */
+ FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
+}
+