From 44b904efbb5c9b7fdd0b15f677d418e8e8dfe3f6 Mon Sep 17 00:00:00 2001 From: charles forsyth Date: Thu, 12 May 2011 22:06:54 +0100 Subject: [PATCH] 20110512-2206 --- LICENSE | 16 + authnone.c | 25 + authp9any.c | 538 +++++++++++++++ authrhosts.c | 38 ++ convD2M.c | 89 +++ convM2D.c | 92 +++ convM2S.c | 382 +++++++++++ convS2M.c | 423 ++++++++++++ cygwin.c | 62 ++ des.c | 355 ++++++++++ dirmodeconv.c | 47 ++ doprint.c | 610 +++++++++++++++++ fcall.h | 123 ++++ fcallconv.c | 228 +++++++ makefile | 63 ++ oldfcall.c | 521 ++++++++++++++ oldfcall.h | 50 ++ plan9.h | 198 ++++++ print.c | 87 +++ random.c | 58 ++ readn.c | 21 + remotehost.c | 32 + rune.c | 148 ++++ safecpy.c | 12 + strecpy.c | 14 + sun-inttypes.h | 16 + tokenize.c | 42 ++ u9fs.c | 1765 ++++++++++++++++++++++++++++++++++++++++++++++++ u9fs.h | 30 + u9fsauth.h | 7 + utfrune.c | 29 + 31 files changed, 6121 insertions(+) create mode 100644 LICENSE create mode 100644 authnone.c create mode 100644 authp9any.c create mode 100644 authrhosts.c create mode 100644 convD2M.c create mode 100644 convM2D.c create mode 100644 convM2S.c create mode 100644 convS2M.c create mode 100644 cygwin.c create mode 100644 des.c create mode 100644 dirmodeconv.c create mode 100644 doprint.c create mode 100644 fcall.h create mode 100644 fcallconv.c create mode 100644 makefile create mode 100644 oldfcall.c create mode 100644 oldfcall.h create mode 100644 plan9.h create mode 100644 print.c create mode 100644 random.c create mode 100644 readn.c create mode 100644 remotehost.c create mode 100644 rune.c create mode 100644 safecpy.c create mode 100644 strecpy.c create mode 100644 sun-inttypes.h create mode 100644 tokenize.c create mode 100644 u9fs.c create mode 100644 u9fs.h create mode 100644 u9fsauth.h create mode 100644 utfrune.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..13a5f81 --- /dev/null +++ b/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/authnone.c b/authnone.c new file mode 100644 index 0000000..7a8145c --- /dev/null +++ b/authnone.c @@ -0,0 +1,25 @@ +#include +#include +#include + +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/authp9any.c b/authp9any.c new file mode 100644 index 0000000..c62b95c --- /dev/null +++ b/authp9any.c @@ -0,0 +1,538 @@ +/* + * 4th Edition p9any/p9sk1 authentication based on auth9p1.c + * Nigel Roles (nigel@9fs.org) 2003 + */ + +#include +#include +#include +#include /* 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/authrhosts.c b/authrhosts.c new file mode 100644 index 0000000..de15085 --- /dev/null +++ b/authrhosts.c @@ -0,0 +1,38 @@ +#include +#include +#include + +/* + * 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/convD2M.c b/convD2M.c new file mode 100644 index 0000000..c630b1b --- /dev/null +++ b/convD2M.c @@ -0,0 +1,89 @@ +#include +#include + +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/convM2D.c b/convM2D.c new file mode 100644 index 0000000..b83c957 --- /dev/null +++ b/convM2D.c @@ -0,0 +1,92 @@ +#include +#include + +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/convM2S.c b/convM2S.c new file mode 100644 index 0000000..2fc6389 --- /dev/null +++ b/convM2S.c @@ -0,0 +1,382 @@ +#include +#include + +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; inwname; 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; inwqid; 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/convS2M.c b/convS2M.c new file mode 100644 index 0000000..52cb486 --- /dev/null +++ b/convS2M.c @@ -0,0 +1,423 @@ +#include +#include + +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; inwname; 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; inwname; 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; inwqid; 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/cygwin.c b/cygwin.c new file mode 100644 index 0000000..773d02c --- /dev/null +++ b/cygwin.c @@ -0,0 +1,62 @@ +/* compatability layer for u9fs support on CYGWIN */ + +#include +#include + +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/des.c b/des.c new file mode 100644 index 0000000..6152369 --- /dev/null +++ b/des.c @@ -0,0 +1,355 @@ +#include + +/* + * 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/dirmodeconv.c b/dirmodeconv.c new file mode 100644 index 0000000..9941b16 --- /dev/null +++ b/dirmodeconv.c @@ -0,0 +1,47 @@ +#include +#include + +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/doprint.c b/doprint.c new file mode 100644 index 0000000..b51c432 --- /dev/null +++ b/doprint.c @@ -0,0 +1,610 @@ +#include + +#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 = ""; + 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/fcall.h b/fcall.h new file mode 100644 index 0000000..4a0d6f2 --- /dev/null +++ b/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/fcallconv.c b/fcallconv.c new file mode 100644 index 0000000..ca422eb --- /dev/null +++ b/fcallconv.c @@ -0,0 +1,228 @@ +#include +#include +#include + +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; inwname; 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; inwqid; 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; i127) + printable = 0; + p = ans; + *p++ = '\''; + if(printable){ + memmove(p, buf, count); + p += count; + }else{ + for(i=0; i0 && i%4==0) + *p++ = ' '; + sprint(p, "%2.2ux", (uchar)buf[i]); + p += 2; + } + } + *p++ = '\''; + *p = 0; + return p - ans; +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..b834bca --- /dev/null +++ b/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 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/oldfcall.c b/oldfcall.c new file mode 100644 index 0000000..d9c8ca3 --- /dev/null +++ b/oldfcall.c @@ -0,0 +1,521 @@ +#include +#include +#include + +/* + * 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/oldfcall.h b/oldfcall.h new file mode 100644 index 0000000..90c00d6 --- /dev/null +++ b/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/plan9.h b/plan9.h new file mode 100644 index 0000000..caecb8e --- /dev/null +++ b/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 +#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 /* for int64_t et al. */ +#include /* 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 +#include /* for memmove */ +#include /* 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 */ + 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/print.c b/print.c new file mode 100644 index 0000000..8011df6 --- /dev/null +++ b/print.c @@ -0,0 +1,87 @@ +#include + +#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/random.c b/random.c new file mode 100644 index 0000000..6b4ab58 --- /dev/null +++ b/random.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +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>16; + case 2: + r[1] = l>>8; + case 1: + r[0] = l; + } + } +} diff --git a/readn.c b/readn.c new file mode 100644 index 0000000..89f0603 --- /dev/null +++ b/readn.c @@ -0,0 +1,21 @@ +#include + +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/remotehost.c b/remotehost.c new file mode 100644 index 0000000..d1a3696 --- /dev/null +++ b/remotehost.c @@ -0,0 +1,32 @@ +#include +#include /* various networking crud */ +#include +#include +#include +#include + +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/rune.c b/rune.c new file mode 100644 index 0000000..a0822d6 --- /dev/null +++ b/rune.c @@ -0,0 +1,148 @@ +#include + +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< 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/safecpy.c b/safecpy.c new file mode 100644 index 0000000..a751349 --- /dev/null +++ b/safecpy.c @@ -0,0 +1,12 @@ +#include + +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/strecpy.c b/strecpy.c new file mode 100644 index 0000000..57cedf8 --- /dev/null +++ b/strecpy.c @@ -0,0 +1,14 @@ +#include + +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/sun-inttypes.h b/sun-inttypes.h new file mode 100644 index 0000000..4e147a1 --- /dev/null +++ b/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/tokenize.c b/tokenize.c new file mode 100644 index 0000000..41ab050 --- /dev/null +++ b/tokenize.c @@ -0,0 +1,42 @@ +#include + +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/u9fs.c b/u9fs.c new file mode 100644 index 0000000..eeee21f --- /dev/null +++ b/u9fs.c @@ -0,0 +1,1765 @@ +/* already in plan9.h #include *//* for struct passwd, struct group, struct stat ... */ +/* plan9.h is first to get the large file support definitions as early as possible */ +#include +#include /* for stat, umask */ +#include /* for malloc */ +#include /* for strcpy, memmove */ +#include /* for getpwnam, getpwuid */ +#include /* for getgrnam, getgrgid */ +#include /* for gethostname, pread, pwrite, read, write */ +#include /* for utime */ +#include /* for readdir */ +#include /* for errno */ +#include /* for remove [sic] */ +#include /* for O_RDONLY, etc. */ + +#include /* various networking crud */ +#include +#include + +#include +#include +#include + +/* #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; inwname; 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; inmem; 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; inext) + 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; inext) + 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; inmem; 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; iname, 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/u9fs.h b/u9fs.h new file mode 100644 index 0000000..f553a3a --- /dev/null +++ b/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/u9fsauth.h b/u9fsauth.h new file mode 100644 index 0000000..6d101b1 --- /dev/null +++ b/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/utfrune.c b/utfrune.c new file mode 100644 index 0000000..ea6772f --- /dev/null +++ b/utfrune.c @@ -0,0 +1,29 @@ +#include + +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; +}