/* * 9P supports renames only for files within a directory * from what I could tell. So just support in-directory renames * for now. */ int puffs9p_node_rename(struct puffs_usermount *pu, void *opc, void *src, const struct puffs_cn *pcn_src, void *targ_dir, void *targ, const struct puffs_cn *pcn_targ) { AUTOVAR(pu); struct puffs_node *pn_src = src; struct p9pnode *p9n_src = pn_src->pn_data; if (opc != targ_dir) { rv = EOPNOTSUPP; goto out; } /* 9P doesn't allow to overwrite in rename */ if (targ) { struct puffs_node *pn_targ = targ; rv = noderemove(pu, pn_targ->pn_data); if (rv) goto out; } p9pbuf_put_1(pb, P9PROTO_T_WSTAT); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, p9n_src->fid_base); proto_make_stat(pb, NULL, pcn_targ->pcn_name, pn_src->pn_va.va_type); GETRESPONSE(pb); if (p9pbuf_get_type(pb) != P9PROTO_R_WSTAT) rv = EPROTO; out: RETURN(rv); }
/* * Need to be a bit clever again: the fid is clunked no matter if * the remove succeeds or not. Re-getting a fid would be way too * difficult in case the remove failed for a valid reason (directory * not empty etcetc.). So walk ourselves another fid to prod the * ice with. */ static int noderemove(struct puffs_usermount *pu, struct puffs_node *pn) { AUTOVAR(pu); struct p9pnode *p9n = pn->pn_data; p9pfid_t testfid = NEXTFID(p9p); rv = proto_cc_dupfid(pu, p9n->fid_base, testfid); if (rv) goto out; p9pbuf_put_1(pb, P9PROTO_T_REMOVE); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, testfid); /* * XXX: error handling isn't very robust, but doom is impending * anyway, so just accept we're going belly up and play dead */ GETRESPONSE(pb); if (p9pbuf_get_type(pb) != P9PROTO_R_REMOVE) { rv = EPROTO; } else { proto_cc_clunkfid(pu, p9n->fid_base, 0); p9n->fid_base = P9P_INVALFID; puffs_pn_remove(pn); } out: if (rv == 0) puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2); RETURN(rv); }
int getpathattr(struct puffs_usermount *pu, const char *path, struct vattr *vap) { PSSHFSAUTOVAR(pu); psbuf_req_str(pb, SSH_FXP_LSTAT, reqid, path); GETRESPONSE(pb, pctx->sshfd); rv = psbuf_expect_attrs(pb, vap); out: PSSHFSRETURN(rv); }
static int do_getattr(struct puffs_usermount *pu, struct puffs_node *pn, struct vattr *vap) { AUTOVAR(pu); struct p9pnode *p9n = pn->pn_data; p9pbuf_put_1(pb, P9PROTO_T_STAT); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, p9n->fid_base); GETRESPONSE(pb); rv = proto_expect_stat(pb, vap); out: RETURN(rv); }
int puffs9p_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, off_t offset, size_t *resid, const struct puffs_cred *cred, int ioflag) { AUTOVAR(pu); struct puffs_node *pn = opc; struct p9pnode *p9n = pn->pn_data; uint32_t chunk, count; size_t nwrite; if (ioflag & PUFFS_IO_APPEND) offset = pn->pn_va.va_size; nwrite = 0; while (*resid > 0) { chunk = MIN(*resid, p9p->maxreq-32); p9pbuf_put_1(pb, P9PROTO_T_WRITE); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, p9n->fid_write); p9pbuf_put_8(pb, offset+nwrite); p9pbuf_put_4(pb, chunk); p9pbuf_write_data(pb, buf+nwrite, chunk); GETRESPONSE(pb); if (p9pbuf_get_type(pb) != P9PROTO_R_WRITE) { rv = EPROTO; break; } p9pbuf_get_4(pb, &count); *resid -= count; nwrite += count; if (count != chunk) { rv = EPROTO; break; } p9pbuf_recycleout(pb); } out: RETURN(rv); }
int psshfs_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp) { PSSHFSAUTOVAR(pu); uint64_t tmpval; uint8_t type; memset(sbp, 0, sizeof(*sbp)); sbp->f_bsize = sbp->f_frsize = sbp->f_iosize = 512; if ((pctx->extensions & SFTP_EXT_STATVFS) == 0) goto out; psbuf_req_str(pb, SSH_FXP_EXTENDED, reqid, "*****@*****.**"); psbuf_put_str(pb, pctx->mountpath); GETRESPONSE(pb, pctx->sshfd); type = psbuf_get_type(pb); if (type != SSH_FXP_EXTENDED_REPLY) { /* use the default */ goto out; } psbuf_get_8(pb, &tmpval); sbp->f_bsize = tmpval; psbuf_get_8(pb, &tmpval); sbp->f_frsize = tmpval; psbuf_get_8(pb, &sbp->f_blocks); psbuf_get_8(pb, &sbp->f_bfree); psbuf_get_8(pb, &sbp->f_bavail); psbuf_get_8(pb, &sbp->f_files); psbuf_get_8(pb, &sbp->f_ffree); psbuf_get_8(pb, &sbp->f_favail); psbuf_get_8(pb, &tmpval); /* fsid */ psbuf_get_8(pb, &tmpval); /* flag */ psbuf_get_8(pb, &tmpval); sbp->f_namemax = tmpval; sbp->f_bresvd = sbp->f_bfree - sbp->f_bavail; sbp->f_fresvd = sbp->f_ffree - sbp->f_favail; out: PSSHFSRETURN(rv); }
int puffs9p_node_setattr(struct puffs_usermount *pu, void *opc, const struct vattr *va, const struct puffs_cred *pcr) { AUTOVAR(pu); struct puffs_node *pn = opc; struct p9pnode *p9n = pn->pn_data; p9pbuf_put_1(pb, P9PROTO_T_WSTAT); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, p9n->fid_base); proto_make_stat(pb, va, NULL, pn->pn_va.va_type); GETRESPONSE(pb); if (p9pbuf_get_type(pb) != P9PROTO_R_WSTAT) rv = EPROTO; out: RETURN(rv); }
int puffs9p_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag) { AUTOVAR(pu); struct puffs_node *pn = opc; struct p9pnode *p9n = pn->pn_data; uint32_t count; size_t nread; nread = 0; while (*resid > 0 && (uint64_t)(offset+nread) < pn->pn_va.va_size) { p9pbuf_put_1(pb, P9PROTO_T_READ); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, p9n->fid_read); p9pbuf_put_8(pb, offset+nread); p9pbuf_put_4(pb, MIN((uint32_t)*resid,p9p->maxreq-24)); GETRESPONSE(pb); if (p9pbuf_get_type(pb) != P9PROTO_R_READ) { rv = EPROTO; break; } p9pbuf_get_4(pb, &count); if ((rv = p9pbuf_read_data(pb, buf + nread, count))) break; if (count == 0) break; *resid -= count; nread += count; p9pbuf_recycleout(pb); } out: RETURN(rv); }
int sftp_readdir(struct puffs_usermount *pu, struct psshfs_ctx *pctx, struct puffs_node *pn) { struct puffs_cc *pcc = puffs_cc_getcc(pu); struct psshfs_node *psn = pn->pn_data; struct psshfs_dir *olddir, *testd; struct puffs_framebuf *pb; uint32_t reqid = NEXTREQ(pctx); uint32_t count, dhandlen; int tmpval; char *dhand = NULL; size_t nent; char *longname = NULL; size_t idx; int rv; assert(pn->pn_va.va_type == VDIR); idx = 0; olddir = psn->dir; nent = psn->dentnext; if (psn->dir && psn->dentread && !REFRESHTIMEOUT(pctx, time(NULL) - psn->dentread)) return 0; if (psn->dentread) { if ((rv = puffs_inval_namecache_dir(pu, pn))) warn("readdir: dcache inval fail %p", pn); } pb = psbuf_makeout(); psbuf_req_str(pb, SSH_FXP_OPENDIR, reqid, PNPATH(pn)); if (puffs_framev_enqueue_cc(pcc, pctx->sshfd, pb, 0) == -1) { rv = errno; goto wayout; } rv = psbuf_expect_handle(pb, &dhand, &dhandlen); if (rv) goto wayout; /* * Well, the following is O(n^2), so feel free to improve if it * gets too taxing on your system. */ /* * note: for the "getattr in batch" to work, this must be before * the attribute-getting. Otherwise times for first entries in * large directories might expire before the directory itself and * result in one-by-one attribute fetching. */ psn->dentread = time(NULL); psn->dentnext = 0; psn->denttot = 0; psn->dir = NULL; for (;;) { reqid = NEXTREQ(pctx); psbuf_recycleout(pb); psbuf_req_data(pb, SSH_FXP_READDIR, reqid, dhand, dhandlen); GETRESPONSE(pb, pctx->sshfd); /* check for EOF */ if (psbuf_get_type(pb) == SSH_FXP_STATUS) { rv = psbuf_expect_status(pb); goto out; } rv = psbuf_expect_name(pb, &count); if (rv) goto out; for (; count--; idx++) { if (idx == psn->denttot) allocdirs(psn); if ((rv = psbuf_get_str(pb, &psn->dir[idx].entryname, NULL))) goto out; if ((rv = psbuf_get_str(pb, &longname, NULL)) != 0) goto out; if ((rv = psbuf_get_vattr(pb, &psn->dir[idx].va)) != 0) goto out; if (sscanf(longname, "%*s%d", &tmpval) != 1) { rv = EPROTO; goto out; } psn->dir[idx].va.va_nlink = tmpval; free(longname); longname = NULL; /* * In case of DOT, copy the attributes (mostly * because we want the link count for the root dir). */ if (strcmp(psn->dir[idx].entryname, ".") == 0) { setpnva(pu, pn, &psn->dir[idx].va); } /* * Check if we already have a psshfs_dir for the * name we are processing. If so, use the old one. * If not, create a new one */ testd = lookup(olddir, nent, psn->dir[idx].entryname); if (testd) { psn->dir[idx].entry = testd->entry; /* * Has entry. Update attributes to what * we just got from the server. */ if (testd->entry) { setpnva(pu, testd->entry, &psn->dir[idx].va); psn->dir[idx].va.va_fileid = testd->entry->pn_va.va_fileid; /* * No entry. This can happen in two cases: * 1) the file was created "behind our back" * on the server * 2) we do two readdirs before we instantiate * the node (or run with -t 0). * * Cache attributes from the server in * case we want to instantiate this node * soon. Also preserve the old inode number * which was given when the dirent was created. */ } else { psn->dir[idx].va.va_fileid = testd->va.va_fileid; testd->va = psn->dir[idx].va; } /* No previous entry? Initialize this one. */ } else { psn->dir[idx].entry = NULL; psn->dir[idx].va.va_fileid = pctx->nextino++; } psn->dir[idx].attrread = psn->dentread; psn->dir[idx].valid = 1; } } out: /* XXX: rv */ psn->dentnext = idx; freedircache(olddir, nent); reqid = NEXTREQ(pctx); psbuf_recycleout(pb); psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, dhand, dhandlen); puffs_framev_enqueue_justsend(pu, pctx->sshfd, pb, 1, 0); free(dhand); free(longname); return rv; wayout: free(dhand); PSSHFSRETURN(rv); }
int puffs9p_node_lookup(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni, const struct puffs_cn *pcn) { AUTOVAR(pu); struct vattr va; struct puffs_node *pn, *pn_dir = opc; struct p9pnode *p9n_dir = pn_dir->pn_data; p9ptag_t tfid = NEXTFID(p9p); struct qid9p newqid; uint16_t nqid; p9pbuf_put_1(pb, P9PROTO_T_WALK); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, p9n_dir->fid_base); p9pbuf_put_4(pb, tfid); p9pbuf_put_2(pb, 1); p9pbuf_put_str(pb, pcn->pcn_name); GETRESPONSE(pb); rv = proto_expect_walk_nqids(pb, &nqid); if (rv) { rv = ENOENT; goto out; } if (nqid != 1) { rv = EPROTO; goto out; } if ((rv = proto_getqid(pb, &newqid))) goto out; /* we get the parent vers in walk(?) compensate */ p9pbuf_recycleout(pb); tag = NEXTTAG(p9p); p9pbuf_put_1(pb, P9PROTO_T_STAT); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, tfid); GETRESPONSE(pb); if ((rv = proto_expect_stat(pb, &va)) != 0) { proto_cc_clunkfid(pu, tfid, 0); rv = ENOENT; goto out; } if (newqid.qidpath != va.va_fileid) { proto_cc_clunkfid(pu, tfid, 0); rv = EPROTO; goto out; } newqid.qidvers = va.va_gen; pn = puffs_pn_nodewalk(pu, nodecmp, &newqid); if (pn == NULL) pn = newp9pnode_qid(pu, &newqid, tfid); else proto_cc_clunkfid(pu, tfid, 0); /* assert pn */ memcpy(&pn->pn_va, &va, sizeof(va)); puffs_newinfo_setcookie(pni, pn); puffs_newinfo_setvtype(pni, pn->pn_va.va_type); puffs_newinfo_setsize(pni, pn->pn_va.va_size); puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev); out: RETURN(rv); }
static int nodecreate(struct puffs_usermount *pu, struct puffs_node *pn, struct puffs_newinfo *pni, const char *name, const struct vattr *vap, uint32_t dirbit) { AUTOVAR(pu); struct puffs_node *pn_new; struct p9pnode *p9n = pn->pn_data; p9pfid_t nfid = NEXTFID(p9p); struct qid9p nqid; int tries = 0; again: if (++tries > 5) { rv = EPROTO; goto out; } rv = proto_cc_dupfid(pu, p9n->fid_base, nfid); if (rv) goto out; p9pbuf_put_1(pb, P9PROTO_T_CREATE); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, nfid); p9pbuf_put_str(pb, name); p9pbuf_put_4(pb, dirbit | (vap->va_mode & 0777)); p9pbuf_put_1(pb, 0); GETRESPONSE(pb); rv = proto_expect_qid(pb, P9PROTO_R_CREATE, &nqid); if (rv) goto out; /* * Now, little problem here: create returns an *open* fid. * So, clunk it and walk the parent directory to get a fid * which is not open for I/O yet. */ proto_cc_clunkfid(pu, nfid, 0); nfid = NEXTFID(p9p); p9pbuf_recycleout(pb); p9pbuf_put_1(pb, P9PROTO_T_WALK); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, p9n->fid_base); p9pbuf_put_4(pb, nfid); p9pbuf_put_2(pb, 1); p9pbuf_put_str(pb, name); GETRESPONSE(pb); /* * someone removed it already? try again * note: this is kind of lose/lose */ if (p9pbuf_get_type(pb) != P9PROTO_R_WALK) goto again; pn_new = newp9pnode_va(pu, vap, nfid); qid2vattr(&pn_new->pn_va, &nqid); puffs_newinfo_setcookie(pni, pn_new); out: RETURN(rv); }
/* * Problem is that 9P doesn't allow seeking into a directory. So we * maintain a list of active fids for any given directory. They * start living at the first read and exist either until the directory * is closed or until they reach the end. */ int puffs9p_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent, off_t *readoff, size_t *reslen, const struct puffs_cred *pcr, int *eofflag, off_t *cookies, size_t *ncookies) { AUTOVAR(pu); struct puffs_node *pn = opc; struct p9pnode *p9n = pn->pn_data; struct vattr va; struct dirfid *dfp; char *name; uint32_t count; uint16_t statsize; rv = getdfwithoffset(pu, p9n, *readoff, &dfp); if (rv) goto out; tag = NEXTTAG(p9p); p9pbuf_put_1(pb, P9PROTO_T_READ); p9pbuf_put_2(pb, tag); p9pbuf_put_4(pb, dfp->fid); p9pbuf_put_8(pb, *readoff); p9pbuf_put_4(pb, *reslen); /* XXX */ GETRESPONSE(pb); p9pbuf_get_4(pb, &count); /* * if count is 0, assume we at end-of-dir. dfp is no longer * useful, so nuke it */ if (count == 0) { *eofflag = 1; releasedf(pu, dfp); goto out; } while (count > 0) { if ((rv = proto_getstat(pb, &va, &name, &statsize))) { /* * If there was an error, it's unlikely we'll be * coming back, so just nuke the dfp. If we do * come back for some strange reason, we'll just * regen it. */ releasedf(pu, dfp); goto out; } puffs_nextdent(&dent, name, va.va_fileid, puffs_vtype2dt(va.va_type), reslen); count -= statsize; *readoff += statsize; dfp->seekoff += statsize; free(name); } storedf(p9n, dfp); out: RETURN(rv); }