From e73a712f80c3cb7f4ae3b06f6e0b72a0a4883c06 Mon Sep 17 00:00:00 2001 From: pooka Date: Sat, 21 Apr 2007 14:21:42 +0000 Subject: [PATCH] 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. --- usr.sbin/puffs/mount_9p/Makefile | 12 + usr.sbin/puffs/mount_9p/fs.c | 227 +++++++++++ usr.sbin/puffs/mount_9p/mount_9p.8 | 64 ++++ usr.sbin/puffs/mount_9p/ninebuf.c | 454 ++++++++++++++++++++++ usr.sbin/puffs/mount_9p/nineproto.c | 365 ++++++++++++++++++ usr.sbin/puffs/mount_9p/nineproto.h | 93 +++++ usr.sbin/puffs/mount_9p/ninepuffs.c | 394 ++++++++++++++++++++ usr.sbin/puffs/mount_9p/ninepuffs.h | 215 +++++++++++ usr.sbin/puffs/mount_9p/node.c | 558 ++++++++++++++++++++++++++++ usr.sbin/puffs/mount_9p/subr.c | 197 ++++++++++ 10 files changed, 2579 insertions(+) create mode 100644 usr.sbin/puffs/mount_9p/Makefile create mode 100644 usr.sbin/puffs/mount_9p/fs.c create mode 100644 usr.sbin/puffs/mount_9p/mount_9p.8 create mode 100644 usr.sbin/puffs/mount_9p/ninebuf.c create mode 100644 usr.sbin/puffs/mount_9p/nineproto.c create mode 100644 usr.sbin/puffs/mount_9p/nineproto.h create mode 100644 usr.sbin/puffs/mount_9p/ninepuffs.c create mode 100644 usr.sbin/puffs/mount_9p/ninepuffs.h create mode 100644 usr.sbin/puffs/mount_9p/node.c create mode 100644 usr.sbin/puffs/mount_9p/subr.c diff --git a/usr.sbin/puffs/mount_9p/Makefile b/usr.sbin/puffs/mount_9p/Makefile new file mode 100644 index 000000000000..e5ef5d3ee0b7 --- /dev/null +++ b/usr.sbin/puffs/mount_9p/Makefile @@ -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 diff --git a/usr.sbin/puffs/mount_9p/fs.c b/usr.sbin/puffs/mount_9p/fs.c new file mode 100644 index 000000000000..719cb8317344 --- /dev/null +++ b/usr.sbin/puffs/mount_9p/fs.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: fs.c,v 1.1 2007/04/21 14:21:43 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/usr.sbin/puffs/mount_9p/mount_9p.8 b/usr.sbin/puffs/mount_9p/mount_9p.8 new file mode 100644 index 000000000000..f9e46d9dc67e --- /dev/null +++ b/usr.sbin/puffs/mount_9p/mount_9p.8 @@ -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. diff --git a/usr.sbin/puffs/mount_9p/ninebuf.c b/usr.sbin/puffs/mount_9p/ninebuf.c new file mode 100644 index 000000000000..3f4f59244017 --- /dev/null +++ b/usr.sbin/puffs/mount_9p/ninebuf.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: ninebuf.c,v 1.1 2007/04/21 14:21:43 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/usr.sbin/puffs/mount_9p/nineproto.c b/usr.sbin/puffs/mount_9p/nineproto.c new file mode 100644 index 000000000000..3a64c849bc0c --- /dev/null +++ b/usr.sbin/puffs/mount_9p/nineproto.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: nineproto.c,v 1.1 2007/04/21 14:21:43 pooka Exp $"); +#endif /* !lint */ + +#include + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/usr.sbin/puffs/mount_9p/nineproto.h b/usr.sbin/puffs/mount_9p/nineproto.h new file mode 100644 index 000000000000..8957c54c529d --- /dev/null +++ b/usr.sbin/puffs/mount_9p/nineproto.h @@ -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 + +#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_ */ diff --git a/usr.sbin/puffs/mount_9p/ninepuffs.c b/usr.sbin/puffs/mount_9p/ninepuffs.c new file mode 100644 index 000000000000..c564bbb55b22 --- /dev/null +++ b/usr.sbin/puffs/mount_9p/ninepuffs.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: ninepuffs.c,v 1.1 2007/04/21 14:21:43 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/usr.sbin/puffs/mount_9p/ninepuffs.h b/usr.sbin/puffs/mount_9p/ninepuffs.h new file mode 100644 index 000000000000..0022d5fdbdc4 --- /dev/null +++ b/usr.sbin/puffs/mount_9p/ninepuffs.h @@ -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 + +#include + +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_ */ diff --git a/usr.sbin/puffs/mount_9p/node.c b/usr.sbin/puffs/mount_9p/node.c new file mode 100644 index 000000000000..e4512812aa04 --- /dev/null +++ b/usr.sbin/puffs/mount_9p/node.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: node.c,v 1.1 2007/04/21 14:21:44 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include +#include + +#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; +} diff --git a/usr.sbin/puffs/mount_9p/subr.c b/usr.sbin/puffs/mount_9p/subr.c new file mode 100644 index 000000000000..9b715da3ef38 --- /dev/null +++ b/usr.sbin/puffs/mount_9p/subr.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: subr.c,v 1.1 2007/04/21 14:21:44 pooka Exp $"); +#endif /* !lint */ + +#include + +#include +#include +#include +#include + +#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); + } +}