mount_9p: mount a file server using the Plan9 file sharing protocol

Works, but lots of little things to nibble on:
* fix permissions to work better
* limit the amount of open files required
* do constant folding with psshfs code
* support authentication
etcetc.
This commit is contained in:
pooka 2007-04-21 14:21:42 +00:00
parent caf05d897b
commit e73a712f80
10 changed files with 2579 additions and 0 deletions

View File

@ -0,0 +1,12 @@
# $NetBSD: Makefile,v 1.1 2007/04/21 14:21:42 pooka Exp $
#
PROG= mount_9p
SRCS= ninepuffs.c ninebuf.c nineproto.c fs.c node.c subr.c
LDADD+= -lpuffs -lutil
WARNS= 4
DBG=-g -O0
MAN= mount_9p.8
.include <bsd.prog.mk>

View File

@ -0,0 +1,227 @@
/* $NetBSD: fs.c,v 1.1 2007/04/21 14:21:43 pooka Exp $ */
/*
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: fs.c,v 1.1 2007/04/21 14:21:43 pooka Exp $");
#endif /* !lint */
#include <err.h>
#include <errno.h>
#include <puffs.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ninepuffs.h"
#include "nineproto.h"
struct puffs_node *
p9p_handshake(struct puffs_usermount *pu, const char *username)
{
struct puffs9p *p9p = puffs_getspecific(pu);
struct p9pbuf *pb;
struct puffs_node *pn;
struct vattr rootva;
uint32_t maxreq;
uint16_t dummy;
p9ptag_t tagid;
int rv, x = 1;
/* send initial handshake */
pb = p9pbuf_make(p9p->maxreq, P9PB_OUT);
p9pbuf_put_1(pb, P9PROTO_T_VERSION);
p9pbuf_put_2(pb, P9PROTO_NOTAG);
p9pbuf_put_4(pb, p9p->maxreq);
p9pbuf_put_str(pb, P9PROTO_VERSION);
while ((rv = p9pbuf_write(p9p, pb)) != 1) {
if (rv == -1) {
warn("Tversion send failed");
return NULL;
}
}
p9pbuf_recycle(pb, P9PB_IN);
while ((rv = p9pbuf_read(p9p, pb)) != 1) {
if (rv == -1) {
warn("Rversion receive failed");
return NULL;
}
}
if (pb->type != P9PROTO_R_VERSION) {
warnx("server invalid response to Tversion: %d", pb->type);
return NULL;
}
if (pb->tagid != P9PROTO_NOTAG) {
warnx("server invalid tag: %d vs. %d\n",
P9PROTO_NOTAG, pb->tagid);
return NULL;
}
if (!p9pbuf_get_4(pb, &maxreq)) {
warnx("server invalid response: no request length");
return NULL;
}
if (maxreq < P9P_MINREQLEN) {
warnx("server request length below minimum accepted: %d vs. %d",
P9P_MINREQLEN, maxreq);
return NULL;
}
p9p->maxreq = maxreq;
/* tell the server we don't support authentication */
tagid = NEXTTAG(p9p);
p9pbuf_recycle(pb, P9PB_OUT);
p9pbuf_put_1(pb, P9PROTO_T_AUTH);
p9pbuf_put_2(pb, tagid);
p9pbuf_put_4(pb, P9PROTO_NOFID);
p9pbuf_put_str(pb, username);
p9pbuf_put_str(pb, "");
while ((rv = p9pbuf_write(p9p, pb)) != 1) {
if (rv == -1) {
warn("Tauth send failed");
return NULL;
}
}
p9pbuf_recycle(pb, P9PB_IN);
while ((rv = p9pbuf_read(p9p, pb)) != 1) {
if (rv == -1) {
warn("Rauth receive failed");
return NULL;
}
}
/* assume all Rerror is "no auth" */
if (pb->type != P9PROTO_R_ERROR) {
warnx("mount_9p supports only NO auth");
return NULL;
}
if (pb->tagid != tagid) {
warnx("server invalid tag: %d vs. %d\n", tagid, pb->tagid);
return NULL;
}
/* build attach message */
tagid = NEXTTAG(p9p);
p9pbuf_recycle(pb, P9PB_OUT);
p9pbuf_put_1(pb, P9PROTO_T_ATTACH);
p9pbuf_put_2(pb, tagid);
p9pbuf_put_4(pb, P9P_ROOTFID);
p9pbuf_put_4(pb, P9PROTO_NOFID);
p9pbuf_put_str(pb, username);
p9pbuf_put_str(pb, "");
while ((rv = p9pbuf_write(p9p, pb)) != 1) {
if (rv == -1) {
warn("Tattach send failed");
return NULL;
}
}
p9pbuf_recycle(pb, P9PB_IN);
while ((rv = p9pbuf_read(p9p, pb)) != 1) {
if (rv == -1) {
warn("Rattach receive failed");
return NULL;
}
}
if (pb->type != P9PROTO_R_ATTACH) {
warnx("Rattach not received, got %d", pb->type);
return NULL;
}
if (pb->tagid != tagid) {
warnx("server invalid tag: %d vs. %d\n", tagid, pb->tagid);
return NULL;
}
#if 0
if (!proto_getqid(pb, rqid)) {
warnx("cannot read root node qid");
return NULL;
}
#endif
/* finally, stat the rootnode */
tagid = NEXTTAG(p9p);
p9pbuf_recycle(pb, P9PB_OUT);
p9pbuf_put_1(pb, P9PROTO_T_STAT);
p9pbuf_put_2(pb, tagid);
p9pbuf_put_4(pb, P9P_ROOTFID);
while ((rv = p9pbuf_write(p9p, pb)) != 1) {
if (rv == -1) {
warn("Tstat send failed");
return NULL;
}
}
p9pbuf_recycle(pb, P9PB_IN);
while ((rv = p9pbuf_read(p9p, pb)) != 1) {
if (rv == -1) {
warn("Rstat receive failed");
return NULL;
}
}
if (pb->type != P9PROTO_R_STAT) {
warnx("Rstat not received, got %d", pb->type);
return NULL;
}
if (pb->tagid != tagid) {
warnx("server invalid tag: %d vs. %d\n", tagid, pb->tagid);
return NULL;
}
if (!p9pbuf_get_2(pb, &dummy)) {
warnx("couldn't get stat len parameter");
return NULL;
}
if (!proto_getstat(pb, &rootva, NULL, NULL)) {
warnx("could not parse root attributes");
return NULL;
}
p9pbuf_destroy(pb);
rootva.va_nlink = 0156; /* guess, will be fixed with first readdir */
pn = newp9pnode_va(pu, &rootva, P9P_ROOTFID);
if (ioctl(p9p->servsock, FIONBIO, &x) == -1) {
warnx("cannot set socket in nonblocking mode");
return NULL;
}
return pn;
}
int
puffs9p_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
{
struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
struct puffs9p *p9p = puffs_getspecific(pu);
close(p9p->servsock);
return 0;
}

View File

@ -0,0 +1,64 @@
.\" $NetBSD: mount_9p.8,v 1.1 2007/04/21 14:21:43 pooka Exp $
.\"
.\" Copyright (c) 2007 Antti Kantee. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd April 21, 2007
.Dt MOUNT_9P 8
.Os
.Sh NAME
.Nm mount_9p
.Nd mount a file server using the 9P resource sharing protocol
.Sh SYNOPSIS
.Nm
.Op Ar options
.Ar user
.Ar file_server
.Ar mount_point
.Sh DESCRIPTION
The
.Nm
program is used to mount a file hierarchy served with the Plan9
file sharing protocol: 9P.
After the file system is mounted, the files on the remote
.Ar file_server
will be accessed using the credentials of the user named
.Ar user
and whatever uid the user happens to have on the remote server.
.Sh SEE ALSO
.Xr puffs 3 ,
.Xr puffs 4 ,
.Xr mount 8
.Sh HISTORY
The
.Nm
utility first appeared in
.Nx 5.0 .
.Sh CAVEATS
Permissions are not handled well.
.Pp
Authentication support is missing.
.Pp
Error code handling is missing.
.Pp
Under construction.

