+ if (reopen) {
+ /*
+ * Any problems with the delegation probably indicates that we
+ * should review/return all of our current delegation state.
+ */
+ if ((nmp = NFSTONMP(nofp->nof_np))) {
+ nfs4_delegation_return_enqueue(nofp->nof_np);
+ lck_mtx_lock(&nmp->nm_lock);
+ nfs_need_recover(nmp, NFSERR_EXPIRED);
+ lck_mtx_unlock(&nmp->nm_lock);
+ }
+ if (reopen && (nfs_check_for_locks(noop, nofp) == 0)) {
+ /* just reopen the file on next access */
+ NP(nofp->nof_np, "nfs4_claim_delegated_state_for_open_file: %d, need reopen, %d",
+ reopen, kauth_cred_getuid(nofp->nof_owner->noo_cred));
+ lck_mtx_lock(&nofp->nof_lock);
+ nofp->nof_flags |= NFS_OPEN_FILE_REOPEN;
+ lck_mtx_unlock(&nofp->nof_lock);
+ return (0);
+ }
+ if (reopen)
+ NP(nofp->nof_np, "nfs4_claim_delegated_state_for_open_file: %d, locks prevent reopen, %d",
+ reopen, kauth_cred_getuid(nofp->nof_owner->noo_cred));
+ }
+
+ if (!error && ((nmp = NFSTONMP(nofp->nof_np)))) {
+ /* claim delegated locks */
+ TAILQ_FOREACH(nlop, &nofp->nof_np->n_lock_owners, nlo_link) {
+ if (nlop->nlo_open_owner != noop)
+ continue;
+ TAILQ_FOREACH_SAFE(nflp, &nlop->nlo_locks, nfl_lolink, nextnflp) {
+ /* skip dead & blocked lock requests (shouldn't be any in the held lock list) */
+ if (nflp->nfl_flags & (NFS_FILE_LOCK_DEAD|NFS_FILE_LOCK_BLOCKED))
+ continue;
+ /* skip non-delegated locks */
+ if (!(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED))
+ continue;
+ error = nmp->nm_funcs->nf_setlock_rpc(nofp->nof_np, nofp, nflp, 0, flags, current_thread(), noop->noo_cred);
+ if (error) {
+ NP(nofp->nof_np, "nfs: delegated lock claim (0x%llx, 0x%llx) failed %d, %d",
+ nflp->nfl_start, nflp->nfl_end, error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
+ break;
+ }
+ // else {
+ // NP(nofp->nof_np, "nfs: delegated lock claim (0x%llx, 0x%llx) succeeded, %d",
+ // nflp->nfl_start, nflp->nfl_end, kauth_cred_getuid(nofp->nof_owner->noo_cred));
+ // }
+ }
+ if (error)
+ break;
+ }
+ }
+
+ if (!error) /* all state claimed successfully! */
+ return (0);
+
+ /* restart if it looks like a problem more than just losing the delegation */
+ if (!nfs_mount_state_error_delegation_lost(error) &&
+ ((error == ETIMEDOUT) || nfs_mount_state_error_should_restart(error))) {
+ NP(nofp->nof_np, "nfs delegated lock claim error %d, %d", error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
+ if ((error == ETIMEDOUT) && ((nmp = NFSTONMP(nofp->nof_np))))
+ nfs_need_reconnect(nmp);
+ return (error);
+ }
+
+ /* delegated state lost (once held but now not claimable) */
+ NP(nofp->nof_np, "nfs delegated state claim error %d, state lost, %d", error, kauth_cred_getuid(nofp->nof_owner->noo_cred));
+
+ /*
+ * Any problems with the delegation probably indicates that we
+ * should review/return all of our current delegation state.
+ */
+ if ((nmp = NFSTONMP(nofp->nof_np))) {
+ nfs4_delegation_return_enqueue(nofp->nof_np);
+ lck_mtx_lock(&nmp->nm_lock);
+ nfs_need_recover(nmp, NFSERR_EXPIRED);
+ lck_mtx_unlock(&nmp->nm_lock);
+ }
+
+ /* revoke all open file state */
+ nfs_revoke_open_state_for_node(nofp->nof_np);
+
+ return (error);
+}
+
+/*
+ * Release all open state for the given node.
+ */
+void
+nfs_release_open_state_for_node(nfsnode_t np, int force)
+{
+ struct nfsmount *nmp = NFSTONMP(np);
+ struct nfs_open_file *nofp;
+ struct nfs_file_lock *nflp, *nextnflp;
+
+ /* drop held locks */
+ TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
+ /* skip dead & blocked lock requests */
+ if (nflp->nfl_flags & (NFS_FILE_LOCK_DEAD|NFS_FILE_LOCK_BLOCKED))
+ continue;
+ /* send an unlock if not a delegated lock */
+ if (!force && nmp && !(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED))
+ nmp->nm_funcs->nf_unlock_rpc(np, nflp->nfl_owner, F_WRLCK, nflp->nfl_start, nflp->nfl_end, R_RECOVER,
+ NULL, nflp->nfl_owner->nlo_open_owner->noo_cred);
+ /* kill/remove the lock */
+ lck_mtx_lock(&np->n_openlock);
+ nflp->nfl_flags |= NFS_FILE_LOCK_DEAD;
+ lck_mtx_lock(&nflp->nfl_owner->nlo_lock);
+ TAILQ_REMOVE(&nflp->nfl_owner->nlo_locks, nflp, nfl_lolink);
+ lck_mtx_unlock(&nflp->nfl_owner->nlo_lock);
+ if (nflp->nfl_blockcnt) {
+ /* wake up anyone blocked on this lock */
+ wakeup(nflp);
+ } else {
+ /* remove nflp from lock list and destroy */
+ TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
+ nfs_file_lock_destroy(nflp);
+ }
+ lck_mtx_unlock(&np->n_openlock);
+ }
+
+ lck_mtx_lock(&np->n_openlock);
+
+ /* drop all opens */
+ TAILQ_FOREACH(nofp, &np->n_opens, nof_link) {
+ if (nofp->nof_flags & NFS_OPEN_FILE_LOST)
+ continue;
+ /* mark open state as lost */
+ lck_mtx_lock(&nofp->nof_lock);
+ nofp->nof_flags &= ~NFS_OPEN_FILE_REOPEN;
+ nofp->nof_flags |= NFS_OPEN_FILE_LOST;
+
+ lck_mtx_unlock(&nofp->nof_lock);
+ if (!force && nmp && (nmp->nm_vers >= NFS_VER4))
+ nfs4_close_rpc(np, nofp, NULL, nofp->nof_owner->noo_cred, R_RECOVER);
+ }
+
+ lck_mtx_unlock(&np->n_openlock);
+}
+
+/*
+ * State for a node has been lost, drop it, and revoke the node.
+ * Attempt to return any state if possible in case the server
+ * might somehow think we hold it.
+ */
+void
+nfs_revoke_open_state_for_node(nfsnode_t np)
+{
+ struct nfsmount *nmp;
+
+ /* mark node as needing to be revoked */
+ nfs_node_lock_force(np);
+ if (np->n_flag & NREVOKE) /* already revoked? */
+ {
+ NP(np, "nfs_revoke_open_state_for_node(): already revoked");
+ nfs_node_unlock(np);
+ return;
+ }
+ np->n_flag |= NREVOKE;
+ nfs_node_unlock(np);
+
+ nfs_release_open_state_for_node(np, 0);
+ NP(np, "nfs: state lost for %p 0x%x", np, np->n_flag);
+
+ /* mark mount as needing a revoke scan and have the socket thread do it. */
+ if ((nmp = NFSTONMP(np))) {
+ lck_mtx_lock(&nmp->nm_lock);
+ nmp->nm_state |= NFSSTA_REVOKE;
+ nfs_mount_sock_thread_wake(nmp);
+ lck_mtx_unlock(&nmp->nm_lock);
+ }
+}
+
+/*
+ * Claim the delegated open combinations that each of this node's open files hold.
+ */
+int
+nfs4_claim_delegated_state_for_node(nfsnode_t np, int flags)
+{
+ struct nfs_open_file *nofp;
+ int error = 0;
+
+ lck_mtx_lock(&np->n_openlock);
+
+ /* walk the open file list looking for opens with delegated state to claim */
+restart:
+ TAILQ_FOREACH(nofp, &np->n_opens, nof_link) {
+ if (!nofp->nof_d_rw_drw && !nofp->nof_d_w_drw && !nofp->nof_d_r_drw &&
+ !nofp->nof_d_rw_dw && !nofp->nof_d_w_dw && !nofp->nof_d_r_dw &&
+ !nofp->nof_d_rw && !nofp->nof_d_w && !nofp->nof_d_r)
+ continue;
+ lck_mtx_unlock(&np->n_openlock);
+ error = nfs4_claim_delegated_state_for_open_file(nofp, flags);
+ lck_mtx_lock(&np->n_openlock);
+ if (error)
+ break;
+ goto restart;
+ }
+
+ lck_mtx_unlock(&np->n_openlock);
+
+ return (error);
+}
+
+/*
+ * Mark a node as needed to have its delegation returned.
+ * Queue it up on the delegation return queue.
+ * Make sure the thread is running.
+ */
+void
+nfs4_delegation_return_enqueue(nfsnode_t np)
+{
+ struct nfsmount *nmp;
+
+ nmp = NFSTONMP(np);
+ if (!nmp)
+ return;
+
+ lck_mtx_lock(&np->n_openlock);
+ np->n_openflags |= N_DELEG_RETURN;
+ lck_mtx_unlock(&np->n_openlock);
+
+ lck_mtx_lock(&nmp->nm_lock);
+ if (np->n_dreturn.tqe_next == NFSNOLIST)
+ TAILQ_INSERT_TAIL(&nmp->nm_dreturnq, np, n_dreturn);
+ nfs_mount_sock_thread_wake(nmp);
+ lck_mtx_unlock(&nmp->nm_lock);
+}
+
+/*
+ * return any delegation we may have for the given node
+ */
+int
+nfs4_delegation_return(nfsnode_t np, int flags, thread_t thd, kauth_cred_t cred)
+{
+ struct nfsmount *nmp;
+ fhandle_t fh;
+ nfs_stateid dstateid;
+ int error;
+
+ nmp = NFSTONMP(np);
+ if (!nmp)
+ return (ENXIO);
+
+ /* first, make sure the node's marked for delegation return */
+ lck_mtx_lock(&np->n_openlock);
+ np->n_openflags |= (N_DELEG_RETURN|N_DELEG_RETURNING);
+ lck_mtx_unlock(&np->n_openlock);
+
+ /* make sure nobody else is using the delegation state */
+ if ((error = nfs_open_state_set_busy(np, NULL)))
+ goto out;
+
+ /* claim any delegated state */
+ if ((error = nfs4_claim_delegated_state_for_node(np, flags)))
+ goto out;
+
+ /* return the delegation */
+ lck_mtx_lock(&np->n_openlock);
+ dstateid = np->n_dstateid;
+ fh.fh_len = np->n_fhsize;
+ bcopy(np->n_fhp, &fh.fh_data, fh.fh_len);
+ lck_mtx_unlock(&np->n_openlock);
+ error = nfs4_delegreturn_rpc(NFSTONMP(np), fh.fh_data, fh.fh_len, &dstateid, flags, thd, cred);
+ /* assume delegation is gone for all errors except ETIMEDOUT, NFSERR_*MOVED */
+ if ((error != ETIMEDOUT) && (error != NFSERR_MOVED) && (error != NFSERR_LEASE_MOVED)) {
+ lck_mtx_lock(&np->n_openlock);
+ np->n_openflags &= ~N_DELEG_MASK;
+ lck_mtx_lock(&nmp->nm_lock);
+ if (np->n_dlink.tqe_next != NFSNOLIST) {
+ TAILQ_REMOVE(&nmp->nm_delegations, np, n_dlink);
+ np->n_dlink.tqe_next = NFSNOLIST;
+ }
+ lck_mtx_unlock(&nmp->nm_lock);
+ lck_mtx_unlock(&np->n_openlock);
+ }
+
+out:
+ /* make sure it's no longer on the return queue and clear the return flags */
+ lck_mtx_lock(&nmp->nm_lock);
+ if (np->n_dreturn.tqe_next != NFSNOLIST) {
+ TAILQ_REMOVE(&nmp->nm_dreturnq, np, n_dreturn);
+ np->n_dreturn.tqe_next = NFSNOLIST;
+ }
+ lck_mtx_unlock(&nmp->nm_lock);
+ lck_mtx_lock(&np->n_openlock);
+ np->n_openflags &= ~(N_DELEG_RETURN|N_DELEG_RETURNING);
+ lck_mtx_unlock(&np->n_openlock);
+
+ if (error) {
+ NP(np, "nfs4_delegation_return, error %d", error);
+ if (error == ETIMEDOUT)
+ nfs_need_reconnect(nmp);
+ if (nfs_mount_state_error_should_restart(error)) {
+ /* make sure recovery happens */
+ lck_mtx_lock(&nmp->nm_lock);
+ nfs_need_recover(nmp, nfs_mount_state_error_delegation_lost(error) ? NFSERR_EXPIRED : 0);
+ lck_mtx_unlock(&nmp->nm_lock);
+ }
+ }
+
+ nfs_open_state_clear_busy(np);
+
+ return (error);
+}
+
+/*
+ * RPC to return a delegation for a file handle
+ */
+int
+nfs4_delegreturn_rpc(struct nfsmount *nmp, u_char *fhp, int fhlen, struct nfs_stateid *sid, int flags, thread_t thd, kauth_cred_t cred)
+{
+ int error = 0, status, numops;
+ uint64_t xid;
+ struct nfsm_chain nmreq, nmrep;
+ struct nfsreq_secinfo_args si;
+
+ NFSREQ_SECINFO_SET(&si, NULL, fhp, fhlen, NULL, 0);
+ nfsm_chain_null(&nmreq);
+ nfsm_chain_null(&nmrep);
+
+ // PUTFH, DELEGRETURN
+ numops = 2;
+ nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED);
+ nfsm_chain_add_compound_header(error, &nmreq, "delegreturn", numops);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
+ nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, fhp, fhlen);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_DELEGRETURN);
+ nfsm_chain_add_stateid(error, &nmreq, sid);
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+ error = nfs_request2(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, thd, cred, &si, flags, &nmrep, &xid, &status);
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_DELEGRETURN);
+nfsmout:
+ nfsm_chain_cleanup(&nmreq);
+ nfsm_chain_cleanup(&nmrep);
+ return (error);
+}
+
+
+/*
+ * NFS read call.
+ * Just call nfs_bioread() to do the work.
+ *
+ * Note: the exec code paths have a tendency to call VNOP_READ (and VNOP_MMAP)
+ * without first calling VNOP_OPEN, so we make sure the file is open here.
+ */
+int
+nfs_vnop_read(
+ struct vnop_read_args /* {
+ struct vnodeop_desc *a_desc;
+ vnode_t a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ vfs_context_t a_context;
+ } */ *ap)
+{
+ vnode_t vp = ap->a_vp;
+ vfs_context_t ctx = ap->a_context;
+ nfsnode_t np;
+ struct nfsmount *nmp;
+ struct nfs_open_owner *noop;
+ struct nfs_open_file *nofp;
+ int error;
+
+ if (vnode_vtype(ap->a_vp) != VREG)
+ return (vnode_vtype(vp) == VDIR) ? EISDIR : EPERM;
+
+ np = VTONFS(vp);
+ nmp = NFSTONMP(np);
+ if (!nmp)
+ return (ENXIO);
+ if (np->n_flag & NREVOKE)
+ return (EIO);
+
+ noop = nfs_open_owner_find(nmp, vfs_context_ucred(ctx), 1);
+ if (!noop)
+ return (ENOMEM);
+restart:
+ error = nfs_open_file_find(np, noop, &nofp, 0, 0, 1);
+ if (!error && (nofp->nof_flags & NFS_OPEN_FILE_LOST)) {
+ NP(np, "nfs_vnop_read: LOST %d", kauth_cred_getuid(noop->noo_cred));
+ error = EIO;
+ }
+ if (!error && (nofp->nof_flags & NFS_OPEN_FILE_REOPEN)) {
+ error = nfs4_reopen(nofp, vfs_context_thread(ctx));
+ nofp = NULL;
+ if (!error)
+ goto restart;
+ }
+ if (error) {
+ nfs_open_owner_rele(noop);
+ return (error);
+ }
+ if (!nofp->nof_access) {
+ /* we don't have the file open, so open it for read access */
+ error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx));
+ if (error) {
+ nfs_open_owner_rele(noop);
+ return (error);
+ }
+ if (np->n_flag & NREVOKE) {
+ error = EIO;
+ nfs_mount_state_in_use_end(nmp, 0);
+ nfs_open_owner_rele(noop);
+ return (error);
+ }
+ error = nfs_open_file_set_busy(nofp, vfs_context_thread(ctx));
+ if (error)
+ nofp = NULL;
+ if (!error) {
+ if (nmp->nm_vers < NFS_VER4) {
+ /* NFS v2/v3 opens are always allowed - so just add it. */
+ nfs_open_file_add_open(nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, 0);
+ } else {
+ error = nfs4_open(np, nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, ctx);
+ }
+ }
+ if (!error)
+ nofp->nof_flags |= NFS_OPEN_FILE_NEEDCLOSE;
+ if (nofp)
+ nfs_open_file_clear_busy(nofp);
+ if (nfs_mount_state_in_use_end(nmp, error)) {
+ nofp = NULL;
+ goto restart;
+ }
+ }
+ nfs_open_owner_rele(noop);
+ if (error)
+ return (error);
+ return (nfs_bioread(VTONFS(ap->a_vp), ap->a_uio, ap->a_ioflag, ap->a_context));
+}
+
+/*
+ * Note: the NFSv4 CREATE RPC is for everything EXCEPT regular files.
+ * Files are created using the NFSv4 OPEN RPC. So we must open the
+ * file to create it and then close it.
+ */
+int
+nfs4_vnop_create(
+ struct vnop_create_args /* {
+ struct vnodeop_desc *a_desc;
+ vnode_t a_dvp;
+ vnode_t *a_vpp;
+ struct componentname *a_cnp;
+ struct vnode_attr *a_vap;
+ vfs_context_t a_context;
+ } */ *ap)
+{
+ vfs_context_t ctx = ap->a_context;
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode_attr *vap = ap->a_vap;
+ vnode_t dvp = ap->a_dvp;
+ vnode_t *vpp = ap->a_vpp;
+ struct nfsmount *nmp;
+ nfsnode_t np;
+ int error = 0, busyerror = 0, accessMode, denyMode;
+ struct nfs_open_owner *noop = NULL;
+ struct nfs_open_file *newnofp = NULL, *nofp = NULL;
+
+ nmp = VTONMP(dvp);
+ if (!nmp)
+ return (ENXIO);
+
+ if (vap)
+ nfs_avoid_needless_id_setting_on_create(VTONFS(dvp), vap, ctx);
+
+ noop = nfs_open_owner_find(nmp, vfs_context_ucred(ctx), 1);
+ if (!noop)
+ return (ENOMEM);
+
+restart:
+ error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx));
+ if (error) {
+ nfs_open_owner_rele(noop);
+ return (error);
+ }
+
+ /* grab a provisional, nodeless open file */
+ error = nfs_open_file_find(NULL, noop, &newnofp, 0, 0, 1);
+ if (!error && (newnofp->nof_flags & NFS_OPEN_FILE_LOST)) {
+ printf("nfs_vnop_create: LOST\n");
+ error = EIO;
+ }
+ if (!error && (newnofp->nof_flags & NFS_OPEN_FILE_REOPEN)) {
+ /* This shouldn't happen given that this is a new, nodeless nofp */
+ nfs_mount_state_in_use_end(nmp, 0);
+ error = nfs4_reopen(newnofp, vfs_context_thread(ctx));
+ nfs_open_file_destroy(newnofp);
+ newnofp = NULL;
+ if (!error)
+ goto restart;
+ }
+ if (!error)
+ error = nfs_open_file_set_busy(newnofp, vfs_context_thread(ctx));
+ if (error) {
+ if (newnofp)
+ nfs_open_file_destroy(newnofp);
+ newnofp = NULL;
+ goto out;
+ }
+
+ /*
+ * We're just trying to create the file.
+ * We'll create/open it RW, and set NFS_OPEN_FILE_CREATE.
+ */
+ accessMode = NFS_OPEN_SHARE_ACCESS_BOTH;
+ denyMode = NFS_OPEN_SHARE_DENY_NONE;
+
+ /* Do the open/create */
+ error = nfs4_open_rpc(newnofp, ctx, cnp, vap, dvp, vpp, NFS_OPEN_CREATE, accessMode, denyMode);
+ if ((error == EACCES) && vap && !(vap->va_vaflags & VA_EXCLUSIVE) &&
+ VATTR_IS_ACTIVE(vap, va_mode) && !(vap->va_mode & S_IWUSR)) {
+ /*
+ * Hmm... it looks like we may have a situation where the request was
+ * retransmitted because we didn't get the first response which successfully
+ * created/opened the file and then the second time we were denied the open
+ * because the mode the file was created with doesn't allow write access.
+ *
+ * We'll try to work around this by temporarily updating the mode and
+ * retrying the open.
+ */
+ struct vnode_attr vattr;
+
+ /* first make sure it's there */
+ int error2 = nfs_lookitup(VTONFS(dvp), cnp->cn_nameptr, cnp->cn_namelen, ctx, &np);
+ if (!error2 && np) {
+ nfs_node_unlock(np);
+ *vpp = NFSTOV(np);
+ if (vnode_vtype(NFSTOV(np)) == VREG) {
+ VATTR_INIT(&vattr);
+ VATTR_SET(&vattr, va_mode, (vap->va_mode | S_IWUSR));
+ if (!nfs4_setattr_rpc(np, &vattr, ctx)) {
+ error2 = nfs4_open_rpc(newnofp, ctx, cnp, NULL, dvp, vpp, NFS_OPEN_NOCREATE, accessMode, denyMode);
+ VATTR_INIT(&vattr);
+ VATTR_SET(&vattr, va_mode, vap->va_mode);
+ nfs4_setattr_rpc(np, &vattr, ctx);
+ if (!error2)
+ error = 0;
+ }
+ }
+ if (error) {
+ vnode_put(*vpp);
+ *vpp = NULL;
+ }
+ }
+ }
+ if (!error && !*vpp) {
+ printf("nfs4_open_rpc returned without a node?\n");
+ /* Hmmm... with no node, we have no filehandle and can't close it */
+ error = EIO;
+ }
+ if (error) {
+ /* need to cleanup our temporary nofp */
+ nfs_open_file_clear_busy(newnofp);
+ nfs_open_file_destroy(newnofp);
+ newnofp = NULL;
+ goto out;
+ }
+ /* After we have a node, add our open file struct to the node */
+ np = VTONFS(*vpp);
+ nfs_open_file_add_open(newnofp, accessMode, denyMode, 0);
+ nofp = newnofp;
+ error = nfs_open_file_find_internal(np, noop, &nofp, 0, 0, 0);
+ if (error) {
+ /* This shouldn't happen, because we passed in a new nofp to use. */
+ printf("nfs_open_file_find_internal failed! %d\n", error);
+ goto out;
+ } else if (nofp != newnofp) {
+ /*
+ * Hmm... an open file struct already exists.
+ * Mark the existing one busy and merge our open into it.
+ * Then destroy the one we created.
+ * Note: there's no chance of an open confict because the
+ * open has already been granted.
+ */
+ busyerror = nfs_open_file_set_busy(nofp, NULL);
+ nfs_open_file_add_open(nofp, accessMode, denyMode, 0);
+ nofp->nof_stateid = newnofp->nof_stateid;
+ if (newnofp->nof_flags & NFS_OPEN_FILE_POSIXLOCK)
+ nofp->nof_flags |= NFS_OPEN_FILE_POSIXLOCK;
+ nfs_open_file_clear_busy(newnofp);
+ nfs_open_file_destroy(newnofp);
+ }
+ newnofp = NULL;
+ /* mark the node as holding a create-initiated open */
+ nofp->nof_flags |= NFS_OPEN_FILE_CREATE;
+ nofp->nof_creator = current_thread();
+out:
+ if (nofp && !busyerror)
+ nfs_open_file_clear_busy(nofp);
+ if (nfs_mount_state_in_use_end(nmp, error)) {
+ nofp = newnofp = NULL;
+ busyerror = 0;
+ goto restart;
+ }
+ if (noop)
+ nfs_open_owner_rele(noop);
+ return (error);
+}
+
+/*
+ * Note: the NFSv4 CREATE RPC is for everything EXCEPT regular files.
+ */
+int
+nfs4_create_rpc(
+ vfs_context_t ctx,
+ nfsnode_t dnp,
+ struct componentname *cnp,
+ struct vnode_attr *vap,
+ int type,
+ char *link,
+ nfsnode_t *npp)
+{
+ struct nfsmount *nmp;
+ struct nfs_vattr nvattr;
+ int error = 0, create_error = EIO, lockerror = ENOENT, busyerror = ENOENT, status;
+ int nfsvers, namedattrs, numops;
+ u_int64_t xid, savedxid = 0;
+ nfsnode_t np = NULL;
+ vnode_t newvp = NULL;
+ struct nfsm_chain nmreq, nmrep;
+ uint32_t bitmap[NFS_ATTR_BITMAP_LEN], bmlen;
+ const char *tag;
+ nfs_specdata sd;
+ fhandle_t fh;
+ struct nfsreq rq, *req = &rq;
+ struct nfs_dulookup dul;
+ struct nfsreq_secinfo_args si;
+
+ nmp = NFSTONMP(dnp);
+ if (!nmp)
+ return (ENXIO);
+ nfsvers = nmp->nm_vers;
+ namedattrs = (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_NAMED_ATTR);
+ if (dnp->n_vattr.nva_flags & NFS_FFLAG_TRIGGER_REFERRAL)
+ return (EINVAL);
+
+ sd.specdata1 = sd.specdata2 = 0;
+
+ switch (type) {
+ case NFLNK:
+ tag = "symlink";
+ break;
+ case NFBLK:
+ case NFCHR:
+ tag = "mknod";
+ if (!VATTR_IS_ACTIVE(vap, va_rdev))
+ return (EINVAL);
+ sd.specdata1 = major(vap->va_rdev);
+ sd.specdata2 = minor(vap->va_rdev);
+ break;
+ case NFSOCK:
+ case NFFIFO:
+ tag = "mknod";
+ break;
+ case NFDIR:
+ tag = "mkdir";
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ nfs_avoid_needless_id_setting_on_create(dnp, vap, ctx);
+
+ error = busyerror = nfs_node_set_busy(dnp, vfs_context_thread(ctx));
+ if (!namedattrs)
+ nfs_dulookup_init(&dul, dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx);
+
+ NFSREQ_SECINFO_SET(&si, dnp, NULL, 0, NULL, 0);
+ NVATTR_INIT(&nvattr);
+ nfsm_chain_null(&nmreq);
+ nfsm_chain_null(&nmrep);
+
+ // PUTFH, SAVEFH, CREATE, GETATTR(FH), RESTOREFH, GETATTR
+ numops = 6;
+ nfsm_chain_build_alloc_init(error, &nmreq, 66 * NFSX_UNSIGNED);
+ nfsm_chain_add_compound_header(error, &nmreq, tag, numops);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
+ nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_SAVEFH);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_CREATE);
+ nfsm_chain_add_32(error, &nmreq, type);
+ if (type == NFLNK) {
+ nfsm_chain_add_name(error, &nmreq, link, strlen(link), nmp);
+ } else if ((type == NFBLK) || (type == NFCHR)) {
+ nfsm_chain_add_32(error, &nmreq, sd.specdata1);
+ nfsm_chain_add_32(error, &nmreq, sd.specdata2);
+ }
+ nfsm_chain_add_name(error, &nmreq, cnp->cn_nameptr, cnp->cn_namelen, nmp);
+ nfsm_chain_add_fattr4(error, &nmreq, vap, nmp);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
+ NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
+ nfsm_chain_add_bitmap_supported(error, &nmreq, bitmap, nmp, NULL);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_RESTOREFH);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
+ nfsm_chain_add_bitmap_supported(error, &nmreq, nfs_getattr_bitmap, nmp, dnp);
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+
+ error = nfs_request_async(dnp, NULL, &nmreq, NFSPROC4_COMPOUND,
+ vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
+ if (!error) {
+ if (!namedattrs)
+ nfs_dulookup_start(&dul, dnp, ctx);
+ error = nfs_request_async_finish(req, &nmrep, &xid, &status);
+ }
+
+ if ((lockerror = nfs_node_lock(dnp)))
+ error = lockerror;
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_SAVEFH);
+ nfsmout_if(error);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_CREATE);
+ nfsm_chain_check_change_info(error, &nmrep, dnp);
+ bmlen = NFS_ATTR_BITMAP_LEN;
+ nfsm_chain_get_bitmap(error, &nmrep, bitmap, bmlen);
+ /* At this point if we have no error, the object was created. */
+ /* if we don't get attributes, then we should lookitup. */
+ create_error = error;
+ nfsmout_if(error);
+ nfs_vattr_set_supported(bitmap, vap);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
+ nfsmout_if(error);
+ error = nfs4_parsefattr(&nmrep, NULL, &nvattr, &fh, NULL, NULL);
+ nfsmout_if(error);
+ if (!NFS_BITMAP_ISSET(nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
+ printf("nfs: create/%s didn't return filehandle? %s\n", tag, cnp->cn_nameptr);
+ error = EBADRPC;
+ goto nfsmout;
+ }
+ /* directory attributes: if we don't get them, make sure to invalidate */
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_RESTOREFH);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
+ savedxid = xid;
+ nfsm_chain_loadattr(error, &nmrep, dnp, nfsvers, &xid);
+ if (error)
+ NATTRINVALIDATE(dnp);
+
+nfsmout:
+ nfsm_chain_cleanup(&nmreq);
+ nfsm_chain_cleanup(&nmrep);
+
+ if (!lockerror) {
+ if (!create_error && (dnp->n_flag & NNEGNCENTRIES)) {
+ dnp->n_flag &= ~NNEGNCENTRIES;
+ cache_purge_negatives(NFSTOV(dnp));
+ }
+ dnp->n_flag |= NMODIFIED;
+ nfs_node_unlock(dnp);
+ /* nfs_getattr() will check changed and purge caches */
+ nfs_getattr(dnp, NULL, ctx, NGA_CACHED);
+ }
+
+ if (!error && fh.fh_len) {
+ /* create the vnode with the filehandle and attributes */
+ xid = savedxid;
+ error = nfs_nget(NFSTOMP(dnp), dnp, cnp, fh.fh_data, fh.fh_len, &nvattr, &xid, rq.r_auth, NG_MAKEENTRY, &np);
+ if (!error)
+ newvp = NFSTOV(np);
+ }
+ NVATTR_CLEANUP(&nvattr);
+
+ if (!namedattrs)
+ nfs_dulookup_finish(&dul, dnp, ctx);
+
+ /*
+ * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry
+ * if we can succeed in looking up the object.
+ */
+ if ((create_error == EEXIST) || (!create_error && !newvp)) {
+ error = nfs_lookitup(dnp, cnp->cn_nameptr, cnp->cn_namelen, ctx, &np);
+ if (!error) {
+ newvp = NFSTOV(np);
+ if (vnode_vtype(newvp) != nfstov_type(type, nfsvers))
+ error = EEXIST;
+ }
+ }
+ if (!busyerror)
+ nfs_node_clear_busy(dnp);
+ if (error) {
+ if (newvp) {
+ nfs_node_unlock(np);
+ vnode_put(newvp);
+ }
+ } else {
+ nfs_node_unlock(np);
+ *npp = np;
+ }
+ return (error);
+}
+
+int
+nfs4_vnop_mknod(
+ struct vnop_mknod_args /* {
+ struct vnodeop_desc *a_desc;
+ vnode_t a_dvp;
+ vnode_t *a_vpp;
+ struct componentname *a_cnp;
+ struct vnode_attr *a_vap;
+ vfs_context_t a_context;
+ } */ *ap)
+{
+ nfsnode_t np = NULL;
+ struct nfsmount *nmp;
+ int error;
+
+ nmp = VTONMP(ap->a_dvp);
+ if (!nmp)
+ return (ENXIO);
+
+ if (!VATTR_IS_ACTIVE(ap->a_vap, va_type))
+ return (EINVAL);
+ switch (ap->a_vap->va_type) {
+ case VBLK:
+ case VCHR:
+ case VFIFO:
+ case VSOCK:
+ break;
+ default:
+ return (ENOTSUP);
+ }
+
+ error = nfs4_create_rpc(ap->a_context, VTONFS(ap->a_dvp), ap->a_cnp, ap->a_vap,
+ vtonfs_type(ap->a_vap->va_type, nmp->nm_vers), NULL, &np);
+ if (!error)
+ *ap->a_vpp = NFSTOV(np);
+ return (error);
+}
+
+int
+nfs4_vnop_mkdir(
+ struct vnop_mkdir_args /* {
+ struct vnodeop_desc *a_desc;
+ vnode_t a_dvp;
+ vnode_t *a_vpp;
+ struct componentname *a_cnp;
+ struct vnode_attr *a_vap;
+ vfs_context_t a_context;
+ } */ *ap)
+{
+ nfsnode_t np = NULL;
+ int error;
+
+ error = nfs4_create_rpc(ap->a_context, VTONFS(ap->a_dvp), ap->a_cnp, ap->a_vap,
+ NFDIR, NULL, &np);
+ if (!error)
+ *ap->a_vpp = NFSTOV(np);
+ return (error);
+}
+
+int
+nfs4_vnop_symlink(
+ struct vnop_symlink_args /* {
+ struct vnodeop_desc *a_desc;
+ vnode_t a_dvp;
+ vnode_t *a_vpp;
+ struct componentname *a_cnp;