void closehandles(struct puffs_usermount *pu, struct psshfs_node *psn, int which) { struct psshfs_ctx *pctx = puffs_getspecific(pu); struct puffs_framebuf *pb1, *pb2; uint32_t reqid; if (psn->fhand_r && (which & HANDLE_READ)) { assert(psn->lazyopen_r == NULL); pb1 = psbuf_makeout(); reqid = NEXTREQ(pctx); psbuf_req_data(pb1, SSH_FXP_CLOSE, reqid, psn->fhand_r, psn->fhand_r_len); puffs_framev_enqueue_justsend(pu, pctx->sshfd_data, pb1, 1, 0); free(psn->fhand_r); psn->fhand_r = NULL; } if (psn->fhand_w && (which & HANDLE_WRITE)) { assert(psn->lazyopen_w == NULL); pb2 = psbuf_makeout(); reqid = NEXTREQ(pctx); psbuf_req_data(pb2, SSH_FXP_CLOSE, reqid, psn->fhand_w, psn->fhand_w_len); puffs_framev_enqueue_justsend(pu, pctx->sshfd_data, pb2, 1, 0); free(psn->fhand_w); psn->fhand_w = NULL; } psn->stat |= PSN_HANDLECLOSE; }
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 psshfs_handshake(struct puffs_usermount *pu, int fd) { struct psshfs_ctx *pctx = puffs_getspecific(pu); struct puffs_framebuf *pb; struct puffs_pathobj *po_root; struct puffs_node *pn_root; struct vattr va, *rva; const struct extunit *extu; char *rootpath; char *ext, *val; uint32_t count; int rv, done; pb = psbuf_makeout(); psbuf_put_1(pb, SSH_FXP_INIT); psbuf_put_4(pb, SFTP_PROTOVERSION); DO_IO(psbuf_write, pu, pb, fd, &done, rv); puffs_framebuf_recycle(pb); DO_IO(psbuf_read, pu, pb, fd, &done, rv); if (psbuf_get_type(pb) != SSH_FXP_VERSION) reterr((stderr, "invalid server response: %d", psbuf_get_type(pb)), EPROTO); pctx->protover = psbuf_get_reqid(pb); /* * Check out which extensions are available. Currently * we are only interested in the openssh statvfs extension. */ for (;;) { if (psbuf_get_str(pb, &ext, NULL) != 0) break; if (psbuf_get_str(pb, &val, NULL) != 0) break; for (extu = exttable; extu->ext; extu++) if (strcmp(ext, extu->ext) == 0 && strcmp(val, extu->val) == 0) pctx->extensions |= extu->extflag; } /* scope out our rootpath */ psbuf_recycleout(pb); psbuf_put_1(pb, SSH_FXP_REALPATH); psbuf_put_4(pb, NEXTREQ(pctx)); psbuf_put_str(pb, pctx->mountpath); DO_IO(psbuf_write, pu, pb, fd, &done, rv); puffs_framebuf_recycle(pb); DO_IO(psbuf_read, pu, pb, fd, &done, rv); if (psbuf_get_type(pb) != SSH_FXP_NAME) reterr((stderr, "invalid server realpath response for \"%s\"", pctx->mountpath), EPROTO); if (psbuf_get_4(pb, &count) == -1) reterr((stderr, "invalid realpath response: count"), EPROTO); if (psbuf_get_str(pb, &rootpath, NULL) == -1) reterr((stderr, "invalid realpath response: rootpath"), EPROTO); /* stat the rootdir so that we know it's a dir */ psbuf_recycleout(pb); psbuf_req_str(pb, SSH_FXP_LSTAT, NEXTREQ(pctx), rootpath); DO_IO(psbuf_write, pu, pb, fd, &done, rv); puffs_framebuf_recycle(pb); DO_IO(psbuf_read, pu, pb, fd, &done, rv); rv = psbuf_expect_attrs(pb, &va); if (rv) reterr((stderr, "couldn't stat rootpath"), rv); puffs_framebuf_destroy(pb); if (puffs_mode2vt(va.va_mode) != VDIR) reterr((stderr, "remote path (%s) not a directory", rootpath), ENOTDIR); pn_root = puffs_getroot(pu); rva = &pn_root->pn_va; puffs_setvattr(rva, &va); po_root = puffs_getrootpathobj(pu); if (po_root == NULL) err(1, "getrootpathobj"); po_root->po_path = rootpath; po_root->po_len = strlen(rootpath); return 0; }