int
psbuf_expect_attrs(struct puffs_framebuf *pb, struct vattr *vap)
{

	CHECKCODE(pb, SSH_FXP_ATTRS);
	FAILRV(psbuf_get_vattr(pb, vap));

	return 0;
}
Esempio n. 2
0
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);
}