예제 #1
0
파일: node.c 프로젝트: ryo/netbsd-src
/*
 * 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);
}
예제 #2
0
파일: node.c 프로젝트: ryo/netbsd-src
/*
 * 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);
}
예제 #3
0
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);
}
예제 #4
0
파일: node.c 프로젝트: ryo/netbsd-src
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);
}
예제 #5
0
파일: node.c 프로젝트: ryo/netbsd-src
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);
}
예제 #6
0
파일: fs.c 프로젝트: ryo/netbsd-src
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);
}
예제 #7
0
파일: node.c 프로젝트: ryo/netbsd-src
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);
}
예제 #8
0
파일: node.c 프로젝트: ryo/netbsd-src
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);
}
예제 #9
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);
}
예제 #10
0
파일: node.c 프로젝트: ryo/netbsd-src
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);
}
예제 #11
0
파일: node.c 프로젝트: ryo/netbsd-src
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);
}
예제 #12
0
파일: node.c 프로젝트: ryo/netbsd-src
/*
 * 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);
}