commit 44b904efbb5c9b7fdd0b15f677d418e8e8dfe3f6 Author: charles forsyth Date: Thu May 12 22:06:54 2011 +0100 20110512-2206 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; +}