void puffs_parkdone_asyncbioread(struct puffs_mount *pmp, struct puffs_req *preq, void *arg) { struct puffs_vnmsg_read *read_msg = (void *)preq; struct buf *bp = arg; size_t moved; DPRINTF(("%s\n", __func__)); bp->b_error = checkerr(pmp, preq->preq_rv, __func__); if (bp->b_error == 0) { if (read_msg->pvnr_resid > bp->b_bcount) { puffs_senderr(pmp, PUFFS_ERR_READ, E2BIG, "resid grew", preq->preq_cookie); bp->b_error = E2BIG; } else { moved = bp->b_bcount - read_msg->pvnr_resid; bp->b_resid = read_msg->pvnr_resid; memcpy(bp->b_data, read_msg->pvnr_data, moved); } } biodone(bp); }
void puffs_parkdone_asyncbiowrite(struct puffs_mount *pmp, struct puffs_req *preq, void *arg) { struct puffs_vnmsg_write *write_msg = (void *)preq; struct buf *bp = arg; DPRINTF(("%s\n", __func__)); bp->b_error = checkerr(pmp, preq->preq_rv, __func__); if (bp->b_error == 0) { if (write_msg->pvnr_resid > bp->b_bcount) { puffs_senderr(pmp, PUFFS_ERR_WRITE, E2BIG, "resid grew", preq->preq_cookie); bp->b_error = E2BIG; } else { bp->b_resid = write_msg->pvnr_resid; } } biodone(bp); }
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; }
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_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 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; }
/* * 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; }