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:
parent
caf05d897b
commit
e73a712f80
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
|
@ -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);
|
||||
}
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue