One can actually read a file now! Yay!

I have another patch to allocate each message buffer from a pool as needed 
rather than share one among all requests, but I'm not sure that it makes sense 
to do so, as all requests go thru the same channel anyhow.

diff --git a/sbin/mount_9p/mount_9p.c b/sbin/mount_9p/mount_9p.c
new file mode 100644
index 0000000..418f1f7
--- /dev/null
+++ b/sbin/mount_9p/mount_9p.c
@@ -0,0 +1,18 @@
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <stdio.h>
+#include <err.h>
+
+int
+main (int argc, char *argu[])
+{
+       struct p9p_args args;
+       int c;
+
+       if (argc < 3) errx (1, "usage: %s fd path", argu[0]);
+       args.fd = strtol (argu[1], 0, 10);
+       args.msize = 1 << 20;
+       c = mount (MOUNT_P9P, argu[2], 0, &args);
+       if (c) err (1, "failed to mount");
+       return 0;
+}
diff --git a/sys/9p/9p.c b/sys/9p/9p.c
new file mode 100644
index 0000000..37c5ebd
--- /dev/null
+++ b/sys/9p/9p.c
@@ -0,0 +1,251 @@
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include "9p.h"
+#include "util.h"
+
+int read9pmsg (struct file *fp, uint8_t *msg, size_t n) {
+       ssize_t size;
+       int c;
+
+       if (n < 7) return EMSGSIZE;
+       c = read (fp, msg, 7);
+       if (c) return c;
+       LOAD32LE(size, msg);
+       if (size < 7) return EIO;
+       switch (msg[4] /* type */) {
+       // Special case for TWrite and RRead, lest we copy huge data
+       case RRead:
+               size = 11;
+               break;
+       case TWrite:
+               size = 23;
+               break;
+       }
+       if (n < size) return EMSGSIZE;
+       return read (fp, msg + 7, size - 7);
+}
+
+int write9pmsg (struct file *fp, uint8_t *msg) {
+       size_t size;
+
+       LOAD32LE(size, msg);
+       switch (msg[4] /* type */) {
+       case RRead:
+               size = 11;
+               break;
+       case TWrite:
+               size = 23;
+               break;
+       }
+       return write (fp, msg, size);
+}
+
+size_t loadQid (Qid *p_qid, uint8_t *msg) {
+       p_qid -> type = msg[0];
+       LOAD32LE(p_qid -> vers, msg + 1);
+       LOAD64LE(p_qid -> path, msg + 5);
+       return 13;
+}
+
+size_t storQid (Qid *p_qid, uint8_t *msg) {
+       msg[0] = p_qid -> type;
+       STOR32LE(p_qid -> vers, msg + 1);
+       STOR64LE(p_qid -> path, msg + 5);
+       return 13;
+}
+
+static size_t vsscan9p1 (uint8_t *msg, char fmt, void *p) {
+       size_t l;
+       switch (fmt) {
+       case '0':
+               *(uint8_t *)p = msg[0];
+               return 1;
+       case '1':
+               LOAD16LE(*(uint16_t *)p, msg);
+               return 2;
+       case '2':
+               LOAD32LE(*(uint32_t *)p, msg);
+               return 4;
+       case '3':
+               LOAD64LE(*(uint64_t *)p, msg);
+               return 8;
+       case 'd':
+               return loadDir (p, msg);
+       case 'q':
+               return loadQid (p, msg);
+       case 's':
+               LOAD16LE(l, msg);
+               *(char **)p = malloc (l + 1, M_TEMP, M_WAITOK);
+               strlcpy (*(char **)p, msg + 2, l + 1);
+               return (2 + l);
+       default:
+               panic ("bad 9p message format spec");
+       }
+}
+
+static size_t vsprint9p1 (uint8_t *msg, char fmt, void *p) {
+       size_t l;
+       char *xs;
+       switch (fmt) {
+       case '0':
+               if (msg) msg[0] = *(uint8_t *)p;
+               return 1;
+       case '1':
+               if (msg) STOR16LE(*(uint16_t *)p, msg);
+               return 2;
+       case '2':
+               if (msg) STOR32LE(*(uint32_t *)p, msg);
+               return 4;
+       case '3':
+               if (msg) STOR64LE(*(uint64_t *)p, msg);
+               return 8;
+       case 'd':
+               return storDir (p, msg);
+       case 'q':
+               return (msg ? storQid (p, msg) : 13);
+       case 's':
+               xs = *(char **)p;
+               l = xs ? strlen (xs) : 0;
+               if (msg) {
+                       STOR16LE(l, msg);
+                       if (xs) memcpy (msg + 2, xs, l);
+               }
+               return (2 + l);
+       default:
+               panic ("bad 9p message format spec");
+       }
+}
+
+#define ATOFFSET(t, p, r) (*(t *)((uint8_t *)(p) + r))
+
+static size_t vsscan9p (uint8_t *msg, char *fmt, void *p, size_t rs[]) {
+       size_t n;
+       uint8_t *q, *msg0;
+       msg0 = msg;
+       for (; fmt[0]; fmt++, rs++) switch (fmt[0]) {
+       case 'l':
+               msg += 2;
+               rs--;
+               break;
+       case 'n':
+               LOAD16LE(n, msg);
+               msg += 2;
+               ATOFFSET(uint16_t, p, rs[0]) = n;
+               fmt++;
+               rs++;
+               q = malloc (sizeof (void *)*n, M_TEMP, M_WAITOK);
+               ATOFFSET(void *, p, rs[0]) = q;
+               for (size_t ii = 0; ii < n; ii++) {
+                       size_t m = vsscan9p1 (msg, fmt[0], q);
+                       msg += m;
+                       q += fmt[0] == 's' ? sizeof (void *) : m;
+               }
+               break;
+       default:
+               msg += vsscan9p1 (msg, fmt[0], (uint8_t *)p + rs[0]);
+       }
+       return msg - msg0;
+}
+
+static size_t vsprint9p (uint8_t *msg, char *fmt, void *p, size_t rs[]) {
+       size_t n;
+       uint8_t *q, *msg0;
+       msg0 = msg;
+       for (; fmt[0]; fmt++, rs++) switch (fmt[0]) {
+       case 'l':
+               n = vsprint9p (0, fmt + 1, p, rs);
+               if (msg0) STOR16LE(n, msg);
+               msg += 2;
+               rs--;
+               break;
+       case 'n':
+               n = ATOFFSET(uint16_t, p, rs[0]);
+               if (msg0) STOR16LE(n, msg);
+               msg += 2;
+               fmt++;
+               rs++;
+               q = ATOFFSET(void *, p, rs[0]);
+               for (size_t ii = 0; ii < n; ii++) {
+                       size_t m = vsprint9p1 (msg0 ? msg : 0, fmt[0], q);
+                       msg += m;
+                       q += fmt[0] == 's' ? sizeof (void *) : m;
+               }
+               break;
+       default:
+               msg += vsprint9p1 (msg0 ? msg : 0, fmt[0], (uint8_t *)p + 
rs[0]);
+       }
+       return msg - msg0;
+}
+
+static void *fmts[][2] = {
+       [TVersion]      = { "2s",       (size_t []){ offsetof (Fcall, msize), 
offsetof (Fcall, version) } },
+       [RVersion]      = { "2s",       (size_t []){ offsetof (Fcall, msize), 
offsetof (Fcall, version) } },
+       [TAuth]         = { "2ss",      (size_t []){ offsetof (Fcall, afid), 
offsetof (Fcall, uname), offsetof (Fcall, aname) } },
+       [RAuth]         = { "q",        (size_t []){ offsetof (Fcall, aqid) } },
+       [TError]        = { 0 }, // invalid
+       [RError]        = { "s",        (size_t []){ offsetof (Fcall, ename) } 
},
+       [TFlush]        = { "1",        (size_t []){ offsetof (Fcall, oldtag) } 
},
+       [RFlush]        = { "",         (size_t []){} },
+       [TAttach]       = { "22ss",     (size_t []){ offsetof (Fcall, fid), 
offsetof (Fcall, afid), offsetof (Fcall, uname), offsetof (Fcall, aname) } },
+       [RAttach]       = { "q",        (size_t []){ offsetof (Fcall, qid) } },
+       [TWalk]         = { "22ns",     (size_t []){ offsetof (Fcall, fid), 
offsetof (Fcall, newfid), offsetof (Fcall, nwname), offsetof (Fcall, wname) } },
+       [RWalk]         = { "nq",       (size_t []){ offsetof (Fcall, nwqid), 
offsetof (Fcall, wqid) } },
+       [TOpen]         = { "20",       (size_t []){ offsetof (Fcall, fid), 
offsetof (Fcall, mode) } },
+       [ROpen]         = { "q2",       (size_t []){ offsetof (Fcall, qid), 
offsetof (Fcall, iounit) } },
+       [TCreate]       = { "2s20",     (size_t []){ offsetof (Fcall, fid), 
offsetof (Fcall, name), offsetof (Fcall, perm), offsetof (Fcall, mode) } },
+       [RCreate]       = { "q2",       (size_t []){ offsetof (Fcall, qid), 
offsetof (Fcall, iounit) } },
+       [TRead]         = { "232",      (size_t []){ offsetof (Fcall, fid), 
offsetof (Fcall, offset), offsetof (Fcall, count) } },
+       [RRead]         = { "2",        (size_t []){ offsetof (Fcall, count) } 
},
+       [TWrite]        = { "232",      (size_t []){ offsetof (Fcall, fid), 
offsetof (Fcall, offset), offsetof (Fcall, count) } },
+       [RWrite]        = { "2",        (size_t []){ offsetof (Fcall, count) } 
},
+       [TClunk]        = { "2",        (size_t []){ offsetof (Fcall, fid) } },
+       [RClunk]        = { "",         (size_t []){} },
+       [TRemove]       = { "2",        (size_t []){ offsetof (Fcall, fid) } },
+       [RRemove]       = { "",         (size_t []){} },
+       [TStat]         = { "2",        (size_t []){ offsetof (Fcall, fid) } },
+       [RStat]         = { "ld",       (size_t []){ offsetof (Fcall, st) } },
+       [TWStat]        = { "2ld",      (size_t []){ offsetof (Fcall, fid), 
offsetof (Fcall, st) } },
+       [RWStat]        = { "",         (size_t []){} },
+};
+
+ssize_t loadFcall (Fcall *p_fcall, uint8_t *msg) {
+       p_fcall -> type = msg[4];
+       LOAD16LE(p_fcall -> tag, msg + 5);
+       if (msg[4] < P9PMIN || msg[4] > P9PMAX || !fmts[msg[4]][1]) return (-1);
+       return (vsscan9p (msg + 7, fmts[p_fcall -> type][0], p_fcall, 
fmts[p_fcall -> type][1]) + 7);
+}
+
+ssize_t storFcall (Fcall *p_fcall, uint8_t *msg) {
+       size_t size;
+       size = vsprint9p (msg ? msg + 7 : 0, fmts[p_fcall -> type][0], p_fcall, 
fmts[p_fcall -> type][1]) + 7;
+       if (msg) {
+               msg[4] = p_fcall -> type;
+               STOR16LE(p_fcall -> tag, msg + 5);
+               STOR32LE(size + (msg[4] == RRead ? p_fcall -> count : 0), msg);
+       }
+       return size;
+}
+
+static
+size_t goDir (Dir *p_d, uint8_t *msg, int w) {
+       size_t l = (w ? vsprint9p : vsscan9p) (msg ? msg + 2 : 0, 
"12q2223ssss", p_d, (size_t []){ offsetof (Dir, type), offsetof (Dir, dev), 
offsetof (Dir, qid), offsetof (Dir, mode), offsetof (Dir, atime), offsetof 
(Dir, mtime), offsetof (Dir, length), offsetof (Dir, name), offsetof (Dir, 
uid), offsetof (Dir, gid), offsetof (Dir, muid) }) + 2;
+       if (msg) STOR16LE(l, msg);
+       return l + 2;
+}
+
+size_t loadDir (Dir *p_d, uint8_t *msg) {
+       return goDir (p_d, msg, 0);
+}
+
+size_t storDir (Dir *p_d, uint8_t *msg) {
+       return goDir (p_d, msg, 1);
+}
+
+void freeDir (Dir *p_d) {
+       free (p_d -> name, M_TEMP);
+       free (p_d -> uid,  M_TEMP);
+       free (p_d -> gid,  M_TEMP);
+       free (p_d -> muid, M_TEMP);
+}
diff --git a/sys/9p/9p.h b/sys/9p/9p.h
new file mode 100644
index 0000000..7e0978a
--- /dev/null
+++ b/sys/9p/9p.h
@@ -0,0 +1,171 @@
+/* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
+ * See LICENSE file for license details.
+ */
+
+/* 9P message types */
+enum {
+       P9PMIN          = 0x64,
+       TVersion        = 0x64,
+       RVersion,
+       TAuth           = 0x66,
+       RAuth,
+       TAttach         = 0x68,
+       RAttach,
+       TError          = 0x6A, /* illegal */
+       RError,
+       TFlush          = 0x6C,
+       RFlush,
+       TWalk           = 0x6E,
+       RWalk,
+       TOpen           = 0x70,
+       ROpen,
+       TCreate         = 0x72,
+       RCreate,
+       TRead           = 0x74,
+       RRead,
+       TWrite          = 0x76,
+       RWrite,
+       TClunk          = 0x78,
+       RClunk,
+       TRemove         = 0x7A,
+       RRemove,
+       TStat           = 0x7C,
+       RStat,
+       TWStat          = 0x7E,
+       RWStat,
+       P9PMAX          = 0x7F,
+};
+
+/* from libc.h */
+enum {
+       OREAD           = 0x0000,       /* open for read */
+       OWRITE          = 0x0001,       /* write */
+       ORDWR           = 0x0002,       /* read and write */
+       OEXEC           = 0x0003,       /* execute, == read but check execute 
permission */
+       OTRUNC          = 0x0010,       /* or'ed in (except for exec), truncate 
file first */
+       OCEXEC          = 0x0020,       /* or'ed in, close on exec */
+       ORCLOSE         = 0x0040,       /* or'ed in, remove on close */
+       ODIRECT         = 0x0080,       /* or'ed in, direct access */
+       ONONBLOCK       = 0x0100,       /* or'ed in, non-blocking call */
+       OEXCL           = 0x1000,       /* or'ed in, exclusive use (create 
only) */
+       OLOCK           = 0x2000,       /* or'ed in, lock after opening */
+       OAPPEND         = 0x4000        /* or'ed in, append only */
+};
+
+/* bits in Qid.type */
+enum {
+       QTDIR           = 0x80, /* type bit for directories */
+       QTAPPEND        = 0x40, /* type bit for append only files */
+       QTEXCL          = 0x20, /* type bit for exclusive use files */
+       QTMOUNT         = 0x10, /* type bit for mounted channel */
+       QTAUTH          = 0x08, /* type bit for authentication file */
+       QTTMP           = 0x04, /* type bit for non-backed-up file */
+       QTSYMLINK       = 0x02, /* type bit for symbolic link */
+       QTFILE          = 0x00  /* type bits for plain file */
+};
+
+/* bits in Stat.mode */
+enum {
+       DMEXEC  = 0x1,          /* mode bit for execute permission */
+       DMWRITE = 0x2,          /* mode bit for write permission */
+       DMREAD  = 0x4,          /* mode bit for read permission */
+
+       DMDIR           = 0x80000000,   /* mode bit for directories */
+       DMAPPEND        = 0x40000000,   /* mode bit for append only files */
+       DMEXCL          = 0x20000000,   /* mode bit for exclusive use files */
+       DMMOUNT         = 0x10000000,   /* mode bit for mounted channel */
+       DMAUTH          = 0x08000000,   /* mode bit for authentication file */
+       DMTMP           = 0x04000000,   /* mode bit for non-backed-up file */
+       DMSYMLINK       = 0x02000000,   /* mode bit for symbolic link (Unix, 
9P2000.u) */
+       DMDEVICE        = 0x00800000,   /* mode bit for device file (Unix, 
9P2000.u) */
+       DMNAMEDPIPE     = 0x00200000,   /* mode bit for named pipe (Unix, 
9P2000.u) */
+       DMSOCKET        = 0x00100000,   /* mode bit for socket (Unix, 9P2000.u) 
*/
+       DMSETUID        = 0x00080000,   /* mode bit for setuid (Unix, 9P2000.u) 
*/
+       DMSETGID        = 0x00040000,   /* mode bit for setgid (Unix, 9P2000.u) 
*/
+};
+
+
+typedef struct {
+       uint8_t         type;
+       uint32_t        vers;
+       uint64_t        path;
+} Qid;
+
+typedef struct {
+       uint16_t        type;
+       uint32_t        dev;
+       Qid             qid;
+       uint32_t        mode;
+       uint32_t        atime;
+       uint32_t        mtime;
+       uint64_t        length;
+       char*           name;
+       char*           uid;
+       char*           gid;
+       char*           muid;
+} Dir;
+
+/* from fcall(3) in plan9port */
+typedef struct {
+       uint8_t type;
+       uint16_t tag;
+       uint32_t fid;
+
+       union {
+               struct { /* Tversion, Rversion */
+                       uint32_t        msize;
+                       char            *version;
+               };
+               struct { /* Tflush */
+                       uint16_t        oldtag;
+               };
+               struct { /* Rerror */
+                       char            *ename;
+               };
+               struct { /* Ropen, Rcreate */
+                       Qid             qid;    /* +Rattach */
+                       uint32_t        iounit;
+               };
+               struct { /* Rauth */
+                       Qid             aqid;
+               };
+               struct { /* Tauth, Tattach */
+                       uint32_t        afid;
+                       char            *uname;
+                       char            *aname;
+               };
+               struct { /* Tcreate */
+                       uint32_t        perm;
+                       char            *name;
+                       uint8_t         mode;   /* +Topen */
+               };
+               struct { /* Rwalk */
+                       uint32_t        newfid; /* +Twalk */
+                       union {
+                               uint16_t        nwname;
+                               uint16_t        nwqid;
+                       };
+                       union {
+                               char            **wname;
+                               Qid             *wqid;
+                       };
+               };
+               struct {
+                       uint64_t        offset; /* Tread, Twrite */
+                       uint32_t        count;  /* Tread, Twrite, Rread */
+               };
+               struct { /* Rstat, Twstat */
+                       Dir             st;
+               };
+       };
+} Fcall;
+
+int  read9pmsg (struct file *, uint8_t *, size_t);
+int write9pmsg (struct file *, uint8_t *);
+ssize_t loadFcall (Fcall *, uint8_t *);
+ssize_t storFcall (Fcall *, uint8_t *);
+size_t loadDir (Dir *, uint8_t *);
+size_t storDir (Dir *, uint8_t *);
+size_t loadQid (Qid *, uint8_t *);
+size_t storQid (Qid *, uint8_t *);
+void freeDir (Dir *);
diff --git a/sys/9p/p9p.h b/sys/9p/p9p.h
new file mode 100644
index 0000000..568b776
--- /dev/null
+++ b/sys/9p/p9p.h
@@ -0,0 +1,35 @@
+typedef struct p9p_mount {
+       struct file *fp;
+       struct proc *recverp;
+       size_t msize;
+       struct rwlock tagLock, fidLock, tfpLock, msgLock, rmsgLock;
+       Fcall **requs;
+       uint32_t leastFreeFid;
+       /* This could be allocated per message, but
+        * that would impossibilize saving work if
+        * no memory free.
+        * We could keep a message buffer per vnode,
+        * but that would be messy.
+        */
+       void *msg;
+       /* Recv thread having own message buffer lets
+        * it blockingly recv and not lock up buffer
+        * for senders.
+        */
+       void *rmsg;
+} p9p_mount_t;
+
+typedef struct p9p_node {
+       uint32_t fid;
+} p9p_node_t;
+
+uint16_t p9p_getTag (p9p_mount_t *, void *);
+void p9p_putTag (p9p_mount_t *, uint16_t);
+
+uint32_t p9p_getFid (p9p_mount_t *);
+void p9p_putFid (p9p_mount_t *, uint32_t); /* XXX unimpl */
+
+int p9p_require (p9p_mount_t *, Fcall *, struct uio *, struct ucred *);
+
+extern struct vops p9p_vops;
+extern const struct vfsops p9p_vfsops;
diff --git a/sys/9p/p9p_vfsops.c b/sys/9p/p9p_vfsops.c
new file mode 100644
index 0000000..c70f25d
--- /dev/null
+++ b/sys/9p/p9p_vfsops.c
@@ -0,0 +1,290 @@
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/signalvar.h>
+#include "9p.h"
+#include "p9p.h"
+#include "util.h"
+
+static int
+p9p_connect (p9p_mount_t *p9mp)
+{
+       Fcall fcall;
+       fcall.type = TVersion;
+       fcall.tag = ~0;
+       fcall.msize = p9mp -> msize;
+       fcall.version = "9P2000";
+       int c;
+
+       rw_enter_write (&p9mp -> msgLock); /* no one else should be using this 
now, but make sure */
+       storFcall (&fcall, p9mp -> msg);
+       rw_enter_write (&p9mp -> tfpLock); /* likewise */
+       if (!((c = write9pmsg (p9mp -> fp, p9mp -> msg)) ||
+              (c =  read9pmsg (p9mp -> fp, p9mp -> msg, p9mp -> msize)))) {
+               if (loadFcall (&fcall, p9mp -> msg) < 0) c = EIO;
+       }
+       rw_exit (&p9mp -> tfpLock);
+       rw_exit (&p9mp -> msgLock);
+       if (c) return c;
+       if (fcall.type != RVersion ||
+           fcall.msize > p9mp -> msize ||
+           strcmp (fcall.version, "9P2000") != 0) c = EIO;
+       p9mp -> msize = fcall.msize;
+       free (fcall.version, M_TEMP);
+       return c;
+}
+
+static int
+p9p_disconnect (p9p_mount_t *p9mp)
+{
+       return 0;
+}
+
+static void
+p9p_recver_main (p9p_mount_t *p9mp)
+{
+       Fcall fcall, *fcallp;
+       ssize_t l;
+       int c;
+
+       for (;;) {
+               rw_enter_write (&p9mp -> rmsgLock);
+               c = read9pmsg (p9mp -> fp, p9mp -> rmsg, p9mp -> msize);
+               if (c) kthread_exit (c);
+               l = loadFcall (&fcall, p9mp -> rmsg);
+               rw_exit (&p9mp -> rmsgLock);
+               if (l < 0) {
+                       fcall.type  = RError;
+                       fcall.ename = "bad message type";
+               }
+               rw_enter_read (&p9mp -> tagLock);
+               fcallp = p9mp -> requs[fcall.tag];
+               rw_exit (&p9mp -> tagLock);
+               if (fcallp) {
+                       *fcallp = fcall;
+                       wakeup (fcallp);
+               }
+               if (fcallp && fcall.type == RRead) tsleep (&p9mp -> fp, curproc 
-> p_priority, "9p RRead", 0); /* not clobber read data */
+       }
+}
+
+static int
+p9p_mount (struct mount *mp, const char *path, void *data, struct nameidata 
*ndp, struct proc *p)
+{
+       struct p9p_args args;
+       p9p_mount_t *p9mp;
+       int c;
+
+       if (c = copyin (data, &args, sizeof (struct p9p_args))) return c;
+
+       if (mp -> mnt_flag & MNT_UPDATE) return EOPNOTSUPP;
+
+       p9mp = malloc (sizeof (p9p_mount_t), M_P9PMNT, M_WAITOK | M_CANFAIL | 
M_ZERO);
+       if (!p9mp) {
+               c = ENOMEM;
+               goto fail;
+       }
+       mp -> mnt_data = p9mp;
+       p9mp -> msize = args.msize;
+
+       p9mp -> requs = malloc (sizeof (Fcall *) << 16, M_TEMP, M_WAITOK | 
M_CANFAIL);
+       p9mp -> msg   = malloc (p9mp -> msize,          M_TEMP, M_WAITOK | 
M_CANFAIL);
+       p9mp -> rmsg  = malloc (p9mp -> msize,          M_TEMP, M_WAITOK | 
M_CANFAIL);
+       if (!(p9mp -> requs && p9mp -> msg && p9mp -> rmsg)) {
+               c = ENOMEM;
+               goto fail;
+       }
+
+       rw_init (&p9mp ->  tagLock, 0);
+       rw_init (&p9mp ->  fidLock, 0);
+       rw_init (&p9mp ->  tfpLock, 0);
+       rw_init (&p9mp ->  msgLock, 0);
+       rw_init (&p9mp -> rmsgLock, 0);
+
+       if (!(p9mp -> fp = fd_getfile (p -> p_fd, args.fd))) {
+               c = EBADF;
+               goto fail;
+       }
+       FREF(p9mp -> fp);
+#if 0
+       if ((c = fdrelease (p, args.fd))) goto fail;
+#endif
+
+       vfs_getnewfsid (mp);
+
+       if ((c = p9p_connect (p9mp)) ||
+            (c = kthread_create ((void (*) (void *))p9p_recver_main, p9mp, 
&p9mp -> recverp, "9p recver"))) goto fail;
+       return 0;
+
+fail:
+       FRELE(p9mp -> fp, p);
+       free (p9mp -> requs, M_TEMP);
+       free (p9mp -> msg,   M_TEMP);
+       free (p9mp -> rmsg,  M_TEMP);
+       free (p9mp, M_P9PMNT);
+       mp -> mnt_data = 0;
+       return c;
+}
+
+static int
+p9p_unmount (struct mount *mp, int mntflags, struct proc *p)
+{
+       p9p_mount_t *p9mp;
+       int c, flags = 0;
+
+       p9mp = mp -> mnt_data;
+       KASSERT(p9mp);
+
+       if (mntflags & MNT_FORCE) flags |= FORCECLOSE;
+
+       if (c = vflush (mp, 0, flags)) return c;
+
+       psignal (p9mp -> recverp, SIGKILL);
+
+       c = p9p_disconnect (p9mp);
+
+       FRELE(p9mp -> fp, p);
+       free (p9mp -> requs, M_TEMP);
+       free (p9mp, M_MISCFSMNT);
+       mp -> mnt_data = 0;
+       return c;
+}
+
+static int
+p9p_root (struct mount *mp, struct vnode **vpp)
+{
+       Fcall fcall;
+       p9p_mount_t *p9mp;
+       p9p_node_t *p9np;
+       int c;
+
+       p9mp = mp -> mnt_data;
+       KASSERT(p9mp);
+
+       p9np = 0;
+       *vpp = 0;
+
+       if (c = getnewvnode (VT_P9P, mp, &p9p_vops, vpp)) return c;
+
+       p9np = malloc (sizeof (p9p_node_t), M_P9PNODE, M_WAITOK | M_CANFAIL);
+       if (!p9np) {
+               c = ENOMEM;
+               goto fail;
+       }
+       (*vpp) -> v_data = p9np;
+       fcall.type = TAttach;
+       fcall.fid = p9np -> fid = p9p_getFid (p9mp);
+       fcall.afid = ~0;
+       fcall.uname = "puffy";
+       fcall.aname = "";
+
+       c = p9p_require (p9mp, &fcall, 0, 0);
+       if (c) return c;
+       (*vpp) -> v_flag |= VROOT;
+       (*vpp) -> v_type  = fcall.qid.type & QTDIR ? VDIR : VREG;
+       return 0;
+
+fail:
+       if (*vpp) vrele (*vpp);
+       free (p9np, M_P9PNODE);
+       (*vpp) -> v_data = 0;
+       return c;
+}
+
+static int
+p9p_sync (struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)
+{
+       p9p_mount_t *p9mp;
+       int c = 0;
+
+       p9mp = mp -> mnt_data;
+       KASSERT(p9mp);
+
+       if (waitfor == MNT_WAIT) for (;;) {
+               uint16_t tag;
+               rw_enter_read (&p9mp -> tagLock);
+               for (tag = 0; ~tag; tag++) if (tag) {
+                       /* recver will wake us with thread that made request
+                        * when it recvs response for this tag
+                        */
+                       c = tsleep (&p9mp -> requs[tag], p -> p_priority, 
"sync", 0);
+                       break;
+               }
+               rw_exit (&p9mp -> tagLock);
+               if (c) return c;
+               if (!~tag /* found no used tags */) break;
+               c = 0;
+       }
+
+       return 0;
+}
+
+static int
+p9p_start (struct mount *mp, int flags, struct proc *p)
+{
+       return 0;
+}
+
+static int
+p9p_quotactl (struct mount *mp, int cmds, uid_t uid, caddr_t arg, struct proc 
*p)
+{
+       return EOPNOTSUPP;
+}
+
+static int
+p9p_statfs (struct mount *mp, struct statfs *sbp, struct proc *p)
+{
+       return EOPNOTSUPP;
+}
+
+static int
+p9p_vget (struct mount *mp, ino_t ino, struct vnode **vpp)
+{
+       return EOPNOTSUPP;
+}
+
+static int
+p9p_fhtovp (struct mount *mp, struct fid *fhp, struct vnode **vpp)
+{
+       return EOPNOTSUPP;
+}
+
+static int
+p9p_vptofh (struct vnode *vp, struct fid *fhp)
+{
+       return EOPNOTSUPP;
+}
+
+static int
+p9p_init (struct vfsconf *vfsconfp)
+{
+       return 0;
+}
+
+static int
+p9p_checkexp (struct mount *mp, struct mbuf *nam, int *extflagsp, struct ucred 
**credanonp)
+{
+       return EOPNOTSUPP;
+}
+
+const struct vfsops p9p_vfsops = {
+       .vfs_mount      = p9p_mount,
+       .vfs_unmount    = p9p_unmount,
+       .vfs_start      = p9p_start,
+       .vfs_root       = p9p_root,
+       .vfs_quotactl   = p9p_quotactl,
+       .vfs_statfs     = p9p_statfs,
+       .vfs_sync       = p9p_sync,
+       .vfs_vget       = p9p_vget,
+       .vfs_fhtovp     = p9p_fhtovp,
+       .vfs_vptofh     = p9p_vptofh,
+       .vfs_init       = p9p_init,
+       .vfs_sysctl     = 0,
+       .vfs_checkexp   = p9p_checkexp,
+};
diff --git a/sys/9p/p9p_vnops.c b/sys/9p/p9p_vnops.c
new file mode 100644
index 0000000..11f7792
--- /dev/null
+++ b/sys/9p/p9p_vnops.c
@@ -0,0 +1,454 @@
+#include <lib/libkern/libkern.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/rwlock.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/dirent.h>
+#include "9p.h"
+#include "p9p.h"
+
+/* u9fs seems to limit size of read/written data at msize - 24 */
+#define P9PHDRSIZE 24
+
+uint16_t
+p9p_getTag (p9p_mount_t *p9mp, void *p)
+{
+       uint16_t tag;
+       for (;;) {
+               rw_enter_write (&p9mp -> tagLock);
+               for (tag = 0; ~tag; tag++) if (!p9mp -> requs[tag]) {
+                       p9mp -> requs[tag] = p;
+                       break;
+               }
+               rw_exit (&p9mp -> tagLock);
+               if (~tag) return tag;
+               tsleep (&p9mp -> requs, curproc -> p_priority, "no free tag", 
0);
+       }
+}
+
+void
+p9p_putTag (p9p_mount_t *p9mp, uint16_t tag)
+{
+       rw_enter_write (&p9mp -> tagLock);
+       p9mp -> requs[tag] = 0;
+       rw_exit (&p9mp -> tagLock);
+       wakeup (&p9mp -> requs);
+}
+
+uint32_t
+p9p_getFid (p9p_mount_t *p9mp)
+{
+       uint32_t fid;
+       for (;;) {
+               rw_enter_write (&p9mp -> fidLock);
+               fid = p9mp -> leastFreeFid++;
+               if (!~fid /* oops! no free fid; rolled over */) p9mp -> 
leastFreeFid--;
+               rw_exit (&p9mp -> fidLock);
+               if (~fid) return fid;
+               else tsleep (&p9mp -> leastFreeFid, curproc -> p_priority, "no 
free fid", 0);
+       }
+}
+
+void
+p9p_putFid (p9p_mount_t *p9mp, uint32_t fid)
+{
+       rw_enter_write (&p9mp -> fidLock);
+       if (fid + 1 == p9mp -> leastFreeFid) p9mp -> leastFreeFid--;
+       /* XXX can't put other fid */
+       rw_exit (&p9mp -> fidLock);
+       wakeup (&p9mp -> leastFreeFid);
+}
+
+static int
+fo_rwSome (struct file *fp, off_t *offp, size_t n, struct uio *uiop, struct 
ucred *credp)
+{
+       size_t resid0 = uiop -> uio_resid;
+       int (*fo_op) (struct file *, off_t *, struct uio *, struct ucred *);
+       int c = 0;
+
+       if (resid0 < n) n = resid0;
+       uiop -> uio_resid = n;
+
+       switch (uiop -> uio_rw) {
+       case UIO_READ:
+               fo_op = fp -> f_ops -> fo_read;
+               break;
+       case UIO_WRITE:
+               fo_op = fp -> f_ops -> fo_write;
+               break;
+       default:
+               return EINVAL;
+       }
+       while (uiop -> uio_resid > 0) if (c = fo_op (fp, offp, uiop, credp)) 
break;
+       uiop -> uio_resid += resid0 - n;
+       return c;
+}
+
+/* tag, send, and await response; message should not be tagged when calling 
this */
+int
+p9p_require (p9p_mount_t *p9mp, Fcall *fcallp, struct uio *uiop, struct ucred 
*credp)
+{
+       uint16_t tag;
+       uint8_t type0 = fcallp -> type;
+       int c;
+
+       tag = p9p_getTag (p9mp, fcallp);
+       fcallp -> tag = tag;
+       rw_enter_write (&p9mp -> msgLock);
+       storFcall (fcallp, p9mp -> msg);
+       /* no sense to drop msgLock exclusion, as no one else wants to read our 
message anyhow */
+       rw_enter_write (&p9mp -> tfpLock);
+       c = write9pmsg (p9mp -> fp, p9mp -> msg);
+       rw_exit (&p9mp -> msgLock);
+       if (!c) {
+               if (fcallp -> type == TWrite || fcallp -> type == RRead) {
+                       if (uiop -> uio_rw != UIO_WRITE) c = EINVAL;
+                       else c = fo_rwSome (p9mp -> fp, 0, fcallp -> count, 
uiop, credp);
+               }
+       }
+       rw_exit (&p9mp -> tfpLock);
+       if (c) goto end;
+       if (c = tsleep (fcallp, curproc -> p_priority, "awaiting response", 0)) 
goto end;
+       if (fcallp -> tag != tag || fcallp -> type != type0 + 1) c = EIO;
+end:
+       p9p_putTag (p9mp, fcallp -> tag);
+       return c;
+}
+
+static inline uint8_t
+p9pmode (int fmode)
+{
+       uint8_t mode = OFLAGS(fmode) & O_ACCMODE;
+       if (fmode & O_TRUNC) mode |= 0x10;
+       return mode;
+}
+
+static inline uint32_t
+p9pperm (mode_t fperm)
+{
+       uint32_t perm = fperm & 0x1F;
+       if (S_ISDIR(fperm)) perm |= DMDIR;
+       return perm;
+}
+
+static inline mode_t
+fperm (uint32_t p9pperm)
+{
+       mode_t perm = p9pperm & 0x1F;
+       if (p9pperm & DMDIR) perm |= S_IFDIR;
+       return perm;
+}
+
+static int
+p9p_lookup (struct vop_lookup_args *ap)
+{
+       p9p_mount_t *p9mp;
+       p9p_node_t *p9np, *p9dnp;
+       Fcall fcall;
+       char *name;
+       int c;
+
+       p9mp  = ap -> a_dvp -> v_mount -> mnt_data;
+       p9dnp = ap -> a_dvp -> v_data;
+       KASSERT(p9mp && p9dnp);
+
+       if (c = getnewvnode (VT_P9P, ap -> a_dvp -> v_mount, &p9p_vops, ap -> 
a_vpp)) return c;
+
+       p9np = malloc (sizeof (p9p_node_t), M_P9PNODE, M_WAITOK | M_CANFAIL);
+       if (!p9np) {
+               c = ENOMEM;
+               goto fail;
+       }
+
+       name = malloc (sizeof (char)*(ap -> a_cnp -> cn_namelen + 1), M_TEMP, 
M_WAITOK | M_CANFAIL);
+       if (!name) {
+               c = ENOMEM;
+               goto fail;
+       }
+       strlcpy (name, ap -> a_cnp -> cn_nameptr, ap -> a_cnp -> cn_namelen + 
1);
+
+       fcall.type = TWalk;
+       fcall.fid  = p9dnp -> fid;
+       fcall.newfid = p9np -> fid = p9p_getFid (p9mp);
+       fcall.nwname = 1;
+       fcall.wname = &name;
+       c = p9p_require (p9mp, &fcall, 0, 0);
+       free (name, M_TEMP);
+       name = 0;
+       if (!c && fcall.nwqid == 0) c = EIO;
+       if (c) goto fail;
+       (*ap -> a_vpp) -> v_data = p9np;
+       (*ap -> a_vpp) -> v_type = fcall.wqid[0].type & QTDIR ? VDIR : VREG;
+       return 0;
+fail:
+       free (p9np, M_P9PNODE);
+       (*ap -> a_vpp) -> v_data = 0;
+       vrele (*ap -> a_vpp);
+       *ap -> a_vpp = 0;
+       return c;
+}
+
+static int
+p9p_create (struct vop_create_args *ap)
+{
+       p9p_mount_t *p9mp;
+       p9p_node_t *p9np, *p9dnp;
+       Fcall fcall;
+       int c;
+
+       p9mp  = ap -> a_dvp -> v_mount -> mnt_data;
+       p9dnp = ap -> a_dvp -> v_data;
+       KASSERT(p9mp && p9dnp);
+
+       if (c = getnewvnode (VT_P9P, ap -> a_dvp -> v_mount, &p9p_vops, ap -> 
a_vpp)) return c;
+
+       p9np = malloc (sizeof (p9p_node_t), M_P9PNODE, M_WAITOK | M_CANFAIL);
+       if (!p9np) {
+               c = ENOMEM;
+               goto fail;
+       }
+
+       fcall.type = TCreate;
+       fcall.fid  = p9np -> fid = p9dnp -> fid;
+       fcall.mode = 0;
+       fcall.perm = p9pperm (ap -> a_vap -> va_mode);
+       fcall.name = malloc (sizeof (char)*(ap -> a_cnp -> cn_namelen + 1), 
M_TEMP, M_WAITOK | M_CANFAIL);
+       if (!fcall.name) {
+               c = ENOMEM;
+               goto fail;
+       }
+       strlcpy (fcall.name, ap -> a_cnp -> cn_nameptr, ap -> a_cnp -> 
cn_namelen + 1);
+       c = p9p_require (p9mp, &fcall, 0, 0);
+       free (fcall.name, M_TEMP);
+       fcall.name = 0;
+       if (c) goto fail;
+       (*ap -> a_vpp) -> v_data = p9np;
+       (*ap -> a_vpp) -> v_type = fcall.qid.type & QTDIR ? VDIR : VREG;
+       free (p9dnp, M_P9PNODE);
+       ap -> a_dvp -> v_data = 0;
+       vput (ap -> a_dvp);
+       return 0;
+fail:
+       free (p9np, M_P9PNODE);
+       (*ap -> a_vpp) -> v_data = 0;
+       vrele (*ap -> a_vpp);
+       *ap -> a_vpp = 0;
+       return c;
+}
+
+static int
+p9p_remove (struct vop_remove_args *ap)
+{
+       Fcall fcall;
+
+       fcall.type = TRemove;
+       fcall.fid = ((p9p_node_t *)ap -> a_vp -> v_data) -> fid;
+       return p9p_require (ap -> a_vp -> v_mount -> mnt_data, &fcall, 0, 0);
+}
+
+static int
+p9p_open (struct vop_open_args *ap)
+{
+       Fcall fcall;
+
+       if (ap -> a_mode & FNONBLOCK) return EWOULDBLOCK;
+
+       fcall.type = TOpen;
+       fcall.fid  = ((p9p_node_t *)ap -> a_vp -> v_data) -> fid;
+       fcall.mode = p9pmode (ap -> a_mode);
+       return p9p_require (ap -> a_vp -> v_mount -> mnt_data, &fcall, 0, 0);
+}
+
+static int
+p9p_close (struct vop_close_args *ap)
+{
+       Fcall fcall;
+
+       fcall.type = TClunk;
+       fcall.fid  = ((p9p_node_t *)ap -> a_vp -> v_data) -> fid;
+       return p9p_require (ap -> a_vp -> v_mount -> mnt_data, &fcall, 0, 0);
+}
+
+static int
+p9p_rw (struct vop_read_args *ap)
+{
+       p9p_mount_t *p9mp;
+       Fcall fcall;
+       int c;
+
+       p9mp = ap -> a_vp -> v_mount -> mnt_data;
+       KASSERT(p9mp);
+
+       if (ap -> a_ioflag & IO_NDELAY) return EWOULDBLOCK;
+       if (ap -> a_ioflag & IO_UNIT)   return EOPNOTSUPP;
+       if (ap -> a_ioflag & IO_APPEND) return EOPNOTSUPP; /* XXX */
+
+       switch (ap -> a_uio -> uio_rw) {
+       case UIO_READ:
+               fcall.type = TRead;
+               break;
+       case UIO_WRITE:
+               fcall.type = TWrite;
+               break;
+       default:
+               return EINVAL;
+       }
+       fcall.fid  = ((p9p_node_t *)ap -> a_vp -> v_data) -> fid;
+       fcall.offset = ap -> a_uio -> uio_offset;
+       fcall.count = MIN(ap -> a_uio -> uio_resid, p9mp -> msize - P9PHDRSIZE);
+       if (c = p9p_require (p9mp, &fcall, ap -> a_uio, ap -> a_cred)) return c;
+       if (fcall.type == RRead) {
+               c = fo_rwSome (p9mp -> fp, 0, fcall.count, ap -> a_uio, ap -> 
a_cred);
+               wakeup (&p9mp -> fp);
+       }
+       return c;
+}
+
+static int
+p9p_readdir (struct vop_readdir_args *ap)
+{
+       p9p_mount_t *p9mp;
+       Fcall fcall;
+       struct iovec v;
+       struct uio uio = {
+               .uio_iov = 0,
+               .uio_iovcnt = 0,
+               .uio_segflg = UIO_SYSSPACE,
+               .uio_rw = UIO_READ,
+               .uio_offset = ap -> a_uio -> uio_offset,
+               .uio_resid = 0,
+               .uio_procp = curproc,
+       };
+       struct dirent dent = {
+               .d_off = uio.uio_offset,
+               .d_reclen = sizeof (struct dirent),
+       };
+       size_t l;
+       char *msg;
+       int c;
+
+       p9mp = ap -> a_vp -> v_mount -> mnt_data;
+       KASSERT(p9mp);
+
+       fcall.type = TRead;
+       fcall.fid  = ((p9p_node_t *)ap -> a_vp -> v_data) -> fid;
+       fcall.offset = ap -> a_uio -> uio_offset;
+       fcall.count = p9mp -> msize - P9PHDRSIZE;
+       if (c = p9p_require (p9mp, &fcall, &uio, ap -> a_cred)) return c;
+       v.iov_base = p9mp -> rmsg;
+       v.iov_len  = fcall.count;
+       uio.uio_iov = &v;
+       uio.uio_resid = fcall.count;
+       rw_enter_write (&p9mp -> rmsgLock);
+       if (c = p9mp -> fp -> f_ops -> fo_read (p9mp -> fp, 0, ap -> a_uio, ap 
-> a_cred)) goto end;
+       for (msg = p9mp -> rmsg; msg - (char *)p9mp -> rmsg < fcall.count; msg 
+= l) {
+               Dir dir;
+
+               l = loadDir (&dir, msg);
+               dent.d_off += l;
+               dent.d_fileno = dir.qid.path;
+               dent.d_namlen = MIN(MAXNAMLEN, strlen (dir.name));
+               if (dir.qid.type & QTDIR) dent.d_type = DT_DIR;
+               else dent.d_type = DT_REG;
+               /* could copy name out immediately, but this method simplifies 
code */
+               strlcpy (dent.d_name, dir.name, dent.d_namlen + 1);
+               c = uiomove (&dent, sizeof (struct dirent), ap -> a_uio);
+               freeDir (&dir);
+               if (c) goto end;
+       }
+end:
+       rw_exit (&p9mp -> rmsgLock);
+       wakeup (&p9mp -> fp);
+       return c;
+}
+
+static int
+p9p_getattr (struct vop_getattr_args *ap)
+{
+       Fcall fcall;
+       int c;
+
+       fcall.type = TStat;
+       fcall.fid  = ((p9p_node_t *)ap -> a_vp -> v_data) -> fid;
+       if (c = p9p_require (ap -> a_vp -> v_mount -> mnt_data, &fcall, 0, 0)) 
return c;
+       *ap -> a_vap = (struct vattr){
+               .va_type        = fcall.st.qid.type & QTDIR ? VDIR : VREG,
+               .va_mode        = fperm (fcall.st.mode),
+               .va_nlink       = 1,
+               .va_uid         = 0,
+               .va_gid         = 0,
+               .va_fsid        = ap -> a_vp -> v_mount -> 
mnt_stat.f_fsid.val[0],
+               .va_fileid      = fcall.st.qid.path,
+               .va_size        = fcall.st.length,
+               .va_blocksize   = 0,
+               .va_atime       = {
+                       .tv_sec  = fcall.st.atime,
+                       .tv_nsec = 0,
+               },
+               .va_mtime       = {
+                       .tv_sec  = fcall.st.mtime,
+                       .tv_nsec = 0,
+               },
+               .va_ctime       = { 0 },
+               .va_gen         = 0,
+               .va_flags       = 0,
+               .va_rdev        = 0,
+               .va_bytes       = 0,
+               .va_filerev     = fcall.st.qid.vers,
+               .va_vaflags     = 0,
+       };
+       return 0;
+}
+
+static int
+p9p_reclaim (struct vop_reclaim_args *ap)
+{
+       VOP_CLOSE(ap -> a_vp, 0, FSCRED, ap -> a_p);
+       free (ap -> a_vp -> v_data, M_P9PNODE);
+       p9p_putFid (ap -> a_vp -> v_mount -> mnt_data, ((p9p_node_t *) ap -> 
a_vp -> v_data) -> fid);
+       return 0;
+}
+
+struct vops p9p_vops = {
+       .vop_access     = nullop,
+       .vop_lookup     = (int (*) (void *))p9p_lookup,
+       .vop_create     = (int (*) (void *))p9p_create,
+       .vop_remove     = (int (*) (void *))p9p_remove,
+       .vop_rename     = eopnotsupp,
+       .vop_link       = eopnotsupp,
+       .vop_mkdir      = (int (*) (void *))p9p_create,
+       .vop_rmdir      = (int (*) (void *))p9p_remove,
+       .vop_readdir    = (int (*) (void *))p9p_readdir,
+       .vop_open       = (int (*) (void *))p9p_open,
+       .vop_close      = (int (*) (void *))p9p_close,
+       .vop_read       = (int (*) (void *))p9p_rw,
+       .vop_write      = (int (*) (void *))p9p_rw,
+       .vop_getattr    = (int (*) (void *))p9p_getattr,
+       .vop_setattr    = (int (*) (void *))eopnotsupp,
+       .vop_fsync      = nullop,
+       .vop_lock       = vop_generic_lock,
+       .vop_unlock     = vop_generic_unlock,
+       .vop_islocked   = vop_generic_islocked,
+       .vop_revoke     = vop_generic_revoke,
+       .vop_bmap       = vop_generic_bmap,
+       .vop_bwrite     = vop_generic_bwrite,
+       .vop_kqfilter   = vop_generic_kqfilter,
+       .vop_abortop    = vop_generic_abortop,
+       .vop_advlock    = eopnotsupp,
+       .vop_poll       = eopnotsupp,
+       .vop_ioctl      = eopnotsupp,
+       .vop_mknod      = eopnotsupp,
+       .vop_symlink    = eopnotsupp,
+       .vop_readlink   = eopnotsupp,
+       .vop_strategy   = eopnotsupp,
+       .vop_inactive   = eopnotsupp,
+       .vop_reclaim    = (int (*) (void *))p9p_reclaim,
+       .vop_print      = eopnotsupp,
+       .vop_pathconf   = eopnotsupp,
+};
diff --git a/sys/9p/util.h b/sys/9p/util.h
new file mode 100644
index 0000000..2a5a1bd
--- /dev/null
+++ b/sys/9p/util.h
@@ -0,0 +1,61 @@
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#define SWAP(x, y) { (x) ^= (y); (y) ^= (x); (x) ^= (y); }
+
+#define ABS(x) ((x)>=0?(x):(-(x)))
+
+#define ROL(x,n) ((x)<<(n)|(x)>>(8*sizeof(x) - (n)))
+#define ROR(x,n) ((x)>>(n)|(x)<<(8*sizeof(x) - (n)))
+
+#define LOAD16LE(y, x) { (y) = (uint16_t)((x)[0]) << 0 | (uint16_t)((x)[1]) << 
8; }
+#define LOAD32LE(y, x) { (y) = (uint32_t)((x)[0]) << 0 | (uint32_t)((x)[1]) << 
8 | (uint32_t)((x)[2]) << 16 | (uint32_t)((x)[3]) << 24; }
+#define LOAD64LE(y, x) { (y) = (uint64_t)((x)[0]) << 0 | (uint64_t)((x)[1]) << 
8 | (uint64_t)((x)[2]) << 16 | (uint64_t)((x)[3]) << 24 | (uint64_t)((x)[4]) << 
32 | (uint64_t)((x)[5]) << 40 | (uint64_t)((x)[6]) << 48 | (uint64_t)((x)[7]) 
<< 56; }
+
+#define STOR16LE(x, y) { (y)[0] = (uint8_t)((x) >> 0); (y)[1] = (uint8_t)((x) 
>> 8); }
+#define STOR32LE(x, y) { (y)[0] = (uint8_t)((x) >> 0); (y)[1] = (uint8_t)((x) 
>> 8); (y)[2] = (uint8_t)((x) >> 16); (y)[3] = (uint8_t)((x) >> 24); }
+#define STOR64LE(x, y) { (y)[0] = (uint8_t)((x) >> 0); (y)[1] = (uint8_t)((x) 
>> 8); (y)[2] = (uint8_t)((x) >> 16); (y)[3] = (uint8_t)((x) >> 24); (y)[4] = 
(uint8_t)((x) >> 32); (y)[5] = (uint8_t)((x) >> 40); (y)[6] = (uint8_t)((x) >> 
48); (y)[7] = (uint8_t)((x) >> 56); }
+
+#define LOAD16BE(y, x) { (y) =                                                 
                                                                                
                                  (uint16_t)((x)[0]) << 8 | (uint16_t)((x)[1]) 