View File

@ -0,0 +1,454 @@
/* $NetBSD: ninebuf.c,v 1.1 2007/04/21 14:21:43 pooka Exp $ */
/*
* Copyright (c) 2006, 2007 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: ninebuf.c,v 1.1 2007/04/21 14:21:43 pooka Exp $");
#endif /* !lint */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <util.h>
#include <unistd.h>
#include "ninepuffs.h"
/*
* Originally from my psshfs implementation. Maybe need to look into
* unifying these at some level, although there are minor variations.
*
* Such as the fact that 9P is a little endian protocol ....
*/
int
p9pbuf_read(struct puffs9p *p9p, struct p9pbuf *pb)
{
ssize_t n;
assert(pb->state != P9PBUF_PUT);
again:
n = read(p9p->servsock, pb->buf+pb->offset, pb->remain);
switch (n) {
case 0:
errno = EIO;
return -1;
case -1:
if (errno == EAGAIN)
return 0;
return -1;
default:
pb->offset += n;
pb->remain -= n;
}
if (pb->remain != 0)
return 0;
/* ok, at least there's something to do */
assert(pb->state == P9PBUF_GETLEN || pb->state == P9PBUF_GETDATA);
if (pb->state == P9PBUF_GETLEN) {
uint32_t len;
memcpy(&len, pb->buf, 4);
pb->remain = le32toh(len) - 4;
assert(pb->remain <= pb->len); /* XXX */
pb->offset = 0;
pb->state = P9PBUF_GETDATA;
goto again;
} else if (pb->state == P9PBUF_GETDATA) {
pb->remain = pb->offset;
pb->offset = 0;
pb->state = P9PBUF_GETREADY;
/* sloppy */
if (!p9pbuf_get_1(pb, &pb->type))
errx(1, "invalid server response, no type");
if (!p9pbuf_get_2(pb, &pb->tagid))
errx(1, "invalid server response, no tag");
return 1;
}
return -1; /* XXX: impossible */
}
int
p9pbuf_write(struct puffs9p *p9p, struct p9pbuf *pb)
{
ssize_t n;
if (pb->state == P9PBUF_PUT) {
uint32_t len;
len = htole32(pb->offset);
memcpy(pb->buf, &len, sizeof(len));
pb->remain = pb->offset;
pb->offset = 0;
pb->state = P9PBUF_PUTDONE;
}
assert(pb->state == P9PBUF_PUTDONE);
n = write(p9p->servsock, pb->buf + pb->offset, pb->remain);
if (n == 0) {
errno = EIO;
return -1;
}
if (n == -1) {
if (errno == EAGAIN)
return 0;
else
return -1;
}
pb->offset += n;
pb->remain -= n;
if (pb->remain == 0)
return 1;
else
return 0;
}
struct p9pbuf *
p9pbuf_make(size_t reqsize, int incoming)
{
struct p9pbuf *pb;
pb = emalloc(sizeof(struct p9pbuf));
memset(pb, 0, sizeof(struct p9pbuf));
pb->buf = emalloc(reqsize);
pb->len = reqsize;
p9pbuf_recycle(pb, incoming);
return pb;
}
void
p9pbuf_destroy(struct p9pbuf *pb)
{
free(pb->buf);
free(pb);
}
void
p9pbuf_recycle(struct p9pbuf *pb, int incoming)
{
if (incoming) {
pb->offset = 0;
pb->remain = 4;
pb->state = P9PBUF_GETLEN;
} else {
/* save space for len */
pb->remain = pb->len - 4;
pb->offset = 4;
pb->state = P9PBUF_PUT;
}
}
/*
* allow put 1,2,4,8 in the middle and do *not* adjust remain
* in that case. However, do check the extending is possible
* only from the end
*/
int
p9pbuf_put_1(struct p9pbuf *pb, uint8_t val)
{
assert(pb->state == P9PBUF_PUT);
P9PB_CHECK(pb, 1);
memcpy(pb->buf + pb->offset, &val, 1);
if (pb->offset + pb->remain == pb->len)
pb->remain -= 1;
pb->offset += 1;
return 0;
}
int
p9pbuf_put_2(struct p9pbuf *pb, uint16_t val)
{
assert(pb->state == P9PBUF_PUT);
P9PB_CHECK(pb, 2);
HTOLE16(val);
memcpy(pb->buf + pb->offset, &val, 2);
if (pb->offset + pb->remain == pb->len)
pb->remain -= 2;
else
assert(pb->offset + pb->remain + 2 <= pb->len);
pb->offset += 2;
return 0;
}
int
p9pbuf_put_4(struct p9pbuf *pb, uint32_t val)
{
assert(pb->state == P9PBUF_PUT);
P9PB_CHECK(pb, 4);
HTOLE32(val);
memcpy(pb->buf + pb->offset, &val, 4);
if (pb->offset + pb->remain == pb->len)
pb->remain -= 4;
else
assert(pb->offset + pb->remain + 4 <= pb->len);
pb->offset += 4;
return 0;
}
int
p9pbuf_put_8(struct p9pbuf *pb, uint64_t val)
{
assert(pb->state == P9PBUF_PUT);
P9PB_CHECK(pb, 8);
HTOLE64(val);
memcpy(pb->buf + pb->offset, &val, 8);
if (pb->offset + pb->remain == pb->len)
pb->remain -= 8;
else
assert(pb->offset + pb->remain + 8 <= pb->len);
pb->offset += 8;
return 0;
}
int
p9pbuf_put_data(struct p9pbuf *pb, const void *data, uint16_t dlen)
{
assert(pb->state == P9PBUF_PUT);
P9PB_CHECK(pb, dlen + 2);
p9pbuf_put_2(pb, dlen);
memcpy(pb->buf + pb->offset, data, dlen);
pb->offset += dlen;
pb->remain -= dlen;
return 0;
}
int
p9pbuf_put_str(struct p9pbuf *pb, const char *str)
{
return p9pbuf_put_data(pb, str, strlen(str));
}
int
p9pbuf_write_data(struct p9pbuf *pb, uint8_t *data, uint32_t dlen)
{
assert(pb->state == P9PBUF_PUT);
P9PB_CHECK(pb, dlen);
memcpy(pb->buf + pb->offset, data, dlen);
pb->offset += dlen;
pb->remain -= dlen;
return 0;
}
int
p9pbuf_get_1(struct p9pbuf *pb, uint8_t *val)
{
assert(pb->state == P9PBUF_GETREADY);
if (pb->remain < 1)
return 0;
memcpy(val, pb->buf + pb->offset, 1);
pb->offset += 1;
pb->remain -= 1;
return 1;
}
int
p9pbuf_get_2(struct p9pbuf *pb, uint16_t *val)
{
uint16_t v;
assert(pb->state == P9PBUF_GETREADY);
if (pb->remain < 2)
return 0;
memcpy(&v, pb->buf + pb->offset, 2);
pb->offset += 2;
pb->remain -= 2;
*val = le16toh(v);
return 1;
}
int
p9pbuf_get_4(struct p9pbuf *pb, uint32_t *val)
{
uint32_t v;
assert(pb->state == P9PBUF_GETREADY);
if (pb->remain < 4)
return 0;
memcpy(&v, pb->buf + pb->offset, 4);
pb->offset += 4;
pb->remain -= 4;
*val = le32toh(v);
return 1;
}
int
p9pbuf_get_8(struct p9pbuf *pb, uint64_t *val)
{
uint64_t v;
assert(pb->state == P9PBUF_GETREADY);
if (pb->remain < 8)
return 0;
memcpy(&v, pb->buf + pb->offset, 8);
pb->offset += 8;
pb->remain -= 8;
*val = le64toh(v);
return 1;
}
int
p9pbuf_get_data(struct p9pbuf *pb, uint8_t **dp, uint16_t *dlenp)
{
uint8_t *d;
uint16_t len;
assert(pb->state == P9PBUF_GETREADY);
if (!(p9pbuf_get_2(pb, &len)))
return 0;
if (pb->remain < len)
return 0;
if (dp) {
d = emalloc(len+1);
memcpy(d, pb->buf + pb->offset, len);
d[len] = '\0';
*dp = d;
}
pb->offset += len;
pb->remain -= len;
if (dlenp)
*dlenp = len;
return 1;
}
int
p9pbuf_read_data(struct p9pbuf *pb, uint8_t *buf, uint32_t dlen)
{
assert(pb->state == P9PBUF_GETREADY);
if (pb->remain < dlen)
return 0;
memcpy(buf, pb->buf + pb->offset, dlen);
pb->offset += dlen;
pb->remain -= dlen;
return 1;
}
int
p9pbuf_get_str(struct p9pbuf *pb, char **dp, uint16_t *dlenp)
{
return p9pbuf_get_data(pb, (uint8_t **)dp, dlenp);
}
int
p9pbuf_tell(struct p9pbuf *pb)
{
return pb->offset;
}
int
p9pbuf_remaining(struct p9pbuf *pb)
{
return pb->remain;
}
void
p9pbuf_seekset(struct p9pbuf *pb, int newoff)
{
if (newoff > pb->offset)
pb->remain -= newoff - pb->offset;
pb->offset = newoff;
}

