/* * XXX: can't use callremove now because can't catch setbacks with * it due to lack of a pnode argument. */ static int puffs_vnop_remove(struct vop_nremove_args *ap) { PUFFS_MSG_VARS(vn, remove); struct vnode *dvp = ap->a_dvp; struct vnode *vp; struct puffs_node *dpn = VPTOPP(dvp); struct puffs_node *pn; struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; struct mount *mp = dvp->v_mount; struct puffs_mount *pmp = MPTOPUFFSMP(mp); int error; if (!EXISTSOP(pmp, REMOVE)) return EOPNOTSUPP; error = vget(dvp, LK_EXCLUSIVE); if (error != 0) { DPRINTF(("puffs_vnop_remove: EAGAIN on parent vnode %p %s\n", dvp, ncp->nc_name)); return EAGAIN; } error = cache_vget(nch, cred, LK_EXCLUSIVE, &vp); if (error != 0) { DPRINTF(("puffs_vnop_remove: cache_vget error: %p %s\n", dvp, ncp->nc_name)); return EAGAIN; } if (vp->v_type == VDIR) { error = EISDIR; goto out; } pn = VPTOPP(vp); PUFFS_MSG_ALLOC(vn, remove); remove_msg->pvnr_cookie_targ = VPTOPNC(vp); puffs_makecn(&remove_msg->pvnr_cn, &remove_msg->pvnr_cn_cred, ncp, cred); puffs_msg_setinfo(park_remove, PUFFSOP_VN, PUFFS_VN_REMOVE, VPTOPNC(dvp)); puffs_msg_enqueue(pmp, park_remove); error = puffs_msg_wait2(pmp, park_remove, dpn, pn); PUFFS_MSG_RELEASE(remove); error = checkerr(pmp, error, __func__); out: vput(dvp); vn_unlock(vp); if (error == 0) cache_unlink(nch); vrele(vp); return error; }
static int puffs_vnop_link(struct vop_nlink_args *ap) { PUFFS_MSG_VARS(vn, link); struct vnode *dvp = ap->a_dvp; struct vnode *vp = ap->a_vp; struct puffs_node *dpn = VPTOPP(dvp); struct puffs_node *pn = VPTOPP(vp); struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount); struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; int error; if (!EXISTSOP(pmp, LINK)) return EOPNOTSUPP; if (vp->v_mount != dvp->v_mount) return EXDEV; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { DPRINTF(("puffs_vnop_link: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name)); return EAGAIN; } PUFFS_MSG_ALLOC(vn, link); link_msg->pvnr_cookie_targ = VPTOPNC(vp); puffs_makecn(&link_msg->pvnr_cn, &link_msg->pvnr_cn_cred, ncp, cred); puffs_msg_setinfo(park_link, PUFFSOP_VN, PUFFS_VN_LINK, VPTOPNC(dvp)); puffs_msg_enqueue(pmp, park_link); error = puffs_msg_wait2(pmp, park_link, dpn, pn); PUFFS_MSG_RELEASE(link); error = checkerr(pmp, error, __func__); /* * XXX: stay in touch with the cache. I don't like this, but * don't have a better solution either. See also puffs_rename(). */ if (error == 0) { puffs_updatenode(pn, PUFFS_UPDATECTIME); } vput(dvp); if (error == 0) { cache_setunresolved(nch); cache_setvp(nch, vp); } return error; }
static int puffs_vnop_advlock(struct vop_advlock_args *ap) { PUFFS_MSG_VARS(vn, advlock); struct vnode *vp = ap->a_vp; struct puffs_node *pn = VPTOPP(vp); struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); int error; if (!EXISTSOP(pmp, ADVLOCK)) return lf_advlock(ap, &pn->pn_lockf, vp->v_filesize); PUFFS_MSG_ALLOC(vn, advlock); (void)memcpy(&advlock_msg->pvnr_fl, ap->a_fl, sizeof(advlock_msg->pvnr_fl)); advlock_msg->pvnr_id = ap->a_id; advlock_msg->pvnr_op = ap->a_op; advlock_msg->pvnr_flags = ap->a_flags; puffs_msg_setinfo(park_advlock, PUFFSOP_VN, PUFFS_VN_ADVLOCK, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_advlock, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); PUFFS_MSG_RELEASE(advlock); return error; }
static int puffs_vnop_print(struct vop_print_args *ap) { PUFFS_MSG_VARS(vn, print); struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); struct puffs_node *pn = VPTOPP(vp); int error; /* kernel portion */ kprintf("tag VT_PUFFS, vnode %p, puffs node: %p,\n" "\tuserspace cookie: %p", vp, pn, pn->pn_cookie); if (vp->v_type == VFIFO) fifo_printinfo(vp); kprintf("\n"); /* userspace portion */ if (EXISTSOP(pmp, PRINT)) { PUFFS_MSG_ALLOC(vn, print); puffs_msg_setinfo(park_print, PUFFSOP_VN, PUFFS_VN_PRINT, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_print, vp->v_data, NULL, error); PUFFS_MSG_RELEASE(print); } return 0; }
static int puffs_vnop_mknod(struct vop_nmknod_args *ap) { PUFFS_MSG_VARS(vn, mknod); struct vnode *dvp = ap->a_dvp; struct vattr *vap = ap->a_vap; struct puffs_node *dpn = VPTOPP(dvp); struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; struct mount *mp = dvp->v_mount; struct puffs_mount *pmp = MPTOPUFFSMP(mp); int error; if (!EXISTSOP(pmp, MKNOD)) return EOPNOTSUPP; DPRINTF(("puffs_mknod: dvp %p, name: %s\n", dvp, ncp->nc_name)); if (vap->va_type != VFIFO) return EINVAL; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { DPRINTF(("puffs_vnop_mknod: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name)); return EAGAIN; } PUFFS_MSG_ALLOC(vn, mknod); puffs_makecn(&mknod_msg->pvnr_cn, &mknod_msg->pvnr_cn_cred, ncp, cred); mknod_msg->pvnr_va = *ap->a_vap; puffs_msg_setinfo(park_mknod, PUFFSOP_VN, PUFFS_VN_MKNOD, VPTOPNC(dvp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mknod, dvp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) goto out; error = puffs_newnode(mp, dvp, ap->a_vpp, mknod_msg->pvnr_newnode, vap->va_type); if (error) puffs_abortbutton(pmp, PUFFS_ABORT_MKNOD, dpn->pn_cookie, mknod_msg->pvnr_newnode, ncp, cred); out: vput(dvp); if (!error) { cache_setunresolved(nch); cache_setvp(nch, *ap->a_vpp); } PUFFS_MSG_RELEASE(mknod); return error; }
static int puffs_vnop_symlink(struct vop_nsymlink_args *ap) { PUFFS_MSG_VARS(vn, symlink); struct vnode *dvp = ap->a_dvp; struct puffs_node *dpn = VPTOPP(dvp); struct mount *mp = dvp->v_mount; struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount); struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; int error; if (!EXISTSOP(pmp, SYMLINK)) return EOPNOTSUPP; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { DPRINTF(("puffs_vnop_symlink: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name)); return EAGAIN; } *ap->a_vpp = NULL; PUFFS_MSG_ALLOC(vn, symlink); puffs_makecn(&symlink_msg->pvnr_cn, &symlink_msg->pvnr_cn_cred, ncp, cred); symlink_msg->pvnr_va = *ap->a_vap; (void)strlcpy(symlink_msg->pvnr_link, ap->a_target, sizeof(symlink_msg->pvnr_link)); puffs_msg_setinfo(park_symlink, PUFFSOP_VN, PUFFS_VN_SYMLINK, VPTOPNC(dvp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_symlink, dvp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) goto out; error = puffs_newnode(mp, dvp, ap->a_vpp, symlink_msg->pvnr_newnode, VLNK); if (error) puffs_abortbutton(pmp, PUFFS_ABORT_SYMLINK, dpn->pn_cookie, symlink_msg->pvnr_newnode, ncp, cred); out: vput(dvp); PUFFS_MSG_RELEASE(symlink); if (!error) { cache_setunresolved(nch); cache_setvp(nch, *ap->a_vpp); } return error; }
void puffs_gop_markupdate(struct vnode *vp, int flags) { int uflags = 0; if (flags & GOP_UPDATE_ACCESSED) uflags |= PUFFS_UPDATEATIME; if (flags & GOP_UPDATE_MODIFIED) uflags |= PUFFS_UPDATEMTIME; puffs_updatenode(VPTOPP(vp), uflags, 0); }
static int puffs_vnop_getattr(struct vop_getattr_args *ap) { PUFFS_MSG_VARS(vn, getattr); struct vnode *vp = ap->a_vp; struct mount *mp = vp->v_mount; struct puffs_mount *pmp = MPTOPUFFSMP(mp); struct vattr *vap, *rvap; struct puffs_node *pn = VPTOPP(vp); int error = 0; if (vp->v_type == VBLK || vp->v_type == VCHR) return ENOTSUP; vap = ap->a_vap; PUFFS_MSG_ALLOC(vn, getattr); vattr_null(&getattr_msg->pvnr_va); puffs_credcvt(&getattr_msg->pvnr_cred, curproc->p_ucred); puffs_msg_setinfo(park_getattr, PUFFSOP_VN, PUFFS_VN_GETATTR, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_getattr, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) goto out; rvap = &getattr_msg->pvnr_va; (void) memcpy(vap, rvap, sizeof(struct vattr)); vap->va_fsid = mp->mnt_stat.f_fsid.val[0]; if (pn->pn_stat & PNODE_METACACHE_ATIME) vap->va_atime = pn->pn_mc_atime; if (pn->pn_stat & PNODE_METACACHE_CTIME) vap->va_ctime = pn->pn_mc_ctime; if (pn->pn_stat & PNODE_METACACHE_MTIME) vap->va_mtime = pn->pn_mc_mtime; if (pn->pn_stat & PNODE_METACACHE_SIZE) { vap->va_size = pn->pn_mc_size; } else { if (rvap->va_size != VNOVAL && vp->v_type != VBLK && vp->v_type != VCHR) { pn->pn_serversize = rvap->va_size; if (vp->v_type == VREG) puffs_meta_setsize(vp, rvap->va_size, 0); } } out: PUFFS_MSG_RELEASE(getattr); return error; }
/* * always FAF, we don't really care if the server wants to fail to * reclaim the node or not */ static int puffs_vnop_reclaim(struct vop_reclaim_args *ap) { struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); struct puffs_node *pnode = VPTOPP(vp); boolean_t notifyserver = TRUE; vinvalbuf(vp, V_SAVE, 0, 0); /* * first things first: check if someone is trying to reclaim the * root vnode. do not allow that to travel to userspace. * Note that we don't need to take the lock similarly to * puffs_root(), since there is only one of us. */ if (vp->v_flag & VROOT) { lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE); KKASSERT(pmp->pmp_root != NULL); pmp->pmp_root = NULL; lockmgr(&pmp->pmp_lock, LK_RELEASE); notifyserver = FALSE; } /* * purge info from kernel before issueing FAF, since we * don't really know when we'll get around to it after * that and someone might race us into node creation */ lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE); LIST_REMOVE(pnode, pn_hashent); lockmgr(&pmp->pmp_lock, LK_RELEASE); if (notifyserver) callreclaim(MPTOPUFFSMP(vp->v_mount), VPTOPNC(vp)); puffs_putvnode(vp); vp->v_data = NULL; return 0; }
static int puffs_vnop_fsync(struct vop_fsync_args *ap) { PUFFS_MSG_VARS(vn, fsync); struct vnode *vp = ap->a_vp; int waitfor = ap->a_waitfor; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); struct puffs_node *pn = VPTOPP(vp); int error, dofaf; error = flushvncache(vp, waitfor); if (error) return error; /* * HELLO! We exit already here if the user server does not * support fsync OR if we should call fsync for a node which * has references neither in the kernel or the fs server. * Otherwise we continue to issue fsync() forward. */ if (!EXISTSOP(pmp, FSYNC) || (pn->pn_stat & PNODE_DYING)) return 0; dofaf = (waitfor & MNT_WAIT) == 0 || (waitfor & MNT_LAZY) != 0; PUFFS_MSG_ALLOC(vn, fsync); if (dofaf) puffs_msg_setfaf(park_fsync); fsync_msg->pvnr_flags = ap->a_flags; puffs_msg_setinfo(park_fsync, PUFFSOP_VN, PUFFS_VN_FSYNC, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_fsync, vp->v_data, NULL, error); PUFFS_MSG_RELEASE(fsync); error = checkerr(pmp, error, __func__); return error; }
/* XXX: callinactive can't setback */ static int puffs_vnop_inactive(struct vop_inactive_args *ap) { PUFFS_MSG_VARS(vn, inactive); struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); struct puffs_node *pnode = VPTOPP(vp); flushvncache(vp, MNT_NOWAIT); if (doinact(pmp, pnode->pn_stat & PNODE_DOINACT)) { /* * do not wait for reply from userspace, otherwise it may * deadlock. */ PUFFS_MSG_ALLOC(vn, inactive); puffs_msg_setfaf(park_inactive); puffs_msg_setinfo(park_inactive, PUFFSOP_VN, PUFFS_VN_INACTIVE, VPTOPNC(vp)); puffs_msg_enqueue(pmp, park_inactive); PUFFS_MSG_RELEASE(inactive); } pnode->pn_stat &= ~PNODE_DOINACT; /* * file server thinks it's gone? then don't be afraid care, * node's life was already all it would ever be */ if (pnode->pn_stat & PNODE_NOREFS) { pnode->pn_stat |= PNODE_DYING; vrecycle(vp); } return 0; }
static int flushvncache(struct vnode *vp, int waitfor) { struct puffs_node *pn = VPTOPP(vp); struct vattr va; int error = 0; /* flush out information from our metacache, see vop_setattr */ if (pn->pn_stat & PNODE_METACACHE_MASK && (pn->pn_stat & PNODE_DYING) == 0) { vattr_null(&va); error = dosetattr(vp, &va, FSCRED, SETATTR_CHSIZE | (waitfor == MNT_NOWAIT ? 0 : SETATTR_ASYNC)); if (error) return error; } /* * flush pages to avoid being overly dirty */ vfsync(vp, waitfor, 0, NULL, NULL); return error; }
static int dosetattr(struct vnode *vp, struct vattr *vap, struct ucred *cred, int flags) { PUFFS_MSG_VARS(vn, setattr); struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); struct puffs_node *pn = VPTOPP(vp); int error = 0; if ((vp->v_mount->mnt_flag & MNT_RDONLY) && (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL)) return EROFS; if ((vp->v_mount->mnt_flag & MNT_RDONLY) && vp->v_type == VREG && vap->va_size != VNOVAL) return EROFS; /* * Flush metacache first. If we are called with some explicit * parameters, treat them as information overriding metacache * information. */ if (pn->pn_stat & PNODE_METACACHE_MASK) { if ((pn->pn_stat & PNODE_METACACHE_ATIME) && vap->va_atime.tv_sec == VNOVAL) vap->va_atime = pn->pn_mc_atime; if ((pn->pn_stat & PNODE_METACACHE_CTIME) && vap->va_ctime.tv_sec == VNOVAL) vap->va_ctime = pn->pn_mc_ctime; if ((pn->pn_stat & PNODE_METACACHE_MTIME) && vap->va_mtime.tv_sec == VNOVAL) vap->va_mtime = pn->pn_mc_mtime; if ((pn->pn_stat & PNODE_METACACHE_SIZE) && vap->va_size == VNOVAL) vap->va_size = pn->pn_mc_size; pn->pn_stat &= ~PNODE_METACACHE_MASK; } PUFFS_MSG_ALLOC(vn, setattr); (void)memcpy(&setattr_msg->pvnr_va, vap, sizeof(struct vattr)); puffs_credcvt(&setattr_msg->pvnr_cred, cred); puffs_msg_setinfo(park_setattr, PUFFSOP_VN, PUFFS_VN_SETATTR, VPTOPNC(vp)); if (flags & SETATTR_ASYNC) puffs_msg_setfaf(park_setattr); puffs_msg_enqueue(pmp, park_setattr); if ((flags & SETATTR_ASYNC) == 0) error = puffs_msg_wait2(pmp, park_setattr, vp->v_data, NULL); PUFFS_MSG_RELEASE(setattr); if ((flags & SETATTR_ASYNC) == 0) { error = checkerr(pmp, error, __func__); if (error) return error; } else { error = 0; } if (vap->va_size != VNOVAL) { pn->pn_serversize = vap->va_size; if (flags & SETATTR_CHSIZE) puffs_meta_setsize(vp, vap->va_size, 0); } return 0; }
static int puffs_vnop_lookupdotdot(struct vop_nlookupdotdot_args *ap) { PUFFS_MSG_VARS(vn, lookupdotdot); struct puffs_mount *pmp = MPTOPUFFSMP(ap->a_dvp->v_mount); struct ucred *cred = ap->a_cred; struct vnode *vp, *dvp = ap->a_dvp; struct puffs_node *dpn; int error; *ap->a_vpp = NULL; DPRINTF(("puffs_lookupdotdot: vnode %p\n", dvp)); PUFFS_MSG_ALLOC(vn, lookupdotdot); puffs_credcvt(&lookupdotdot_msg->pvnr_cred, cred); puffs_msg_setinfo(park_lookupdotdot, PUFFSOP_VN, PUFFS_VN_LOOKUPDOTDOT, VPTOPNC(dvp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_lookupdotdot, dvp->v_data, NULL, error); DPRINTF(("puffs_lookupdotdot: return of the userspace, part %d\n", error)); if (error) { error = checkerr(pmp, error, __func__); goto out; } /* * Check that we don't get our node back, that would cause * a pretty obvious deadlock. */ dpn = VPTOPP(dvp); if (lookupdotdot_msg->pvnr_newnode == dpn->pn_cookie) { puffs_senderr(pmp, PUFFS_ERR_LOOKUP, EINVAL, "lookupdotdot produced the same cookie", lookupdotdot_msg->pvnr_newnode); error = EPROTO; goto out; } error = puffs_cookie2vnode(pmp, lookupdotdot_msg->pvnr_newnode, 1, &vp); if (error == PUFFS_NOSUCHCOOKIE) { error = puffs_getvnode(dvp->v_mount, lookupdotdot_msg->pvnr_newnode, VDIR, 0, &vp); if (error) { puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp), lookupdotdot_msg->pvnr_newnode, NULL, cred); goto out; } } else if (error) { puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp), lookupdotdot_msg->pvnr_newnode, NULL, cred); goto out; } *ap->a_vpp = vp; vn_unlock(vp); out: DPRINTF(("puffs_lookupdotdot: returning %d %p\n", error, *ap->a_vpp)); PUFFS_MSG_RELEASE(lookupdotdot); return error; }
static int puffs_vnop_rename(struct vop_nrename_args *ap) { PUFFS_MSG_VARS(vn, rename); struct nchandle *fnch = ap->a_fnch; struct nchandle *tnch = ap->a_tnch; struct vnode *fdvp = ap->a_fdvp; struct vnode *fvp = fnch->ncp->nc_vp; struct vnode *tdvp = ap->a_tdvp; struct vnode *tvp = tnch->ncp->nc_vp; struct ucred *cred = ap->a_cred; struct puffs_mount *pmp = MPTOPUFFSMP(fdvp->v_mount); int error; if (!EXISTSOP(pmp, RENAME)) return EOPNOTSUPP; error = vget(tdvp, LK_EXCLUSIVE); if (error != 0) { DPRINTF(("puffs_vnop_rename: EAGAIN on tdvp vnode %p %s\n", tdvp, tnch->ncp->nc_name)); return EAGAIN; } if (tvp != NULL) { error = vget(tvp, LK_EXCLUSIVE); if (error != 0) { DPRINTF(("puffs_vnop_rename: EAGAIN on tvp vnode %p %s\n", tvp, tnch->ncp->nc_name)); vput(tdvp); return EAGAIN; } } if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto out; } if (tvp) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } PUFFS_MSG_ALLOC(vn, rename); rename_msg->pvnr_cookie_src = VPTOPNC(fvp); rename_msg->pvnr_cookie_targdir = VPTOPNC(tdvp); if (tvp) rename_msg->pvnr_cookie_targ = VPTOPNC(tvp); else rename_msg->pvnr_cookie_targ = NULL; puffs_makecn(&rename_msg->pvnr_cn_src, &rename_msg->pvnr_cn_src_cred, fnch->ncp, cred); puffs_makecn(&rename_msg->pvnr_cn_targ, &rename_msg->pvnr_cn_targ_cred, tnch->ncp, cred); puffs_msg_setinfo(park_rename, PUFFSOP_VN, PUFFS_VN_RENAME, VPTOPNC(fdvp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_rename, fdvp->v_data, NULL, error); PUFFS_MSG_RELEASE(rename); error = checkerr(pmp, error, __func__); if (error == 0) puffs_updatenode(VPTOPP(fvp), PUFFS_UPDATECTIME); out: if (tvp != NULL) vn_unlock(tvp); if (tdvp != tvp) vn_unlock(tdvp); if (error == 0) cache_rename(fnch, tnch); if (tvp != NULL) vrele(tvp); vrele(tdvp); return error; }
/* * Begin vnode operations. * * A word from the keymaster about locks: generally we don't want * to use the vnode locks at all: it creates an ugly dependency between * the userlandia file server and the kernel. But we'll play along with * the kernel vnode locks for now. However, even currently we attempt * to release locks as early as possible. This is possible for some * operations which a) don't need a locked vnode after the userspace op * and b) return with the vnode unlocked. Theoretically we could * unlock-do op-lock for others and order the graph in userspace, but I * don't want to think of the consequences for the time being. */ static int puffs_vnop_lookup(struct vop_nresolve_args *ap) { PUFFS_MSG_VARS(vn, lookup); struct puffs_mount *pmp = MPTOPUFFSMP(ap->a_dvp->v_mount); struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; struct vnode *vp = NULL, *dvp = ap->a_dvp; struct puffs_node *dpn; int error; DPRINTF(("puffs_lookup: \"%s\", parent vnode %p\n", ncp->nc_name, dvp)); PUFFS_MSG_ALLOC(vn, lookup); puffs_makecn(&lookup_msg->pvnr_cn, &lookup_msg->pvnr_cn_cred, ncp, cred); puffs_msg_setinfo(park_lookup, PUFFSOP_VN, PUFFS_VN_LOOKUP, VPTOPNC(dvp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_lookup, dvp->v_data, NULL, error); DPRINTF(("puffs_lookup: return of the userspace, part %d\n", error)); if (error) { error = checkerr(pmp, error, __func__); if (error == ENOENT) cache_setvp(nch, NULL); goto out; } /* * Check that we don't get our parent node back, that would cause * a pretty obvious deadlock. */ dpn = VPTOPP(dvp); if (lookup_msg->pvnr_newnode == dpn->pn_cookie) { puffs_senderr(pmp, PUFFS_ERR_LOOKUP, EINVAL, "lookup produced parent cookie", lookup_msg->pvnr_newnode); error = EPROTO; goto out; } error = puffs_cookie2vnode(pmp, lookup_msg->pvnr_newnode, 1, &vp); if (error == PUFFS_NOSUCHCOOKIE) { error = puffs_getvnode(dvp->v_mount, lookup_msg->pvnr_newnode, lookup_msg->pvnr_vtype, lookup_msg->pvnr_size, &vp); if (error) { puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp), lookup_msg->pvnr_newnode, ncp, cred); goto out; } } else if (error) { puffs_abortbutton(pmp, PUFFS_ABORT_LOOKUP, VPTOPNC(dvp), lookup_msg->pvnr_newnode, ncp, cred); goto out; } out: if (!error && vp != NULL) { vn_unlock(vp); cache_setvp(nch, vp); vrele(vp); } DPRINTF(("puffs_lookup: returning %d\n", error)); PUFFS_MSG_RELEASE(lookup); return error; }