Exemplo n.º 1
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);
}
Exemplo n.º 2
0
Arquivo: fs.c Projeto: glk/puffs
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;
}