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_open(struct vop_open_args *ap) { PUFFS_MSG_VARS(vn, open); struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); int mode = ap->a_mode; int error; DPRINTF(("puffs_open: vp %p, mode 0x%x\n", vp, mode)); if (vp->v_type == VREG && mode & FWRITE && !EXISTSOP(pmp, WRITE)) ERROUT(EROFS); if (!EXISTSOP(pmp, OPEN)) ERROUT(0); PUFFS_MSG_ALLOC(vn, open); open_msg->pvnr_mode = mode; puffs_credcvt(&open_msg->pvnr_cred, ap->a_cred); puffs_msg_setinfo(park_open, PUFFSOP_VN, PUFFS_VN_OPEN, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_open, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); out: DPRINTF(("puffs_open: returning %d\n", error)); PUFFS_MSG_RELEASE(open); if (error) return error; return vop_stdopen(ap); }
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; }
/* * 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_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_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_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; }
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; }
static void callreclaim(struct puffs_mount *pmp, puffs_cookie_t ck) { PUFFS_MSG_VARS(vn, reclaim); if (!EXISTSOP(pmp, RECLAIM)) return; PUFFS_MSG_ALLOC(vn, reclaim); puffs_msg_setfaf(park_reclaim); puffs_msg_setinfo(park_reclaim, PUFFSOP_VN, PUFFS_VN_RECLAIM, ck); puffs_msg_enqueue(pmp, park_reclaim); PUFFS_MSG_RELEASE(reclaim); }
static void callinactive(struct puffs_mount *pmp, puffs_cookie_t ck, int iaflag) { int error; PUFFS_MSG_VARS(vn, inactive); if (doinact(pmp, iaflag)) { PUFFS_MSG_ALLOC(vn, inactive); puffs_msg_setinfo(park_inactive, PUFFSOP_VN, PUFFS_VN_INACTIVE, ck); PUFFS_MSG_ENQUEUEWAIT(pmp, park_inactive, error); PUFFS_MSG_RELEASE(inactive); } }
void puffs_senderr(struct puffs_mount *pmp, int type, int error, const char *str, puffs_cookie_t ck) { struct puffs_msgpark *park; struct puffs_error *perr; puffs_msgmem_alloc(sizeof(struct puffs_error), &park, (void *)&perr, 1); puffs_msg_setfaf(park); puffs_msg_setinfo(park, PUFFSOP_ERROR, type, ck); perr->perr_error = error; strlcpy(perr->perr_str, str, sizeof(perr->perr_str)); puffs_msg_enqueue(pmp, park); puffs_msgmem_release(park); }
static int callremove(struct puffs_mount *pmp, puffs_cookie_t dck, puffs_cookie_t ck, struct namecache *ncp, struct ucred *cred) { PUFFS_MSG_VARS(vn, remove); int error; PUFFS_MSG_ALLOC(vn, remove); remove_msg->pvnr_cookie_targ = ck; puffs_makecn(&remove_msg->pvnr_cn, &remove_msg->pvnr_cn_cred, ncp, cred); puffs_msg_setinfo(park_remove, PUFFSOP_VN, PUFFS_VN_REMOVE, dck); PUFFS_MSG_ENQUEUEWAIT(pmp, park_remove, error); PUFFS_MSG_RELEASE(remove); return checkerr(pmp, error, __func__); }
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; }
static int puffs_vnop_close(struct vop_close_args *ap) { PUFFS_MSG_VARS(vn, close); struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); vn_lock(vp, LK_UPGRADE | LK_RETRY); if (!EXISTSOP(pmp, CLOSE)) return vop_stdclose(ap); PUFFS_MSG_ALLOC(vn, close); puffs_msg_setfaf(park_close); close_msg->pvnr_fflag = ap->a_fflag; puffs_msg_setinfo(park_close, PUFFSOP_VN, PUFFS_VN_CLOSE, VPTOPNC(vp)); puffs_msg_enqueue(pmp, park_close); PUFFS_MSG_RELEASE(close); return vop_stdclose(ap); }
static int puffs_vnop_readlink(struct vop_readlink_args *ap) { PUFFS_MSG_VARS(vn, readlink); struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(ap->a_vp->v_mount); size_t linklen; int error; if (!EXISTSOP(pmp, READLINK)) return EOPNOTSUPP; PUFFS_MSG_ALLOC(vn, readlink); puffs_credcvt(&readlink_msg->pvnr_cred, ap->a_cred); linklen = sizeof(readlink_msg->pvnr_link); readlink_msg->pvnr_linklen = linklen; puffs_msg_setinfo(park_readlink, PUFFSOP_VN, PUFFS_VN_READLINK, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_readlink, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) goto out; /* bad bad user file server */ if (readlink_msg->pvnr_linklen > linklen) { puffs_senderr(pmp, PUFFS_ERR_READLINK, E2BIG, "linklen too big", VPTOPNC(ap->a_vp)); error = EPROTO; goto out; } error = uiomove(readlink_msg->pvnr_link, readlink_msg->pvnr_linklen, ap->a_uio); out: PUFFS_MSG_RELEASE(readlink); return error; }
static int puffs_vnop_access(struct vop_access_args *ap) { PUFFS_MSG_VARS(vn, access); struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); int mode = ap->a_mode; int error; if (mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: if ((vp->v_mount->mnt_flag & MNT_RDONLY) || !EXISTSOP(pmp, WRITE)) return EROFS; break; default: break; } } if (!EXISTSOP(pmp, ACCESS)) return 0; PUFFS_MSG_ALLOC(vn, access); access_msg->pvnr_mode = ap->a_mode; puffs_credcvt(&access_msg->pvnr_cred, ap->a_cred); puffs_msg_setinfo(park_access, PUFFSOP_VN, PUFFS_VN_ACCESS, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_access, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); PUFFS_MSG_RELEASE(access); return error; }
static int puffs_vnop_pathconf(struct vop_pathconf_args *ap) { PUFFS_MSG_VARS(vn, pathconf); struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); int error; if (!EXISTSOP(pmp, PATHCONF)) return EOPNOTSUPP; PUFFS_MSG_ALLOC(vn, pathconf); pathconf_msg->pvnr_name = ap->a_name; puffs_msg_setinfo(park_pathconf, PUFFSOP_VN, PUFFS_VN_PATHCONF, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_pathconf, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (!error) *ap->a_retval = pathconf_msg->pvnr_retval; PUFFS_MSG_RELEASE(pathconf); 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; }
/* * 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; }
int puffs_directread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred) { PUFFS_MSG_VARS(vn, read); struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); size_t tomove, argsize; int error; KKASSERT(vp->v_type == VREG); if (uio->uio_offset < 0) return EINVAL; if (uio->uio_resid == 0) return 0; read_msg = NULL; error = 0; /* std sanity */ if (uio->uio_resid == 0) return 0; if (uio->uio_offset < 0) return EINVAL; /* * in case it's not a regular file or we're operating * uncached, do read in the old-fashioned style, * i.e. explicit read operations */ tomove = PUFFS_TOMOVE(uio->uio_resid, pmp); argsize = sizeof(struct puffs_vnmsg_read); puffs_msgmem_alloc(argsize + tomove, &park_read, (void *)&read_msg, 1); error = 0; while (uio->uio_resid > 0) { tomove = PUFFS_TOMOVE(uio->uio_resid, pmp); memset(read_msg, 0, argsize); /* XXX: touser KASSERT */ RWARGS(read_msg, ioflag, tomove, uio->uio_offset, cred); puffs_msg_setinfo(park_read, PUFFSOP_VN, PUFFS_VN_READ, VPTOPNC(vp)); puffs_msg_setdelta(park_read, tomove); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_read, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) break; if (read_msg->pvnr_resid > tomove) { puffs_senderr(pmp, PUFFS_ERR_READ, E2BIG, "resid grew", VPTOPNC(vp)); error = EPROTO; break; } error = uiomove(read_msg->pvnr_data, tomove - read_msg->pvnr_resid, uio); /* * in case the file is out of juice, resid from * userspace is != 0. and the error-case is * quite obvious */ if (error || read_msg->pvnr_resid) break; } puffs_msgmem_release(park_read); return error; }
int puffs_directwrite(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred) { PUFFS_MSG_VARS(vn, write); struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); size_t tomove, argsize; int error, uflags; KKASSERT(vp->v_type == VREG); if (uio->uio_offset < 0) return EINVAL; if (uio->uio_resid == 0) return 0; error = uflags = 0; write_msg = NULL; /* tomove is non-increasing */ tomove = PUFFS_TOMOVE(uio->uio_resid, pmp); argsize = sizeof(struct puffs_vnmsg_write) + tomove; puffs_msgmem_alloc(argsize, &park_write, (void *)&write_msg,1); while (uio->uio_resid > 0) { /* move data to buffer */ tomove = PUFFS_TOMOVE(uio->uio_resid, pmp); memset(write_msg, 0, argsize); /* XXX: touser KASSERT */ RWARGS(write_msg, ioflag, tomove, uio->uio_offset, cred); error = uiomove(write_msg->pvnr_data, tomove, uio); if (error) break; /* move buffer to userspace */ puffs_msg_setinfo(park_write, PUFFSOP_VN, PUFFS_VN_WRITE, VPTOPNC(vp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_write, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) break; if (write_msg->pvnr_resid > tomove) { puffs_senderr(pmp, PUFFS_ERR_WRITE, E2BIG, "resid grew", VPTOPNC(vp)); error = EPROTO; break; } if (PUFFS_USE_PAGECACHE(pmp)) KKASSERT(vp->v_filesize >= uio->uio_offset); /* didn't move everything? bad userspace. bail */ if (write_msg->pvnr_resid != 0) { error = EIO; break; } } puffs_msgmem_release(park_write); 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; }
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_readdir(struct vop_readdir_args *ap) { PUFFS_MSG_VARS(vn, readdir); struct vnode *vp = ap->a_vp; struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount); size_t argsize, tomove, cookiemem, cookiesmax; struct uio *uio = ap->a_uio; size_t howmuch, resid; int error; if (!EXISTSOP(pmp, READDIR)) return EOPNOTSUPP; /* * ok, so we need: resid + cookiemem = maxreq * => resid + cookiesize * (resid/minsize) = maxreq * => resid + cookiesize/minsize * resid = maxreq * => (cookiesize/minsize + 1) * resid = maxreq * => resid = maxreq / (cookiesize/minsize + 1) * * Since cookiesize <= minsize and we're not very big on floats, * we approximate that to be 1. Therefore: * * resid = maxreq / 2; * * Well, at least we didn't have to use differential equations * or the Gram-Schmidt process. * * (yes, I'm very afraid of this) */ KKASSERT(CSIZE <= _DIRENT_RECLEN(1)); if (ap->a_cookies) { KKASSERT(ap->a_ncookies != NULL); if (pmp->pmp_args.pa_fhsize == 0) return EOPNOTSUPP; resid = PUFFS_TOMOVE(uio->uio_resid, pmp) / 2; cookiesmax = resid/_DIRENT_RECLEN(1); cookiemem = ALIGN(cookiesmax*CSIZE); /* play safe */ } else { resid = PUFFS_TOMOVE(uio->uio_resid, pmp); cookiesmax = 0; cookiemem = 0; } argsize = sizeof(struct puffs_vnmsg_readdir); tomove = resid + cookiemem; puffs_msgmem_alloc(argsize + tomove, &park_readdir, (void *)&readdir_msg, 1); puffs_credcvt(&readdir_msg->pvnr_cred, ap->a_cred); readdir_msg->pvnr_offset = uio->uio_offset; readdir_msg->pvnr_resid = resid; readdir_msg->pvnr_ncookies = cookiesmax; readdir_msg->pvnr_eofflag = 0; readdir_msg->pvnr_dentoff = cookiemem; puffs_msg_setinfo(park_readdir, PUFFSOP_VN, PUFFS_VN_READDIR, VPTOPNC(vp)); puffs_msg_setdelta(park_readdir, tomove); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_readdir, vp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) goto out; /* userspace is cheating? */ if (readdir_msg->pvnr_resid > resid) { puffs_senderr(pmp, PUFFS_ERR_READDIR, E2BIG, "resid grew", VPTOPNC(vp)); ERROUT(EPROTO); } if (readdir_msg->pvnr_ncookies > cookiesmax) { puffs_senderr(pmp, PUFFS_ERR_READDIR, E2BIG, "too many cookies", VPTOPNC(vp)); ERROUT(EPROTO); } /* check eof */ if (readdir_msg->pvnr_eofflag) *ap->a_eofflag = 1; /* bouncy-wouncy with the directory data */ howmuch = resid - readdir_msg->pvnr_resid; /* force eof if no data was returned (getcwd() needs this) */ if (howmuch == 0) { *ap->a_eofflag = 1; goto out; } error = uiomove(readdir_msg->pvnr_data + cookiemem, howmuch, uio); if (error) goto out; /* provide cookies to caller if so desired */ if (ap->a_cookies) { *ap->a_cookies = kmalloc(readdir_msg->pvnr_ncookies*CSIZE, M_TEMP, M_WAITOK); *ap->a_ncookies = readdir_msg->pvnr_ncookies; memcpy(*ap->a_cookies, readdir_msg->pvnr_data, *ap->a_ncookies*CSIZE); } /* next readdir starts here */ uio->uio_offset = readdir_msg->pvnr_offset; out: puffs_msgmem_release(park_readdir); 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; }