View File

@ -0,0 +1,365 @@
/* $NetBSD: nineproto.c,v 1.1 2007/04/21 14:21:43 pooka Exp $ */
/*
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: nineproto.c,v 1.1 2007/04/21 14:21:43 pooka Exp $");
#endif /* !lint */
#include <sys/types.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <puffs.h>
#include <stdio.h>
#include <stdlib.h>
#include "ninepuffs.h"
#include "nineproto.h"
int
proto_getqid(struct p9pbuf *pb, struct qid9p *qid)
{
if (pb->remain < 13)
return 0;
p9pbuf_get_1(pb, &qid->qidtype);
p9pbuf_get_4(pb, &qid->qidvers);
p9pbuf_get_8(pb, &qid->qidpath);
return 1;
}
static uid_t
ustr2uid(char *uid)
{
struct passwd *pw;
pw = getpwnam(uid);
if (pw == NULL)
return 0; /* XXXXX */
return pw->pw_uid;
}
static gid_t
gstr2gid(char *gid)
{
struct group *grr;
grr = getgrnam(gid);
if (grr == NULL)
return 0; /* more XXXX */
return grr->gr_gid;
}
static const char *
uid2ustr(uid_t uid)
{
struct passwd *pw;
pw = getpwuid(uid);
if (pw == NULL)
return "root"; /* XXXXX */
return pw->pw_name;
}
static const char *
gid2gstr(gid_t gid)
{
struct group *grr;
grr = getgrgid(gid);
if (grr == NULL)
return "wheel"; /* XXXXXX */
return grr->gr_name;
}
#define GETFIELD(a,b,unitsize) \
do { \
if (size < unitsize) return 0; \
if (!(a(pb, b))) return 0; \
size -= unitsize; \
} while (/*CONSTCOND*/0)
#define GETSTR(val,strsize) \
do { \
if (!(p9pbuf_get_str(pb, val, strsize))) return 0; \
if (*strsize > size) return 0; \
size -= *strsize; \
} while (/*CONSTCOND*/0)
int
proto_getstat(struct p9pbuf *pb, struct vattr *vap, char **name, uint16_t *rs)
{
char *uid, *gid;
struct qid9p qid;
uint64_t flen;
uint32_t rdev, mode, atime, mtime;
uint16_t size, v16;
/* check size */
if (!p9pbuf_get_2(pb, &size))
return 0;
if (p9pbuf_remaining(pb) < size)
return 0;
if (rs)
*rs = size+2; /* compensate for size field itself */
GETFIELD(p9pbuf_get_2, &v16, 2);
if (v16)
printf("%d\n", v16);
GETFIELD(p9pbuf_get_4, &rdev, 4);
GETFIELD(proto_getqid, &qid, 13);
GETFIELD(p9pbuf_get_4, &mode, 4);
GETFIELD(p9pbuf_get_4, &atime, 4);
GETFIELD(p9pbuf_get_4, &mtime, 4);
GETFIELD(p9pbuf_get_8, &flen, 8);
GETSTR(name, &v16);
GETSTR(&uid, &v16);
GETSTR(&gid, &v16);
if (rdev)
printf("%d\n", rdev);
vap->va_rdev = rdev;
vap->va_mode = mode & 0777; /* may contain other uninteresting bits */
vap->va_atime.tv_sec = atime;
vap->va_mtime.tv_sec = mtime;
vap->va_ctime.tv_sec = mtime;
vap->va_atime.tv_nsec=vap->va_mtime.tv_nsec=vap->va_ctime.tv_nsec = 0;
vap->va_birthtime.tv_sec = vap->va_birthtime.tv_nsec = 0;
vap->va_size = vap->va_bytes = flen;
vap->va_uid = ustr2uid(uid);
vap->va_gid = gstr2gid(gid);
qid2vattr(vap, &qid);
/* some defaults */
if (vap->va_type == VDIR)
vap->va_nlink = 1906;
else
vap->va_nlink = 1;
vap->va_blocksize = 512;
vap->va_flags = vap->va_vaflags = 0;
vap->va_filerev = PUFFS_VNOVAL;
/* muid, not used */
GETSTR(NULL, &v16);
return 1;
}
int
proto_cc_dupfid(struct puffs_cc *pcc, p9pfid_t oldfid, p9pfid_t newfid)
{
struct puffs9p *p9p = puffs_cc_getspecific(pcc);
struct p9pbuf *pb;
p9ptag_t tag = NEXTTAG(p9p);
uint16_t qids;
int rv, error = 0;
pb = p9pbuf_make(p9p->maxreq, P9PB_OUT);
p9pbuf_put_1(pb, P9PROTO_T_WALK);
p9pbuf_put_2(pb, tag);
p9pbuf_put_4(pb, oldfid);
p9pbuf_put_4(pb, newfid);
p9pbuf_put_2(pb, 0);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
rv = proto_expect_walk_nqids(pb, &qids);
if (rv)
error = rv;
if (qids != 0)
error = EPROTO;
p9pbuf_destroy(pb);
return error;
}
int
proto_cc_clunkfid(struct puffs_cc *pcc, p9pfid_t fid, int waitforit)
{
struct puffs9p *p9p = puffs_cc_getspecific(pcc);
struct p9pbuf *pb;
p9ptag_t tag = NEXTTAG(p9p);
int error = 0;
pb = p9pbuf_make(p9p->maxreq, P9PB_OUT);
p9pbuf_put_1(pb, P9PROTO_T_CLUNK);
p9pbuf_put_2(pb, tag);
p9pbuf_put_4(pb, fid);
if (waitforit) {
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
if (pb->type != P9PROTO_R_CLUNK)
error = EPROTO;
p9pbuf_destroy(pb);
} else {
outbuf_enqueue_nocc(p9p, pb, NULL, NULL, tag);
}
return error;
}
/*
* walk a new fid, then open it
*/
int
proto_cc_open(struct puffs_cc *pcc, p9pfid_t fid, p9pfid_t newfid, int mode)
{
struct puffs9p *p9p = puffs_cc_getspecific(pcc);
struct p9pbuf *pb;
p9ptag_t tag = NEXTTAG(p9p);
int error;
error = proto_cc_dupfid(pcc, fid, newfid);
if (error)
return error;
pb = p9pbuf_make(p9p->maxreq, P9PB_OUT);
p9pbuf_put_1(pb, P9PROTO_T_OPEN);
p9pbuf_put_2(pb, tag);
p9pbuf_put_4(pb, newfid);
p9pbuf_put_1(pb, mode);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
if (pb->type != P9PROTO_R_OPEN)
error = EPROTO;
p9pbuf_destroy(pb);
return error;
}
void
proto_make_stat(struct p9pbuf *pb, const struct vattr *vap,
const char *filename)
{
struct vattr fakeva;
uint32_t mode, atime, mtime;
uint64_t flen;
const char *owner, *group;
int startoff, curoff;
if (vap == NULL) {
puffs_vattr_null(&fakeva);
vap = &fakeva;
}
startoff = p9pbuf_tell(pb);
p9pbuf_seekset(pb, startoff + 2 + 2); /* stat[n], containing stat[2] */
if (vap->va_mode != (mode_t)PUFFS_VNOVAL)
mode = vap->va_mode;
else
mode = P9PROTO_STAT_NOVAL4;
if (vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL)
atime = vap->va_atime.tv_sec;
else
atime = P9PROTO_STAT_NOVAL4;
if (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)
mtime = vap->va_mtime.tv_sec;
else
mtime = P9PROTO_STAT_NOVAL4;
if (vap->va_size != (u_quad_t)PUFFS_VNOVAL)
flen = vap->va_size;
else
flen = P9PROTO_STAT_NOVAL8;
if (vap->va_uid != (uid_t)PUFFS_VNOVAL)
owner = uid2ustr(vap->va_uid);
else
owner = "";
if (vap->va_gid != (gid_t)PUFFS_VNOVAL)
group = gid2gstr(vap->va_gid);
else
group = "";
p9pbuf_put_2(pb, P9PROTO_STAT_NOVAL2); /* kernel type */
p9pbuf_put_4(pb, P9PROTO_STAT_NOVAL4); /* dev */
p9pbuf_put_1(pb, P9PROTO_STAT_NOVAL1); /* type */
p9pbuf_put_4(pb, P9PROTO_STAT_NOVAL4); /* version */
p9pbuf_put_8(pb, P9PROTO_STAT_NOVAL8); /* path */
p9pbuf_put_4(pb, mode);
p9pbuf_put_4(pb, atime);
p9pbuf_put_4(pb, mtime);
p9pbuf_put_8(pb, flen);
p9pbuf_put_str(pb, filename ? filename : "");
p9pbuf_put_str(pb, owner);
p9pbuf_put_str(pb, group);
p9pbuf_put_str(pb, ""); /* muid */
curoff = p9pbuf_tell(pb);
p9pbuf_seekset(pb, startoff);
p9pbuf_put_2(pb, curoff-(startoff+2)); /* stat[n] size */
p9pbuf_put_2(pb, curoff-(startoff+4)); /* size[2] stat */
p9pbuf_seekset(pb, curoff);
}
int
proto_expect_walk_nqids(struct p9pbuf *pb, uint16_t *nqids)
{
if (pb->type != P9PROTO_R_WALK)
return EPROTO;
if (!p9pbuf_get_2(pb, nqids))
return EPROTO;
return 0;
}
int
proto_expect_qid(struct p9pbuf *pb, uint8_t op, struct qid9p *qid)
{
if (pb->type != op)
return EPROTO;
if (!proto_getqid(pb, qid))
return EPROTO;
return 0;
}
int
proto_expect_stat(struct p9pbuf *pb, struct vattr *va)
{
uint16_t dummy;
if (pb->type != P9PROTO_R_STAT)
return EPROTO;
if (!p9pbuf_get_2(pb, &dummy))
return EPROTO;
if (!proto_getstat(pb, va, NULL, NULL))
return EPROTO;
return 0;
}