<< 0; }
+#define LOAD32BE(y, x) { (y) =                                                 
                                                            (uint32_t)((x)[0]) 
<< 24 | (uint32_t)((x)[1]) << 16 | (uint32_t)((x)[2]) << 8 | (uint32_t)((x)[3]) 
<< 0; }
+#define LOAD64BE(y, x) { (y) = (uint64_t)((x)[0]) << 56 | (uint64_t)((x)[1]) 
<< 48 | (uint64_t)((x)[2]) << 40 | (uint64_t)((x)[3]) << 32 | 
(uint64_t)((x)[4]) << 24 | (uint64_t)((x)[5]) << 16 | (uint64_t)((x)[6]) << 8 | 
(uint64_t)((x)[7]) << 0; }
+
+#define STOR16BE(x, y) {                                                       
                                                                                
                                                    (y)[0] = (uint8_t)((x) >> 
8); (y)[1] = (uint8_t)((x) >> 0); }
+#define STOR32BE(x, y) {                                                       
                                                                      (y)[0] = 
(uint8_t)((x) >> 24); (y)[1] = (uint8_t)((x) >> 16); (y)[2] = (uint8_t)((x) >> 
8); (y)[3] = (uint8_t)((x) >> 0); }
+#define STOR64BE(x, y) { (y)[0] = (uint8_t)((x) >> 56); (y)[1] = (uint8_t)((x) 
>> 48); (y)[2] = (uint8_t)((x) >> 40); (y)[3] = (uint8_t)((x) >> 32); (y)[4] = 
(uint8_t)((x) >> 24); (y)[5] = (uint8_t)((x) >> 16); (y)[6] = (uint8_t)((x) >> 
8); (y)[7] = (uint8_t)((x) >> 0); }
+
+#define LOADNLE(n, y, x) switch (n) { case 1: (y)[0] = (uint8_t)(x); break; 
case 2: LOAD16LE(y, x); break; case 4: LOAD32LE(y, x); break; case 8: 
LOAD64LE(y, x); break; }
+
+#define LOADNBE(n, y, x) switch (n) { case 1: (y)[0] = (uint8_t)(x); break; 
case 2: LOAD16BE(y, x); break; case 4: LOAD32BE(y, x); break; case 8: 
LOAD64BE(y, x); break; }
+
+static inline int read (struct file *fp, caddr_t x, size_t n) {
+       struct iovec v = { .iov_base = x, .iov_len = n };
+       struct uio uio = {
+               .uio_iov = &v,
+               .uio_iovcnt = 1,
+               .uio_segflg = UIO_SYSSPACE,
+               .uio_rw = UIO_READ,
+               .uio_offset = 0,
+               .uio_resid = n,
+               .uio_procp = curproc,
+       };
+
+       return fp -> f_ops -> fo_read (fp, 0, &uio, FSCRED);
+}
+
+static inline int write (struct file *fp, caddr_t x, size_t n) {
+       struct iovec v = { .iov_base = x, .iov_len = n };
+       struct uio uio = {
+               .uio_iov = &v,
+               .uio_iovcnt = 1,
+               .uio_segflg = UIO_SYSSPACE,
+               .uio_rw = UIO_WRITE,
+               .uio_offset = 0,
+               .uio_resid = n,
+               .uio_procp = curproc,
+       };
+
+       return fp -> f_ops -> fo_write (fp, 0, &uio, FSCRED);
+}
+
+#endif
diff --git a/sys/conf/GENERIC b/sys/conf/GENERIC
index cc98b98..183708f 100644
--- a/sys/conf/GENERIC
+++ b/sys/conf/GENERIC
@@ -46,6 +46,7 @@ option                UDF             # UDF (DVD) file system
 option         MSDOSFS         # MS-DOS file system
 option         FIFO            # FIFOs; RECOMMENDED
 option         TMPFS           # efficient memory file system
