summaryrefslogtreecommitdiff
path: root/sys/src/cmd/unix/u9fs
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/unix/u9fs
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/unix/u9fs')
-rwxr-xr-xsys/src/cmd/unix/u9fs/LICENSE16
-rwxr-xr-xsys/src/cmd/unix/u9fs/authnone.c25
-rwxr-xr-xsys/src/cmd/unix/u9fs/authp9any.c538
-rwxr-xr-xsys/src/cmd/unix/u9fs/authrhosts.c38
-rwxr-xr-xsys/src/cmd/unix/u9fs/convD2M.c89
-rwxr-xr-xsys/src/cmd/unix/u9fs/convM2D.c92
-rwxr-xr-xsys/src/cmd/unix/u9fs/convM2S.c382
-rwxr-xr-xsys/src/cmd/unix/u9fs/convS2M.c423
-rwxr-xr-xsys/src/cmd/unix/u9fs/cygwin.c62
-rwxr-xr-xsys/src/cmd/unix/u9fs/des.c355
-rwxr-xr-xsys/src/cmd/unix/u9fs/dirmodeconv.c47
-rwxr-xr-xsys/src/cmd/unix/u9fs/doprint.c610
-rwxr-xr-xsys/src/cmd/unix/u9fs/fcall.h123
-rwxr-xr-xsys/src/cmd/unix/u9fs/fcallconv.c228
-rwxr-xr-xsys/src/cmd/unix/u9fs/makefile63
-rwxr-xr-xsys/src/cmd/unix/u9fs/oldfcall.c521
-rwxr-xr-xsys/src/cmd/unix/u9fs/oldfcall.h50
-rwxr-xr-xsys/src/cmd/unix/u9fs/plan9.h198
-rwxr-xr-xsys/src/cmd/unix/u9fs/print.c87
-rwxr-xr-xsys/src/cmd/unix/u9fs/random.c58
-rwxr-xr-xsys/src/cmd/unix/u9fs/readn.c21
-rwxr-xr-xsys/src/cmd/unix/u9fs/remotehost.c32
-rwxr-xr-xsys/src/cmd/unix/u9fs/rune.c148
-rwxr-xr-xsys/src/cmd/unix/u9fs/safecpy.c12
-rwxr-xr-xsys/src/cmd/unix/u9fs/strecpy.c14
-rwxr-xr-xsys/src/cmd/unix/u9fs/sun-inttypes.h16
-rwxr-xr-xsys/src/cmd/unix/u9fs/tokenize.c42
-rwxr-xr-xsys/src/cmd/unix/u9fs/u9fs.c1765
-rwxr-xr-xsys/src/cmd/unix/u9fs/u9fs.h30
-rwxr-xr-xsys/src/cmd/unix/u9fs/u9fsauth.h7
-rwxr-xr-xsys/src/cmd/unix/u9fs/utfrune.c29
31 files changed, 6121 insertions, 0 deletions
diff --git a/sys/src/cmd/unix/u9fs/LICENSE b/sys/src/cmd/unix/u9fs/LICENSE
new file mode 100755
index 000000000..13a5f81c3
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/LICENSE
@@ -0,0 +1,16 @@
+The authors of this software are Bob Flandrena, Ken Thompson,
+Rob Pike, and Russ Cox.
+
+ Copyright (c) 1992-2002 by Lucent Technologies.
+
+Permission to use, copy, modify, and distribute this software for any
+purpose without fee is hereby granted, provided that this entire notice
+is included in all copies of any software which is or includes a copy
+or modification of this software and in all copies of the supporting
+documentation for such software.
+
+THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+
diff --git a/sys/src/cmd/unix/u9fs/authnone.c b/sys/src/cmd/unix/u9fs/authnone.c
new file mode 100755
index 000000000..7a8145cc5
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/authnone.c
@@ -0,0 +1,25 @@
+#include <plan9.h>
+#include <fcall.h>
+#include <u9fs.h>
+
+static char*
+noneauth(Fcall *rx, Fcall *tx)
+{
+ USED(rx);
+ USED(tx);
+ return "u9fs authnone: no authentication required";
+}
+
+static char*
+noneattach(Fcall *rx, Fcall *tx)
+{
+ USED(rx);
+ USED(tx);
+ return nil;
+}
+
+Auth authnone = {
+ "none",
+ noneauth,
+ noneattach,
+};
diff --git a/sys/src/cmd/unix/u9fs/authp9any.c b/sys/src/cmd/unix/u9fs/authp9any.c
new file mode 100755
index 000000000..c62b95cbe
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/authp9any.c
@@ -0,0 +1,538 @@
+/*
+ * 4th Edition p9any/p9sk1 authentication based on auth9p1.c
+ * Nigel Roles (nigel@9fs.org) 2003
+ */
+
+#include <plan9.h>
+#include <fcall.h>
+#include <u9fs.h>
+#include <stdlib.h> /* for random stuff */
+
+typedef struct Ticket Ticket;
+typedef struct Ticketreq Ticketreq;
+typedef struct Authenticator Authenticator;
+
+enum
+{
+ DOMLEN= 48, /* length of an authentication domain name */
+ CHALLEN= 8 /* length of a challenge */
+};
+
+enum {
+ HaveProtos,
+ NeedProto,
+ NeedChal,
+ HaveTreq,
+ NeedTicket,
+ HaveAuth,
+ Established,
+};
+
+/* encryption numberings (anti-replay) */
+enum
+{
+ AuthTreq=1, /* ticket request */
+ AuthChal=2, /* challenge box request */
+ AuthPass=3, /* change password */
+ AuthOK=4, /* fixed length reply follows */
+ AuthErr=5, /* error follows */
+ AuthMod=6, /* modify user */
+ AuthApop=7, /* apop authentication for pop3 */
+ AuthOKvar=9, /* variable length reply follows */
+ AuthChap=10, /* chap authentication for ppp */
+ AuthMSchap=11, /* MS chap authentication for ppp */
+ AuthCram=12, /* CRAM verification for IMAP (RFC2195 & rfc2104) */
+ AuthHttp=13, /* http domain login */
+ AuthVNC=14, /* http domain login */
+
+
+ AuthTs=64, /* ticket encrypted with server's key */
+ AuthTc, /* ticket encrypted with client's key */
+ AuthAs, /* server generated authenticator */
+ AuthAc, /* client generated authenticator */
+ AuthTp, /* ticket encrypted with client's key for password change */
+ AuthHr /* http reply */
+};
+
+struct Ticketreq
+{
+ char type;
+ char authid[NAMELEN]; /* server's encryption id */
+ char authdom[DOMLEN]; /* server's authentication domain */
+ char chal[CHALLEN]; /* challenge from server */
+ char hostid[NAMELEN]; /* host's encryption id */
+ char uid[NAMELEN]; /* uid of requesting user on host */
+};
+#define TICKREQLEN (3*NAMELEN+CHALLEN+DOMLEN+1)
+
+struct Ticket
+{
+ char num; /* replay protection */
+ char chal[CHALLEN]; /* server challenge */
+ char cuid[NAMELEN]; /* uid on client */
+ char suid[NAMELEN]; /* uid on server */
+ char key[DESKEYLEN]; /* nonce DES key */
+};
+#define TICKETLEN (CHALLEN+2*NAMELEN+DESKEYLEN+1)
+
+struct Authenticator
+{
+ char num; /* replay protection */
+ char chal[CHALLEN];
+ ulong id; /* authenticator id, ++'d with each auth */
+};
+#define AUTHENTLEN (CHALLEN+4+1)
+
+extern int chatty9p;
+
+static int convT2M(Ticket*, char*, char*);
+static void convM2T(char*, Ticket*, char*);
+static void convM2Tnoenc(char*, Ticket*);
+static int convA2M(Authenticator*, char*, char*);
+static void convM2A(char*, Authenticator*, char*);
+static int convTR2M(Ticketreq*, char*);
+static void convM2TR(char*, Ticketreq*);
+static int passtokey(char*, char*);
+
+/*
+ * destructively encrypt the buffer, which
+ * must be at least 8 characters long.
+ */
+static int
+encrypt9p(void *key, void *vbuf, int n)
+{
+ char ekey[128], *buf;
+ int i, r;
+
+ if(n < 8)
+ return 0;
+ key_setup(key, ekey);
+ buf = vbuf;
+ n--;
+ r = n % 7;
+ n /= 7;
+ for(i = 0; i < n; i++){
+ block_cipher(ekey, buf, 0);
+ buf += 7;
+ }
+ if(r)
+ block_cipher(ekey, buf - 7 + r, 0);
+ return 1;
+}
+
+/*
+ * destructively decrypt the buffer, which
+ * must be at least 8 characters long.
+ */
+static int
+decrypt9p(void *key, void *vbuf, int n)
+{
+ char ekey[128], *buf;
+ int i, r;
+
+ if(n < 8)
+ return 0;
+ key_setup(key, ekey);
+ buf = vbuf;
+ n--;
+ r = n % 7;
+ n /= 7;
+ buf += n * 7;
+ if(r)
+ block_cipher(ekey, buf - 7 + r, 1);
+ for(i = 0; i < n; i++){
+ buf -= 7;
+ block_cipher(ekey, buf, 1);
+ }
+ return 1;
+}
+
+#define CHAR(x) *p++ = f->x
+#define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2
+#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
+#define LONG(x) VLONG(f->x)
+#define STRING(x,n) memmove(p, f->x, n); p += n
+
+static int
+convTR2M(Ticketreq *f, char *ap)
+{
+ int n;
+ uchar *p;
+
+ p = (uchar*)ap;
+ CHAR(type);
+ STRING(authid, NAMELEN);
+ STRING(authdom, DOMLEN);
+ STRING(chal, CHALLEN);
+ STRING(hostid, NAMELEN);
+ STRING(uid, NAMELEN);
+ n = p - (uchar*)ap;
+ return n;
+}
+
+static int
+convT2M(Ticket *f, char *ap, char *key)
+{
+ int n;
+ uchar *p;
+
+ p = (uchar*)ap;
+ CHAR(num);
+ STRING(chal, CHALLEN);
+ STRING(cuid, NAMELEN);
+ STRING(suid, NAMELEN);
+ STRING(key, DESKEYLEN);
+ n = p - (uchar*)ap;
+ if(key)
+ encrypt9p(key, ap, n);
+ return n;
+}
+
+int
+convA2M(Authenticator *f, char *ap, char *key)
+{
+ int n;
+ uchar *p;
+
+ p = (uchar*)ap;
+ CHAR(num);
+ STRING(chal, CHALLEN);
+ LONG(id);
+ n = p - (uchar*)ap;
+ if(key)
+ encrypt9p(key, ap, n);
+ return n;
+}
+
+#undef CHAR
+#undef SHORT
+#undef VLONG
+#undef LONG
+#undef STRING
+
+#define CHAR(x) f->x = *p++
+#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2
+#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
+#define LONG(x) VLONG(f->x)
+#define STRING(x,n) memmove(f->x, p, n); p += n
+
+void
+convM2A(char *ap, Authenticator *f, char *key)
+{
+ uchar *p;
+
+ if(key)
+ decrypt9p(key, ap, AUTHENTLEN);
+ p = (uchar*)ap;
+ CHAR(num);
+ STRING(chal, CHALLEN);
+ LONG(id);
+ USED(p);
+}
+
+void
+convM2T(char *ap, Ticket *f, char *key)
+{
+ uchar *p;
+
+ if(key)
+ decrypt9p(key, ap, TICKETLEN);
+ p = (uchar*)ap;
+ CHAR(num);
+ STRING(chal, CHALLEN);
+ STRING(cuid, NAMELEN);
+ f->cuid[NAMELEN-1] = 0;
+ STRING(suid, NAMELEN);
+ f->suid[NAMELEN-1] = 0;
+ STRING(key, DESKEYLEN);
+ USED(p);
+}
+
+#undef CHAR
+#undef SHORT
+#undef LONG
+#undef VLONG
+#undef STRING
+
+static int
+passtokey(char *key, char *p)
+{
+ uchar buf[NAMELEN], *t;
+ int i, n;
+
+ n = strlen(p);
+ if(n >= NAMELEN)
+ n = NAMELEN-1;
+ memset(buf, ' ', 8);
+ t = buf;
+ strncpy((char*)t, p, n);
+ t[n] = 0;
+ memset(key, 0, DESKEYLEN);
+ for(;;){
+ for(i = 0; i < DESKEYLEN; i++)
+ key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1)));
+ if(n <= 8)
+ return 1;
+ n -= 8;
+ t += 8;
+ if(n < 8){
+ t -= 8 - n;
+ n = 8;
+ }
+ encrypt9p(key, t, 8);
+ }
+ return 1; /* not reached */
+}
+
+static char authkey[DESKEYLEN];
+static char *authid;
+static char *authdom;
+static char *haveprotosmsg;
+static char *needprotomsg;
+
+static void
+p9anyinit(void)
+{
+ int n, fd;
+ char abuf[200];
+ char *af, *f[4];
+
+ af = autharg;
+ if(af == nil)
+ af = "/etc/u9fs.key";
+
+ if((fd = open(af, OREAD)) < 0)
+ sysfatal("can't open key file '%s'", af);
+
+ if((n = readn(fd, abuf, sizeof(abuf)-1)) < 0)
+ sysfatal("can't read key file '%s'", af);
+ if (n > 0 && abuf[n - 1] == '\n')
+ n--;
+ abuf[n] = '\0';
+
+ if(getfields(abuf, f, nelem(f), 0, "\n") != 3)
+ sysfatal("key file '%s' not exactly 3 lines", af);
+
+ passtokey(authkey, f[0]);
+ authid = strdup(f[1]);
+ authdom = strdup(f[2]);
+ haveprotosmsg = malloc(strlen("p9sk1") + 1 + strlen(authdom) + 1);
+ sprint(haveprotosmsg, "p9sk1@%s", authdom);
+ needprotomsg = malloc(strlen("p9sk1") + 1 + strlen(authdom) + 1);
+ sprint(needprotomsg, "p9sk1 %s", authdom);
+}
+
+typedef struct AuthSession {
+ int state;
+ char *uname;
+ char *aname;
+ char cchal[CHALLEN];
+ Ticketreq tr;
+ Ticket t;
+} AuthSession;
+
+static char*
+p9anyauth(Fcall *rx, Fcall *tx)
+{
+ AuthSession *sp;
+ Fid *f;
+ char *ep;
+
+ sp = malloc(sizeof(AuthSession));
+ f = newauthfid(rx->afid, sp, &ep);
+ if (f == nil) {
+ free(sp);
+ return ep;
+ }
+ if (chatty9p)
+ fprint(2, "p9anyauth: afid %d\n", rx->afid);
+ sp->state = HaveProtos;
+ sp->uname = strdup(rx->uname);
+ sp->aname = strdup(rx->aname);
+ tx->aqid.type = QTAUTH;
+ tx->aqid.path = 1;
+ tx->aqid.vers = 0;
+ return nil;
+}
+
+static char *
+p9anyattach(Fcall *rx, Fcall *tx)
+{
+ AuthSession *sp;
+ Fid *f;
+ char *ep;
+
+ f = oldauthfid(rx->afid, (void **)&sp, &ep);
+ if (f == nil)
+ return ep;
+ if (chatty9p)
+ fprint(2, "p9anyattach: afid %d state %d\n", rx->afid, sp->state);
+ if (sp->state == Established && strcmp(rx->uname, sp->uname) == 0
+ && strcmp(rx->aname, sp->aname) == 0)
+ return nil;
+ return "authentication failed";
+}
+
+static int
+readstr(Fcall *rx, Fcall *tx, char *s, int len)
+{
+ if (rx->offset >= len)
+ return 0;
+ tx->count = len - rx->offset;
+ if (tx->count > rx->count)
+ tx->count = rx->count;
+ memcpy(tx->data, s + rx->offset, tx->count);
+ return tx->count;
+}
+
+static char *
+p9anyread(Fcall *rx, Fcall *tx)
+{
+ AuthSession *sp;
+ char *ep;
+
+ Fid *f;
+ f = oldauthfid(rx->afid, (void **)&sp, &ep);
+ if (f == nil)
+ return ep;
+ if (chatty9p)
+ fprint(2, "p9anyread: afid %d state %d\n", rx->fid, sp->state);
+ switch (sp->state) {
+ case HaveProtos:
+ readstr(rx, tx, haveprotosmsg, strlen(haveprotosmsg) + 1);
+ if (rx->offset + tx->count == strlen(haveprotosmsg) + 1)
+ sp->state = NeedProto;
+ return nil;
+ case HaveTreq:
+ if (rx->count != TICKREQLEN)
+ goto botch;
+ convTR2M(&sp->tr, tx->data);
+ tx->count = TICKREQLEN;
+ sp->state = NeedTicket;
+ return nil;
+ case HaveAuth: {
+ Authenticator a;
+ if (rx->count != AUTHENTLEN)
+ goto botch;
+ a.num = AuthAs;
+ memmove(a.chal, sp->cchal, CHALLEN);
+ a.id = 0;
+ convA2M(&a, (char*)tx->data, sp->t.key);
+ memset(sp->t.key, 0, sizeof(sp->t.key));
+ tx->count = rx->count;
+ sp->state = Established;
+ return nil;
+ }
+ default:
+ botch:
+ return "protocol botch";
+ }
+}
+
+static char *
+p9anywrite(Fcall *rx, Fcall *tx)
+{
+ AuthSession *sp;
+ char *ep;
+
+ Fid *f;
+
+ f = oldauthfid(rx->afid, (void **)&sp, &ep);
+ if (f == nil)
+ return ep;
+ if (chatty9p)
+ fprint(2, "p9anywrite: afid %d state %d\n", rx->fid, sp->state);
+ switch (sp->state) {
+ case NeedProto:
+ if (rx->count != strlen(needprotomsg) + 1)
+ return "protocol response wrong length";
+ if (memcmp(rx->data, needprotomsg, rx->count) != 0)
+ return "unacceptable protocol";
+ sp->state = NeedChal;
+ tx->count = rx->count;
+ return nil;
+ case NeedChal:
+ if (rx->count != CHALLEN)
+ goto botch;
+ memmove(sp->cchal, rx->data, CHALLEN);
+ sp->tr.type = AuthTreq;
+ safecpy(sp->tr.authid, authid, sizeof(sp->tr.authid));
+ safecpy(sp->tr.authdom, authdom, sizeof(sp->tr.authdom));
+ randombytes((uchar *)sp->tr.chal, CHALLEN);
+ safecpy(sp->tr.hostid, "", sizeof(sp->tr.hostid));
+ safecpy(sp->tr.uid, "", sizeof(sp->tr.uid));
+ tx->count = rx->count;
+ sp->state = HaveTreq;
+ return nil;
+ case NeedTicket: {
+ Authenticator a;
+
+ if (rx->count != TICKETLEN + AUTHENTLEN) {
+ fprint(2, "bad length in attach");
+ goto botch;
+ }
+ convM2T((char*)rx->data, &sp->t, authkey);
+ if (sp->t.num != AuthTs) {
+ fprint(2, "bad AuthTs in attach\n");
+ goto botch;
+ }
+ if (memcmp(sp->t.chal, sp->tr.chal, CHALLEN) != 0) {
+ fprint(2, "bad challenge in attach\n");
+ goto botch;
+ }
+ convM2A((char*)rx->data + TICKETLEN, &a, sp->t.key);
+ if (a.num != AuthAc) {
+ fprint(2, "bad AuthAs in attach\n");
+ goto botch;
+ }
+ if(memcmp(a.chal, sp->tr.chal, CHALLEN) != 0) {
+ fprint(2, "bad challenge in attach 2\n");
+ goto botch;
+ }
+ sp->state = HaveAuth;
+ tx->count = rx->count;
+ return nil;
+ }
+ default:
+ botch:
+ return "protocol botch";
+ }
+}
+
+static void
+safefree(char *p)
+{
+ if (p) {
+ memset(p, 0, strlen(p));
+ free(p);
+ }
+}
+
+static char *
+p9anyclunk(Fcall *rx, Fcall *tx)
+{
+ Fid *f;
+ AuthSession *sp;
+ char *ep;
+
+ f = oldauthfid(rx->afid, (void **)&sp, &ep);
+ if (f == nil)
+ return ep;
+ if (chatty9p)
+ fprint(2, "p9anyclunk: afid %d\n", rx->fid);
+ safefree(sp->uname);
+ safefree(sp->aname);
+ memset(sp, 0, sizeof(sp));
+ free(sp);
+ return nil;
+}
+
+Auth authp9any = {
+ "p9any",
+ p9anyauth,
+ p9anyattach,
+ p9anyinit,
+ p9anyread,
+ p9anywrite,
+ p9anyclunk,
+};
diff --git a/sys/src/cmd/unix/u9fs/authrhosts.c b/sys/src/cmd/unix/u9fs/authrhosts.c
new file mode 100755
index 000000000..de150850e
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/authrhosts.c
@@ -0,0 +1,38 @@
+#include <plan9.h>
+#include <fcall.h>
+#include <u9fs.h>
+
+/*
+ * return whether the user is authenticated.
+ * uses berkeley-style rhosts ``authentication''.
+ * this is only a good idea behind a firewall,
+ * where you trust your network, and even then
+ * not such a great idea. it's grandfathered.
+ */
+
+static char*
+rhostsauth(Fcall *rx, Fcall *tx)
+{
+ USED(rx);
+ USED(tx);
+
+ return "u9fs rhostsauth: no authentication required";
+}
+
+static char*
+rhostsattach(Fcall *rx, Fcall *tx)
+{
+ USED(tx);
+
+ if(ruserok(remotehostname, 0, rx->uname, rx->uname) < 0){
+ fprint(2, "ruserok(%s, %s) not okay\n", remotehostname, rx->uname);
+ return "u9fs: rhosts authentication failed";
+ }
+ return 0;
+}
+
+Auth authrhosts = {
+ "rhosts",
+ rhostsauth,
+ rhostsattach,
+};
diff --git a/sys/src/cmd/unix/u9fs/convD2M.c b/sys/src/cmd/unix/u9fs/convD2M.c
new file mode 100755
index 000000000..c630b1b93
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/convD2M.c
@@ -0,0 +1,89 @@
+#include <plan9.h>
+#include <fcall.h>
+
+uint
+sizeD2M(Dir *d)
+{
+ char *sv[4];
+ int i, ns;
+
+ sv[0] = d->name;
+ sv[1] = d->uid;
+ sv[2] = d->gid;
+ sv[3] = d->muid;
+
+ ns = 0;
+ for(i = 0; i < 4; i++)
+ ns += strlen(sv[i]);
+
+ return STATFIXLEN + ns;
+}
+
+uint
+convD2M(Dir *d, uchar *buf, uint nbuf)
+{
+ uchar *p, *ebuf;
+ char *sv[4];
+ int i, ns, nsv[4], ss;
+
+ if(nbuf < BIT16SZ)
+ return 0;
+
+ p = buf;
+ ebuf = buf + nbuf;
+
+ sv[0] = d->name;
+ sv[1] = d->uid;
+ sv[2] = d->gid;
+ sv[3] = d->muid;
+
+ ns = 0;
+ for(i = 0; i < 4; i++){
+ nsv[i] = strlen(sv[i]);
+ ns += nsv[i];
+ }
+
+ ss = STATFIXLEN + ns;
+
+ /* set size befor erroring, so user can know how much is needed */
+ /* note that length excludes count field itself */
+ PBIT16(p, ss-BIT16SZ);
+ p += BIT16SZ;
+
+ if(ss > nbuf)
+ return BIT16SZ;
+
+ PBIT16(p, d->type);
+ p += BIT16SZ;
+ PBIT32(p, d->dev);
+ p += BIT32SZ;
+ PBIT8(p, d->qid.type);
+ p += BIT8SZ;
+ PBIT32(p, d->qid.vers);
+ p += BIT32SZ;
+ PBIT64(p, d->qid.path);
+ p += BIT64SZ;
+ PBIT32(p, d->mode);
+ p += BIT32SZ;
+ PBIT32(p, d->atime);
+ p += BIT32SZ;
+ PBIT32(p, d->mtime);
+ p += BIT32SZ;
+ PBIT64(p, d->length);
+ p += BIT64SZ;
+
+ for(i = 0; i < 4; i++){
+ ns = nsv[i];
+ if(p + ns + BIT16SZ > ebuf)
+ return 0;
+ PBIT16(p, ns);
+ p += BIT16SZ;
+ memmove(p, sv[i], ns);
+ p += ns;
+ }
+
+ if(ss != p - buf)
+ return 0;
+
+ return p - buf;
+}
diff --git a/sys/src/cmd/unix/u9fs/convM2D.c b/sys/src/cmd/unix/u9fs/convM2D.c
new file mode 100755
index 000000000..b83c957ef
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/convM2D.c
@@ -0,0 +1,92 @@
+#include <plan9.h>
+#include <fcall.h>
+
+int
+statcheck(uchar *buf, uint nbuf)
+{
+ uchar *ebuf;
+ int i;
+
+ ebuf = buf + nbuf;
+
+ buf += STATFIXLEN - 4 * BIT16SZ;
+
+ for(i = 0; i < 4; i++){
+ if(buf + BIT16SZ > ebuf)
+ return -1;
+ buf += BIT16SZ + GBIT16(buf);
+ }
+
+ if(buf != ebuf)
+ return -1;
+
+ return 0;
+}
+
+static char nullstring[] = "";
+
+uint
+convM2D(uchar *buf, uint nbuf, Dir *d, char *strs)
+{
+ uchar *p, *ebuf;
+ char *sv[4];
+ int i, ns;
+
+ p = buf;
+ ebuf = buf + nbuf;
+
+ p += BIT16SZ; /* ignore size */
+ d->type = GBIT16(p);
+ p += BIT16SZ;
+ d->dev = GBIT32(p);
+ p += BIT32SZ;
+ d->qid.type = GBIT8(p);
+ p += BIT8SZ;
+ d->qid.vers = GBIT32(p);
+ p += BIT32SZ;
+ d->qid.path = GBIT64(p);
+ p += BIT64SZ;
+ d->mode = GBIT32(p);
+ p += BIT32SZ;
+ d->atime = GBIT32(p);
+ p += BIT32SZ;
+ d->mtime = GBIT32(p);
+ p += BIT32SZ;
+ d->length = GBIT64(p);
+ p += BIT64SZ;
+
+ d->name = nil;
+ d->uid = nil;
+ d->gid = nil;
+ d->muid = nil;
+
+ for(i = 0; i < 4; i++){
+ if(p + BIT16SZ > ebuf)
+ return 0;
+ ns = GBIT16(p);
+ p += BIT16SZ;
+ if(p + ns > ebuf)
+ return 0;
+ if(strs){
+ sv[i] = strs;
+ memmove(strs, p, ns);
+ strs += ns;
+ *strs++ = '\0';
+ }
+ p += ns;
+ }
+
+ if(strs){
+ d->name = sv[0];
+ d->uid = sv[1];
+ d->gid = sv[2];
+ d->muid = sv[3];
+ }else{
+ d->name = nullstring;
+ d->uid = nullstring;
+ d->gid = nullstring;
+ d->muid = nullstring;
+ }
+
+ return p - buf;
+}
diff --git a/sys/src/cmd/unix/u9fs/convM2S.c b/sys/src/cmd/unix/u9fs/convM2S.c
new file mode 100755
index 000000000..2fc6389f9
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/convM2S.c
@@ -0,0 +1,382 @@
+#include <plan9.h>
+#include <fcall.h>
+
+static
+uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{
+ uint n;
+
+ if(p+BIT16SZ > ep)
+ return nil;
+ n = GBIT16(p);
+ p += BIT16SZ - 1;
+ if(p+n+1 > ep)
+ return nil;
+ /* move it down, on top of count, to make room for '\0' */
+ memmove(p, p + 1, n);
+ p[n] = '\0';
+ *s = (char*)p;
+ p += n+1;
+ return p;
+}
+
+static
+uchar*
+gqid(uchar *p, uchar *ep, Qid *q)
+{
+ if(p+QIDSZ > ep)
+ return nil;
+ q->type = GBIT8(p);
+ p += BIT8SZ;
+ q->vers = GBIT32(p);
+ p += BIT32SZ;
+ q->path = GBIT64(p);
+ p += BIT64SZ;
+ return p;
+}
+
+/*
+ * no syntactic checks.
+ * three causes for error:
+ * 1. message size field is incorrect
+ * 2. input buffer too short for its own data (counts too long, etc.)
+ * 3. too many names or qids
+ * gqid() and gstring() return nil if they would reach beyond buffer.
+ * main switch statement checks range and also can fall through
+ * to test at end of routine.
+ */
+uint
+convM2S(uchar *ap, uint nap, Fcall *f)
+{
+ uchar *p, *ep;
+ uint i, size;
+
+ p = ap;
+ ep = p + nap;
+
+ if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
+ return 0;
+ size = GBIT32(p);
+ p += BIT32SZ;
+
+ if(size > nap)
+ return 0;
+ if(size < BIT32SZ+BIT8SZ+BIT16SZ)
+ return 0;
+
+ f->type = GBIT8(p);
+ p += BIT8SZ;
+ f->tag = GBIT16(p);
+ p += BIT16SZ;
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->msize = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->version);
+ break;
+
+/*
+ case Tsession:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nchal = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nchal > ep)
+ return 0;
+ f->chal = p;
+ p += f->nchal;
+ break;
+*/
+
+ case Tflush:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->oldtag = GBIT16(p);
+ p += BIT16SZ;
+ break;
+
+ case Tauth:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->afid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ break;
+
+/*
+b
+ case Tattach:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nauth = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nauth > ep)
+ return 0;
+ f->auth = p;
+ p += f->nauth;
+ break;
+*/
+
+ case Tattach:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->afid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ break;
+
+
+ case Twalk:
+ if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->newfid = GBIT32(p);
+ p += BIT32SZ;
+ f->nwname = GBIT16(p);
+ p += BIT16SZ;
+ if(f->nwname > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwname; i++){
+ p = gstring(p, ep, &f->wname[i]);
+ if(p == nil)
+ break;
+ }
+ break;
+
+ case Topen:
+ if(p+BIT32SZ+BIT8SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->mode = GBIT8(p);
+ p += BIT8SZ;
+ break;
+
+ case Tcreate:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->name);
+ if(p == nil)
+ break;
+ if(p+BIT32SZ+BIT8SZ > ep)
+ return 0;
+ f->perm = GBIT32(p);
+ p += BIT32SZ;
+ f->mode = GBIT8(p);
+ p += BIT8SZ;
+ break;
+
+ case Tread:
+ if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->offset = GBIT64(p);
+ p += BIT64SZ;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Twrite:
+ if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->offset = GBIT64(p);
+ p += BIT64SZ;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Tstat:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Twstat:
+ if(p+BIT32SZ+BIT16SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->nstat = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nstat > ep)
+ return 0;
+ f->stat = p;
+ p += f->nstat;
+ break;
+
+/*
+ */
+ case Rversion:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->msize = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->version);
+ break;
+
+/*
+ case Rsession:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nchal = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nchal > ep)
+ return 0;
+ f->chal = p;
+ p += f->nchal;
+ p = gstring(p, ep, &f->authid);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->authdom);
+ break;
+*/
+
+ case Rerror:
+ p = gstring(p, ep, &f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+/*
+ case Rattach:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nrauth = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nrauth > ep)
+ return 0;
+ f->rauth = p;
+ p += f->nrauth;
+ break;
+*/
+
+ case Rattach:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ break;
+
+
+ case Rwalk:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nwqid = GBIT16(p);
+ p += BIT16SZ;
+ if(f->nwqid > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwqid; i++){
+ p = gqid(p, ep, &f->wqid[i]);
+ if(p == nil)
+ break;
+ }
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->iounit = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Rread:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case Rwrite:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Rclunk:
+ case Rremove:
+ break;
+
+ case Rstat:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nstat = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nstat > ep)
+ return 0;
+ f->stat = p;
+ p += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+
+ if(p==nil || p>ep)
+ return 0;
+ if(ap+size == p)
+ return size;
+ return 0;
+}
diff --git a/sys/src/cmd/unix/u9fs/convS2M.c b/sys/src/cmd/unix/u9fs/convS2M.c
new file mode 100755
index 000000000..52cb486fb
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/convS2M.c
@@ -0,0 +1,423 @@
+#include <plan9.h>
+#include <fcall.h>
+
+static
+uchar*
+pstring(uchar *p, char *s)
+{
+ uint n;
+
+ n = strlen(s);
+ PBIT16(p, n);
+ p += BIT16SZ;
+ memmove(p, s, n);
+ p += n;
+ return p;
+}
+
+static
+uchar*
+pqid(uchar *p, Qid *q)
+{
+ PBIT8(p, q->type);
+ p += BIT8SZ;
+ PBIT32(p, q->vers);
+ p += BIT32SZ;
+ PBIT64(p, q->path);
+ p += BIT64SZ;
+ return p;
+}
+
+static
+uint
+stringsz(char *s)
+{
+ return BIT16SZ+strlen(s);
+}
+
+static
+uint
+sizeS2M(Fcall *f)
+{
+ uint n;
+ int i;
+
+ n = 0;
+ n += BIT32SZ; /* size */
+ n += BIT8SZ; /* type */
+ n += BIT16SZ; /* tag */
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ n += BIT32SZ;
+ n += stringsz(f->version);
+ break;
+
+/*
+ case Tsession:
+ n += BIT16SZ;
+ n += f->nchal;
+ break;
+*/
+
+ case Tflush:
+ n += BIT16SZ;
+ break;
+
+ case Tauth:
+ n += BIT32SZ;
+ n += stringsz(f->uname);
+ n += stringsz(f->aname);
+ break;
+
+ case Tattach:
+ n += BIT32SZ;
+ n += BIT32SZ;
+ n += stringsz(f->uname);
+ n += stringsz(f->aname);
+ break;
+
+
+ case Twalk:
+ n += BIT32SZ;
+ n += BIT32SZ;
+ n += BIT16SZ;
+ for(i=0; i<f->nwname; i++)
+ n += stringsz(f->wname[i]);
+ break;
+
+ case Topen:
+ n += BIT32SZ;
+ n += BIT8SZ;
+ break;
+
+ case Tcreate:
+ n += BIT32SZ;
+ n += stringsz(f->name);
+ n += BIT32SZ;
+ n += BIT8SZ;
+ break;
+
+ case Tread:
+ n += BIT32SZ;
+ n += BIT64SZ;
+ n += BIT32SZ;
+ break;
+
+ case Twrite:
+ n += BIT32SZ;
+ n += BIT64SZ;
+ n += BIT32SZ;
+ n += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ n += BIT32SZ;
+ break;
+
+ case Tstat:
+ n += BIT32SZ;
+ break;
+
+ case Twstat:
+ n += BIT32SZ;
+ n += BIT16SZ;
+ n += f->nstat;
+ break;
+/*
+ */
+
+ case Rversion:
+ n += BIT32SZ;
+ n += stringsz(f->version);
+ break;
+
+/*
+ case Rsession:
+ n += BIT16SZ;
+ n += f->nchal;
+ n += stringsz(f->authid);
+ n += stringsz(f->authdom);
+ break;
+
+*/
+ case Rerror:
+ n += stringsz(f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ n += QIDSZ;
+ break;
+
+/*
+ case Rattach:
+ n += QIDSZ;
+ n += BIT16SZ;
+ n += f->nrauth;
+ break;
+*/
+
+ case Rattach:
+ n += QIDSZ;
+ break;
+
+
+ case Rwalk:
+ n += BIT16SZ;
+ n += f->nwqid*QIDSZ;
+ break;
+
+ case Ropen:
+ case Rcreate:
+ n += QIDSZ;
+ n += BIT32SZ;
+ break;
+
+ case Rread:
+ n += BIT32SZ;
+ n += f->count;
+ break;
+
+ case Rwrite:
+ n += BIT32SZ;
+ break;
+
+ case Rclunk:
+ break;
+
+ case Rremove:
+ break;
+
+ case Rstat:
+ n += BIT16SZ;
+ n += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+ return n;
+}
+
+uint
+convS2M(Fcall *f, uchar *ap, uint nap)
+{
+ uchar *p;
+ uint i, size;
+
+ size = sizeS2M(f);
+ if(size == 0)
+ return 0;
+ if(size > nap)
+ return 0;
+
+ p = (uchar*)ap;
+
+ PBIT32(p, size);
+ p += BIT32SZ;
+ PBIT8(p, f->type);
+ p += BIT8SZ;
+ PBIT16(p, f->tag);
+ p += BIT16SZ;
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ PBIT32(p, f->msize);
+ p += BIT32SZ;
+ p = pstring(p, f->version);
+ break;
+
+/*
+ case Tsession:
+ PBIT16(p, f->nchal);
+ p += BIT16SZ;
+ f->chal = p;
+ p += f->nchal;
+ break;
+*/
+
+ case Tflush:
+ PBIT16(p, f->oldtag);
+ p += BIT16SZ;
+ break;
+
+ case Tauth:
+ PBIT32(p, f->afid);
+ p += BIT32SZ;
+ p = pstring(p, f->uname);
+ p = pstring(p, f->aname);
+ break;
+
+ case Tattach:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT32(p, f->afid);
+ p += BIT32SZ;
+ p = pstring(p, f->uname);
+ p = pstring(p, f->aname);
+ break;
+
+ case Twalk:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT32(p, f->newfid);
+ p += BIT32SZ;
+ PBIT16(p, f->nwname);
+ p += BIT16SZ;
+ if(f->nwname > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwname; i++)
+ p = pstring(p, f->wname[i]);
+ break;
+
+ case Topen:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT8(p, f->mode);
+ p += BIT8SZ;
+ break;
+
+ case Tcreate:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ p = pstring(p, f->name);
+ PBIT32(p, f->perm);
+ p += BIT32SZ;
+ PBIT8(p, f->mode);
+ p += BIT8SZ;
+ break;
+
+ case Tread:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT64(p, f->offset);
+ p += BIT64SZ;
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ break;
+
+ case Twrite:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT64(p, f->offset);
+ p += BIT64SZ;
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ break;
+
+ case Tstat:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ break;
+
+ case Twstat:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT16(p, f->nstat);
+ p += BIT16SZ;
+ memmove(p, f->stat, f->nstat);
+ p += f->nstat;
+ break;
+/*
+ */
+
+ case Rversion:
+ PBIT32(p, f->msize);
+ p += BIT32SZ;
+ p = pstring(p, f->version);
+ break;
+
+/*
+ case Rsession:
+ PBIT16(p, f->nchal);
+ p += BIT16SZ;
+ f->chal = p;
+ p += f->nchal;
+ p = pstring(p, f->authid);
+ p = pstring(p, f->authdom);
+ break;
+*/
+
+ case Rerror:
+ p = pstring(p, f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ p = pqid(p, &f->aqid);
+ break;
+
+ case Rattach:
+ p = pqid(p, &f->qid);
+ break;
+
+ case Rwalk:
+ PBIT16(p, f->nwqid);
+ p += BIT16SZ;
+ if(f->nwqid > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwqid; i++)
+ p = pqid(p, &f->wqid[i]);
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = pqid(p, &f->qid);
+ PBIT32(p, f->iounit);
+ p += BIT32SZ;
+ break;
+
+ case Rread:
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Rwrite:
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ break;
+
+ case Rclunk:
+ break;
+
+ case Rremove:
+ break;
+
+ case Rstat:
+ PBIT16(p, f->nstat);
+ p += BIT16SZ;
+ memmove(p, f->stat, f->nstat);
+ p += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+ if(size != p-ap)
+ return 0;
+ return size;
+}
diff --git a/sys/src/cmd/unix/u9fs/cygwin.c b/sys/src/cmd/unix/u9fs/cygwin.c
new file mode 100755
index 000000000..773d02cfb
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/cygwin.c
@@ -0,0 +1,62 @@
+/* compatability layer for u9fs support on CYGWIN */
+
+#include <unistd.h>
+#include <errno.h>
+
+ssize_t
+pread(int fd, void *p, size_t n, off_t off)
+{
+ off_t ooff;
+ int oerrno;
+
+ if ((ooff = lseek(fd, off, SEEK_SET)) == -1)
+ return -1;
+
+ n = read(fd, p, n);
+
+ oerrno = errno;
+ lseek(fd, ooff, SEEK_SET);
+ errno = oerrno;
+
+ return n;
+}
+
+ssize_t
+pwrite(int fd, const void *p, size_t n, off_t off)
+{
+ off_t ooff;
+ int oerrno;
+
+ if ((ooff = lseek(fd, off, SEEK_SET)) == -1)
+ return -1;
+
+ n = write(fd, p, n);
+
+ oerrno = errno;
+ lseek(fd, ooff, SEEK_SET);
+ errno = oerrno;
+
+ return n;
+}
+
+int
+setreuid(int ruid, int euid)
+{
+ if (ruid != -1)
+ if (setuid(ruid) == -1)
+ return(-1);
+ if (euid != -1)
+ if (seteuid(euid) == -1)
+ return(-1);
+}
+
+int
+setregid(int rgid, int egid)
+{
+ if (rgid != -1)
+ if (setgid(rgid) == -1)
+ return(-1);
+ if (egid != -1)
+ if (setegid(egid) == -1)
+ return(-1);
+}
diff --git a/sys/src/cmd/unix/u9fs/des.c b/sys/src/cmd/unix/u9fs/des.c
new file mode 100755
index 000000000..6152369cc
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/des.c
@@ -0,0 +1,355 @@
+#include <plan9.h>
+
+/*
+ * Data Encryption Standard
+ * D.P.Mitchell 83/06/08.
+ *
+ * block_cipher(key, block, decrypting)
+ */
+
+static long ip_low(char [8]);
+static long ip_high(char [8]);
+static void fp(long, long, char[8]);
+
+extern int chatty9p;
+
+/*
+ * Tables for Combined S and P Boxes
+ */
+
+static long s0p[] = {
+0x00410100,0x00010000,0x40400000,0x40410100,0x00400000,0x40010100,0x40010000,0x40400000,
+0x40010100,0x00410100,0x00410000,0x40000100,0x40400100,0x00400000,0x00000000,0x40010000,
+0x00010000,0x40000000,0x00400100,0x00010100,0x40410100,0x00410000,0x40000100,0x00400100,
+0x40000000,0x00000100,0x00010100,0x40410000,0x00000100,0x40400100,0x40410000,0x00000000,
+0x00000000,0x40410100,0x00400100,0x40010000,0x00410100,0x00010000,0x40000100,0x00400100,
+0x40410000,0x00000100,0x00010100,0x40400000,0x40010100,0x40000000,0x40400000,0x00410000,
+0x40410100,0x00010100,0x00410000,0x40400100,0x00400000,0x40000100,0x40010000,0x00000000,
+0x00010000,0x00400000,0x40400100,0x00410100,0x40000000,0x40410000,0x00000100,0x40010100,
+};
+
+static long s1p[] = {
+0x08021002,0x00000000,0x00021000,0x08020000,0x08000002,0x00001002,0x08001000,0x00021000,
+0x00001000,0x08020002,0x00000002,0x08001000,0x00020002,0x08021000,0x08020000,0x00000002,
+0x00020000,0x08001002,0x08020002,0x00001000,0x00021002,0x08000000,0x00000000,0x00020002,
+0x08001002,0x00021002,0x08021000,0x08000002,0x08000000,0x00020000,0x00001002,0x08021002,
+0x00020002,0x08021000,0x08001000,0x00021002,0x08021002,0x00020002,0x08000002,0x00000000,
+0x08000000,0x00001002,0x00020000,0x08020002,0x00001000,0x08000000,0x00021002,0x08001002,
+0x08021000,0x00001000,0x00000000,0x08000002,0x00000002,0x08021002,0x00021000,0x08020000,
+0x08020002,0x00020000,0x00001002,0x08001000,0x08001002,0x00000002,0x08020000,0x00021000,
+};
+
+static long s2p[] = {
+0x20800000,0x00808020,0x00000020,0x20800020,0x20008000,0x00800000,0x20800020,0x00008020,
+0x00800020,0x00008000,0x00808000,0x20000000,0x20808020,0x20000020,0x20000000,0x20808000,
+0x00000000,0x20008000,0x00808020,0x00000020,0x20000020,0x20808020,0x00008000,0x20800000,
+0x20808000,0x00800020,0x20008020,0x00808000,0x00008020,0x00000000,0x00800000,0x20008020,
+0x00808020,0x00000020,0x20000000,0x00008000,0x20000020,0x20008000,0x00808000,0x20800020,
+0x00000000,0x00808020,0x00008020,0x20808000,0x20008000,0x00800000,0x20808020,0x20000000,
+0x20008020,0x20800000,0x00800000,0x20808020,0x00008000,0x00800020,0x20800020,0x00008020,
+0x00800020,0x00000000,0x20808000,0x20000020,0x20800000,0x20008020,0x00000020,0x00808000,
+};
+
+static long s3p[] = {
+0x00080201,0x02000200,0x00000001,0x02080201,0x00000000,0x02080000,0x02000201,0x00080001,
+0x02080200,0x02000001,0x02000000,0x00000201,0x02000001,0x00080201,0x00080000,0x02000000,
+0x02080001,0x00080200,0x00000200,0x00000001,0x00080200,0x02000201,0x02080000,0x00000200,
+0x00000201,0x00000000,0x00080001,0x02080200,0x02000200,0x02080001,0x02080201,0x00080000,
+0x02080001,0x00000201,0x00080000,0x02000001,0x00080200,0x02000200,0x00000001,0x02080000,
+0x02000201,0x00000000,0x00000200,0x00080001,0x00000000,0x02080001,0x02080200,0x00000200,
+0x02000000,0x02080201,0x00080201,0x00080000,0x02080201,0x00000001,0x02000200,0x00080201,
+0x00080001,0x00080200,0x02080000,0x02000201,0x00000201,0x02000000,0x02000001,0x02080200,
+};
+
+static long s4p[] = {
+0x01000000,0x00002000,0x00000080,0x01002084,0x01002004,0x01000080,0x00002084,0x01002000,
+0x00002000,0x00000004,0x01000004,0x00002080,0x01000084,0x01002004,0x01002080,0x00000000,
+0x00002080,0x01000000,0x00002004,0x00000084,0x01000080,0x00002084,0x00000000,0x01000004,
+0x00000004,0x01000084,0x01002084,0x00002004,0x01002000,0x00000080,0x00000084,0x01002080,
+0x01002080,0x01000084,0x00002004,0x01002000,0x00002000,0x00000004,0x01000004,0x01000080,
+0x01000000,0x00002080,0x01002084,0x00000000,0x00002084,0x01000000,0x00000080,0x00002004,
+0x01000084,0x00000080,0x00000000,0x01002084,0x01002004,0x01002080,0x00000084,0x00002000,
+0x00002080,0x01002004,0x01000080,0x00000084,0x00000004,0x00002084,0x01002000,0x01000004,
+};
+
+static long s5p[] = {
+0x10000008,0x00040008,0x00000000,0x10040400,0x00040008,0x00000400,0x10000408,0x00040000,
+0x00000408,0x10040408,0x00040400,0x10000000,0x10000400,0x10000008,0x10040000,0x00040408,
+0x00040000,0x10000408,0x10040008,0x00000000,0x00000400,0x00000008,0x10040400,0x10040008,
+0x10040408,0x10040000,0x10000000,0x00000408,0x00000008,0x00040400,0x00040408,0x10000400,
+0x00000408,0x10000000,0x10000400,0x00040408,0x10040400,0x00040008,0x00000000,0x10000400,
+0x10000000,0x00000400,0x10040008,0x00040000,0x00040008,0x10040408,0x00040400,0x00000008,
+0x10040408,0x00040400,0x00040000,0x10000408,0x10000008,0x10040000,0x00040408,0x00000000,
+0x00000400,0x10000008,0x10000408,0x10040400,0x10040000,0x00000408,0x00000008,0x10040008,
+};
+
+static long s6p[] = {
+0x00000800,0x00000040,0x00200040,0x80200000,0x80200840,0x80000800,0x00000840,0x00000000,
+0x00200000,0x80200040,0x80000040,0x00200800,0x80000000,0x00200840,0x00200800,0x80000040,
+0x80200040,0x00000800,0x80000800,0x80200840,0x00000000,0x00200040,0x80200000,0x00000840,
+0x80200800,0x80000840,0x00200840,0x80000000,0x80000840,0x80200800,0x00000040,0x00200000,
+0x80000840,0x00200800,0x80200800,0x80000040,0x00000800,0x00000040,0x00200000,0x80200800,
+0x80200040,0x80000840,0x00000840,0x00000000,0x00000040,0x80200000,0x80000000,0x00200040,
+0x00000000,0x80200040,0x00200040,0x00000840,0x80000040,0x00000800,0x80200840,0x00200000,
+0x00200840,0x80000000,0x80000800,0x80200840,0x80200000,0x00200840,0x00200800,0x80000800,
+};
+
+static long s7p[] = {
+0x04100010,0x04104000,0x00004010,0x00000000,0x04004000,0x00100010,0x04100000,0x04104010,
+0x00000010,0x04000000,0x00104000,0x00004010,0x00104010,0x04004010,0x04000010,0x04100000,
+0x00004000,0x00104010,0x00100010,0x04004000,0x04104010,0x04000010,0x00000000,0x00104000,
+0x04000000,0x00100000,0x04004010,0x04100010,0x00100000,0x00004000,0x04104000,0x00000010,
+0x00100000,0x00004000,0x04000010,0x04104010,0x00004010,0x04000000,0x00000000,0x00104000,
+0x04100010,0x04004010,0x04004000,0x00100010,0x04104000,0x00000010,0x00100010,0x04004000,
+0x04104010,0x00100000,0x04100000,0x04000010,0x00104000,0x00004010,0x04004010,0x04100000,
+0x00000010,0x04104000,0x00104010,0x00000000,0x04000000,0x04100010,0x00004000,0x00104010,
+};
+
+/*
+ * DES electronic codebook encryption of one block
+ */
+void
+block_cipher(char expanded_key[128], char text[8], int decrypting)
+{
+ char *key;
+ long crypto, temp, right, left;
+ int i, key_offset;
+
+ key = expanded_key;
+ left = ip_low(text);
+ right = ip_high(text);
+ if (decrypting) {
+ key_offset = 16;
+ key = key + 128 - 8;
+ } else
+ key_offset = 0;
+ for (i = 0; i < 16; i++) {
+ temp = (right << 1) | ((right >> 31) & 1);
+ crypto = s0p[(temp & 0x3f) ^ *key++];
+ crypto |= s1p[((temp >> 4) & 0x3f) ^ *key++];
+ crypto |= s2p[((temp >> 8) & 0x3f) ^ *key++];
+ crypto |= s3p[((temp >> 12) & 0x3f) ^ *key++];
+ crypto |= s4p[((temp >> 16) & 0x3f) ^ *key++];
+ crypto |= s5p[((temp >> 20) & 0x3f) ^ *key++];
+ crypto |= s6p[((temp >> 24) & 0x3f) ^ *key++];
+ temp = ((right & 1) << 5) | ((right >> 27) & 0x1f);
+ crypto |= s7p[temp ^ *key++];
+ temp = left;
+ left = right;
+ right = temp ^ crypto;
+ key -= key_offset;
+ }
+ /*
+ * standard final permutation (IPI)
+ * left and right are reversed here
+ */
+ fp(right, left, text);
+}
+
+/*
+ * Initial Permutation
+ */
+static long iptab[] = {
+ 0x00000000, 0x00008000, 0x00000000, 0x00008000,
+ 0x00000080, 0x00008080, 0x00000080, 0x00008080
+};
+
+static long
+ip_low(char block[8])
+{
+ int i;
+ long l;
+
+ l = 0;
+ for(i = 0; i < 8; i++){
+ l |= iptab[(block[i] >> 4) & 7] >> i;
+ l |= iptab[block[i] & 7] << (16 - i);
+ }
+ return l;
+}
+
+static long
+ip_high(char block[8])
+{
+ int i;
+ long l;
+
+ l = 0;
+ for(i = 0; i < 8; i++){
+ l |= iptab[(block[i] >> 5) & 7] >> i;
+ l |= iptab[(block[i] >> 1) & 7] << (16 - i);
+ }
+ return l;
+}
+
+/*
+ * Final Permutation
+ */
+static unsigned long fptab[] = {
+0x00000000,0x80000000,0x00800000,0x80800000,0x00008000,0x80008000,0x00808000,0x80808000,
+0x00000080,0x80000080,0x00800080,0x80800080,0x00008080,0x80008080,0x00808080,0x80808080,
+};
+
+static void
+fp(long left, long right, char text[8])
+{
+ unsigned long ta[2], t, v[2];
+ int i, j, sh;
+
+ ta[0] = right;
+ ta[1] = left;
+ v[0] = v[1] = 0;
+ for(i = 0; i < 2; i++){
+ t = ta[i];
+ sh = i;
+ for(j = 0; j < 4; j++){
+ v[1] |= fptab[t & 0xf] >> sh;
+ t >>= 4;
+ v[0] |= fptab[t & 0xf] >> sh;
+ t >>= 4;
+ sh += 2;
+ }
+ }
+ for(i = 0; i < 2; i++)
+ for(j = 0; j < 4; j++){
+ *text++ = v[i];
+ v[i] >>= 8;
+ }
+}
+
+/*
+ * Key set-up
+ */
+static uchar keyexpand[][15][2] = {
+ { 3, 2, 9, 8, 18, 8, 27, 32, 33, 2, 42, 16, 48, 8, 65, 16,
+ 74, 2, 80, 2, 89, 4, 99, 16, 104, 4, 122, 32, 0, 0, },
+ { 1, 4, 8, 1, 18, 4, 25, 32, 34, 32, 41, 8, 50, 8, 59, 32,
+ 64, 16, 75, 4, 90, 1, 97, 16, 106, 2, 112, 2, 123, 1, },
+ { 2, 1, 19, 8, 35, 1, 40, 1, 50, 4, 57, 32, 75, 2, 80, 32,
+ 89, 1, 96, 16, 107, 4, 120, 8, 0, 0, 0, 0, 0, 0, },
+ { 4, 32, 20, 2, 31, 4, 37, 32, 47, 1, 54, 1, 63, 2, 68, 1,
+ 78, 4, 84, 8, 101, 16, 108, 4, 119, 16, 126, 8, 0, 0, },
+ { 5, 4, 15, 4, 21, 32, 31, 1, 38, 1, 47, 2, 53, 2, 68, 8,
+ 85, 16, 92, 4, 103, 16, 108, 32, 118, 32, 124, 2, 0, 0, },
+ { 15, 2, 21, 2, 39, 8, 46, 16, 55, 32, 61, 1, 71, 16, 76, 32,
+ 86, 32, 93, 4, 102, 2, 108, 16, 117, 8, 126, 1, 0, 0, },
+ { 14, 16, 23, 32, 29, 1, 38, 8, 52, 2, 63, 4, 70, 2, 76, 16,
+ 85, 8, 100, 1, 110, 4, 116, 8, 127, 8, 0, 0, 0, 0, },
+ { 1, 8, 8, 32, 17, 1, 24, 16, 35, 4, 50, 1, 57, 16, 67, 8,
+ 83, 1, 88, 1, 98, 4, 105, 32, 114, 32, 123, 2, 0, 0, },
+ { 0, 1, 11, 16, 16, 4, 35, 2, 40, 32, 49, 1, 56, 16, 65, 2,
+ 74, 16, 80, 8, 99, 8, 115, 1, 121, 4, 0, 0, 0, 0, },
+ { 9, 16, 18, 2, 24, 2, 33, 4, 43, 16, 48, 4, 66, 32, 73, 8,
+ 82, 8, 91, 32, 97, 2, 106, 16, 112, 8, 122, 1, 0, 0, },
+ { 14, 32, 21, 4, 30, 2, 36, 16, 45, 8, 60, 1, 69, 2, 87, 8,
+ 94, 16, 103, 32, 109, 1, 118, 8, 124, 32, 0, 0, 0, 0, },
+ { 7, 4, 14, 2, 20, 16, 29, 8, 44, 1, 54, 4, 60, 8, 71, 8,
+ 78, 16, 87, 32, 93, 1, 102, 8, 116, 2, 125, 4, 0, 0, },
+ { 7, 2, 12, 1, 22, 4, 28, 8, 45, 16, 52, 4, 63, 16, 70, 8,
+ 84, 2, 95, 4, 101, 32, 111, 1, 118, 1, 0, 0, 0, 0, },
+ { 6, 16, 13, 16, 20, 4, 31, 16, 36, 32, 46, 32, 53, 4, 62, 2,
+ 69, 32, 79, 1, 86, 1, 95, 2, 101, 2, 119, 8, 0, 0, },
+ { 0, 32, 10, 8, 19, 32, 25, 2, 34, 16, 40, 8, 59, 8, 66, 2,
+ 72, 2, 81, 4, 91, 16, 96, 4, 115, 2, 121, 8, 0, 0, },
+ { 3, 16, 10, 4, 17, 32, 26, 32, 33, 8, 42, 8, 51, 32, 57, 2,
+ 67, 4, 82, 1, 89, 16, 98, 2, 104, 2, 113, 4, 120, 1, },
+ { 1, 16, 11, 8, 27, 1, 32, 1, 42, 4, 49, 32, 58, 32, 67, 2,
+ 72, 32, 81, 1, 88, 16, 99, 4, 114, 1, 0, 0, 0, 0, },
+ { 6, 32, 12, 2, 23, 4, 29, 32, 39, 1, 46, 1, 55, 2, 61, 2,
+ 70, 4, 76, 8, 93, 16, 100, 4, 111, 16, 116, 32, 0, 0, },
+ { 6, 2, 13, 32, 23, 1, 30, 1, 39, 2, 45, 2, 63, 8, 77, 16,
+ 84, 4, 95, 16, 100, 32, 110, 32, 117, 4, 127, 4, 0, 0, },
+ { 4, 1, 13, 2, 31, 8, 38, 16, 47, 32, 53, 1, 62, 8, 68, 32,
+ 78, 32, 85, 4, 94, 2, 100, 16, 109, 8, 127, 2, 0, 0, },
+ { 5, 16, 15, 32, 21, 1, 30, 8, 44, 2, 55, 4, 61, 32, 68, 16,
+ 77, 8, 92, 1, 102, 4, 108, 8, 126, 16, 0, 0, 0, 0, },
+ { 2, 8, 9, 1, 16, 16, 27, 4, 42, 1, 49, 16, 58, 2, 75, 1,
+ 80, 1, 90, 4, 97, 32, 106, 32, 113, 8, 120, 32, 0, 0, },
+ { 2, 4, 8, 4, 27, 2, 32, 32, 41, 1, 48, 16, 59, 4, 66, 16,
+ 72, 8, 91, 8, 107, 1, 112, 1, 123, 16, 0, 0, 0, 0, },
+ { 3, 8, 10, 2, 16, 2, 25, 4, 35, 16, 40, 4, 59, 2, 65, 8,
+ 74, 8, 83, 32, 89, 2, 98, 16, 104, 8, 121, 16, 0, 0, },
+ { 4, 2, 13, 4, 22, 2, 28, 16, 37, 8, 52, 1, 62, 4, 79, 8,
+ 86, 16, 95, 32, 101, 1, 110, 8, 126, 32, 0, 0, 0, 0, },
+ { 5, 32, 12, 16, 21, 8, 36, 1, 46, 4, 52, 8, 70, 16, 79, 32,
+ 85, 1, 94, 8, 108, 2, 119, 4, 126, 2, 0, 0, 0, 0, },
+ { 5, 2, 14, 4, 20, 8, 37, 16, 44, 4, 55, 16, 60, 32, 76, 2,
+ 87, 4, 93, 32, 103, 1, 110, 1, 119, 2, 124, 1, 0, 0, },
+ { 7, 32, 12, 4, 23, 16, 28, 32, 38, 32, 45, 4, 54, 2, 60, 16,
+ 71, 1, 78, 1, 87, 2, 93, 2, 111, 8, 118, 16, 125, 16, },
+ { 1, 1, 11, 32, 17, 2, 26, 16, 32, 8, 51, 8, 64, 2, 73, 4,
+ 83, 16, 88, 4, 107, 2, 112, 32, 122, 8, 0, 0, 0, 0, },
+ { 0, 4, 9, 32, 18, 32, 25, 8, 34, 8, 43, 32, 49, 2, 58, 16,
+ 74, 1, 81, 16, 90, 2, 96, 2, 105, 4, 115, 16, 122, 4, },
+ { 2, 2, 19, 1, 24, 1, 34, 4, 41, 32, 50, 32, 57, 8, 64, 32,
+ 73, 1, 80, 16, 91, 4, 106, 1, 113, 16, 123, 8, 0, 0, },
+ { 3, 4, 10, 16, 16, 8, 35, 8, 51, 1, 56, 1, 67, 16, 72, 4,
+ 91, 2, 96, 32, 105, 1, 112, 16, 121, 2, 0, 0, 0, 0, },
+ { 4, 16, 15, 1, 22, 1, 31, 2, 37, 2, 55, 8, 62, 16, 69, 16,
+ 76, 4, 87, 16, 92, 32, 102, 32, 109, 4, 118, 2, 125, 32, },
+ { 6, 4, 23, 8, 30, 16, 39, 32, 45, 1, 54, 8, 70, 32, 77, 4,
+ 86, 2, 92, 16, 101, 8, 116, 1, 125, 2, 0, 0, 0, 0, },
+ { 4, 4, 13, 1, 22, 8, 36, 2, 47, 4, 53, 32, 63, 1, 69, 8,
+ 84, 1, 94, 4, 100, 8, 117, 16, 127, 32, 0, 0, 0, 0, },
+ { 3, 32, 8, 16, 19, 4, 34, 1, 41, 16, 50, 2, 56, 2, 67, 1,
+ 72, 1, 82, 4, 89, 32, 98, 32, 105, 8, 114, 8, 121, 1, },
+ { 1, 32, 19, 2, 24, 32, 33, 1, 40, 16, 51, 4, 64, 8, 83, 8,
+ 99, 1, 104, 1, 114, 4, 120, 4, 0, 0, 0, 0, 0, 0, },
+ { 8, 2, 17, 4, 27, 16, 32, 4, 51, 2, 56, 32, 66, 8, 75, 32,
+ 81, 2, 90, 16, 96, 8, 115, 8, 122, 2, 0, 0, 0, 0, },
+ { 2, 16, 18, 1, 25, 16, 34, 2, 40, 2, 49, 4, 59, 16, 66, 4,
+ 73, 32, 82, 32, 89, 8, 98, 8, 107, 32, 113, 2, 123, 4, },
+ { 7, 1, 13, 8, 28, 1, 38, 4, 44, 8, 61, 16, 71, 32, 77, 1,
+ 86, 8, 100, 2, 111, 4, 117, 32, 124, 16, 0, 0, 0, 0, },
+ { 12, 8, 29, 16, 36, 4, 47, 16, 52, 32, 62, 32, 68, 2, 79, 4,
+ 85, 32, 95, 1, 102, 1, 111, 2, 117, 2, 126, 4, 0, 0, },
+ { 5, 1, 15, 16, 20, 32, 30, 32, 37, 4, 46, 2, 52, 16, 61, 8,
+ 70, 1, 79, 2, 85, 2, 103, 8, 110, 16, 119, 32, 124, 4, },
+ { 0, 16, 9, 2, 18, 16, 24, 8, 43, 8, 59, 1, 65, 4, 75, 16,
+ 80, 4, 99, 2, 104, 32, 113, 1, 123, 32, 0, 0, 0, 0, },
+ { 10, 32, 17, 8, 26, 8, 35, 32, 41, 2, 50, 16, 56, 8, 66, 1,
+ 73, 16, 82, 2, 88, 2, 97, 4, 107, 16, 112, 4, 121, 32, },
+ { 0, 2, 11, 1, 16, 1, 26, 4, 33, 32, 42, 32, 49, 8, 58, 8,
+ 65, 1, 72, 16, 83, 4, 98, 1, 105, 16, 114, 2, 0, 0, },
+ { 8, 8, 27, 8, 43, 1, 48, 1, 58, 4, 64, 4, 83, 2, 88, 32,
+ 97, 1, 104, 16, 115, 4, 122, 16, 0, 0, 0, 0, 0, 0, },
+ { 5, 8, 14, 1, 23, 2, 29, 2, 47, 8, 54, 16, 63, 32, 68, 4,
+ 79, 16, 84, 32, 94, 32, 101, 4, 110, 2, 116, 16, 127, 1, },
+ { 4, 8, 15, 8, 22, 16, 31, 32, 37, 1, 46, 8, 60, 2, 69, 4,
+ 78, 2, 84, 16, 93, 8, 108, 1, 118, 4, 0, 0, 0, 0, },
+ { 7, 16, 14, 8, 28, 2, 39, 4, 45, 32, 55, 1, 62, 1, 76, 1,
+ 86, 4, 92, 8, 109, 16, 116, 4, 125, 1, 0, 0, 0, 0, },
+ { 1, 2, 11, 4, 26, 1, 33, 16, 42, 2, 48, 2, 57, 4, 64, 1,
+ 74, 4, 81, 32, 90, 32, 97, 8, 106, 8, 115, 32, 120, 16, },
+ { 2, 32, 11, 2, 16, 32, 25, 1, 32, 16, 43, 4, 58, 1, 75, 8,
+ 91, 1, 96, 1, 106, 4, 113, 32, 0, 0, 0, 0, 0, 0, },
+ { 3, 1, 9, 4, 19, 16, 24, 4, 43, 2, 48, 32, 57, 1, 67, 32,
+ 73, 2, 82, 16, 88, 8, 107, 8, 120, 2, 0, 0, 0, 0, },
+ { 0, 8, 10, 1, 17, 16, 26, 2, 32, 2, 41, 4, 51, 16, 56, 4,
+ 65, 32, 74, 32, 81, 8, 90, 8, 99, 32, 105, 2, 114, 16, },
+ { 6, 1, 20, 1, 30, 4, 36, 8, 53, 16, 60, 4, 69, 1, 78, 8,
+ 92, 2, 103, 4, 109, 32, 119, 1, 125, 8, 0, 0, 0, 0, },
+ { 7, 8, 21, 16, 28, 4, 39, 16, 44, 32, 54, 32, 61, 4, 71, 4,
+ 77, 32, 87, 1, 94, 1, 103, 2, 109, 2, 124, 8, 0, 0, },
+ { 6, 8, 12, 32, 22, 32, 29, 4, 38, 2, 44, 16, 53, 8, 71, 2,
+ 77, 2, 95, 8, 102, 16, 111, 32, 117, 1, 127, 16, 0, 0, }
+};
+
+void
+key_setup(char key[DESKEYLEN], char *ek)
+{
+ int i, j, k, mask;
+ uchar (*x)[2];
+
+ memset(ek, 0, 128);
+ x = keyexpand[0];
+ for(i = 0; i < 7; i++){
+ k = key[i];
+ for(mask = 0x80; mask; mask >>= 1){
+ if(k & mask)
+ for(j = 0; j < 15; j++)
+ ek[x[j][0]] |= x[j][1];
+ x += 15;
+ }
+ }
+}
diff --git a/sys/src/cmd/unix/u9fs/dirmodeconv.c b/sys/src/cmd/unix/u9fs/dirmodeconv.c
new file mode 100755
index 000000000..9941b165d
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/dirmodeconv.c
@@ -0,0 +1,47 @@
+#include <plan9.h>
+#include <fcall.h>
+
+static char *modes[] =
+{
+ "---",
+ "--x",
+ "-w-",
+ "-wx",
+ "r--",
+ "r-x",
+ "rw-",
+ "rwx",
+};
+
+static void
+rwx(long m, char *s)
+{
+ strncpy(s, modes[m], 3);
+}
+
+int
+dirmodeconv(va_list *arg, Fconv *f)
+{
+ static char buf[16];
+ ulong m;
+
+ m = va_arg(*arg, ulong);
+
+ if(m & DMDIR)
+ buf[0]='d';
+ else if(m & DMAPPEND)
+ buf[0]='a';
+ else
+ buf[0]='-';
+ if(m & DMEXCL)
+ buf[1]='l';
+ else
+ buf[1]='-';
+ rwx((m>>6)&7, buf+2);
+ rwx((m>>3)&7, buf+5);
+ rwx((m>>0)&7, buf+8);
+ buf[11] = 0;
+
+ strconv(buf, f);
+ return 0;
+}
diff --git a/sys/src/cmd/unix/u9fs/doprint.c b/sys/src/cmd/unix/u9fs/doprint.c
new file mode 100755
index 000000000..b51c432bc
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/doprint.c
@@ -0,0 +1,610 @@
+#include <plan9.h>
+
+#define lock(x)
+#define unlock(x)
+
+enum
+{
+ IDIGIT = 40,
+ MAXCONV = 40,
+ FDIGIT = 30,
+ FDEFLT = 6,
+ NONE = -1000,
+ MAXFMT = 512,
+
+ FPLUS = 1<<0,
+ FMINUS = 1<<1,
+ FSHARP = 1<<2,
+ FLONG = 1<<3,
+ FUNSIGN = 1<<5,
+ FVLONG = 1<<6,
+ FPOINTER= 1<<7
+};
+
+int printcol;
+
+static struct
+{
+/* Lock; */
+ int convcount;
+ char index[MAXFMT];
+ int (*conv[MAXCONV])(va_list*, Fconv*);
+} fmtalloc;
+
+static int noconv(va_list*, Fconv*);
+static int flags(va_list*, Fconv*);
+
+static int cconv(va_list*, Fconv*);
+static int sconv(va_list*, Fconv*);
+static int percent(va_list*, Fconv*);
+static int column(va_list*, Fconv*);
+
+extern int numbconv(va_list*, Fconv*);
+
+
+static void
+initfmt(void)
+{
+ int cc;
+
+ lock(&fmtalloc);
+ if(fmtalloc.convcount <= 0) {
+ cc = 0;
+ fmtalloc.conv[cc] = noconv;
+ cc++;
+
+ fmtalloc.conv[cc] = flags;
+ fmtalloc.index['+'] = cc;
+ fmtalloc.index['-'] = cc;
+ fmtalloc.index['#'] = cc;
+ fmtalloc.index['l'] = cc;
+ fmtalloc.index['u'] = cc;
+ cc++;
+
+ fmtalloc.conv[cc] = numbconv;
+ fmtalloc.index['d'] = cc;
+ fmtalloc.index['o'] = cc;
+ fmtalloc.index['x'] = cc;
+ fmtalloc.index['X'] = cc;
+ fmtalloc.index['p'] = cc;
+ cc++;
+
+
+ fmtalloc.conv[cc] = cconv;
+ fmtalloc.index['c'] = cc;
+ fmtalloc.index['C'] = cc;
+ cc++;
+
+ fmtalloc.conv[cc] = sconv;
+ fmtalloc.index['s'] = cc;
+ fmtalloc.index['S'] = cc;
+ cc++;
+
+ fmtalloc.conv[cc] = percent;
+ fmtalloc.index['%'] = cc;
+ cc++;
+
+ fmtalloc.conv[cc] = column;
+ fmtalloc.index['|'] = cc;
+ cc++;
+
+ fmtalloc.convcount = cc;
+ }
+ unlock(&fmtalloc);
+}
+
+int
+fmtinstall(int c, int (*f)(va_list*, Fconv*))
+{
+
+ if(fmtalloc.convcount <= 0)
+ initfmt();
+
+ lock(&fmtalloc);
+ if(c < 0 || c >= MAXFMT) {
+ unlock(&fmtalloc);
+ return -1;
+ }
+ if(fmtalloc.convcount >= MAXCONV) {
+ unlock(&fmtalloc);
+ return -1;
+ }
+ fmtalloc.conv[fmtalloc.convcount] = f;
+ fmtalloc.index[c] = fmtalloc.convcount;
+ fmtalloc.convcount++;
+
+ unlock(&fmtalloc);
+ return 0;
+}
+
+static void
+pchar(Rune c, Fconv *fp)
+{
+ int n;
+
+ n = fp->eout - fp->out;
+ if(n > 0) {
+ if(c < Runeself) {
+ *fp->out++ = c;
+ return;
+ }
+ if(n >= UTFmax || n >= runelen(c)) {
+ n = runetochar(fp->out, &c);
+ fp->out += n;
+ return;
+ }
+ fp->eout = fp->out;
+ }
+}
+
+char*
+doprint(char *s, char *es, char *fmt, va_list *argp)
+{
+ int n, c;
+ Rune rune;
+ Fconv local;
+
+ if(fmtalloc.convcount <= 0)
+ initfmt();
+
+ if(s >= es)
+ return s;
+ local.out = s;
+ local.eout = es-1;
+
+loop:
+ c = *fmt & 0xff;
+ if(c >= Runeself) {
+ n = chartorune(&rune, fmt);
+ fmt += n;
+ c = rune;
+ } else
+ fmt++;
+ switch(c) {
+ case 0:
+ *local.out = 0;
+ return local.out;
+
+ default:
+ printcol++;
+ goto common;
+
+ case '\n':
+ printcol = 0;
+ goto common;
+
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ goto common;
+
+ common:
+ pchar(c, &local);
+ goto loop;
+
+ case '%':
+ break;
+ }
+ local.f1 = NONE;
+ local.f2 = NONE;
+ local.f3 = 0;
+
+ /*
+ * read one of the following
+ * 1. number, => f1, f2 in order.
+ * 2. '*' same as number (from args)
+ * 3. '.' ignored (separates numbers)
+ * 4. flag => f3
+ * 5. verb and terminate
+ */
+l0:
+ c = *fmt & 0xff;
+ if(c >= Runeself) {
+ n = chartorune(&rune, fmt);
+ fmt += n;
+ c = rune;
+ } else
+ fmt++;
+
+l1:
+ if(c == 0) {
+ fmt--;
+ goto loop;
+ }
+ if(c == '.') {
+ if(local.f1 == NONE)
+ local.f1 = 0;
+ local.f2 = 0;
+ goto l0;
+ }
+ if((c >= '1' && c <= '9') ||
+ (c == '0' && local.f1 != NONE)) { /* '0' is a digit for f2 */
+ n = 0;
+ while(c >= '0' && c <= '9') {
+ n = n*10 + c-'0';
+ c = *fmt++;
+ }
+ if(local.f1 == NONE)
+ local.f1 = n;
+ else
+ local.f2 = n;
+ goto l1;
+ }
+ if(c == '*') {
+ n = va_arg(*argp, int);
+ if(local.f1 == NONE)
+ local.f1 = n;
+ else
+ local.f2 = n;
+ goto l0;
+ }
+ n = 0;
+ if(c >= 0 && c < MAXFMT)
+ n = fmtalloc.index[c];
+ local.chr = c;
+ n = (*fmtalloc.conv[n])(argp, &local);
+ if(n < 0) {
+ local.f3 |= -n;
+ goto l0;
+ }
+ goto loop;
+}
+
+int
+numbconv(va_list *arg, Fconv *fp)
+{
+ char s[IDIGIT];
+ int i, f, n, b, ucase;
+ long v;
+ vlong vl;
+
+ SET(v);
+ SET(vl);
+
+ ucase = 0;
+ b = fp->chr;
+ switch(fp->chr) {
+ case 'u':
+ fp->f3 |= FUNSIGN;
+ case 'd':
+ b = 10;
+ break;
+
+ case 'b':
+ b = 2;
+ break;
+
+ case 'o':
+ b = 8;
+ break;
+
+ case 'X':
+ ucase = 1;
+ case 'x':
+ b = 16;
+ break;
+ case 'p':
+ fp->f3 |= FPOINTER|FUNSIGN;
+ b = 16;
+ break;
+ }
+
+ f = 0;
+ switch(fp->f3 & (FVLONG|FLONG|FUNSIGN|FPOINTER)) {
+ case FVLONG|FLONG:
+ vl = va_arg(*arg, vlong);
+ break;
+
+ case FUNSIGN|FVLONG|FLONG:
+ vl = va_arg(*arg, uvlong);
+ break;
+
+ case FUNSIGN|FPOINTER:
+ v = (ulong)va_arg(*arg, void*);
+ break;
+
+ case FLONG:
+ v = va_arg(*arg, long);
+ break;
+
+ case FUNSIGN|FLONG:
+ v = va_arg(*arg, ulong);
+ break;
+
+ default:
+ v = va_arg(*arg, int);
+ break;
+
+ case FUNSIGN:
+ v = va_arg(*arg, unsigned);
+ break;
+ }
+ if(fp->f3 & FVLONG) {
+ if(!(fp->f3 & FUNSIGN) && vl < 0) {
+ vl = -vl;
+ f = 1;
+ }
+ } else {
+ if(!(fp->f3 & FUNSIGN) && v < 0) {
+ v = -v;
+ f = 1;
+ }
+ }
+ s[IDIGIT-1] = 0;
+ for(i = IDIGIT-2;; i--) {
+ if(fp->f3 & FVLONG)
+ n = (uvlong)vl % b;
+ else
+ n = (ulong)v % b;
+ n += '0';
+ if(n > '9') {
+ n += 'a' - ('9'+1);
+ if(ucase)
+ n += 'A'-'a';
+ }
+ s[i] = n;
+ if(i < 2)
+ break;
+ if(fp->f3 & FVLONG)
+ vl = (uvlong)vl / b;
+ else
+ v = (ulong)v / b;
+ if(fp->f2 != NONE && i >= IDIGIT-fp->f2)
+ continue;
+ if(fp->f3 & FVLONG) {
+ if(vl <= 0)
+ break;
+ continue;
+ }
+ if(v <= 0)
+ break;
+ }
+
+ if(fp->f3 & FSHARP) {
+ if(b == 8 && s[i] != '0')
+ s[--i] = '0';
+ if(b == 16) {
+ if(ucase)
+ s[--i] = 'X';
+ else
+ s[--i] = 'x';
+ s[--i] = '0';
+ }
+ }
+ if(f)
+ s[--i] = '-';
+ else if(fp->f3 & FPLUS)
+ s[--i] = '+';
+
+ fp->f2 = NONE;
+ strconv(s+i, fp);
+ return 0;
+}
+
+void
+Strconv(Rune *s, Fconv *fp)
+{
+ int n, c;
+
+ if(fp->f3 & FMINUS)
+ fp->f1 = -fp->f1;
+ n = 0;
+ if(fp->f1 != NONE && fp->f1 >= 0) {
+ for(; s[n]; n++)
+ ;
+ while(n < fp->f1) {
+ pchar(' ', fp);
+ printcol++;
+ n++;
+ }
+ }
+ for(;;) {
+ c = *s++;
+ if(c == 0)
+ break;
+ n++;
+ if(fp->f2 == NONE || fp->f2 > 0) {
+ pchar(c, fp);
+ if(fp->f2 != NONE)
+ fp->f2--;
+ switch(c) {
+ default:
+ printcol++;
+ break;
+ case '\n':
+ printcol = 0;
+ break;
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ break;
+ }
+ }
+ }
+ if(fp->f1 != NONE && fp->f1 < 0) {
+ fp->f1 = -fp->f1;
+ while(n < fp->f1) {
+ pchar(' ', fp);
+ printcol++;
+ n++;
+ }
+ }
+}
+
+void
+strconv(char *s, Fconv *fp)
+{
+ int n, c, i;
+ Rune rune;
+
+ if(fp->f3 & FMINUS)
+ fp->f1 = -fp->f1;
+ n = 0;
+ if(fp->f1 != NONE && fp->f1 >= 0) {
+ n = utflen(s);
+ while(n < fp->f1) {
+ pchar(' ', fp);
+ printcol++;
+ n++;
+ }
+ }
+ for(;;) {
+ c = *s & 0xff;
+ if(c >= Runeself) {
+ i = chartorune(&rune, s);
+ s += i;
+ c = rune;
+ } else
+ s++;
+ if(c == 0)
+ break;
+ n++;
+ if(fp->f2 == NONE || fp->f2 > 0) {
+ pchar(c, fp);
+ if(fp->f2 != NONE)
+ fp->f2--;
+ switch(c) {
+ default:
+ printcol++;
+ break;
+ case '\n':
+ printcol = 0;
+ break;
+ case '\t':
+ printcol = (printcol+8) & ~7;
+ break;
+ }
+ }
+ }
+ if(fp->f1 != NONE && fp->f1 < 0) {
+ fp->f1 = -fp->f1;
+ while(n < fp->f1) {
+ pchar(' ', fp);
+ printcol++;
+ n++;
+ }
+ }
+}
+
+static int
+noconv(va_list *va, Fconv *fp)
+{
+ char s[10];
+
+ USED(va);
+ s[0] = '*';
+ s[1] = fp->chr;
+ s[2] = '*';
+ s[3] = 0;
+ fp->f1 = 0;
+ fp->f2 = NONE;
+ fp->f3 = 0;
+ strconv(s, fp);
+ return 0;
+}
+
+static int
+cconv(va_list *arg, Fconv *fp)
+{
+ char s[10];
+ Rune rune;
+
+ rune = va_arg(*arg, int);
+ if(fp->chr == 'c')
+ rune &= 0xff;
+ s[runetochar(s, &rune)] = 0;
+
+ fp->f2 = NONE;
+ strconv(s, fp);
+ return 0;
+}
+
+static Rune null[] = { L'<', L'n', L'u', L'l', L'l', L'>', L'\0' };
+
+static int
+sconv(va_list *arg, Fconv *fp)
+{
+ char *s;
+ Rune *r;
+
+ if(fp->chr == 's') {
+ s = va_arg(*arg, char*);
+ if(s == 0)
+ s = "<null>";
+ strconv(s, fp);
+ } else {
+ r = va_arg(*arg, Rune*);
+ if(r == 0)
+ r = null;
+ Strconv(r, fp);
+ }
+ return 0;
+}
+
+static int
+percent(va_list *va, Fconv *fp)
+{
+ USED(va);
+
+ pchar('%', fp);
+ printcol++;
+ return 0;
+}
+
+static int
+column(va_list *arg, Fconv *fp)
+{
+ int col, pc;
+
+ col = va_arg(*arg, int);
+ while(printcol < col) {
+ pc = (printcol+8) & ~7;
+ if(pc <= col) {
+ pchar('\t', fp);
+ printcol = pc;
+ } else {
+ pchar(' ', fp);
+ printcol++;
+ }
+ }
+ return 0;
+}
+
+static int
+flags(va_list *va, Fconv *fp)
+{
+ int f;
+
+ USED(va);
+ f = 0;
+ switch(fp->chr) {
+ case '+':
+ f = FPLUS;
+ break;
+
+ case '-':
+ f = FMINUS;
+ break;
+
+ case '#':
+ f = FSHARP;
+ break;
+
+ case 'l':
+ f = FLONG;
+ if(fp->f3 & FLONG)
+ f = FVLONG;
+ break;
+
+ case 'u':
+ f = FUNSIGN;
+ break;
+ }
+ return -f;
+}
+
+/*
+ * This code is superseded by the more accurate (but more complex)
+ * algorithm in fltconv.c and dtoa.c. Uncomment this routine to avoid
+ * using the more complex code.
+ *
+ */
+
diff --git a/sys/src/cmd/unix/u9fs/fcall.h b/sys/src/cmd/unix/u9fs/fcall.h
new file mode 100755
index 000000000..4a0d6f2cc
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/fcall.h
@@ -0,0 +1,123 @@
+#define VERSION9P "9P2000"
+#define MAXWELEM 16
+
+typedef
+struct Fcall
+{
+ uchar type;
+ u32int fid;
+ ushort tag;
+
+ u32int msize; /* Tversion, Rversion */
+ char *version; /* Tversion, Rversion */
+
+ u32int oldtag; /* Tflush */
+
+ char *ename; /* Rerror */
+
+ Qid qid; /* Rattach, Ropen, Rcreate */
+ u32int iounit; /* Ropen, Rcreate */
+
+ char *uname; /* Tattach, Tauth */
+ char *aname; /* Tattach, Tauth */
+
+
+ u32int perm; /* Tcreate */
+ char *name; /* Tcreate */
+ uchar mode; /* Tcreate, Topen */
+
+ u32int newfid; /* Twalk */
+ ushort nwname; /* Twalk */
+ char *wname[MAXWELEM]; /* Twalk */
+
+ ushort nwqid; /* Rwalk */
+ Qid wqid[MAXWELEM]; /* Rwalk */
+
+ vlong offset; /* Tread, Twrite */
+ u32int count; /* Tread, Twrite, Rread */
+ char *data; /* Twrite, Rread */
+
+ ushort nstat; /* Twstat, Rstat */
+ uchar *stat; /* Twstat, Rstat */
+
+ u32int afid; /* Tauth, Tattach */
+ Qid aqid; /* Rauth */
+} Fcall;
+
+
+#define GBIT8(p) ((p)[0])
+#define GBIT16(p) ((p)[0]|((p)[1]<<8))
+#define GBIT32(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define GBIT64(p) ((ulong)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\
+ ((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32))
+
+#define PBIT8(p,v) (p)[0]=(v)
+#define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8
+#define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+#define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\
+ (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56
+
+#define BIT8SZ 1
+#define BIT16SZ 2
+#define BIT32SZ 4
+#define BIT64SZ 8
+#define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ)
+
+/* STATFIXLEN includes leading 16-bit count */
+/* The count, however, excludes itself; total size is BIT16SZ+count */
+#define STATFIXLEN (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ) /* amount of fixed length data in a stat buffer */
+
+#define MAXMSG 10000 /* max header sans data */
+#define NOTAG ~0U /* Dummy tag */
+#define IOHDRSZ 24 /* ample room for Twrite/Rread header (iounit) */
+
+enum
+{
+ Tversion = 100,
+ Rversion,
+ Tauth = 102,
+ Rauth,
+ Tattach = 104,
+ Rattach,
+ Terror = 106, /* illegal */
+ Rerror,
+ Tflush = 108,
+ Rflush,
+ Twalk = 110,
+ Rwalk,
+ Topen = 112,
+ Ropen,
+ Tcreate = 114,
+ Rcreate,
+ Tread = 116,
+ Rread,
+ Twrite = 118,
+ Rwrite,
+ Tclunk = 120,
+ Rclunk,
+ Tremove = 122,
+ Rremove,
+ Tstat = 124,
+ Rstat,
+ Twstat = 126,
+ Rwstat,
+ Tmax
+};
+
+uint convM2S(uchar*, uint, Fcall*);
+uint convS2M(Fcall*, uchar*, uint);
+
+int statcheck(uchar *abuf, uint nbuf);
+uint convM2D(uchar*, uint, Dir*, char*);
+uint convD2M(Dir*, uchar*, uint);
+uint sizeD2M(Dir*);
+
+int fcallconv(va_list*, Fconv*);
+int dirconv(va_list*, Fconv*);
+int dirmodeconv(va_list*, Fconv*);
+
+int read9pmsg(int, void*, uint);
+
+enum {
+ NOFID = 0xFFFFFFFF,
+};
diff --git a/sys/src/cmd/unix/u9fs/fcallconv.c b/sys/src/cmd/unix/u9fs/fcallconv.c
new file mode 100755
index 000000000..ca422ebfc
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/fcallconv.c
@@ -0,0 +1,228 @@
+#include <plan9.h>
+#include <fcall.h>
+#include <oldfcall.h>
+
+extern int old9p;
+
+static uint dumpsome(char*, char*, long);
+static void fdirconv(char*, Dir*);
+static char *qidtype(char*, uchar);
+
+#define QIDFMT "(%.16llux %lud %s)"
+
+int
+fcallconv(va_list *arg, Fconv *f1)
+{
+ Fcall *f;
+ int fid, type, tag, n, i;
+ char buf[512], tmp[200];
+ Dir *d;
+ Qid *q;
+
+ f = va_arg(*arg, Fcall*);
+ type = f->type;
+ fid = f->fid;
+ tag = f->tag;
+ switch(type){
+ case Tversion: /* 100 */
+ sprint(buf, "Tversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Rversion:
+ sprint(buf, "Rversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Tauth: /* 102 */
+ sprint(buf, "Tauth tag %ud afid %d uname %s aname %s", tag,
+ f->afid, f->uname, f->aname);
+ break;
+ case Rauth:
+ sprint(buf, "Rauth tag %ud qid " QIDFMT, tag,
+ f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type));
+ break;
+ case Tattach: /* 104 */
+ sprint(buf, "Tattach tag %ud fid %d afid %d uname %s aname %s", tag,
+ fid, f->afid, f->uname, f->aname);
+ break;
+ case Rattach:
+ sprint(buf, "Rattach tag %ud qid " QIDFMT, tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type));
+ break;
+ case Rerror: /* 107; 106 (Terror) illegal */
+ sprint(buf, "Rerror tag %ud ename %s", tag, f->ename);
+ break;
+ case Tflush: /* 108 */
+ sprint(buf, "Tflush tag %ud oldtag %ud", tag, f->oldtag);
+ break;
+ case Rflush:
+ sprint(buf, "Rflush tag %ud", tag);
+ break;
+ case Twalk: /* 110 */
+ n = sprint(buf, "Twalk tag %ud fid %d newfid %d nwname %d ", tag, fid, f->newfid, f->nwname);
+ for(i=0; i<f->nwname; i++)
+ n += sprint(buf+n, "%d:%s ", i, f->wname[i]);
+ break;
+ case Rwalk:
+ n = sprint(buf, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid);
+ for(i=0; i<f->nwqid; i++){
+ q = &f->wqid[i];
+ n += sprint(buf+n, "%d:" QIDFMT " ", i,
+ q->path, q->vers, qidtype(tmp, q->type));
+ }
+ break;
+ case Topen: /* 112 */
+ sprint(buf, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode);
+ break;
+ case Ropen:
+ sprint(buf, "Ropen tag %ud qid " QIDFMT " iounit %ud ", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ break;
+ case Tcreate: /* 114 */
+ sprint(buf, "Tcreate tag %ud fid %ud perm %M mode %d", tag, fid, (ulong)f->perm, f->mode);
+ break;
+ case Rcreate:
+ sprint(buf, "Rcreate tag %ud qid " QIDFMT " iounit %ud ", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ break;
+ case Tread: /* 116 */
+ sprint(buf, "Tread tag %ud fid %d offset %lld count %ud",
+ tag, fid, f->offset, f->count);
+ break;
+ case Rread:
+ n = sprint(buf, "Rread tag %ud count %ud ", tag, f->count);
+ dumpsome(buf+n, f->data, f->count);
+ break;
+ case Twrite: /* 118 */
+ n = sprint(buf, "Twrite tag %ud fid %d offset %lld count %ud ",
+ tag, fid, f->offset, f->count);
+ dumpsome(buf+n, f->data, f->count);
+ break;
+ case Rwrite:
+ sprint(buf, "Rwrite tag %ud count %ud", tag, f->count);
+ break;
+ case Tclunk: /* 120 */
+ sprint(buf, "Tclunk tag %ud fid %ud", tag, fid);
+ break;
+ case Rclunk:
+ sprint(buf, "Rclunk tag %ud", tag);
+ break;
+ case Tremove: /* 122 */
+ sprint(buf, "Tremove tag %ud fid %ud", tag, fid);
+ break;
+ case Rremove:
+ sprint(buf, "Rremove tag %ud", tag);
+ break;
+ case Tstat: /* 124 */
+ sprint(buf, "Tstat tag %ud fid %ud", tag, fid);
+ break;
+ case Rstat:
+ n = sprint(buf, "Rstat tag %ud ", tag);
+ if(f->nstat > sizeof tmp)
+ sprint(buf+n, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ (old9p?convM2Dold:convM2D)(f->stat, f->nstat, d, (char*)(d+1));
+ sprint(buf+n, " stat ");
+ fdirconv(buf+n+6, d);
+ }
+ break;
+ case Twstat: /* 126 */
+ n = sprint(buf, "Twstat tag %ud fid %ud", tag, fid);
+ if(f->nstat > sizeof tmp)
+ sprint(buf+n, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ (old9p?convM2Dold:convM2D)(f->stat, f->nstat, d, (char*)(d+1));
+ sprint(buf+n, " stat ");
+ fdirconv(buf+n+6, d);
+ }
+ break;
+ case Rwstat:
+ sprint(buf, "Rwstat tag %ud", tag);
+ break;
+ default:
+ sprint(buf, "unknown type %d", type);
+ }
+ strconv(buf, f1);
+ return(sizeof(Fcall*));
+}
+
+static char*
+qidtype(char *s, uchar t)
+{
+ char *p;
+
+ p = s;
+ if(t & QTDIR)
+ *p++ = 'd';
+ if(t & QTAPPEND)
+ *p++ = 'a';
+ if(t & QTEXCL)
+ *p++ = 'l';
+ if(t & QTMOUNT)
+ *p++ = 'm';
+ if(t & QTAUTH)
+ *p++ = 'A';
+ *p = '\0';
+ return s;
+}
+
+int
+dirconv(va_list *arg, Fconv *f)
+{
+ char buf[160];
+
+ fdirconv(buf, va_arg(*arg, Dir*));
+ strconv(buf, f);
+ return(sizeof(Dir*));
+}
+
+static void
+fdirconv(char *buf, Dir *d)
+{
+ char tmp[16];
+
+ sprint(buf, "'%s' '%s' '%s' '%s' "
+ "q " QIDFMT " m %#luo "
+ "at %ld mt %ld l %lld "
+ "t %d d %d",
+ d->name, d->uid, d->gid, d->muid,
+ d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode,
+ d->atime, d->mtime, d->length,
+ d->type, d->dev);
+}
+
+/*
+ * dump out count (or DUMPL, if count is bigger) bytes from
+ * buf to ans, as a string if they are all printable,
+ * else as a series of hex bytes
+ */
+#define DUMPL 64
+
+static uint
+dumpsome(char *ans, char *buf, long count)
+{
+ int i, printable;
+ char *p;
+
+ printable = 1;
+ if(count > DUMPL)
+ count = DUMPL;
+ for(i=0; i<count && printable; i++)
+ if((buf[i]<32 && buf[i] !='\n' && buf[i] !='\t') || (uchar)buf[i]>127)
+ printable = 0;
+ p = ans;
+ *p++ = '\'';
+ if(printable){
+ memmove(p, buf, count);
+ p += count;
+ }else{
+ for(i=0; i<count; i++){
+ if(i>0 && i%4==0)
+ *p++ = ' ';
+ sprint(p, "%2.2ux", (uchar)buf[i]);
+ p += 2;
+ }
+ }
+ *p++ = '\'';
+ *p = 0;
+ return p - ans;
+}
diff --git a/sys/src/cmd/unix/u9fs/makefile b/sys/src/cmd/unix/u9fs/makefile
new file mode 100755
index 000000000..b834bcaba
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/makefile
@@ -0,0 +1,63 @@
+#
+# The goal is to keep as much per-system stuff autodetected in plan9.h
+# as possible. Still, sometimes you can't help it. Look for your system.
+#
+
+# SGI
+#
+# To correctly handle 64-bit files and offsets, add -64 to CFLAGS and LDFLAGS
+# On Irix 5.X, add -DIRIX5X to hack around their own #include problems (see plan9.h).
+#
+# SunOS
+#
+# SunOS 5.5.1 does not provide inttypes.h; add -lsunos to CFLAGS and
+# change CC and LD to gcc. Add -lsocket, -lnsl to LDTAIL.
+# If you need <inttypes.h> copy sun-inttypes.h to inttypes.h.
+#
+#CC=cc
+CFLAGS=-g -I.
+LD=cc
+LDFLAGS=
+LDTAIL=
+
+OFILES=\
+ authnone.o\
+ authrhosts.o\
+ authp9any.o\
+ convD2M.o\
+ convM2D.o\
+ convM2S.o\
+ convS2M.o\
+ des.o\
+ dirmodeconv.o\
+ doprint.o\
+ fcallconv.o\
+ oldfcall.o\
+ print.o\
+ random.o\
+ readn.o\
+ remotehost.o\
+ rune.o\
+ safecpy.o\
+ strecpy.o\
+ tokenize.o\
+ u9fs.o\
+ utfrune.o
+
+HFILES=\
+ fcall.h\
+ plan9.h
+
+u9fs: $(OFILES)
+ $(LD) $(LDFLAGS) -o u9fs $(OFILES) $(LDTAIL)
+
+%.o: %.c $(HFILES)
+ $(CC) $(CFLAGS) -c $*.c
+
+clean:
+ rm -f *.o u9fs
+
+install: u9fs
+ cp u9fs ../../bin
+
+.PHONY: clean install
diff --git a/sys/src/cmd/unix/u9fs/oldfcall.c b/sys/src/cmd/unix/u9fs/oldfcall.c
new file mode 100755
index 000000000..d9c8ca3b1
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/oldfcall.c
@@ -0,0 +1,521 @@
+#include <plan9.h>
+#include <fcall.h>
+#include <oldfcall.h>
+
+/*
+ * routines to package the old protocol in the new structures.
+ */
+
+#define SHORT(x) p[0]=f->x; p[1]=f->x>>8; p += 2
+#define LONG(x) p[0]=f->x; p[1]=f->x>>8; p[2]=f->x>>16; p[3]=f->x>>24; p += 4
+#define VLONG(x) p[0]=f->x; p[1]=f->x>>8;\
+ p[2]=f->x>>16; p[3]=f->x>>24;\
+ p[4]=f->x>>32; p[5]=f->x>>40;\
+ p[6]=f->x>>48; p[7]=f->x>>56;\
+ p += 8
+#define STRING(x,n) strecpy((char*)p, (char*)p+n, f->x); p += n;
+#define FIXQID(q) q.path ^= (q.path>>33); q.path &= 0x7FFFFFFF; q.path |= (q.type&0x80)<<24
+
+uint
+oldhdrsize(uchar type)
+{
+ switch(type){
+ default:
+ return 0;
+ case oldTnop:
+ return 3;
+ case oldTflush:
+ return 3+2;
+ case oldTclone:
+ return 3+2+2;
+ case oldTwalk:
+ return 3+2+28;
+ case oldTopen:
+ return 3+2+1;
+ case oldTcreate:
+ return 3+2+28+4+1;
+ case oldTread:
+ return 3+2+8+2;
+ case oldTwrite:
+ return 3+2+8+2+1;
+ case oldTclunk:
+ return 3+2;
+ case oldTremove:
+ return 3+2;
+ case oldTstat:
+ return 3+2;
+ case oldTwstat:
+ return 3+2+116;
+ case oldTsession:
+ return 3+8;
+ case oldTattach:
+ return 3+2+28+28+72+13;
+ }
+}
+
+uint
+iosize(uchar *p)
+{
+ if(p[0] != oldTwrite)
+ return 0;
+ return p[3+2+8] | (p[3+2+8+1]<<8);
+}
+
+uint
+sizeS2M(Fcall *f)
+{
+ switch(f->type)
+ {
+ default:
+ abort();
+ return 0;
+
+ /* no T messages */
+
+/*
+ */
+ case Rversion:
+ return 1+2;
+
+/*
+ case Rsession:
+ return 1+2+8+28+48;
+*/
+
+ case Rattach:
+ return 1+2+2+4+4+13;
+
+ case Rerror:
+ return 1+2+64;
+
+ case Rflush:
+ if(f->tag&0x8000)
+ return 1+2+8+28+48; /* session */
+ return 1+2;
+
+ /* assumes we don't ever see Tclwalk requests ... */
+ case Rwalk:
+ if(f->nwqid == 0)
+ return 1+2+2;
+ else
+ return 1+2+2+4+4;
+
+ case Ropen:
+ return 1+2+2+4+4;
+
+ case Rcreate:
+ return 1+2+2+4+4;
+
+ case Rread:
+ return 1+2+2+2+1+f->count;
+
+ case Rwrite:
+ return 1+2+2+2;
+
+ case Rclunk:
+ return 1+2+2;
+
+ case Rremove:
+ return 1+2+2;
+
+ case Rstat:
+ return 1+2+2+116;
+
+ case Rwstat:
+ return 1+2+2;
+ }
+}
+
+uint
+convS2Mold(Fcall *f, uchar *ap, uint nap)
+{
+ uchar *p;
+
+ if(nap < sizeS2M(f))
+ return 0;
+
+ p = ap;
+ switch(f->type)
+ {
+ default:
+ abort();
+ return 0;
+
+ /* no T messages */
+
+/*
+ */
+ case Rversion:
+ *p++ = oldRnop;
+ SHORT(tag);
+ break;
+
+/*
+ case Rsession:
+ *p++ = oldRsession;
+ SHORT(tag);
+
+ if(f->nchal > 8)
+ f->nchal = 8;
+ memmove(p, f->chal, f->nchal);
+ p += f->nchal;
+ if(f->nchal < 8){
+ memset(p, 0, 8 - f->nchal);
+ p += 8 - f->nchal;
+ }
+
+ STRING(authid, 28);
+ STRING(authdom, 48);
+ break;
+*/
+
+ case Rattach:
+ *p++ = oldRattach;
+ SHORT(tag);
+ SHORT(fid);
+ FIXQID(f->qid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ memset(p, 0, 13);
+ p += 13;
+ break;
+
+ case Rerror:
+ *p++ = oldRerror;
+ SHORT(tag);
+ STRING(ename, 64);
+ break;
+
+ case Rflush:
+ if(f->tag&0x8000){
+ *p++ = oldRsession;
+ f->tag &= ~0x8000;
+ SHORT(tag);
+ memset(p, 0, 8+28+48);
+ p += 8+28+48;
+ }else{
+ *p++ = oldRflush;
+ SHORT(tag);
+ }
+ break;
+
+ /* assumes we don't ever see Tclwalk requests ... */
+ case Rwalk:
+ if(f->nwqid == 0){ /* successful clone */
+ *p++ = oldRclone;
+ SHORT(tag);
+ SHORT(fid);
+ }else{ /* successful 1-element walk */
+ *p++ = oldRwalk;
+ SHORT(tag);
+ SHORT(fid);
+ FIXQID(f->wqid[0]);
+ LONG(wqid[0].path);
+ LONG(wqid[0].vers);
+ }
+ break;
+
+ case Ropen:
+ *p++ = oldRopen;
+ SHORT(tag);
+ SHORT(fid);
+ FIXQID(f->qid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Rcreate:
+ *p++ = oldRcreate;
+ SHORT(tag);
+ SHORT(fid);
+ FIXQID(f->qid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ break;
+
+ case Rread:
+ *p++ = oldRread;
+ SHORT(tag);
+ SHORT(fid);
+ SHORT(count);
+ p++; /* pad(1) */
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Rwrite:
+ *p++ = oldRwrite;
+ SHORT(tag);
+ SHORT(fid);
+ SHORT(count);
+ break;
+
+ case Rclunk:
+ *p++ = oldRclunk;
+ SHORT(tag);
+ SHORT(fid);
+ break;
+
+ case Rremove:
+ *p++ = oldRremove;
+ SHORT(tag);
+ SHORT(fid);
+ break;
+
+ case Rstat:
+ *p++ = oldRstat;
+ SHORT(tag);
+ SHORT(fid);
+ memmove(p, f->stat, 116);
+ p += 116;
+ break;
+
+ case Rwstat:
+ *p++ = oldRwstat;
+ SHORT(tag);
+ SHORT(fid);
+ break;
+ }
+ return p - ap;
+}
+
+uint
+sizeD2Mold(Dir *d)
+{
+ return 116;
+}
+
+uint
+convD2Mold(Dir *f, uchar *ap, uint nap)
+{
+ uchar *p;
+
+ if(nap < 116)
+ return 0;
+
+ p = ap;
+ STRING(name, 28);
+ STRING(uid, 28);
+ STRING(gid, 28);
+ FIXQID(f->qid);
+ LONG(qid.path);
+ LONG(qid.vers);
+ LONG(mode);
+ LONG(atime);
+ LONG(mtime);
+ VLONG(length);
+ SHORT(type);
+ SHORT(dev);
+
+ return p - ap;
+}
+
+#undef SHORT
+#undef LONG
+#undef VLONG
+#undef STRING
+#define CHAR(x) f->x = *p++
+#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2
+#define LONG(x) f->x = (p[0] | (p[1]<<8) |\
+ (p[2]<<16) | (p[3]<<24)); p += 4
+#define VLONG(x) f->x = (ulong)(p[0] | (p[1]<<8) |\
+ (p[2]<<16) | (p[3]<<24)) |\
+ ((vlong)(p[4] | (p[5]<<8) |\
+ (p[6]<<16) | (p[7]<<24)) << 32); p += 8
+#define STRING(x,n) f->x = (char*)p; p += n
+
+uint
+convM2Sold(uchar *ap, uint nap, Fcall *f)
+{
+ uchar *p, *q, *ep;
+
+ p = ap;
+ ep = p + nap;
+
+ if(p+3 > ep)
+ return 0;
+
+ switch(*p++){
+ case oldTnop:
+ f->type = Tversion;
+ SHORT(tag);
+ f->msize = 0;
+ f->version = "9P1";
+ break;
+
+ case oldTflush:
+ f->type = Tflush;
+ SHORT(tag);
+ if(p+2 > ep)
+ return 0;
+ SHORT(oldtag);
+ break;
+
+ case oldTclone:
+ f->type = Twalk;
+ SHORT(tag);
+ if(p+2+2 > ep)
+ return 0;
+ SHORT(fid);
+ SHORT(newfid);
+ f->nwname = 0;
+ break;
+
+ case oldTwalk:
+ f->type = Twalk;
+ SHORT(tag);
+ if(p+2+28 > ep)
+ return 0;
+ SHORT(fid);
+ f->newfid = f->fid;
+ f->nwname = 1;
+ f->wname[0] = (char*)p;
+ p += 28;
+ break;
+
+ case oldTopen:
+ f->type = Topen;
+ SHORT(tag);
+ if(p+2+1 > ep)
+ return 0;
+ SHORT(fid);
+ CHAR(mode);
+ break;
+
+ case oldTcreate:
+ f->type = Tcreate;
+ SHORT(tag);
+ if(p+2+28+4+1 > ep)
+ return 0;
+ SHORT(fid);
+ f->name = (char*)p;
+ p += 28;
+ LONG(perm);
+ CHAR(mode);
+ break;
+
+ case oldTread:
+ f->type = Tread;
+ SHORT(tag);
+ if(p+2+8+2 > ep)
+ return 0;
+ SHORT(fid);
+ VLONG(offset);
+ SHORT(count);
+ break;
+
+ case oldTwrite:
+ f->type = Twrite;
+ SHORT(tag);
+ if(p+2+8+2+1 > ep)
+ return 0;
+ SHORT(fid);
+ VLONG(offset);
+ SHORT(count);
+ p++; /* pad(1) */
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case oldTclunk:
+ f->type = Tclunk;
+ SHORT(tag);
+ if(p+2 > ep)
+ return 0;
+ SHORT(fid);
+ break;
+
+ case oldTremove:
+ f->type = Tremove;
+ SHORT(tag);
+ if(p+2 > ep)
+ return 0;
+ SHORT(fid);
+ break;
+
+ case oldTstat:
+ f->type = Tstat;
+ f->nstat = 116;
+ SHORT(tag);
+ if(p+2 > ep)
+ return 0;
+ SHORT(fid);
+ break;
+
+ case oldTwstat:
+ f->type = Twstat;
+ SHORT(tag);
+ if(p+2+116 > ep)
+ return 0;
+ SHORT(fid);
+ f->stat = p;
+ q = p+28*3+5*4;
+ memset(q, 0xFF, 8); /* clear length to ``don't care'' */
+ p += 116;
+ break;
+
+/*
+ case oldTsession:
+ f->type = Tsession;
+ SHORT(tag);
+ if(p+8 > ep)
+ return 0;
+ f->chal = p;
+ p += 8;
+ f->nchal = 8;
+ break;
+*/
+ case oldTsession:
+ f->type = Tflush;
+ SHORT(tag);
+ f->tag |= 0x8000;
+ f->oldtag = f->tag;
+ p += 8;
+ break;
+
+ case oldTattach:
+ f->type = Tattach;
+ SHORT(tag);
+ if(p+2+28+28+72+13 > ep)
+ return 0;
+ SHORT(fid);
+ STRING(uname, 28);
+ STRING(aname, 28);
+ p += 72+13;
+ f->afid = NOFID;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return p-ap;
+}
+
+uint
+convM2Dold(uchar *ap, uint nap, Dir *f, char *strs)
+{
+ uchar *p;
+
+ USED(strs);
+
+ if(nap < 116)
+ return 0;
+
+ p = (uchar*)ap;
+ STRING(name, 28);
+ STRING(uid, 28);
+ STRING(gid, 28);
+ LONG(qid.path);
+ LONG(qid.vers);
+ LONG(mode);
+ LONG(atime);
+ LONG(mtime);
+ VLONG(length);
+ SHORT(type);
+ SHORT(dev);
+ f->qid.type = (f->mode>>24)&0xF0;
+ return p - (uchar*)ap;
+}
diff --git a/sys/src/cmd/unix/u9fs/oldfcall.h b/sys/src/cmd/unix/u9fs/oldfcall.h
new file mode 100755
index 000000000..90c00d6ad
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/oldfcall.h
@@ -0,0 +1,50 @@
+uint convM2Dold(uchar*, uint, Dir*, char*);
+uint convD2Mold(Dir*, uchar*, uint);
+uint sizeD2Mold(Dir*);
+uint convM2Sold(uchar*, uint, Fcall*);
+uint convS2Mold(Fcall*, uchar*, uint);
+uint oldhdrsize(uchar);
+uint iosize(uchar*);
+
+enum
+{
+ oldTnop = 50,
+ oldRnop,
+ oldTosession = 52, /* illegal */
+ oldRosession, /* illegal */
+ oldTerror = 54, /* illegal */
+ oldRerror,
+ oldTflush = 56,
+ oldRflush,
+ oldToattach = 58, /* illegal */
+ oldRoattach, /* illegal */
+ oldTclone = 60,
+ oldRclone,
+ oldTwalk = 62,
+ oldRwalk,
+ oldTopen = 64,
+ oldRopen,
+ oldTcreate = 66,
+ oldRcreate,
+ oldTread = 68,
+ oldRread,
+ oldTwrite = 70,
+ oldRwrite,
+ oldTclunk = 72,
+ oldRclunk,
+ oldTremove = 74,
+ oldRremove,
+ oldTstat = 76,
+ oldRstat,
+ oldTwstat = 78,
+ oldRwstat,
+ oldTclwalk = 80,
+ oldRclwalk,
+ oldTauth = 82, /* illegal */
+ oldRauth, /* illegal */
+ oldTsession = 84,
+ oldRsession,
+ oldTattach = 86,
+ oldRattach,
+ oldTmax
+};
diff --git a/sys/src/cmd/unix/u9fs/plan9.h b/sys/src/cmd/unix/u9fs/plan9.h
new file mode 100755
index 000000000..caecb8ed7
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/plan9.h
@@ -0,0 +1,198 @@
+/* magic to get SUSV2 standard, including pread, pwrite*/
+#define _XOPEN_SOURCE 500
+/* magic to get 64-bit pread/pwrite */
+#define _LARGEFILE64_SOURCE
+/* magic to get 64-bit stat on Linux, maybe others */
+#define _FILE_OFFSET_BITS 64
+
+#ifdef sgi
+#define _BSD_TYPES 1 /* for struct timeval */
+#include <sys/select.h>
+#define _BSD_SOURCE 1 /* for ruserok */
+/*
+ * SGI IRIX 5.x doesn't allow inclusion of both inttypes.h and
+ * sys/types.h. These definitions are the ones we need from
+ * inttypes.h that aren't in sys/types.h.
+ *
+ * Unlike most of our #ifdef's, IRIX5X must be set in the makefile.
+ */
+#ifdef IRIX5X
+#define __inttypes_INCLUDED
+typedef unsigned int uint32_t;
+typedef signed long long int int64_t;
+typedef unsigned long long int uint64_t;
+#endif /* IRIX5X */
+#endif /* sgi */
+
+
+#ifdef sun /* sparc and __svr4__ are also defined on the offending machine */
+#define __EXTENSIONS__ 1 /* for struct timeval */
+#endif
+
+#include <inttypes.h> /* for int64_t et al. */
+#include <stdarg.h> /* for va_list, vararg macros */
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy __va_copy
+#else
+#define va_copy(d, s) memmove(&(d), &(s), sizeof(va_list))
+#endif /* __va_copy */
+#endif /* va_copy */
+#include <sys/types.h>
+#include <string.h> /* for memmove */
+#include <unistd.h> /* for write */
+
+#define ulong p9ulong /* because sys/types.h has some of these sometimes */
+#define ushort p9ushort
+#define uchar p9uchar
+#define uint p9uint
+#define vlong p9vlong
+#define uvlong p9uvlong
+#define u32int p9u32int
+
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef int64_t vlong;
+typedef uint64_t uvlong;
+typedef uint32_t u32int;
+typedef uint64_t u64int;
+typedef ushort Rune;
+
+#define nil ((void*)0)
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+#ifndef offsetof
+#define offsetof(s, m) (ulong)(&(((s*)0)->m))
+#endif
+#define assert(x) if(x);else _assert("x")
+
+extern char *argv0;
+#define ARGBEGIN for((void)(argv0||(argv0=*argv)),argv++,argc--;\
+ argv[0] && argv[0][0]=='-' && argv[0][1];\
+ argc--, argv++) {\
+ char *_args, *_argt;\
+ Rune _argc;\
+ _args = &argv[0][1];\
+ if(_args[0]=='-' && _args[1]==0){\
+ argc--; argv++; break;\
+ }\
+ _argc = 0;\
+ while(*_args && (_args += chartorune(&_argc, _args)))\
+ switch(_argc)
+#define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}\
+ USED(argv);USED(argc);
+#define ARGF() (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): 0))
+#define EARGF(x) (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0)))
+
+#define ARGC() _argc
+
+#define SET(x) (x) = 0
+#define USED(x) (void)(x)
+
+enum
+{
+ UTFmax = 3, /* maximum bytes per rune */
+ Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
+ Runeself = 0x80, /* rune and UTF sequences are the same (<) */
+ Runeerror = 0x80 /* decoding error in UTF */
+};
+
+extern int runetochar(char*, Rune*);
+extern int chartorune(Rune*, char*);
+extern int runelen(long);
+extern int utflen(char*);
+extern char* strecpy(char*, char*, char*);
+extern int tokenize(char*, char**, int);
+extern int getfields(char*, char**, int, int, char*);
+
+/*
+ * print routines
+ */
+typedef struct Fconv Fconv;
+struct Fconv
+{
+ char* out; /* pointer to next output */
+ char* eout; /* pointer to end */
+ int f1;
+ int f2;
+ int f3;
+ int chr;
+};
+extern char* doprint(char*, char*, char*, va_list *argp);
+extern int print(char*, ...);
+extern char* seprint(char*, char*, char*, ...);
+extern int snprint(char*, int, char*, ...);
+extern int sprint(char*, char*, ...);
+extern int fprint(int, char*, ...);
+
+extern int fmtinstall(int, int (*)(va_list*, Fconv*));
+extern int numbconv(va_list*, Fconv*);
+extern void strconv(char*, Fconv*);
+extern int fltconv(va_list*, Fconv*);
+
+#define OREAD 0 /* open for read */
+#define OWRITE 1 /* write */
+#define ORDWR 2 /* read and write */
+#define OEXEC 3 /* execute, == read but check execute permission */
+#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */
+#define OCEXEC 32 /* or'ed in, close on exec */
+#define ORCLOSE 64 /* or'ed in, remove on close */
+#define OEXCL 0x1000 /* or'ed in, exclusive use */
+
+/* bits in Qid.type */
+#define QTDIR 0x80 /* type bit for directories */
+#define QTAPPEND 0x40 /* type bit for append only files */
+#define QTEXCL 0x20 /* type bit for exclusive use files */
+#define QTMOUNT 0x10 /* type bit for mounted channel */
+#define QTAUTH 0x08
+#define QTFILE 0x00 /* plain file */
+
+/* bits in Dir.mode */
+#define DMDIR 0x80000000 /* mode bit for directories */
+#define DMAPPEND 0x40000000 /* mode bit for append only files */
+#define DMEXCL 0x20000000 /* mode bit for exclusive use files */
+#define DMMOUNT 0x10000000 /* mode bit for mounted channel */
+#define DMREAD 0x4 /* mode bit for read permission */
+#define DMWRITE 0x2 /* mode bit for write permission */
+#define DMEXEC 0x1 /* mode bit for execute permission */
+
+typedef
+struct Qid
+{
+ vlong path;
+ ulong vers;
+ uchar type;
+} Qid;
+
+typedef
+struct Dir {
+ /* system-modified data */
+ ushort type; /* server type */
+ uint dev; /* server subtype */
+ /* file data */
+ Qid qid; /* unique id from server */
+ ulong mode; /* permissions */
+ ulong atime; /* last read time */
+ ulong mtime; /* last write time */
+ vlong length; /* file length: see <u.h> */
+ char *name; /* last element of path */
+ char *uid; /* owner name */
+ char *gid; /* group name */
+ char *muid; /* last modifier name */
+} Dir;
+
+long readn(int, void*, long);
+void remotehost(char*, int);
+
+enum {
+ NAMELEN = 28,
+ ERRLEN = 64
+};
+
+/* DES */
+#define DESKEYLEN 7
+void key_setup(char key[DESKEYLEN], char expandedkey[128]);
+void block_cipher(char expandedkey[128], char buf[8], int decrypting);
diff --git a/sys/src/cmd/unix/u9fs/print.c b/sys/src/cmd/unix/u9fs/print.c
new file mode 100755
index 000000000..8011df6b9
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/print.c
@@ -0,0 +1,87 @@
+#include <plan9.h>
+
+#define SIZE 4096
+extern int printcol;
+
+int
+print(char *fmt, ...)
+{
+ char buf[SIZE], *out;
+ va_list arg, temp;
+ int n;
+
+ va_start(arg, fmt);
+ va_copy(temp, arg);
+ out = doprint(buf, buf+SIZE, fmt, &temp);
+ va_end(temp);
+ va_end(arg);
+ n = write(1, buf, (long)(out-buf));
+ return n;
+}
+
+int
+fprint(int f, char *fmt, ...)
+{
+ char buf[SIZE], *out;
+ va_list arg, temp;
+ int n;
+
+ va_start(arg, fmt);
+ va_copy(temp, arg);
+ out = doprint(buf, buf+SIZE, fmt, &temp);
+ va_end(temp);
+ va_end(arg);
+ n = write(f, buf, (long)(out-buf));
+ return n;
+}
+
+int
+sprint(char *buf, char *fmt, ...)
+{
+ char *out;
+ va_list arg, temp;
+ int scol;
+
+ scol = printcol;
+ va_start(arg, fmt);
+ va_copy(temp, arg);
+ out = doprint(buf, buf+SIZE, fmt, &temp);
+ va_end(temp);
+ va_end(arg);
+ printcol = scol;
+ return out-buf;
+}
+
+int
+snprint(char *buf, int len, char *fmt, ...)
+{
+ char *out;
+ va_list arg, temp;
+ int scol;
+
+ scol = printcol;
+ va_start(arg, fmt);
+ va_copy(temp, arg);
+ out = doprint(buf, buf+len, fmt, &temp);
+ va_end(temp);
+ va_end(arg);
+ printcol = scol;
+ return out-buf;
+}
+
+char*
+seprint(char *buf, char *e, char *fmt, ...)
+{
+ char *out;
+ va_list arg, temp;
+ int scol;
+
+ scol = printcol;
+ va_start(arg, fmt);
+ va_copy(temp, arg);
+ out = doprint(buf, e, fmt, &temp);
+ va_end(temp);
+ va_end(arg);
+ printcol = scol;
+ return out;
+}
diff --git a/sys/src/cmd/unix/u9fs/random.c b/sys/src/cmd/unix/u9fs/random.c
new file mode 100755
index 000000000..6b4ab584b
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/random.c
@@ -0,0 +1,58 @@
+#include <plan9.h>
+#include <fcall.h>
+#include <u9fs.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+static long
+getseed(void)
+{
+ struct timeval tv;
+ long seed;
+ int fd, len;
+
+ len = 0;
+ fd = open("/dev/urandom", O_RDONLY);
+ if(fd > 0){
+ len = readn(fd, &seed, sizeof(seed));
+ close(fd);
+ }
+ if(len != sizeof(seed)){
+ gettimeofday(&tv, nil);
+ seed = tv.tv_sec ^ tv.tv_usec ^ (getpid()<<8);
+ }
+ return seed;
+}
+
+static int seeded;
+
+void
+randombytes(uchar *r, uint nr)
+{
+ int i;
+ ulong l;
+
+ if(!seeded){
+ seeded=1;
+ srand48(getseed());
+ }
+ for(i=0; i+4<=nr; i+=4,r+=4){
+ l = (ulong)mrand48();
+ r[0] = l;
+ r[1] = l>>8;
+ r[2] = l>>16;
+ r[3] = l>>24;
+ }
+ if(i<nr){
+ l = (ulong)mrand48();
+ switch(nr-i){
+ case 3:
+ r[2] = l>>16;
+ case 2:
+ r[1] = l>>8;
+ case 1:
+ r[0] = l;
+ }
+ }
+}
diff --git a/sys/src/cmd/unix/u9fs/readn.c b/sys/src/cmd/unix/u9fs/readn.c
new file mode 100755
index 000000000..89f060315
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/readn.c
@@ -0,0 +1,21 @@
+#include <plan9.h>
+
+long
+readn(int f, void *av, long n)
+{
+ char *a;
+ long m, t;
+
+ a = av;
+ t = 0;
+ while(t < n){
+ m = read(f, a+t, n-t);
+ if(m <= 0){
+ if(t == 0)
+ return m;
+ break;
+ }
+ t += m;
+ }
+ return t;
+}
diff --git a/sys/src/cmd/unix/u9fs/remotehost.c b/sys/src/cmd/unix/u9fs/remotehost.c
new file mode 100755
index 000000000..d1a369624
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/remotehost.c
@@ -0,0 +1,32 @@
+#include <sys/types.h>
+#include <sys/socket.h> /* various networking crud */
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <plan9.h>
+
+void
+getremotehostname(char *name, int nname)
+{
+ struct sockaddr_in sock;
+ struct hostent *hp;
+ uint len;
+ int on;
+
+ strecpy(name, name+nname, "unknown");
+ len = sizeof sock;
+ if(getpeername(0, (struct sockaddr*)&sock, (void*)&len) < 0)
+ return;
+
+ hp = gethostbyaddr((char *)&sock.sin_addr, sizeof (struct in_addr),
+ sock.sin_family);
+ if(hp == 0)
+ return;
+
+ strecpy(name, name+nname, hp->h_name);
+ on = 1;
+ setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
+
+ on = 1;
+ setsockopt(0, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on));
+}
diff --git a/sys/src/cmd/unix/u9fs/rune.c b/sys/src/cmd/unix/u9fs/rune.c
new file mode 100755
index 000000000..a0822d625
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/rune.c
@@ -0,0 +1,148 @@
+#include <plan9.h>
+
+char *argv0;
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+ int c, c1, c2;
+ long l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(uchar*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(uchar*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(uchar*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+ long c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+int
+runelen(long c)
+{
+ Rune rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+int
+utflen(char *s)
+{
+ int c;
+ long n;
+ Rune rune;
+
+ n = 0;
+ for(;;) {
+ c = *(uchar*)s;
+ if(c < Runeself) {
+ if(c == 0)
+ return n;
+ s++;
+ } else
+ s += chartorune(&rune, s);
+ n++;
+ }
+ return 0;
+}
diff --git a/sys/src/cmd/unix/u9fs/safecpy.c b/sys/src/cmd/unix/u9fs/safecpy.c
new file mode 100755
index 000000000..a7513494d
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/safecpy.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+
+void
+safecpy(char *to, char *from, int tolen)
+{
+ int fromlen;
+ memset(to, 0, tolen);
+ fromlen = from ? strlen(from) : 0;
+ if (fromlen > tolen)
+ fromlen = tolen;
+ memcpy(to, from, fromlen);
+}
diff --git a/sys/src/cmd/unix/u9fs/strecpy.c b/sys/src/cmd/unix/u9fs/strecpy.c
new file mode 100755
index 000000000..57cedf808
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/strecpy.c
@@ -0,0 +1,14 @@
+#include <plan9.h>
+
+char*
+strecpy(char *to, char *e, char *from)
+{
+ if(to >= e)
+ return to;
+ to = memccpy(to, from, '\0', e - to);
+ if(to == nil){
+ to = e - 1;
+ *to = '\0';
+ }
+ return to;
+}
diff --git a/sys/src/cmd/unix/u9fs/sun-inttypes.h b/sys/src/cmd/unix/u9fs/sun-inttypes.h
new file mode 100755
index 000000000..4e147a1f3
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/sun-inttypes.h
@@ -0,0 +1,16 @@
+/* inttypes.h for SunOS cuff.link.cs.cmu.edu 5.5.1 Generic_103640-29 sun4u sparc SUNW,Ultra-Enterprise */
+#ifndef INTTYPES_H
+#define INTTYPES_H
+
+typedef char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+typedef long long int64_t;
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long uint64_t;
+typedef long int intptr_t;
+typedef unsigned long int uintptr_t;
+
+#endif
diff --git a/sys/src/cmd/unix/u9fs/tokenize.c b/sys/src/cmd/unix/u9fs/tokenize.c
new file mode 100755
index 000000000..41ab050e3
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/tokenize.c
@@ -0,0 +1,42 @@
+#include <plan9.h>
+
+int
+getfields(char *str, char **args, int max, int mflag, char *set)
+{
+ Rune r;
+ int nr, intok, narg;
+
+ if(max <= 0)
+ return 0;
+
+ narg = 0;
+ args[narg] = str;
+ if(!mflag)
+ narg++;
+ intok = 0;
+ for(;; str += nr) {
+ nr = chartorune(&r, str);
+ if(r == 0)
+ break;
+ if(utfrune(set, r)) {
+ if(narg >= max)
+ break;
+ *str = 0;
+ intok = 0;
+ args[narg] = str + nr;
+ if(!mflag)
+ narg++;
+ } else {
+ if(!intok && mflag)
+ narg++;
+ intok = 1;
+ }
+ }
+ return narg;
+}
+
+int
+tokenize(char *str, char **args, int max)
+{
+ return getfields(str, args, max, 1, " \t\n\r");
+}
diff --git a/sys/src/cmd/unix/u9fs/u9fs.c b/sys/src/cmd/unix/u9fs/u9fs.c
new file mode 100755
index 000000000..eeee21f12
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/u9fs.c
@@ -0,0 +1,1765 @@
+/* already in plan9.h #include <sys/types.h> *//* for struct passwd, struct group, struct stat ... */
+/* plan9.h is first to get the large file support definitions as early as possible */
+#include <plan9.h>
+#include <sys/stat.h> /* for stat, umask */
+#include <stdlib.h> /* for malloc */
+#include <string.h> /* for strcpy, memmove */
+#include <pwd.h> /* for getpwnam, getpwuid */
+#include <grp.h> /* for getgrnam, getgrgid */
+#include <unistd.h> /* for gethostname, pread, pwrite, read, write */
+#include <utime.h> /* for utime */
+#include <dirent.h> /* for readdir */
+#include <errno.h> /* for errno */
+#include <stdio.h> /* for remove [sic] */
+#include <fcntl.h> /* for O_RDONLY, etc. */
+
+#include <sys/socket.h> /* various networking crud */
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <fcall.h>
+#include <oldfcall.h>
+#include <u9fs.h>
+
+/* #ifndef because can be given in makefile */
+#ifndef DEFAULTLOG
+#define DEFAULTLOG "/tmp/u9fs.log"
+#endif
+
+char *logfile = DEFAULTLOG;
+
+#define S_ISSPECIAL(m) (S_ISCHR(m) || S_ISBLK(m) || S_ISFIFO(m))
+
+enum {
+ Tdot = 1,
+ Tdotdot
+};
+
+enum {
+ P9P1,
+ P9P2000
+};
+
+typedef struct User User;
+struct User {
+ int id;
+ gid_t defaultgid;
+ char *name;
+ char **mem; /* group members */
+ int nmem;
+ User *next;
+};
+
+struct Fid {
+ int fid;
+ char *path;
+ struct stat st;
+ User *u;
+ int omode;
+ DIR *dir;
+ int diroffset;
+ int fd;
+ struct dirent *dirent;
+ int direof;
+ Fid *next;
+ Fid *prev;
+ int auth;
+ void *authmagic;
+};
+
+void* emalloc(size_t);
+void* erealloc(void*, size_t);
+char* estrdup(char*);
+char* estrpath(char*, char*, int);
+void sysfatal(char*, ...);
+int okuser(char*);
+
+void rversion(Fcall*, Fcall*);
+void rauth(Fcall*, Fcall*);
+void rattach(Fcall*, Fcall*);
+void rflush(Fcall*, Fcall*);
+void rclone(Fcall*, Fcall*);
+void rwalk(Fcall*, Fcall*);
+void ropen(Fcall*, Fcall*);
+void rcreate(Fcall*, Fcall*);
+void rread(Fcall*, Fcall*);
+void rwrite(Fcall*, Fcall*);
+void rclunk(Fcall*, Fcall*);
+void rstat(Fcall*, Fcall*);
+void rwstat(Fcall*, Fcall*);
+void rclwalk(Fcall*, Fcall*);
+void rremove(Fcall*, Fcall*);
+
+User* uname2user(char*);
+User* gname2user(char*);
+User* uid2user(int);
+User* gid2user(int);
+
+Fid* newfid(int, char**);
+Fid* oldfidex(int, int, char**);
+Fid* oldfid(int, char**);
+int fidstat(Fid*, char**);
+void freefid(Fid*);
+
+int userchange(User*, char**);
+int userwalk(User*, char**, char*, Qid*, char**);
+int useropen(Fid*, int, char**);
+int usercreate(Fid*, char*, int, long, char**);
+int userremove(Fid*, char**);
+int userperm(User*, char*, int, int);
+int useringroup(User*, User*);
+
+Qid stat2qid(struct stat*);
+
+void getfcallold(int, Fcall*, int);
+void putfcallold(int, Fcall*);
+
+char Eauth[] = "authentication failed";
+char Ebadfid[] = "fid unknown or out of range";
+char Ebadoffset[] = "bad offset in directory read";
+char Ebadusefid[] = "bad use of fid";
+char Edirchange[] = "wstat can't convert between files and directories";
+char Eexist[] = "file or directory already exists";
+char Efidactive[] = "fid already in use";
+char Enotdir[] = "not a directory";
+char Enotingroup[] = "not a member of proposed group";
+char Enotowner[] = "only owner can change group in wstat";
+char Eperm[] = "permission denied";
+char Especial0[] = "already attached without access to special files";
+char Especial1[] = "already attached with access to special files";
+char Especial[] = "no access to special file";
+char Etoolarge[] = "i/o count too large";
+char Eunknowngroup[] = "unknown group";
+char Eunknownuser[] = "unknown user";
+char Ewstatbuffer[] = "bogus wstat buffer";
+
+ulong msize = IOHDRSZ+8192;
+uchar* rxbuf;
+uchar* txbuf;
+void* databuf;
+int connected;
+int devallowed;
+char* autharg;
+char* defaultuser;
+char hostname[256];
+char remotehostname[256];
+int chatty9p = 0;
+int network = 1;
+int old9p = -1;
+int authed;
+User* none;
+
+Auth *authmethods[] = { /* first is default */
+ &authrhosts,
+ &authp9any,
+ &authnone,
+};
+
+Auth *auth;
+
+/*
+ * frogs: characters not valid in plan9
+ * filenames, keep this list in sync with
+ * /sys/src/9/port/chan.c:1656
+ */
+char isfrog[256]={
+ /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1,
+ ['/'] 1,
+ [0x7f] 1,
+};
+
+void
+getfcallnew(int fd, Fcall *fc, int have)
+{
+ int len;
+
+ if(have > BIT32SZ)
+ sysfatal("cannot happen");
+
+ if(have < BIT32SZ && readn(fd, rxbuf+have, BIT32SZ-have) != BIT32SZ-have)
+ sysfatal("couldn't read message");
+
+ len = GBIT32(rxbuf);
+ if(len <= BIT32SZ)
+ sysfatal("bogus message");
+
+ len -= BIT32SZ;
+ if(readn(fd, rxbuf+BIT32SZ, len) != len)
+ sysfatal("short message");
+
+ if(convM2S(rxbuf, len+BIT32SZ, fc) != len+BIT32SZ)
+ sysfatal("badly sized message type %d", rxbuf[0]);
+}
+
+void
+getfcallold(int fd, Fcall *fc, int have)
+{
+ int len, n;
+
+ if(have > 3)
+ sysfatal("cannot happen");
+
+ if(have < 3 && readn(fd, rxbuf, 3-have) != 3-have)
+ sysfatal("couldn't read message");
+
+ len = oldhdrsize(rxbuf[0]);
+ if(len < 3)
+ sysfatal("bad message %d", rxbuf[0]);
+ if(len > 3 && readn(fd, rxbuf+3, len-3) != len-3)
+ sysfatal("couldn't read message");
+
+ n = iosize(rxbuf);
+ if(readn(fd, rxbuf+len, n) != n)
+ sysfatal("couldn't read message");
+ len += n;
+
+ if(convM2Sold(rxbuf, len, fc) != len)
+ sysfatal("badly sized message type %d", rxbuf[0]);
+}
+
+void
+putfcallnew(int wfd, Fcall *tx)
+{
+ uint n;
+
+ if((n = convS2M(tx, txbuf, msize)) == 0)
+ sysfatal("couldn't format message type %d", tx->type);
+ if(write(wfd, txbuf, n) != n)
+ sysfatal("couldn't send message");
+}
+
+void
+putfcallold(int wfd, Fcall *tx)
+{
+ uint n;
+
+ if((n = convS2Mold(tx, txbuf, msize)) == 0)
+ sysfatal("couldn't format message type %d", tx->type);
+ if(write(wfd, txbuf, n) != n)
+ sysfatal("couldn't send message");
+}
+
+void
+getfcall(int fd, Fcall *fc)
+{
+ if(old9p == 1){
+ getfcallold(fd, fc, 0);
+ return;
+ }
+ if(old9p == 0){
+ getfcallnew(fd, fc, 0);
+ return;
+ }
+
+ /* auto-detect */
+ if(readn(fd, rxbuf, 3) != 3)
+ sysfatal("couldn't read message");
+
+ /* is it an old (9P1) message? */
+ if(50 <= rxbuf[0] && rxbuf[0] <= 87 && (rxbuf[0]&1)==0 && GBIT16(rxbuf+1) == 0xFFFF){
+ old9p = 1;
+ getfcallold(fd, fc, 3);
+ return;
+ }
+
+ getfcallnew(fd, fc, 3);
+ old9p = 0;
+}
+
+void
+seterror(Fcall *f, char *error)
+{
+ f->type = Rerror;
+ f->ename = error ? error : "programmer error";
+}
+
+int
+isowner(User *u, Fid *f)
+{
+ return u->id == f->st.st_uid;
+}
+
+
+
+void
+serve(int rfd, int wfd)
+{
+ Fcall rx, tx;
+
+ for(;;){
+ getfcall(rfd, &rx);
+
+ if(chatty9p)
+ fprint(2, "<- %F\n", &rx);
+
+ memset(&tx, 0, sizeof tx);
+ tx.type = rx.type+1;
+ tx.tag = rx.tag;
+ switch(rx.type){
+ case Tflush:
+ break;
+ case Tversion:
+ rversion(&rx, &tx);
+ break;
+ case Tauth:
+ rauth(&rx, &tx);
+ break;
+ case Tattach:
+ rattach(&rx, &tx);
+ break;
+ case Twalk:
+ rwalk(&rx, &tx);
+ break;
+ case Tstat:
+ tx.stat = databuf;
+ rstat(&rx, &tx);
+ break;
+ case Twstat:
+ rwstat(&rx, &tx);
+ break;
+ case Topen:
+ ropen(&rx, &tx);
+ break;
+ case Tcreate:
+ rcreate(&rx, &tx);
+ break;
+ case Tread:
+ tx.data = databuf;
+ rread(&rx, &tx);
+ break;
+ case Twrite:
+ rwrite(&rx, &tx);
+ break;
+ case Tclunk:
+ rclunk(&rx, &tx);
+ break;
+ case Tremove:
+ rremove(&rx, &tx);
+ break;
+ default:
+ fprint(2, "unknown message %F\n", &rx);
+ seterror(&tx, "bad message");
+ break;
+ }
+
+ if(chatty9p)
+ fprint(2, "-> %F\n", &tx);
+
+ (old9p ? putfcallold : putfcallnew)(wfd, &tx);
+ }
+}
+
+void
+rversion(Fcall *rx, Fcall *tx)
+{
+ if(msize > rx->msize)
+ msize = rx->msize;
+ tx->msize = msize;
+ if(strncmp(rx->version, "9P", 2) != 0)
+ tx->version = "unknown";
+ else
+ tx->version = "9P2000";
+}
+
+void
+rauth(Fcall *rx, Fcall *tx)
+{
+ char *e;
+
+ if((e = auth->auth(rx, tx)) != nil)
+ seterror(tx, e);
+}
+
+void
+rattach(Fcall *rx, Fcall *tx)
+{
+ char *e;
+ Fid *fid;
+ User *u;
+
+ if(rx->aname == nil)
+ rx->aname = "";
+
+ if(strcmp(rx->aname, "device") == 0){
+ if(connected && !devallowed){
+ seterror(tx, Especial0);
+ return;
+ }
+ devallowed = 1;
+ }else{
+ if(connected && devallowed){
+ seterror(tx, Especial1);
+ return;
+ }
+ }
+
+ if(strcmp(rx->uname, "none") == 0){
+ if(authed == 0){
+ seterror(tx, Eauth);
+ return;
+ }
+ } else {
+ if((e = auth->attach(rx, tx)) != nil){
+ seterror(tx, e);
+ return;
+ }
+ authed++;
+ }
+
+ if((fid = newfid(rx->fid, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+ fid->path = estrdup("/");
+ if(fidstat(fid, &e) < 0){
+ seterror(tx, e);
+ freefid(fid);
+ return;
+ }
+
+ if(defaultuser)
+ rx->uname = defaultuser;
+
+ if((u = uname2user(rx->uname)) == nil
+ || (!defaultuser && u->id == 0)){
+ /* we don't know anyone named root... */
+ seterror(tx, Eunknownuser);
+ freefid(fid);
+ return;
+ }
+
+ fid->u = u;
+ tx->qid = stat2qid(&fid->st);
+ return;
+}
+
+void
+rwalk(Fcall *rx, Fcall *tx)
+{
+ int i;
+ char *path, *e;
+ Fid *fid, *nfid;
+
+ e = nil;
+ if((fid = oldfid(rx->fid, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+
+ if(fid->omode != -1){
+ seterror(tx, Ebadusefid);
+ return;
+ }
+
+ if(fidstat(fid, &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+
+ if(!S_ISDIR(fid->st.st_mode) && rx->nwname){
+ seterror(tx, Enotdir);
+ return;
+ }
+
+ nfid = nil;
+ if(rx->newfid != rx->fid && (nfid = newfid(rx->newfid, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+
+ path = estrdup(fid->path);
+ e = nil;
+ for(i=0; i<rx->nwname; i++)
+ if(userwalk(fid->u, &path, rx->wname[i], &tx->wqid[i], &e) < 0)
+ break;
+
+ if(i == rx->nwname){ /* successful clone or walk */
+ tx->nwqid = i;
+ if(nfid){
+ nfid->path = path;
+ nfid->u = fid->u;
+ }else{
+ free(fid->path);
+ fid->path = path;
+ }
+ }else{
+ if(i > 0) /* partial walk? */
+ tx->nwqid = i;
+ else
+ seterror(tx, e);
+
+ if(nfid) /* clone implicit new fid */
+ freefid(nfid);
+ free(path);
+ }
+ return;
+}
+
+void
+ropen(Fcall *rx, Fcall *tx)
+{
+ char *e;
+ Fid *fid;
+
+ if((fid = oldfid(rx->fid, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+
+ if(fid->omode != -1){
+ seterror(tx, Ebadusefid);
+ return;
+ }
+
+ if(fidstat(fid, &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+
+ if(!devallowed && S_ISSPECIAL(fid->st.st_mode)){
+ seterror(tx, Especial);
+ return;
+ }
+
+ if(useropen(fid, rx->mode, &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+
+ tx->iounit = 0;
+ tx->qid = stat2qid(&fid->st);
+}
+
+void
+rcreate(Fcall *rx, Fcall *tx)
+{
+ char *e;
+ Fid *fid;
+
+ if((fid = oldfid(rx->fid, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+
+ if(fid->omode != -1){
+ seterror(tx, Ebadusefid);
+ return;
+ }
+
+ if(fidstat(fid, &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+
+ if(!S_ISDIR(fid->st.st_mode)){
+ seterror(tx, Enotdir);
+ return;
+ }
+
+ if(usercreate(fid, rx->name, rx->mode, rx->perm, &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+
+ if(fidstat(fid, &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+
+ tx->iounit = 0;
+ tx->qid = stat2qid(&fid->st);
+}
+
+uchar
+modebyte(struct stat *st)
+{
+ uchar b;
+
+ b = 0;
+
+ if(S_ISDIR(st->st_mode))
+ b |= QTDIR;
+
+ /* no way to test append-only */
+ /* no real way to test exclusive use, but mark devices as such */
+ if(S_ISSPECIAL(st->st_mode))
+ b |= QTEXCL;
+
+ return b;
+}
+
+ulong
+plan9mode(struct stat *st)
+{
+ return ((ulong)modebyte(st)<<24) | (st->st_mode & 0777);
+}
+
+/*
+ * this is for chmod, so don't worry about S_IFDIR
+ */
+mode_t
+unixmode(Dir *d)
+{
+ return (mode_t)(d->mode&0777);
+}
+
+Qid
+stat2qid(struct stat *st)
+{
+ uchar *p, *ep, *q;
+ Qid qid;
+
+ /*
+ * For now, ignore the device number.
+ */
+ qid.path = 0;
+ p = (uchar*)&qid.path;
+ ep = p+sizeof(qid.path);
+ q = p+sizeof(ino_t);
+ if(q > ep){
+ fprint(2, "warning: inode number too big\n");
+ q = ep;
+ }
+ memmove(p, &st->st_ino, q-p);
+ q = q+sizeof(dev_t);
+ if(q > ep){
+/*
+ * fprint(2, "warning: inode number + device number too big %d+%d\n",
+ * sizeof(ino_t), sizeof(dev_t));
+ */
+ q = ep - sizeof(dev_t);
+ if(q < p)
+ fprint(2, "warning: device number too big by itself\n");
+ else
+ *(dev_t*)q ^= st->st_dev;
+ }
+
+ qid.vers = st->st_mtime ^ (st->st_size << 8);
+ qid.type = modebyte(st);
+ return qid;
+}
+
+char *
+enfrog(char *src)
+{
+ char *d, *dst;
+ uchar *s;
+
+ d = dst = emalloc(strlen(src)*3 + 1);
+ for (s = (uchar *)src; *s; s++)
+ if(isfrog[*s] || *s == '\\')
+ d += sprintf(d, "\\%02x", *s);
+ else
+ *d++ = *s;
+ *d = 0;
+ return dst;
+}
+
+char *
+defrog(char *s)
+{
+ char *d, *dst, buf[3];
+
+ d = dst = emalloc(strlen(s) + 1);
+ for(; *s; s++)
+ if(*s == '\\' && strlen(s) >= 3){
+ buf[0] = *++s; /* skip \ */
+ buf[1] = *++s;
+ buf[2] = 0;
+ *d++ = strtoul(buf, NULL, 16);
+ } else
+ *d++ = *s;
+ *d = 0;
+ return dst;
+}
+
+void
+stat2dir(char *path, struct stat *st, Dir *d)
+{
+ User *u;
+ char *q, *p, *npath;
+
+ memset(d, 0, sizeof(*d));
+ d->qid = stat2qid(st);
+ d->mode = plan9mode(st);
+ d->atime = st->st_atime;
+ d->mtime = st->st_mtime;
+ d->length = st->st_size;
+
+ d->uid = (u = uid2user(st->st_uid)) ? u->name : "???";
+ d->gid = (u = gid2user(st->st_gid)) ? u->name : "???";
+ d->muid = "";
+
+ if((q = strrchr(path, '/')) != nil)
+ d->name = enfrog(q+1);
+ else
+ d->name = enfrog(path);
+}
+
+void
+rread(Fcall *rx, Fcall *tx)
+{
+ char *e, *path;
+ uchar *p, *ep;
+ int n;
+ Fid *fid;
+ Dir d;
+ struct stat st;
+
+ if(rx->count > msize-IOHDRSZ){
+ seterror(tx, Etoolarge);
+ return;
+ }
+
+ if((fid = oldfidex(rx->fid, -1, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+
+ if (fid->auth) {
+ char *e;
+ e = auth->read(rx, tx);
+ if (e)
+ seterror(tx, e);
+ return;
+ }
+
+ if(fid->omode == -1 || (fid->omode&3) == OWRITE){
+ seterror(tx, Ebadusefid);
+ return;
+ }
+
+ if(fid->dir){
+ if(rx->offset != fid->diroffset){
+ if(rx->offset != 0){
+ seterror(tx, Ebadoffset);
+ return;
+ }
+ rewinddir(fid->dir);
+ fid->diroffset = 0;
+ fid->direof = 0;
+ }
+ if(fid->direof){
+ tx->count = 0;
+ return;
+ }
+
+ p = (uchar*)tx->data;
+ ep = (uchar*)tx->data+rx->count;
+ for(;;){
+ if(p+BIT16SZ >= ep)
+ break;
+ if(fid->dirent == nil) /* one entry cache for when convD2M fails */
+ if((fid->dirent = readdir(fid->dir)) == nil){
+ fid->direof = 1;
+ break;
+ }
+ if(strcmp(fid->dirent->d_name, ".") == 0
+ || strcmp(fid->dirent->d_name, "..") == 0){
+ fid->dirent = nil;
+ continue;
+ }
+ path = estrpath(fid->path, fid->dirent->d_name, 0);
+ memset(&st, 0, sizeof st);
+ if(stat(path, &st) < 0){
+ fprint(2, "dirread: stat(%s) failed: %s\n", path, strerror(errno));
+ fid->dirent = nil;
+ free(path);
+ continue;
+ }
+ free(path);
+ stat2dir(fid->dirent->d_name, &st, &d);
+ if((n=(old9p ? convD2Mold : convD2M)(&d, p, ep-p)) <= BIT16SZ)
+ break;
+ p += n;
+ fid->dirent = nil;
+ }
+ tx->count = p - (uchar*)tx->data;
+ fid->diroffset += tx->count;
+ }else{
+ if((n = pread(fid->fd, tx->data, rx->count, rx->offset)) < 0){
+ seterror(tx, strerror(errno));
+ return;
+ }
+ tx->count = n;
+ }
+}
+
+void
+rwrite(Fcall *rx, Fcall *tx)
+{
+ char *e;
+ Fid *fid;
+ int n;
+
+ if(rx->count > msize-IOHDRSZ){
+ seterror(tx, Etoolarge);
+ return;
+ }
+
+ if((fid = oldfidex(rx->fid, -1, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+
+ if (fid->auth) {
+ char *e;
+ e = auth->write(rx, tx);
+ if (e)
+ seterror(tx, e);
+ return;
+ }
+
+ if(fid->omode == -1 || (fid->omode&3) == OREAD || (fid->omode&3) == OEXEC){
+ seterror(tx, Ebadusefid);
+ return;
+ }
+
+ if((n = pwrite(fid->fd, rx->data, rx->count, rx->offset)) < 0){
+ seterror(tx, strerror(errno));
+ return;
+ }
+ tx->count = n;
+}
+
+void
+rclunk(Fcall *rx, Fcall *tx)
+{
+ char *e;
+ Fid *fid;
+
+ if((fid = oldfidex(rx->fid, -1, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+ if (fid->auth) {
+ if (auth->clunk) {
+ e = (*auth->clunk)(rx, tx);
+ if (e) {
+ seterror(tx, e);
+ return;
+ }
+ }
+ }
+ else if(fid->omode != -1 && fid->omode&ORCLOSE)
+ remove(fid->path);
+ freefid(fid);
+}
+
+void
+rremove(Fcall *rx, Fcall *tx)
+{
+ char *e;
+ Fid *fid;
+
+ if((fid = oldfid(rx->fid, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+ if(userremove(fid, &e) < 0)
+ seterror(tx, e);
+ freefid(fid);
+}
+
+void
+rstat(Fcall *rx, Fcall *tx)
+{
+ char *e;
+ Fid *fid;
+ Dir d;
+
+ if((fid = oldfid(rx->fid, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+
+ if(fidstat(fid, &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+
+ stat2dir(fid->path, &fid->st, &d);
+ if((tx->nstat=(old9p ? convD2Mold : convD2M)(&d, tx->stat, msize)) <= BIT16SZ)
+ seterror(tx, "convD2M fails");
+}
+
+void
+rwstat(Fcall *rx, Fcall *tx)
+{
+ char *e;
+ char *p, *old, *new, *dir;
+ gid_t gid;
+ Dir d;
+ Fid *fid;
+
+ if((fid = oldfid(rx->fid, &e)) == nil){
+ seterror(tx, e);
+ return;
+ }
+
+ /*
+ * wstat is supposed to be atomic.
+ * we check all the things we can before trying anything.
+ * still, if we are told to truncate a file and rename it and only
+ * one works, we're screwed. in such cases we leave things
+ * half broken and return an error. it's hardly perfect.
+ */
+ if((old9p ? convM2Dold : convM2D)(rx->stat, rx->nstat, &d, (char*)rx->stat) <= BIT16SZ){
+ seterror(tx, Ewstatbuffer);
+ return;
+ }
+
+ if(fidstat(fid, &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+
+ /*
+ * The casting is necessary because d.mode is ulong and might,
+ * on some systems, be 64 bits. We only want to compare the
+ * bottom 32 bits, since that's all that gets sent in the protocol.
+ *
+ * Same situation for d.mtime and d.length (although that last check
+ * is admittedly superfluous, given the current lack of 128-bit machines).
+ */
+ gid = (gid_t)-1;
+ if(d.gid[0] != '\0'){
+ User *g;
+
+ g = gname2user(d.gid);
+ if(g == nil){
+ seterror(tx, Eunknowngroup);
+ return;
+ }
+ gid = (gid_t)g->id;
+
+ if(groupchange(fid->u, gid2user(gid), &e) < 0){
+ seterror(tx, e);
+ return;
+ }
+ }
+
+ if((u32int)d.mode != (u32int)~0 && (((d.mode&DMDIR)!=0) ^ (S_ISDIR(fid->st.st_mode)!=0))){
+ seterror(tx, Edirchange);
+ return;
+ }
+
+ if(strcmp(fid->path, "/") == 0){
+ seterror(tx, "no wstat of root");
+ return;
+ }
+
+ /*
+ * try things in increasing order of harm to the file.
+ * mtime should come after truncate so that if you
+ * do both the mtime actually takes effect, but i'd rather
+ * leave truncate until last.
+ * (see above comment about atomicity).
+ */
+ if((u32int)d.mode != (u32int)~0 && chmod(fid->path, unixmode(&d)) < 0){
+ if(chatty9p)
+ fprint(2, "chmod(%s, 0%luo) failed\n", fid->path, unixmode(&d));
+ seterror(tx, strerror(errno));
+ return;
+ }
+
+ if((u32int)d.mtime != (u32int)~0){
+ struct utimbuf t;
+
+ t.actime = 0;
+ t.modtime = d.mtime;
+ if(utime(fid->path, &t) < 0){
+ if(chatty9p)
+ fprint(2, "utime(%s) failed\n", fid->path);
+ seterror(tx, strerror(errno));
+ return;
+ }
+ }
+
+ if(gid != (gid_t)-1 && gid != fid->st.st_gid){
+ if(chown(fid->path, (uid_t)-1, gid) < 0){
+ if(chatty9p)
+ fprint(2, "chgrp(%s, %d) failed\n", fid->path, gid);
+ seterror(tx, strerror(errno));
+ return;
+ }
+ }
+
+ if(d.name[0]){
+ old = fid->path;
+ dir = estrdup(fid->path);
+ if((p = strrchr(dir, '/')) > dir)
+ *p = '\0';
+ else{
+ seterror(tx, "whoops: can't happen in u9fs");
+ return;
+ }
+ new = estrpath(dir, d.name, 1);
+ if(strcmp(old, new) != 0 && rename(old, new) < 0){
+ if(chatty9p)
+ fprint(2, "rename(%s, %s) failed\n", old, new);
+ seterror(tx, strerror(errno));
+ free(new);
+ free(dir);
+ return;
+ }
+ fid->path = new;
+ free(old);
+ free(dir);
+ }
+
+ if((u64int)d.length != (u64int)~0 && truncate(fid->path, d.length) < 0){
+ fprint(2, "truncate(%s, %lld) failed\n", fid->path, d.length);
+ seterror(tx, strerror(errno));
+ return;
+ }
+}
+
+/*
+ * we keep a table by numeric id. by name lookups happen infrequently
+ * while by-number lookups happen once for every directory entry read
+ * and every stat request.
+ */
+User *utab[64];
+User *gtab[64];
+
+User*
+adduser(struct passwd *p)
+{
+ User *u;
+
+ u = emalloc(sizeof(*u));
+ u->id = p->pw_uid;
+ u->name = estrdup(p->pw_name);
+ u->next = utab[p->pw_uid%nelem(utab)];
+ u->defaultgid = p->pw_gid;
+ utab[p->pw_uid%nelem(utab)] = u;
+ return u;
+}
+
+int
+useringroup(User *u, User *g)
+{
+ int i;
+
+ for(i=0; i<g->nmem; i++)
+ if(strcmp(g->mem[i], u->name) == 0)
+ return 1;
+
+ /*
+ * Hack around common Unix problem that everyone has
+ * default group "user" but /etc/group lists no members.
+ */
+ if(u->defaultgid == g->id)
+ return 1;
+ return 0;
+}
+
+User*
+addgroup(struct group *g)
+{
+ User *u;
+ char **p;
+ int n;
+
+ u = emalloc(sizeof(*u));
+ n = 0;
+ for(p=g->gr_mem; *p; p++)
+ n++;
+ u->mem = emalloc(sizeof(u->mem[0])*n);
+ n = 0;
+ for(p=g->gr_mem; *p; p++)
+ u->mem[n++] = estrdup(*p);
+ u->nmem = n;
+ u->id = g->gr_gid;
+ u->name = estrdup(g->gr_name);
+ u->next = gtab[g->gr_gid%nelem(gtab)];
+ gtab[g->gr_gid%nelem(gtab)] = u;
+ return u;
+}
+
+User*
+uname2user(char *name)
+{
+ int i;
+ User *u;
+ struct passwd *p;
+
+ for(i=0; i<nelem(utab); i++)
+ for(u=utab[i]; u; u=u->next)
+ if(strcmp(u->name, name) == 0)
+ return u;
+
+ if((p = getpwnam(name)) == nil)
+ return nil;
+ return adduser(p);
+}
+
+User*
+uid2user(int id)
+{
+ User *u;
+ struct passwd *p;
+
+ for(u=utab[id%nelem(utab)]; u; u=u->next)
+ if(u->id == id)
+ return u;
+
+ if((p = getpwuid(id)) == nil)
+ return nil;
+ return adduser(p);
+}
+
+User*
+gname2user(char *name)
+{
+ int i;
+ User *u;
+ struct group *g;
+
+ for(i=0; i<nelem(gtab); i++)
+ for(u=gtab[i]; u; u=u->next)
+ if(strcmp(u->name, name) == 0)
+ return u;
+
+ if((g = getgrnam(name)) == nil)
+ return nil;
+ return addgroup(g);
+}
+
+User*
+gid2user(int id)
+{
+ User *u;
+ struct group *g;
+
+ for(u=gtab[id%nelem(gtab)]; u; u=u->next)
+ if(u->id == id)
+ return u;
+
+ if((g = getgrgid(id)) == nil)
+ return nil;
+ return addgroup(g);
+}
+
+void
+sysfatal(char *fmt, ...)
+{
+ char buf[1024];
+ va_list va, temp;
+
+ va_start(va, fmt);
+ va_copy(temp, va);
+ doprint(buf, buf+sizeof buf, fmt, &temp);
+ va_end(temp);
+ va_end(va);
+ fprint(2, "u9fs: %s\n", buf);
+ fprint(2, "last unix error: %s\n", strerror(errno));
+ exit(1);
+}
+
+void*
+emalloc(size_t n)
+{
+ void *p;
+
+ if(n == 0)
+ n = 1;
+ p = malloc(n);
+ if(p == 0)
+ sysfatal("malloc(%ld) fails", (long)n);
+ memset(p, 0, n);
+ return p;
+}
+
+void*
+erealloc(void *p, size_t n)
+{
+ if(p == 0)
+ p = malloc(n);
+ else
+ p = realloc(p, n);
+ if(p == 0)
+ sysfatal("realloc(..., %ld) fails", (long)n);
+ return p;
+}
+
+char*
+estrdup(char *p)
+{
+ p = strdup(p);
+ if(p == 0)
+ sysfatal("strdup(%.20s) fails", p);
+ return p;
+}
+
+char*
+estrpath(char *p, char *q, int frog)
+{
+ char *r, *s;
+
+ if(strcmp(q, "..") == 0){
+ r = estrdup(p);
+ if((s = strrchr(r, '/')) && s > r)
+ *s = '\0';
+ else if(s == r)
+ s[1] = '\0';
+ return r;
+ }
+
+ if(frog)
+ q = defrog(q);
+ else
+ q = strdup(q);
+ r = emalloc(strlen(p)+1+strlen(q)+1);
+ strcpy(r, p);
+ if(r[0]=='\0' || r[strlen(r)-1] != '/')
+ strcat(r, "/");
+ strcat(r, q);
+ free(q);
+ return r;
+}
+
+Fid *fidtab[1];
+
+Fid*
+lookupfid(int fid)
+{
+ Fid *f;
+
+ for(f=fidtab[fid%nelem(fidtab)]; f; f=f->next)
+ if(f->fid == fid)
+ return f;
+ return nil;
+}
+
+Fid*
+newfid(int fid, char **ep)
+{
+ Fid *f;
+
+ if(lookupfid(fid) != nil){
+ *ep = Efidactive;
+ return nil;
+ }
+
+ f = emalloc(sizeof(*f));
+ f->next = fidtab[fid%nelem(fidtab)];
+ if(f->next)
+ f->next->prev = f;
+ fidtab[fid%nelem(fidtab)] = f;
+ f->fid = fid;
+ f->fd = -1;
+ f->omode = -1;
+ return f;
+}
+
+Fid*
+newauthfid(int fid, void *magic, char **ep)
+{
+ Fid *af;
+ af = newfid(fid, ep);
+ if (af == nil)
+ return nil;
+ af->auth = 1;
+ af->authmagic = magic;
+ return af;
+}
+
+Fid*
+oldfidex(int fid, int auth, char **ep)
+{
+ Fid *f;
+
+ if((f = lookupfid(fid)) == nil){
+ *ep = Ebadfid;
+ return nil;
+ }
+
+ if (auth != -1 && f->auth != auth) {
+ *ep = Ebadfid;
+ return nil;
+ }
+
+ if (!f->auth) {
+ if(userchange(f->u, ep) < 0)
+ return nil;
+ }
+
+ return f;
+}
+
+Fid*
+oldfid(int fid, char **ep)
+{
+ return oldfidex(fid, 0, ep);
+}
+
+Fid*
+oldauthfid(int fid, void **magic, char **ep)
+{
+ Fid *af;
+ af = oldfidex(fid, 1, ep);
+ if (af == nil)
+ return nil;
+ *magic = af->authmagic;
+ return af;
+}
+
+void
+freefid(Fid *f)
+{
+ if(f->prev)
+ f->prev->next = f->next;
+ else
+ fidtab[f->fid%nelem(fidtab)] = f->next;
+ if(f->next)
+ f->next->prev = f->prev;
+ if(f->dir)
+ closedir(f->dir);
+ if(f->fd)
+ close(f->fd);
+ free(f->path);
+ free(f);
+}
+
+int
+fidstat(Fid *fid, char **ep)
+{
+ if(stat(fid->path, &fid->st) < 0){
+ fprint(2, "fidstat(%s) failed\n", fid->path);
+ if(ep)
+ *ep = strerror(errno);
+ return -1;
+ }
+ if(S_ISDIR(fid->st.st_mode))
+ fid->st.st_size = 0;
+ return 0;
+}
+
+int
+userchange(User *u, char **ep)
+{
+ if(defaultuser)
+ return 0;
+
+ if(setreuid(0, 0) < 0){
+ fprint(2, "setreuid(0, 0) failed\n");
+ *ep = "cannot setuid back to root";
+ return -1;
+ }
+
+ /*
+ * Initgroups does not appear to be SUSV standard.
+ * But it exists on SGI and on Linux, which makes me
+ * think it's standard enough. We have to do something
+ * like this, and the closest other function I can find is
+ * setgroups (which initgroups eventually calls).
+ * Setgroups is the same as far as standardization though,
+ * so we're stuck using a non-SUSV call. Sigh.
+ */
+ if(initgroups(u->name, u->defaultgid) < 0)
+ fprint(2, "initgroups(%s) failed: %s\n", u->name, strerror(errno));
+
+ if(setreuid(-1, u->id) < 0){
+ fprint(2, "setreuid(-1, %s) failed\n", u->name);
+ *ep = strerror(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * We do our own checking here, then switch to root temporarily
+ * to set our gid. In a perfect world, you'd be allowed to set your
+ * egid to any of the supplemental groups of your euid, but this
+ * is not the case on Linux 2.2.14 (and perhaps others).
+ *
+ * This is a race, of course, but it's a race against processes
+ * that can edit the group lists. If you can do that, you can
+ * change your own group without our help.
+ */
+int
+groupchange(User *u, User *g, char **ep)
+{
+ if(g == nil)
+ return -1;
+ if(!useringroup(u, g)){
+ if(chatty9p)
+ fprint(2, "%s not in group %s\n", u->name, g->name);
+ *ep = Enotingroup;
+ return -1;
+ }
+
+ setreuid(0,0);
+ if(setregid(-1, g->id) < 0){
+ fprint(2, "setegid(%s/%d) failed in groupchange\n", g->name, g->id);
+ *ep = strerror(errno);
+ return -1;
+ }
+ if(userchange(u, ep) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * An attempt to enforce permissions by looking at the
+ * file system. Separation of checking permission and
+ * actually performing the action is a terrible idea, of
+ * course, so we use setreuid for most of the permission
+ * enforcement. This is here only so we can give errors
+ * on open(ORCLOSE) in some cases.
+ */
+int
+userperm(User *u, char *path, int type, int need)
+{
+ char *p, *q;
+ int i, have;
+ struct stat st;
+ User *g;
+
+ switch(type){
+ default:
+ fprint(2, "bad type %d in userperm\n", type);
+ return -1;
+ case Tdot:
+ if(stat(path, &st) < 0){
+ fprint(2, "userperm: stat(%s) failed\n", path);
+ return -1;
+ }
+ break;
+ case Tdotdot:
+ p = estrdup(path);
+ if((q = strrchr(p, '/'))==nil){
+ fprint(2, "userperm(%s, ..): bad path\n", p);
+ free(p);
+ return -1;
+ }
+ if(q > p)
+ *q = '\0';
+ else
+ *(q+1) = '\0';
+ if(stat(p, &st) < 0){
+ fprint(2, "userperm: stat(%s) (dotdot of %s) failed\n",
+ p, path);
+ free(p);
+ return -1;
+ }
+ free(p);
+ break;
+ }
+
+ if(u == none){
+ fprint(2, "userperm: none wants %d in 0%luo\n", need, st.st_mode);
+ have = st.st_mode&7;
+ if((have&need)==need)
+ return 0;
+ return -1;
+ }
+ have = st.st_mode&7;
+ if((uid_t)u->id == st.st_uid)
+ have |= (st.st_mode>>6)&7;
+ if((have&need)==need)
+ return 0;
+ if(((have|((st.st_mode>>3)&7))&need) != need) /* group won't help */
+ return -1;
+ g = gid2user(st.st_gid);
+ for(i=0; i<g->nmem; i++){
+ if(strcmp(g->mem[i], u->name) == 0){
+ have |= (st.st_mode>>3)&7;
+ break;
+ }
+ }
+ if((have&need)==need)
+ return 0;
+ return -1;
+}
+
+int
+userwalk(User *u, char **path, char *elem, Qid *qid, char **ep)
+{
+ char *npath;
+ struct stat st;
+
+ npath = estrpath(*path, elem, 1);
+ if(stat(npath, &st) < 0){
+ free(npath);
+ *ep = strerror(errno);
+ return -1;
+ }
+ *qid = stat2qid(&st);
+ free(*path);
+ *path = npath;
+ return 0;
+}
+
+int
+useropen(Fid *fid, int omode, char **ep)
+{
+ int a, o;
+
+ /*
+ * Check this anyway, to try to head off problems later.
+ */
+ if((omode&ORCLOSE) && userperm(fid->u, fid->path, Tdotdot, W_OK) < 0){
+ *ep = Eperm;
+ return -1;
+ }
+
+ switch(omode&3){
+ default:
+ *ep = "programmer error";
+ return -1;
+ case OREAD:
+ a = R_OK;
+ o = O_RDONLY;
+ break;
+ case ORDWR:
+ a = R_OK|W_OK;
+ o = O_RDWR;
+ break;
+ case OWRITE:
+ a = W_OK;
+ o = O_WRONLY;
+ break;
+ case OEXEC:
+ a = X_OK;
+ o = O_RDONLY;
+ break;
+ }
+ if(omode & OTRUNC){
+ a |= W_OK;
+ o |= O_TRUNC;
+ }
+
+ if(S_ISDIR(fid->st.st_mode)){
+ if(a != R_OK){
+ fprint(2, "attempt by %s to open dir %d\n", fid->u->name, omode);
+ *ep = Eperm;
+ return -1;
+ }
+ if((fid->dir = opendir(fid->path)) == nil){
+ *ep = strerror(errno);
+ return -1;
+ }
+ }else{
+ /*
+ * This is wrong because access used the real uid
+ * and not the effective uid. Let the open sort it out.
+ *
+ if(access(fid->path, a) < 0){
+ *ep = strerror(errno);
+ return -1;
+ }
+ *
+ */
+ if((fid->fd = open(fid->path, o)) < 0){
+ *ep = strerror(errno);
+ return -1;
+ }
+ }
+ fid->omode = omode;
+ return 0;
+}
+
+int
+usercreate(Fid *fid, char *elem, int omode, long perm, char **ep)
+{
+ int o, m;
+ char *opath, *npath;
+ struct stat st, parent;
+
+ if(stat(fid->path, &parent) < 0){
+ *ep = strerror(errno);
+ return -1;
+ }
+
+ /*
+ * Change group so that created file has expected group
+ * by Plan 9 semantics. If that fails, might as well go
+ * with the user's default group.
+ */
+ if(groupchange(fid->u, gid2user(parent.st_gid), ep) < 0
+ && groupchange(fid->u, gid2user(fid->u->defaultgid), ep) < 0)
+ return -1;
+
+ m = (perm & DMDIR) ? 0777 : 0666;
+ perm = perm & (~m | (fid->st.st_mode & m));
+
+ npath = estrpath(fid->path, elem, 1);
+ if(perm & DMDIR){
+ if((omode&~ORCLOSE) != OREAD){
+ *ep = Eperm;
+ free(npath);
+ return -1;
+ }
+ if(stat(npath, &st) >= 0 || errno != ENOENT){
+ *ep = Eexist;
+ free(npath);
+ return -1;
+ }
+ /* race */
+ if(mkdir(npath, perm&0777) < 0){
+ *ep = strerror(errno);
+ free(npath);
+ return -1;
+ }
+ if((fid->dir = opendir(npath)) == nil){
+ *ep = strerror(errno);
+ remove(npath); /* race */
+ free(npath);
+ return -1;
+ }
+ }else{
+ o = O_CREAT|O_EXCL;
+ switch(omode&3){
+ default:
+ *ep = "programmer error";
+ return -1;
+ case OREAD:
+ case OEXEC:
+ o |= O_RDONLY;
+ break;
+ case ORDWR:
+ o |= O_RDWR;
+ break;
+ case OWRITE:
+ o |= O_WRONLY;
+ break;
+ }
+ if(omode & OTRUNC)
+ o |= O_TRUNC;
+ if((fid->fd = open(npath, o, perm&0777)) < 0){
+ if(chatty9p)
+ fprint(2, "create(%s, 0x%x, 0%o) failed\n", npath, o, perm&0777);
+ *ep = strerror(errno);
+ free(npath);
+ return -1;
+ }
+ }
+
+ opath = fid->path;
+ fid->path = npath;
+ if(fidstat(fid, ep) < 0){
+ fprint(2, "stat after create on %s failed\n", npath);
+ remove(npath); /* race */
+ free(npath);
+ fid->path = opath;
+ if(fid->fd >= 0){
+ close(fid->fd);
+ fid->fd = -1;
+ }else{
+ closedir(fid->dir);
+ fid->dir = nil;
+ }
+ return -1;
+ }
+ fid->omode = omode;
+ free(opath);
+ return 0;
+}
+
+int
+userremove(Fid *fid, char **ep)
+{
+ if(remove(fid->path) < 0){
+ *ep = strerror(errno);
+ return -1;
+ }
+ return 0;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: u9fs [-Dnz] [-a authmethod] [-m msize] [-u user] [root]\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *authtype;
+ int i;
+ int fd;
+ int logflag;
+
+ auth = authmethods[0];
+ logflag = O_WRONLY|O_APPEND|O_CREAT;
+ ARGBEGIN{
+ case 'D':
+ chatty9p = 1;
+ break;
+ case 'a':
+ authtype = EARGF(usage());
+ auth = nil;
+ for(i=0; i<nelem(authmethods); i++)
+ if(strcmp(authmethods[i]->name, authtype)==0)
+ auth = authmethods[i];
+ if(auth == nil)
+ sysfatal("unknown auth type '%s'", authtype);
+ break;
+ case 'A':
+ autharg = EARGF(usage());
+ break;
+ case 'l':
+ logfile = EARGF(usage());
+ break;
+ case 'm':
+ msize = strtol(EARGF(usage()), 0, 0);
+ break;
+ case 'n':
+ network = 0;
+ break;
+ case 'u':
+ defaultuser = EARGF(usage());
+ break;
+ case 'z':
+ logflag |= O_TRUNC;
+ }ARGEND
+
+ if(argc > 1)
+ usage();
+
+ fd = open(logfile, logflag, 0666);
+ if(fd < 0)
+ sysfatal("cannot open log '%s'", logfile);
+
+ if(dup2(fd, 2) < 0)
+ sysfatal("cannot dup fd onto stderr");
+ fprint(2, "u9fs\nkill %d\n", (int)getpid());
+
+ fmtinstall('F', fcallconv);
+ fmtinstall('D', dirconv);
+ fmtinstall('M', dirmodeconv);
+
+ rxbuf = emalloc(msize);
+ txbuf = emalloc(msize);
+ databuf = emalloc(msize);
+
+ if(auth->init)
+ auth->init();
+
+ if(network)
+ getremotehostname(remotehostname, sizeof remotehostname);
+
+ if(gethostname(hostname, sizeof hostname) < 0)
+ strcpy(hostname, "gnot");
+
+ umask(0);
+
+ if(argc == 1)
+ if(chroot(argv[0]) < 0)
+ sysfatal("chroot '%s' failed", argv[0]);
+
+ none = uname2user("none");
+
+ serve(0, 1);
+ return 0;
+}
diff --git a/sys/src/cmd/unix/u9fs/u9fs.h b/sys/src/cmd/unix/u9fs/u9fs.h
new file mode 100755
index 000000000..f553a3a43
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/u9fs.h
@@ -0,0 +1,30 @@
+typedef struct Auth Auth;
+struct Auth {
+ char *name;
+
+ char* (*auth)(Fcall*, Fcall*);
+ char* (*attach)(Fcall*, Fcall*);
+ void (*init)(void);
+ char* (*read)(Fcall*, Fcall*);
+ char* (*write)(Fcall*, Fcall*);
+ char* (*clunk)(Fcall*, Fcall*);
+};
+
+extern char remotehostname[];
+extern char Eauth[];
+extern char *autharg;
+
+extern Auth authp9any;
+extern Auth authrhosts;
+extern Auth authnone;
+
+extern ulong truerand(void);
+extern void randombytes(uchar*, uint);
+
+extern ulong msize;
+
+typedef struct Fid Fid;
+Fid *newauthfid(int fid, void *magic, char **ep);
+Fid *oldauthfid(int fid, void **magic, char **ep);
+
+void safecpy(char *to, char *from, int len);
diff --git a/sys/src/cmd/unix/u9fs/u9fsauth.h b/sys/src/cmd/unix/u9fs/u9fsauth.h
new file mode 100755
index 000000000..6d101b121
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/u9fsauth.h
@@ -0,0 +1,7 @@
+typedef struct Auth Auth;
+struct Auth {
+ char *name;
+
+ char *(*session)(Fcall*, Fcall*);
+ char *(*attach)(Fcall*, Fcall*);
+};
diff --git a/sys/src/cmd/unix/u9fs/utfrune.c b/sys/src/cmd/unix/u9fs/utfrune.c
new file mode 100755
index 000000000..ea6772f06
--- /dev/null
+++ b/sys/src/cmd/unix/u9fs/utfrune.c
@@ -0,0 +1,29 @@
+#include <plan9.h>
+
+char*
+utfrune(char *s, long c)
+{
+ long c1;
+ Rune r;
+ int n;
+
+ if(c < Runesync) /* not part of utf sequence */
+ return strchr(s, c);
+
+ for(;;) {
+ c1 = *(uchar*)s;
+ if(c1 < Runeself) { /* one byte rune */
+ if(c1 == 0)
+ return 0;
+ if(c1 == c)
+ return s;
+ s++;
+ continue;
+ }
+ n = chartorune(&r, s);
+ if(r == c)
+ return s;
+ s += n;
+ }
+ return 0;
+}