View File

@ -0,0 +1,93 @@
/* $NetBSD: nineproto.h,v 1.1 2007/04/21 14:21:43 pooka Exp $ */
/*
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef PUFFS9P_9PROTO_H_
#define PUFFS9P_9PROTO_H_
#include <stdint.h>
#define P9PROTO_VERSION "9P2000"
#define P9PROTO_T_VERSION 100
#define P9PROTO_R_VERSION 101
#define P9PROTO_T_AUTH 102
#define P9PROTO_R_AUTH 103
#define P9PROTO_T_ATTACH 104
#define P9PROTO_R_ATTACH 105
#define P9PROTO_T_ERROR 106
#define P9PROTO_R_ERROR 107
#define P9PROTO_T_FLUSH 108
#define P9PROTO_R_FLUSH 109
#define P9PROTO_T_WALK 110
#define P9PROTO_R_WALK 111
#define P9PROTO_T_OPEN 112
#define P9PROTO_R_OPEN 113
#define P9PROTO_T_CREATE 114
#define P9PROTO_R_CREATE 115
#define P9PROTO_T_READ 116
#define P9PROTO_R_READ 117
#define P9PROTO_T_WRITE 118
#define P9PROTO_R_WRITE 119
#define P9PROTO_T_CLUNK 120
#define P9PROTO_R_CLUNK 121
#define P9PROTO_T_REMOVE 122
#define P9PROTO_R_REMOVE 123
#define P9PROTO_T_STAT 124
#define P9PROTO_R_STAT 125
#define P9PROTO_T_WSTAT 126
#define P9PROTO_R_WSTAT 127
#define P9PROTO_MIN 9PROTO_T_VERSION
#define P9PROTO_MAX 9PROTO_R_MAX
#define P9PROTO_NOFID (uint32_t)~0
#define P9PROTO_NOTAG (uint16_t)~0
/* type field in a qid */
#define P9PROTO_QID_TYPE_DIR 0x80
#define P9PROTO_QID_TYPE_APPEND 0x40
#define P9PROTO_QID_TYPE_EXCL 0x20
#define P9PROTO_QID_TYPE_MOUNT 0x10
#define P9PROTO_QID_TYPE_AUTH 0x08
/* mode in open */
#define P9PROTO_OMODE_READ 0x00
#define P9PROTO_OMODE_WRITE 0x01
#define P9PROTO_OMODE_RDWR 0x02
#define P9PROTO_OMODE_EXEC 0x03
#define P9PROTO_OMODE_TRUNC 0x10
#define P9PROTO_OMODE_RMCLOSE 0x40
/* for creating directories */
#define P9PROTO_CPERM_DIR 0x80000000
/* stat non-values */
#define P9PROTO_STAT_NOVAL1 (uint8_t)~0
#define P9PROTO_STAT_NOVAL2 (uint16_t)~0
#define P9PROTO_STAT_NOVAL4 (uint32_t)~0
#define P9PROTO_STAT_NOVAL8 (uint64_t)~0
#endif /* PUFFS9P_PROTO_H_ */

View File