+option         P9P             # Plan 9 remote filesystem protocol
 option         FUSE            # FUSE
 
 option         SOCKET_SPLICE   # Socket Splicing for TCP and UDP
diff --git a/sys/conf/files b/sys/conf/files
index 80229a4..d783a95 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -952,6 +952,9 @@ file ufs/ext2fs/ext2fs_readwrite.c  ext2fs
 file ufs/ext2fs/ext2fs_subr.c          ext2fs
 file ufs/ext2fs/ext2fs_vfsops.c                ext2fs
 file ufs/ext2fs/ext2fs_vnops.c         ext2fs
+file 9p/9p.c                           p9p
+file 9p/p9p_vfsops.c                   p9p
+file 9p/p9p_vnops.c                    p9p
 file uvm/uvm_addr.c
 file uvm/uvm_amap.c
 file uvm/uvm_anon.c
diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c
index cd84da1..1898179 100644
--- a/sys/kern/vfs_init.c
+++ b/sys/kern/vfs_init.c
@@ -94,6 +94,10 @@ extern const struct vfsops fusefs_vfsops;
 extern  const struct vfsops tmpfs_vfsops;
 #endif
 
+#ifdef P9P
+extern const struct vfsops p9p_vfsops;
+#endif
+
 /* Set up the filesystem operations for vnodes. */
 static struct vfsconf vfsconflist[] = {
 #ifdef FFS
@@ -139,6 +143,10 @@ static struct vfsconf vfsconflist[] = {
 #ifdef TMPFS
        { &tmpfs_vfsops, MOUNT_TMPFS, 19, 0, MNT_LOCAL, NULL },
 #endif
+
+#ifdef P9P
+       { &p9p_vfsops, MOUNT_P9P, 20, 0, 0, NULL },
+#endif
 };
 
 
diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h
index 34bed05..641270a 100644
--- a/sys/sys/malloc.h
+++ b/sys/sys/malloc.h
@@ -121,6 +121,8 @@
 #define        M_EXEC          63      /* argument lists & other mem used by 
exec */
 #define        M_MISCFSMNT     64      /* miscfs mount structures */
 #define        M_FUSEFS        65      /* fusefs mount structures */
+#define M_P9PMNT       66      /* 9p mount structures */
+#define M_P9PNODE      67      /* 9p nodes */
 /* 66-73 - free */
 #define        M_PFKEY         74      /* pfkey data */
 #define        M_TDB           75      /* Transforms database */
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index d35b0cb..e73e166 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -262,6 +262,14 @@ struct tmpfs_args {
 };
 
 /*
+ * Arguments to mount 9p filesystems
+ */
+struct p9p_args {
+       int fd;
+       size_t msize;
+};
+
+/*
  * Arguments to mount procfs filesystems
  */
 struct procfs_args {
@@ -353,6 +361,7 @@ struct statfs {
 #define        MOUNT_NTFS      "ntfs"          /* NTFS */
 #define        MOUNT_UDF       "udf"           /* UDF */
 #define        MOUNT_TMPFS     "tmpfs"         /* tmpfs */
+#define        MOUNT_P9P       "9p"            /* 9p */
 #define        MOUNT_FUSEFS    "fuse"          /* FUSE */
 
 /*
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index a8ea897..c22362a 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -70,12 +70,14 @@ enum vtagtype       {
        VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_MSDOSFS,
        VT_PORTAL, VT_PROCFS, VT_AFS, VT_ISOFS, VT_ADOSFS,
        VT_EXT2FS, VT_VFS, VT_NTFS, VT_UDF, VT_FUSEFS, VT_TMPFS,
+       VT_P9P,
 };
 
 #define        VTAG_NAMES \
     "NON", "UFS", "NFS", "MFS", "MSDOSFS",                     \
     "PORTAL", "PROCFS", "AFS", "ISOFS", "ADOSFS",              \
-    "EXT2FS", "VFS", "NTFS", "UDF", "FUSEFS", "TMPFS"
+    "EXT2FS", "VFS", "NTFS", "UDF", "FUSEFS", "TMPFS",         \
+    "9P"
 
 /*
  * Each underlying filesystem allocates its own private area and hangs

Reply via email to