@ -0,0 +1,394 @@
/* $NetBSD: ninepuffs.c,v 1.1 2007/04/21 14:21:43 pooka Exp $ */
/*
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* 9puffs: access a 9P file server as a vfs via puffs
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: ninepuffs.c,v 1.1 2007/04/21 14:21:43 pooka Exp $");
#endif /* !lint */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <assert.h>
#include <err.h>
#include <netdb.h>
#include <puffs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ninepuffs.h"
#define DEFPORT_9P 564
static void puffs9p_eventloop(struct puffs_usermount *, struct puffs9p *);
static void
usage(void)
{
errx(1, "usage: %s user server mountpoint", getprogname());
}
/*
* TCPv4 connection to 9P file server, forget IL and IPv6 for now.
* Return connected socket or exit with error.
*/
static int
serverconnect(const char *addr, unsigned short port)
{
struct sockaddr_in mysin;
struct hostent *he;
int s;
he = gethostbyname2(addr, AF_INET);
if (he == NULL) {
herror("gethostbyname");
exit(1);
}
s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == -1)
err(1, "socket");
memset(&mysin, 0, sizeof(struct sockaddr_in));
mysin.sin_family = AF_INET;
mysin.sin_port = htons(port);
memcpy(&mysin.sin_addr, he->h_addr, sizeof(struct in_addr));
if (connect(s, (struct sockaddr *)&mysin, sizeof(mysin)) == -1)
err(1, "connect");
return s;
}
int
main(int argc, char *argv[])
{
struct puffs9p p9p;
struct puffs_usermount *pu;
struct puffs_ops *pops;
struct puffs_node *pn_root;
struct statvfs svfsb;
mntoptparse_t mp;
char *srvhost;
char *user;
unsigned short port;
int mntflags, pflags, ch;
int detach;
setprogname(argv[0]);
if (argc < 3)
usage();
mntflags = pflags = 0;
detach = 1;
port = DEFPORT_9P;
while ((ch = getopt(argc, argv, "o:p:s")) != -1) {
switch (ch) {
case 'o':
mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
if (mp == NULL)
err(1, "getmntopts");
freemntopts(mp);
break;
case 'p':
port = atoi(optarg);
break;
case 's':
detach = 0;
break;
default:
usage();
/*NOTREACHED*/
}
}
argc -= optind;
argv += optind;
if (argc != 3)
usage();
user = argv[0];
srvhost = argv[1];
if (pflags & PUFFS_FLAG_OPDUMP)
detach = 0;
PUFFSOP_INIT(pops);
PUFFSOP_SET(pops, puffs9p, fs, unmount);
PUFFSOP_SETFSNOP(pops, sync);
PUFFSOP_SETFSNOP(pops, statvfs);
PUFFSOP_SET(pops, puffs9p, node, lookup);
PUFFSOP_SET(pops, puffs9p, node, readdir);
PUFFSOP_SET(pops, puffs9p, node, getattr);
PUFFSOP_SET(pops, puffs9p, node, setattr);
PUFFSOP_SET(pops, puffs9p, node, create);
PUFFSOP_SET(pops, puffs9p, node, open);
PUFFSOP_SET(pops, puffs9p, node, close);
PUFFSOP_SET(pops, puffs9p, node, mkdir);
PUFFSOP_SET(pops, puffs9p, node, remove);
PUFFSOP_SET(pops, puffs9p, node, rmdir);
PUFFSOP_SET(pops, puffs9p, node, read);
PUFFSOP_SET(pops, puffs9p, node, write);
PUFFSOP_SET(pops, puffs9p, node, rename);
#if 0
PUFFSOP_SET(pops, puffs9p, node, reclaim);
PUFFSOP_SET(pops, puffs9p, node, mknod);
#endif
memset(&p9p, 0, sizeof(p9p));
p9p.maxreq = P9P_DEFREQLEN;
p9p.nextfid = 1;
TAILQ_INIT(&p9p.outbufq);
TAILQ_INIT(&p9p.req_queue);
p9p.servsock = serverconnect(srvhost, port);
pu = puffs_init(pops, "9P", &p9p, pflags);
if (pu == NULL)
err(1, "puffs_init");
if ((pn_root = p9p_handshake(pu, user)) == NULL) {
puffs_exit(pu, 1);
exit(1);
}
if (puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK) == -1)
err(1, "setblockingmode");
if (puffs_domount(pu, argv[2], mntflags) == -1)
err(1, "puffs_domount");
puffs_zerostatvfs(&svfsb);
if (puffs_start(pu, pn_root, &svfsb) == -1)
err(1, "puffs_start");
if (detach)
daemon(1, 0);
puffs9p_eventloop(pu, &p9p);
return 0;
}
/*
* enqueue buffer to be handled with cc
*/
void
outbuf_enqueue(struct puffs9p *p9p, struct p9pbuf *pb,
struct puffs_cc *pcc, uint16_t tagid)
{
pb->p9pr.tagid = tagid;
pb->p9pr.pcc = pcc;
pb->p9pr.func = NULL;
pb->p9pr.arg = NULL;
TAILQ_INSERT_TAIL(&p9p->outbufq, pb, p9pr.entries);
}
/*
* enqueue buffer to be handled with "f". "f" must not block.
* gives up struct p9pbuf ownership.
*/
void
outbuf_enqueue_nocc(struct puffs9p *p9p, struct p9pbuf *pb,
void (*f)(struct puffs9p *, struct p9pbuf *, void *), void *arg,
uint16_t tagid)
{
pb->p9pr.tagid = tagid;
pb->p9pr.pcc = NULL;
pb->p9pr.func = f;
pb->p9pr.arg = arg;
TAILQ_INSERT_TAIL(&p9p->outbufq, pb, p9pr.entries);
}
struct p9pbuf *
req_get(struct puffs9p *p9p, uint16_t tagid)
{
struct p9pbuf *pb;
TAILQ_FOREACH(pb, &p9p->req_queue, p9pr.entries)
if (pb->p9pr.tagid == tagid)
break;
if (!pb)
return NULL;
TAILQ_REMOVE(&p9p->req_queue, pb, p9pr.entries);
return pb;
}
static void
handlebuf(struct puffs9p *p9p, struct p9pbuf *datapb,
struct puffs_putreq *ppr)
{
struct p9preq p9prtmp;
struct p9pbuf *pb;
/* is this something we are expecting? */
pb = req_get(p9p, datapb->tagid);
if (pb == NULL) {
printf("invalid server request response %d\n", datapb->tagid);
p9pbuf_destroy(datapb);
return;
}
/* keep p9preq clean, xxx uknow */
p9prtmp = pb->p9pr;
*pb = *datapb;
pb->p9pr = p9prtmp;
free(datapb);
/* don't allow both cc and handler func, but allow neither */
assert((pb->p9pr.pcc && pb->p9pr.func) == 0);
if (pb->p9pr.pcc) {
puffs_docc(pb->p9pr.pcc, ppr);
} else if (pb->p9pr.func) {
pb->p9pr.func(p9p, pb, pb->p9pr.arg);
} else {
assert(pb->p9pr.arg == NULL);
p9pbuf_destroy(pb);
}
}
static int
psshinput(struct puffs9p *p9p, struct puffs_putreq *ppr)
{
struct p9pbuf *cb;
int rv;
for (;;) {
if ((cb = p9p->curpb) == NULL) {
cb = p9pbuf_make(p9p->maxreq, P9PB_IN);
if (cb == NULL)
return -1;
p9p->curpb = cb;
}
rv = p9pbuf_read(p9p, cb);
if (rv == -1)
err(1, "p9pbuf read");
if (rv == 0)
break;
handlebuf(p9p, cb, ppr);
p9p->curpb = NULL;
}
return rv;
}
static int
psshoutput(struct puffs9p *p9p)
{
struct p9pbuf *pb;
int rv;
TAILQ_FOREACH(pb, &p9p->outbufq, p9pr.entries) {
rv = p9pbuf_write(p9p, pb);
if (rv == -1)
return -1;
if (rv == 0)
return 0;
/* sent everything, move to cookiequeue */
TAILQ_REMOVE(&p9p->outbufq, pb, p9pr.entries);
free(pb->buf);
TAILQ_INSERT_TAIL(&p9p->req_queue, pb, p9pr.entries);
}
return 1;
}
#define PFD_SOCK 0
#define PFD_PUFFS 1
static void
puffs9p_eventloop(struct puffs_usermount *pu, struct puffs9p *p9p)
{
struct puffs_getreq *pgr;
struct puffs_putreq *ppr;
struct pollfd pfds[2];
pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0);
if (!pgr)
err(1, "makegetreq");
ppr = puffs_req_makeput(pu);
if (!ppr)
err(1, "makeputreq");
while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
memset(pfds, 0, sizeof(pfds));
pfds[PFD_SOCK].events = POLLIN;
if (!TAILQ_EMPTY(&p9p->outbufq))
pfds[PFD_SOCK].events |= POLLOUT;
pfds[PFD_SOCK].fd = p9p->servsock;
pfds[PFD_PUFFS].fd = puffs_getselectable(pu);
pfds[PFD_PUFFS].events = POLLIN;
if (poll(pfds, 2, INFTIM) == -1)
err(1, "poll");
if (pfds[PFD_SOCK].revents & POLLOUT)
if (psshoutput(p9p) == -1)
err(1, "psshoutput");
/* get & possibly dispatch events from kernel */
if (pfds[PFD_PUFFS].revents & POLLIN)
if (puffs_req_handle(pu, pgr, ppr, 0) == -1)
err(1, "puffs_handlereqs");
/* get input from sftpd, possibly build more responses */
if (pfds[PFD_SOCK].revents & POLLIN)
if (psshinput(p9p, ppr) == -1)
errx(1, "psshinput");
/* it's likely we got outputtables, poke the ice with a stick */
if (psshoutput(p9p) == -1)
err(1, "psshoutput");
/* stuff all replies from both of the above into kernel */
if (puffs_req_putput(ppr) == -1)
err(1, "putputreq");
puffs_req_resetput(ppr);
}
puffs_req_destroyget(pgr);
}

View File

@ -0,0 +1,215 @@
/* $NetBSD: ninepuffs.h,v 1.1 2007/04/21 14:21:43 pooka Exp $ */
/*
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef PUFFS9P_H_
#define PUFFS9P_H_
#include <sys/queue.h>
#include <puffs.h>
PUFFSOP_PROTOS(puffs9p);
/* Qid structure. optimized for in-mem. different order on-wire */
struct qid9p {
uint64_t qidpath;
uint32_t qidvers;
uint8_t qidtype;
};
typedef uint16_t p9ptag_t;
typedef uint32_t p9pfid_t;
/*
* refuse (no, not *that* refuse) to play if the server doesn't
* support requests of at least the following size. It would only
* make life difficult
*/
#define P9P_MINREQLEN 512
#define P9P_DEFREQLEN (16*1024)
#define P9P_INVALFID 0
#define P9P_ROOTFID 1
#define NEXTTAG(p9p) \
((++(p9p->nexttag)) == P9PROTO_NOTAG ? p9p->nexttag = 0 : p9p->nexttag)
#define NEXTFID(p9p) \
((++(p9p->nextfid)) == P9P_INVALFID ? p9p->nextfid = 2 : p9p->nextfid)
#define AUTOVAR(pcc) \
struct puffs9p *p9p = puffs_cc_getspecific(pcc); \
uint16_t tag = NEXTTAG(p9p); \
struct p9pbuf *pb = p9pbuf_make(p9p->maxreq, P9PB_OUT); \
int rv = 0
#define RETURN(rv) \
p9pbuf_destroy(pb); \
return (rv)
struct puffs9p;
/*
* XXX: urgh
*
* This is slightly messy / abusatory structure. It is used for multiple
* things. Typical life cycle: create output buffer, append to output
* queue (pcc included) . Once the buffer has been sent, the buffer is
* freed and the structure is appended to reqqueue as psreq. It is kept
* there until matching network data is read. Once this happens, the
* data from the received buffer is copied to buffer stored on the queue.
* This should be rewritten, clearly.
*
* Also, this should not be copypasted from psshfs. But that's
* another matter ;)
*/
#define P9PB_OUT 0
#define P9PB_IN 1
struct p9pbuf {
struct p9preq {
p9ptag_t tagid;
/* either ... */
struct puffs_cc *pcc;
/* ... or */
void (*func)(struct puffs9p *, struct p9pbuf *, void *);
void *arg;
/* no union, we'd need a "which" flag */
TAILQ_ENTRY(p9pbuf) entries;
} p9pr;
/* in / out */
uint32_t len;
uint32_t offset;
uint8_t *buf;
/* helpers for in */
uint32_t remain;
uint8_t type;
p9ptag_t tagid;
int state;
};
#define P9PBUF_PUT 0
#define P9PBUF_PUTDONE 1
#define P9PBUF_GETLEN 2
#define P9PBUF_GETDATA 3
#define P9PBUF_GETREADY 4
#define P9PB_CHECK(pb, space) if (pb->remain < (space)) return ENOMEM
struct puffs9p {
int servsock;
p9ptag_t nexttag;
p9pfid_t nextfid;
size_t maxreq; /* negotiated with server */
struct p9pbuf *curpb;
TAILQ_HEAD(, p9pbuf) outbufq;
TAILQ_HEAD(, p9pbuf) req_queue;
};
struct dirfid {
p9pfid_t fid;
off_t seekoff;
LIST_ENTRY(dirfid) entries;
};
struct p9pnode {
p9pfid_t fid_base;
p9pfid_t fid_open;
int opencount;
LIST_HEAD(,dirfid) dir_openlist;
};
struct p9pbuf *p9pbuf_make(size_t, int);
void p9pbuf_destroy(struct p9pbuf *);
void p9pbuf_recycle(struct p9pbuf *, int);
int p9pbuf_read(struct puffs9p *, struct p9pbuf *);
int p9pbuf_write(struct puffs9p *, struct p9pbuf *);
void outbuf_enqueue(struct puffs9p *, struct p9pbuf *,
struct puffs_cc *, uint16_t);
void outbuf_enqueue_nocc(struct puffs9p *, struct p9pbuf *,
void (*f)(struct puffs9p *,
struct p9pbuf *, void *),
void *, uint16_t);
struct p9pbuf *req_get(struct puffs9p *, uint16_t);
int p9pbuf_put_1(struct p9pbuf *, uint8_t);
int p9pbuf_put_2(struct p9pbuf *, uint16_t);
int p9pbuf_put_4(struct p9pbuf *, uint32_t);
int p9pbuf_put_8(struct p9pbuf *, uint64_t);
int p9pbuf_put_str(struct p9pbuf *, const char *);
int p9pbuf_put_data(struct p9pbuf *, const void *, uint16_t);
int p9pbuf_write_data(struct p9pbuf *, uint8_t *, uint32_t);
int p9pbuf_get_1(struct p9pbuf *, uint8_t *);
int p9pbuf_get_2(struct p9pbuf *, uint16_t *);
int p9pbuf_get_4(struct p9pbuf *, uint32_t *);
int p9pbuf_get_8(struct p9pbuf *, uint64_t *);
int p9pbuf_get_str(struct p9pbuf *, char **, uint16_t *);
int p9pbuf_get_data(struct p9pbuf *, uint8_t **, uint16_t *);
int p9pbuf_read_data(struct p9pbuf *, uint8_t *, uint32_t);
int p9pbuf_remaining(struct p9pbuf *);
int p9pbuf_tell(struct p9pbuf *);
void p9pbuf_seekset(struct p9pbuf *, int);
int proto_getqid(struct p9pbuf *, struct qid9p *);
int proto_getstat(struct p9pbuf *, struct vattr *, char **, uint16_t *);
int proto_expect_walk_nqids(struct p9pbuf *, uint16_t *);
int proto_expect_stat(struct p9pbuf *, struct vattr *);
int proto_expect_qid(struct p9pbuf *, uint8_t, struct qid9p *);
int proto_cc_dupfid(struct puffs_cc *, p9pfid_t, p9pfid_t);
int proto_cc_clunkfid(struct puffs_cc *, p9pfid_t, int);
int proto_cc_open(struct puffs_cc *, p9pfid_t, p9pfid_t, int);
void proto_make_stat(struct p9pbuf *, const struct vattr *, const char *);
struct puffs_node *p9p_handshake(struct puffs_usermount *, const char *);
void qid2vattr(struct vattr *, const struct qid9p *);
struct puffs_node *newp9pnode_va(struct puffs_usermount *,
const struct vattr *, p9pfid_t);
struct puffs_node *newp9pnode_qid(struct puffs_usermount *,
const struct qid9p *, p9pfid_t);
int getdfwithoffset(struct puffs_cc *, struct p9pnode *, off_t,
struct dirfid **);
void storedf(struct p9pnode *, struct dirfid *);
void releasedf(struct puffs_cc *, struct dirfid *);
void nukealldf(struct puffs_cc *, struct p9pnode *);
#endif /* PUFFS9P_H_ */

View File

@ -0,0 +1,558 @@
/* $NetBSD: node.c,v 1.1 2007/04/21 14:21:44 pooka Exp $ */
/*
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: node.c,v 1.1 2007/04/21 14:21:44 pooka Exp $");
#endif /* !lint */
#include <assert.h>
#include <errno.h>
#include <puffs.h>
#include <stdio.h>
#include "ninepuffs.h"
#include "nineproto.h"
static void *
nodecmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
{
struct vattr *vap = &pn->pn_va;
struct qid9p *qid = arg;
if (vap->va_fileid == qid->qidpath)
return pn;
return NULL;
}
int
puffs9p_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
const struct puffs_cn *pcn)
{
AUTOVAR(pcc);
struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
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;
pb = p9pbuf_make(p9p->maxreq, P9PB_OUT);
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);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
rv = proto_expect_walk_nqids(pb, &nqid);
if (rv) {
rv = ENOENT;
goto out;
}
if (nqid != 1 || !proto_getqid(pb, &newqid)) {
rv = EPROTO;
goto out;
}
pn = puffs_pn_nodewalk(pu, nodecmp, &newqid);
if (pn == NULL)
pn = newp9pnode_qid(pu, &newqid, tfid);
else
proto_cc_clunkfid(pcc, tfid, 0);
*newnode = pn;
*newtype = pn->pn_va.va_type;
*newsize = pn->pn_va.va_size;
*newrdev = pn->pn_va.va_rdev;
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_cc *pcc, 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(pcc);
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(pcc, 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 */
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
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(pcc, dfp);
goto out;
}
while (count > 0) {
if (!proto_getstat(pb, &va, &name, &statsize)) {
rv = EINVAL;
goto out;
}
puffs_nextdent(&dent, name, va.va_fileid,
puffs_vtype2dt(va.va_type), reslen);
count -= statsize;
*readoff += statsize;
dfp->seekoff += statsize;
}
storedf(p9n, dfp);
out:
RETURN(rv);
}
int
puffs9p_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *vap,
const struct puffs_cred *pcr, pid_t pid)
{
AUTOVAR(pcc);
struct puffs_node *pn = opc;
struct p9pnode *p9n = pn->pn_data;
pb = p9pbuf_make(p9p->maxreq, P9PB_OUT);
p9pbuf_put_1(pb, P9PROTO_T_STAT);
p9pbuf_put_2(pb, tag);
p9pbuf_put_4(pb, p9n->fid_base);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
rv = proto_expect_stat(pb, &pn->pn_va);
if (rv)
goto out;
memcpy(vap, &pn->pn_va, sizeof(struct vattr));
out:
RETURN(rv);
}
int
puffs9p_node_setattr(struct puffs_cc *pcc, void *opc,
const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
{
AUTOVAR(pcc);
struct puffs_node *pn = opc;
struct p9pnode *p9n = pn->pn_data;
pb = p9pbuf_make(p9p->maxreq, P9PB_OUT);
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);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
if (pb->type != P9PROTO_R_WSTAT)
rv = EPROTO;
RETURN(rv);
}
/*
* Ok, time to get clever. There are two possible cases: we are
* opening a file or we are opening a directory.
*
* If it's a directory, don't bother opening it here, but rather
* wait until readdir, since it's probable we need to be able to
* open a directory there in any case.
*
* If it's a regular file, open it with full permissions here.
* Let the upper layers of the kernel worry about permission
* control.
*
* XXX: this doesn't work too well due to us hitting the open file
* limit pretty soon
*/
int
puffs9p_node_open(struct puffs_cc *pcc, void *opc, int mode,
const struct puffs_cred *pcr, pid_t pid)
{
struct puffs9p *p9p = puffs_cc_getspecific(pcc);
struct puffs_node *pn = opc;
struct p9pnode *p9n = pn->pn_data;
p9pfid_t nfid;
int error = 0;
p9n->opencount++;
if (pn->pn_va.va_type != VDIR && p9n->fid_open == P9P_INVALFID) {
nfid = NEXTFID(p9p);
error = proto_cc_open(pcc, p9n->fid_base, nfid,
P9PROTO_OMODE_RDWR);
if (error)
return error;
p9n->fid_open = nfid;
}
return 0;
}
int
puffs9p_node_close(struct puffs_cc *pcc, void *opc, int flags,
const struct puffs_cred *pcr, pid_t pid)
{
struct puffs_node *pn = opc;
struct p9pnode *p9n = pn->pn_data;
if (--p9n->opencount == 0)
if (pn->pn_va.va_type == VDIR)
nukealldf(pcc, p9n);
return 0;
}
int
puffs9p_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
off_t offset, size_t *resid, const struct puffs_cred *pcr,
int ioflag)
{
AUTOVAR(pcc);
struct puffs_node *pn = opc;
struct p9pnode *p9n = pn->pn_data;
uint32_t count;
size_t nread;
nread = 0;
while (*resid > 0) {
p9pbuf_put_1(pb, P9PROTO_T_READ);
p9pbuf_put_2(pb, tag);
p9pbuf_put_4(pb, p9n->fid_open);
p9pbuf_put_8(pb, offset+nread);
p9pbuf_put_4(pb, MIN((uint32_t)*resid,p9p->maxreq-24));
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
if (pb->type != P9PROTO_R_READ) {
rv = EPROTO;
break;
}
p9pbuf_get_4(pb, &count);
if (!p9pbuf_read_data(pb, buf + nread, count)) {
rv = EPROTO;
break;
}
*resid -= count;
nread += count;
p9pbuf_recycle(pb, P9PB_OUT);
}
RETURN(rv);
}
int
puffs9p_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
off_t offset, size_t *resid, const struct puffs_cred *cred,
int ioflag)
{
AUTOVAR(pcc);
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_open);
p9pbuf_put_8(pb, offset+nwrite);
p9pbuf_put_4(pb, chunk);
p9pbuf_write_data(pb, buf+nwrite, chunk);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
if (pb->type != P9PROTO_R_WRITE) {
rv = EPROTO;
break;
}
p9pbuf_get_4(pb, &count);
*resid -= count;
nwrite += count;
if (count != chunk) {
rv = EPROTO;
break;
}
p9pbuf_recycle(pb, P9PB_OUT);
}
RETURN(rv);
}
static int
nodecreate(struct puffs_cc *pcc, struct puffs_node *pn, void **newnode,
const char *name, const struct vattr *vap, uint32_t dirbit)
{
AUTOVAR(pcc);
struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
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(pcc, 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);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
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(pcc, nfid, 0);
nfid = NEXTFID(p9p);
p9pbuf_recycle(pb, P9PB_OUT);
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);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
/*
* someone removed it already? try again
* note: this is kind of lose/lose
*/
if (pb->type != P9PROTO_R_WALK)
goto again;
pn_new = newp9pnode_va(pu, vap, nfid);
qid2vattr(&pn_new->pn_va, &nqid);
*newnode = pn_new;
out:
RETURN(rv);
}
int
puffs9p_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
const struct puffs_cn *pcn, const struct vattr *va)
{
return nodecreate(pcc, opc, newnode, pcn->pcn_name, va, 0);
}
int
puffs9p_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
const struct puffs_cn *pcn, const struct vattr *va)
{
return nodecreate(pcc, opc, newnode, pcn->pcn_name,
va, P9PROTO_CPERM_DIR);
}
/*
* 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_cc *pcc, struct p9pnode *p9n)
{
AUTOVAR(pcc);
p9pfid_t testfid = NEXTFID(p9p);
rv = proto_cc_dupfid(pcc, p9n->fid_base, testfid);
p9pbuf_put_1(pb, P9PROTO_T_REMOVE);
p9pbuf_put_2(pb, tag);
p9pbuf_put_4(pb, testfid);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
if (pb->type != P9PROTO_R_REMOVE) {
rv = EPROTO;
} else {
proto_cc_clunkfid(pcc, p9n->fid_base, 0);
p9n->fid_base = P9P_INVALFID;
}
RETURN(rv);
}
int
puffs9p_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
const struct puffs_cn *pcn)
{
struct puffs_node *pn = targ;
if (pn->pn_va.va_type == VDIR)
return EISDIR;
return noderemove(pcc, pn->pn_data);
}
int
puffs9p_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
const struct puffs_cn *pcn)
{
struct puffs_node *pn = targ;
if (pn->pn_va.va_type != VDIR)
return ENOTDIR;
return noderemove(pcc, pn->pn_data);
}
/*
* 9P supports renames only for regular files within a directory
* from what I could tell. So just support in-directory renames
* for now.
*/
int
puffs9p_node_rename(struct puffs_cc *pcc, void *opc, void *src,
const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
const struct puffs_cn *pcn_targ)
{
AUTOVAR(pcc);
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(pcc, pn_targ->pn_data);
if (rv)
return rv;
}
pb = p9pbuf_make(p9p->maxreq, P9PB_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);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
if (pb->type != P9PROTO_R_WSTAT)
rv = EPROTO;
out:
RETURN(rv);
}
/*
* - "here's one"
* - "9P"
* ~ "i'm not dead"
* - "you're not fooling anyone you know, you'll be stone dead in a minute
* - "he says he's not quite dead"
* - "isn't there anything you could do?"
* - *clunk*!
* - "thanks"
*/
int
puffs9p_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
{
#if 0
if (p9n->fid_open != P9P_INVALFID)
proto_cc_clunkfid(pcc, p9n->fid_open, 0);
#endif
return 0;
}

View File

@ -0,0 +1,197 @@
/* $NetBSD: subr.c,v 1.1 2007/04/21 14:21:44 pooka Exp $ */
/*
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: subr.c,v 1.1 2007/04/21 14:21:44 pooka Exp $");
#endif /* !lint */
#include <sys/types.h>
#include <errno.h>
#include <puffs.h>
#include <stdlib.h>
#include <util.h>
#include "ninepuffs.h"
#include "nineproto.h"
void
qid2vattr(struct vattr *vap, const struct qid9p *qid)
{
vap->va_fileid = qid->qidpath;
vap->va_gen = qid->qidvers;
if (qid->qidtype & P9PROTO_QID_TYPE_DIR)
vap->va_type = VDIR;
else
vap->va_type = VREG;
}
static struct puffs_node *
makep9pnode(struct puffs_usermount *pu, p9pfid_t fid)
{
struct p9pnode *p9n;
struct puffs_node *pn;
p9n = emalloc(sizeof(struct p9pnode));
memset(p9n, 0, sizeof(struct p9pnode));
p9n->fid_base = fid;
LIST_INIT(&p9n->dir_openlist);
pn = puffs_pn_new(pu, p9n);
if (pn == NULL)
abort();
return pn;
}
struct puffs_node *
newp9pnode_va(struct puffs_usermount *pu, const struct vattr *va, p9pfid_t fid)
{
struct puffs_node *pn;
pn = makep9pnode(pu, fid);
pn->pn_va = *va;
return pn;
}
struct puffs_node *
newp9pnode_qid(struct puffs_usermount *pu, const struct qid9p *qid,
p9pfid_t fid)
{
struct puffs_node *pn;
pn = makep9pnode(pu, fid);
puffs_vattr_null(&pn->pn_va);
qid2vattr(&pn->pn_va, qid);
return pn;
}
/*
* search list of fids, or if none is found, walk a fid for a new one
* and issue dummy readdirs until we get the result we want
*/
int
getdfwithoffset(struct puffs_cc *pcc, struct p9pnode *p9n, off_t wantoff,
struct dirfid **rfid)
{
struct puffs9p *p9p = puffs_cc_getspecific(pcc);
struct p9pbuf *pb;
struct dirfid *dfp = NULL;
p9ptag_t tag = NEXTTAG(p9p);
off_t curoff, advance;
uint32_t count;
int error;
LIST_FOREACH(dfp, &p9n->dir_openlist, entries) {
if (dfp->seekoff == wantoff) {
LIST_REMOVE(dfp, entries);
*rfid = dfp;
return 0;
}
}
/* didn't get off easy? damn, do manual labour */
pb = p9pbuf_make(p9p->maxreq, P9PB_OUT);
dfp = ecalloc(1, sizeof(struct dirfid));
dfp->fid = NEXTFID(p9p);
error = proto_cc_open(pcc, p9n->fid_base, dfp->fid, P9PROTO_OMODE_READ);
if (error)
goto errout;
for (curoff = 0;;) {
advance = wantoff - curoff;
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, 0);
p9pbuf_put_4(pb, advance);
outbuf_enqueue(p9p, pb, pcc, tag);
puffs_cc_yield(pcc);
if (pb->type != P9PROTO_R_READ) {
error = EPROTO;
goto errout;
}
/*
* Check how many bytes we got. If we got the amount we
* wanted, we are at the correct position. If we got
* zero bytes, either the directory doesn't "support" the
* seek offset we want (someone has probably inserted an
* entry meantime) or we at the end of directory. Either
* way, let the upper layer deal with it.
*/
p9pbuf_get_4(pb, &count);
curoff += count;
if (count == advance || count == 0)
break;
p9pbuf_recycle(pb, P9PB_OUT);
}
dfp->seekoff = curoff;
*rfid = dfp;
return 0;
errout:
p9pbuf_destroy(pb);
free(dfp);
return error;
}
void
releasedf(struct puffs_cc *pcc, struct dirfid *dfp)
{
proto_cc_clunkfid(pcc, dfp->fid, 0);
free(dfp);
}
void
storedf(struct p9pnode *p9n, struct dirfid *dfp)
{
LIST_INSERT_HEAD(&p9n->dir_openlist, dfp, entries);
}
void
nukealldf(struct puffs_cc *pcc, struct p9pnode *p9n)
{
struct dirfid *dfp, *dfp_next;
for (dfp = LIST_FIRST(&p9n->dir_openlist); dfp; dfp = dfp_next) {
dfp_next = LIST_NEXT(dfp, entries);
LIST_REMOVE(dfp, entries);
releasedf(pcc, dfp);
}
}