diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/liboventi |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/liboventi')
-rwxr-xr-x | sys/src/liboventi/client.c | 346 | ||||
-rwxr-xr-x | sys/src/liboventi/debug.c | 78 | ||||
-rwxr-xr-x | sys/src/liboventi/errfmt.c | 12 | ||||
-rwxr-xr-x | sys/src/liboventi/fatal.c | 16 | ||||
-rwxr-xr-x | sys/src/liboventi/mkfile | 46 | ||||
-rwxr-xr-x | sys/src/liboventi/pack.c | 147 | ||||
-rwxr-xr-x | sys/src/liboventi/packet.c | 848 | ||||
-rwxr-xr-x | sys/src/liboventi/packet.h | 56 | ||||
-rwxr-xr-x | sys/src/liboventi/parsescore.c | 31 | ||||
-rwxr-xr-x | sys/src/liboventi/plan9-io.c | 146 | ||||
-rwxr-xr-x | sys/src/liboventi/plan9-sha1.c | 78 | ||||
-rwxr-xr-x | sys/src/liboventi/plan9-thread.acid | 531 | ||||
-rwxr-xr-x | sys/src/liboventi/plan9-thread.c | 468 | ||||
-rwxr-xr-x | sys/src/liboventi/readfully.c | 19 | ||||
-rwxr-xr-x | sys/src/liboventi/rpc.c | 471 | ||||
-rwxr-xr-x | sys/src/liboventi/scorefmt.c | 20 | ||||
-rwxr-xr-x | sys/src/liboventi/server.c | 265 | ||||
-rwxr-xr-x | sys/src/liboventi/session.h | 74 | ||||
-rwxr-xr-x | sys/src/liboventi/strdup.c | 19 | ||||
-rwxr-xr-x | sys/src/liboventi/venti.txt | 267 | ||||
-rwxr-xr-x | sys/src/liboventi/zero.c | 79 |
21 files changed, 4017 insertions, 0 deletions
diff --git a/sys/src/liboventi/client.c b/sys/src/liboventi/client.c new file mode 100755 index 000000000..d442e040a --- /dev/null +++ b/sys/src/liboventi/client.c @@ -0,0 +1,346 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> +#include "session.h" + +static char EProtocolBotch[] = "venti protocol botch"; +static char ELumpSize[] = "illegal lump size"; +static char ENotConnected[] = "not connected to venti server"; + +static Packet *vtRPC(VtSession *z, int op, Packet *p); + +VtSession * +vtClientAlloc(void) +{ + VtSession *z = vtAlloc(); + return z; +} + +VtSession * +vtDial(char *host, int canfail) +{ + VtSession *z; + int fd; + char *na; + char e[ERRMAX]; + + if(host == nil) + host = getenv("venti"); + if(host == nil) + host = "$venti"; + + if (host == nil) { + if (!canfail) + werrstr("no venti host set"); + na = ""; + fd = -1; + } else { + na = netmkaddr(host, 0, "venti"); + fd = dial(na, 0, 0, 0); + } + if(fd < 0){ + rerrstr(e, sizeof e); + if(!canfail){ + vtSetError("venti dialstring %s: %s", na, e); + return nil; + } + } + z = vtClientAlloc(); + if(fd < 0) + strcpy(z->fderror, e); + vtSetFd(z, fd); + return z; +} + +int +vtRedial(VtSession *z, char *host) +{ + int fd; + char *na; + + if(host == nil) + host = getenv("venti"); + if(host == nil) + host = "$venti"; + + na = netmkaddr(host, 0, "venti"); + fd = dial(na, 0, 0, 0); + if(fd < 0){ + vtOSError(); + return 0; + } + vtReset(z); + vtSetFd(z, fd); + return 1; +} + +VtSession * +vtStdioServer(char *server) +{ + int pfd[2]; + VtSession *z; + + if(server == nil) + return nil; + + if(access(server, AEXEC) < 0) { + vtOSError(); + return nil; + } + + if(pipe(pfd) < 0) { + vtOSError(); + return nil; + } + + switch(fork()) { + case -1: + close(pfd[0]); + close(pfd[1]); + vtOSError(); + return nil; + case 0: + close(pfd[0]); + dup(pfd[1], 0); + dup(pfd[1], 1); + execl(server, "ventiserver", "-i", nil); + exits("exec failed"); + } + close(pfd[1]); + + z = vtClientAlloc(); + vtSetFd(z, pfd[0]); + return z; +} + +int +vtPing(VtSession *z) +{ + Packet *p = packetAlloc(); + + p = vtRPC(z, VtQPing, p); + if(p == nil) + return 0; + packetFree(p); + return 1; +} + +int +vtHello(VtSession *z) +{ + Packet *p; + uchar buf[10]; + char *sid; + int crypto, codec; + + sid = nil; + + p = packetAlloc(); + if(!vtAddString(p, vtGetVersion(z))) + goto Err; + if(!vtAddString(p, vtGetUid(z))) + goto Err; + buf[0] = vtGetCryptoStrength(z); + buf[1] = 0; + buf[2] = 0; + packetAppend(p, buf, 3); + p = vtRPC(z, VtQHello, p); + if(p == nil) + return 0; + if(!vtGetString(p, &sid)) + goto Err; + if(!packetConsume(p, buf, 2)) + goto Err; + if(packetSize(p) != 0) { + vtSetError(EProtocolBotch); + goto Err; + } + crypto = buf[0]; + codec = buf[1]; + + USED(crypto); + USED(codec); + + packetFree(p); + + vtLock(z->lk); + z->sid = sid; + z->auth.state = VtAuthOK; + vtSha1Free(z->inHash); + z->inHash = nil; + vtSha1Free(z->outHash); + z->outHash = nil; + vtUnlock(z->lk); + + return 1; +Err: + packetFree(p); + vtMemFree(sid); + return 0; +} + +int +vtSync(VtSession *z) +{ + Packet *p = packetAlloc(); + + p = vtRPC(z, VtQSync, p); + if(p == nil) + return 0; + if(packetSize(p) != 0){ + vtSetError(EProtocolBotch); + goto Err; + } + packetFree(p); + return 1; + +Err: + packetFree(p); + return 0; +} + +int +vtWrite(VtSession *z, uchar score[VtScoreSize], int type, uchar *buf, int n) +{ + Packet *p = packetAlloc(); + + packetAppend(p, buf, n); + return vtWritePacket(z, score, type, p); +} + +int +vtWritePacket(VtSession *z, uchar score[VtScoreSize], int type, Packet *p) +{ + int n = packetSize(p); + uchar *hdr; + + if(n > VtMaxLumpSize || n < 0) { + vtSetError(ELumpSize); + goto Err; + } + + if(n == 0) { + memmove(score, vtZeroScore, VtScoreSize); + return 1; + } + + hdr = packetHeader(p, 4); + hdr[0] = type; + hdr[1] = 0; /* pad */ + hdr[2] = 0; /* pad */ + hdr[3] = 0; /* pad */ + p = vtRPC(z, VtQWrite, p); + if(p == nil) + return 0; + if(!packetConsume(p, score, VtScoreSize)) + goto Err; + if(packetSize(p) != 0) { + vtSetError(EProtocolBotch); + goto Err; + } + packetFree(p); + return 1; +Err: + packetFree(p); + return 0; +} + +int +vtRead(VtSession *z, uchar score[VtScoreSize], int type, uchar *buf, int n) +{ + Packet *p; + + p = vtReadPacket(z, score, type, n); + if(p == nil) + return -1; + n = packetSize(p); + packetCopy(p, buf, 0, n); + packetFree(p); + return n; +} + +Packet * +vtReadPacket(VtSession *z, uchar score[VtScoreSize], int type, int n) +{ + Packet *p; + uchar buf[10]; + + if(n < 0 || n > VtMaxLumpSize) { + vtSetError(ELumpSize); + return nil; + } + + p = packetAlloc(); + if(memcmp(score, vtZeroScore, VtScoreSize) == 0) + return p; + + packetAppend(p, score, VtScoreSize); + buf[0] = type; + buf[1] = 0; /* pad */ + buf[2] = n >> 8; + buf[3] = n; + packetAppend(p, buf, 4); + return vtRPC(z, VtQRead, p); +} + + +static Packet * +vtRPC(VtSession *z, int op, Packet *p) +{ + uchar *hdr, buf[2]; + char *err; + + if(z == nil){ + vtSetError(ENotConnected); + return nil; + } + + /* + * single threaded for the momment + */ + vtLock(z->lk); + if(z->cstate != VtStateConnected){ + vtSetError(ENotConnected); + goto Err; + } + hdr = packetHeader(p, 2); + hdr[0] = op; /* op */ + hdr[1] = 0; /* tid */ + vtDebug(z, "client send: "); + vtDebugMesg(z, p, "\n"); + if(!vtSendPacket(z, p)) { + p = nil; + goto Err; + } + p = vtRecvPacket(z); + if(p == nil) + goto Err; + vtDebug(z, "client recv: "); + vtDebugMesg(z, p, "\n"); + if(!packetConsume(p, buf, 2)) + goto Err; + if(buf[0] == VtRError) { + if(!vtGetString(p, &err)) { + vtSetError(EProtocolBotch); + goto Err; + } + vtSetError(err); + vtMemFree(err); + packetFree(p); + vtUnlock(z->lk); + return nil; + } + if(buf[0] != op+1 || buf[1] != 0) { + vtSetError(EProtocolBotch); + goto Err; + } + vtUnlock(z->lk); + return p; +Err: + vtDebug(z, "vtRPC failed: %s\n", vtGetError()); + if(p != nil) + packetFree(p); + vtUnlock(z->lk); + vtDisconnect(z, 1); + return nil; +} diff --git a/sys/src/liboventi/debug.c b/sys/src/liboventi/debug.c new file mode 100755 index 000000000..496860458 --- /dev/null +++ b/sys/src/liboventi/debug.c @@ -0,0 +1,78 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> +#include "session.h" + +void vtDumpSome(Packet*); + +void +vtDebug(VtSession *s, char *fmt, ...) +{ + va_list arg; + + if(!s->debug) + return; + + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); +} + +void +vtDebugMesg(VtSession *z, Packet *p, char *s) +{ + int op; + int tid; + int n; + uchar buf[100], *b; + + + if(!z->debug) + return; + n = packetSize(p); + if(n < 2) { + fprint(2, "runt packet%s", s); + return; + } + b = packetPeek(p, buf, 0, 2); + op = b[0]; + tid = b[1]; + + fprint(2, "%c%d[%d] %d", ((op&1)==0)?'R':'Q', op, tid, n); + vtDumpSome(p); + fprint(2, "%s", s); +} + +void +vtDumpSome(Packet *pkt) +{ + int printable; + int i, n; + char buf[200], *q, *eq; + uchar data[32], *p; + + n = packetSize(pkt); + printable = 1; + q = buf; + eq = buf + sizeof(buf); + q = seprint(q, eq, "(%d) '", n); + + if(n > sizeof(data)) + n = sizeof(data); + p = packetPeek(pkt, data, 0, n); + for(i=0; i<n && printable; i++) + if((p[i]<32 && p[i] !='\n' && p[i] !='\t') || p[i]>127) + printable = 0; + if(printable) { + for(i=0; i<n; i++) + q = seprint(q, eq, "%c", p[i]); + } else { + for(i=0; i<n; i++) { + if(i>0 && i%4==0) + q = seprint(q, eq, " "); + q = seprint(q, eq, "%.2X", p[i]); + } + } + seprint(q, eq, "'"); + fprint(2, "%s", buf); +} diff --git a/sys/src/liboventi/errfmt.c b/sys/src/liboventi/errfmt.c new file mode 100755 index 000000000..e4f611837 --- /dev/null +++ b/sys/src/liboventi/errfmt.c @@ -0,0 +1,12 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +int +vtErrFmt(Fmt *f) +{ + char *s; + + s = vtGetError(); + return fmtstrcpy(f, s); +} diff --git a/sys/src/liboventi/fatal.c b/sys/src/liboventi/fatal.c new file mode 100755 index 000000000..1d593fe02 --- /dev/null +++ b/sys/src/liboventi/fatal.c @@ -0,0 +1,16 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +void +vtFatal(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + fprint(2, "fatal error: "); + vfprint(2, fmt, arg); + fprint(2, "\n"); + va_end(arg); + exits("vtFatal"); +} diff --git a/sys/src/liboventi/mkfile b/sys/src/liboventi/mkfile new file mode 100755 index 000000000..3ee035106 --- /dev/null +++ b/sys/src/liboventi/mkfile @@ -0,0 +1,46 @@ +</$objtype/mkfile + +LIB=/$objtype/lib/liboventi.a + +SYS=plan9 + +OFILES=\ + client.$O\ + debug.$O\ + errfmt.$O\ + fatal.$O\ + pack.$O\ + packet.$O\ + parsescore.$O\ + readfully.$O\ + rpc.$O\ + scorefmt.$O\ + server.$O\ + strdup.$O\ + zero.$O\ + $SYS-io.$O\ + $SYS-sha1.$O\ + $SYS-thread.$O\ + +HFILES=\ + packet.h\ + session.h\ + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + ${LIB:/$objtype/%=/386/%}\ + +CFLAGS=$CFLAGS + +</sys/src/cmd/mksyslib + +$O.vttest: vttest.$O $OFILES + $LD $LDFLAGS -o $target $prereq + +acid: $HFILES packet.c + $CC $INC -a packet.c > acid || rm acid + +test: $O.vttest + $O.vttest diff --git a/sys/src/liboventi/pack.c b/sys/src/liboventi/pack.c new file mode 100755 index 000000000..e577dd146 --- /dev/null +++ b/sys/src/liboventi/pack.c @@ -0,0 +1,147 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +/* + * integer conversion routines + */ +#define U8GET(p) ((p)[0]) +#define U16GET(p) (((p)[0]<<8)|(p)[1]) +#define U32GET(p) ((u32int)(((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3])) +#define U48GET(p) (((vlong)U16GET(p)<<32)|(vlong)U32GET((p)+2)) +#define U64GET(p) (((vlong)U32GET(p)<<32)|(vlong)U32GET((p)+4)) + +#define U8PUT(p,v) (p)[0]=(v) +#define U16PUT(p,v) (p)[0]=(v)>>8;(p)[1]=(v) +#define U32PUT(p,v) (p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v) +#define U48PUT(p,v,t32) t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT((p)+2,t32) +#define U64PUT(p,v,t32) t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT((p)+4,t32) + +static int +checkSize(int n) +{ + if(n < 256 || n > VtMaxLumpSize) { + vtSetError("bad block size"); + return 0; + } + return 1; +} + + +void +vtRootPack(VtRoot *r, uchar *p) +{ + uchar *op = p; + + U16PUT(p, r->version); + p += 2; + memmove(p, r->name, sizeof(r->name)); + p += sizeof(r->name); + memmove(p, r->type, sizeof(r->type)); + p += sizeof(r->type); + memmove(p, r->score, VtScoreSize); + p += VtScoreSize; + U16PUT(p, r->blockSize); + p += 2; + memmove(p, r->prev, VtScoreSize); + p += VtScoreSize; + + assert(p-op == VtRootSize); +} + +int +vtRootUnpack(VtRoot *r, uchar *p) +{ + uchar *op = p; + + memset(r, 0, sizeof(*r)); + + r->version = U16GET(p); + if(r->version != VtRootVersion) { + vtSetError("unknown root version"); + return 0; + } + p += 2; + memmove(r->name, p, sizeof(r->name)); + r->name[sizeof(r->name)-1] = 0; + p += sizeof(r->name); + memmove(r->type, p, sizeof(r->type)); + r->type[sizeof(r->type)-1] = 0; + p += sizeof(r->type); + memmove(r->score, p, VtScoreSize); + p += VtScoreSize; + r->blockSize = U16GET(p); + if(!checkSize(r->blockSize)) + return 0; + p += 2; + memmove(r->prev, p, VtScoreSize); + p += VtScoreSize; + + assert(p-op == VtRootSize); + return 1; +} + +void +vtEntryPack(VtEntry *e, uchar *p, int index) +{ + ulong t32; + int flags; + uchar *op; + + p += index * VtEntrySize; + op = p; + + U32PUT(p, e->gen); + p += 4; + U16PUT(p, e->psize); + p += 2; + U16PUT(p, e->dsize); + p += 2; + flags = e->flags | ((e->depth << VtEntryDepthShift) & VtEntryDepthMask); + U8PUT(p, flags); + p++; + memset(p, 0, 5); + p += 5; + U48PUT(p, e->size, t32); + p += 6; + memmove(p, e->score, VtScoreSize); + p += VtScoreSize; + + assert(p-op == VtEntrySize); +} + +int +vtEntryUnpack(VtEntry *e, uchar *p, int index) +{ + uchar *op; + + p += index * VtEntrySize; + op = p; + + e->gen = U32GET(p); + p += 4; + e->psize = U16GET(p); + p += 2; + e->dsize = U16GET(p); + p += 2; + e->flags = U8GET(p); + e->depth = (e->flags & VtEntryDepthMask) >> VtEntryDepthShift; + e->flags &= ~VtEntryDepthMask; + p++; + p += 5; + e->size = U48GET(p); + p += 6; + memmove(e->score, p, VtScoreSize); + p += VtScoreSize; + + assert(p-op == VtEntrySize); + + if(!(e->flags & VtEntryActive)) + return 1; + + if(!checkSize(e->psize) || !checkSize(e->dsize)) + return 0; + + return 1; +} + diff --git a/sys/src/liboventi/packet.c b/sys/src/liboventi/packet.c new file mode 100755 index 000000000..e799ed563 --- /dev/null +++ b/sys/src/liboventi/packet.c @@ -0,0 +1,848 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> +#include "packet.h" + +static Frag *fragAlloc(Packet*, int n, int pos, Frag *next); +static Frag *fragDup(Packet*, Frag*); +static void fragFree(Frag*); + +static Mem *memAlloc(int, int); +static void memFree(Mem*); +static int memHead(Mem *m, uchar *rp, int n); +static int memTail(Mem *m, uchar *wp, int n); + +static char EPacketSize[] = "bad packet size"; +static char EPacketOffset[] = "bad packet offset"; +static char EBadSize[] = "bad size"; + +static struct { + Lock lk; + Packet *packet; + int npacket; + Frag *frag; + int nfrag; + Mem *bigMem; + int nbigMem; + Mem *smallMem; + int nsmallMem; +} freeList; + +#define FRAGSIZE(f) ((f)->wp - (f)->rp) +#define FRAGASIZE(f) ((f)->mem->ep - (f)->mem->bp) + +#define NOTFREE(p) assert((p)->size>=0) + +Packet * +packetAlloc(void) +{ + Packet *p; + + lock(&freeList.lk); + p = freeList.packet; + if(p != nil) + freeList.packet = p->next; + else + freeList.npacket++; + unlock(&freeList.lk); + + if(p == nil) + p = vtMemBrk(sizeof(Packet)); + else + assert(p->size == -1); + p->size = 0; + p->asize = 0; + p->first = nil; + p->last = nil; + p->next = nil; + + return p; +} + +void +packetFree(Packet *p) +{ + Frag *f, *ff; + +if(0)fprint(2, "packetFree %p\n", p); + + NOTFREE(p); + p->size = -1; + + for(f=p->first; f!=nil; f=ff) { + ff = f->next; + fragFree(f); + } + p->first = nil; + p->last = nil; + + lock(&freeList.lk); + p->next = freeList.packet; + freeList.packet = p; + unlock(&freeList.lk); +} + +Packet * +packetDup(Packet *p, int offset, int n) +{ + Frag *f, *ff; + Packet *pp; + + NOTFREE(p); + if(offset < 0 || n < 0 || offset+n > p->size) { + vtSetError(EBadSize); + return nil; + } + + pp = packetAlloc(); + if(n == 0) + return pp; + + pp->size = n; + + /* skip offset */ + for(f=p->first; offset >= FRAGSIZE(f); f=f->next) + offset -= FRAGSIZE(f); + + /* first frag */ + ff = fragDup(pp, f); + ff->rp += offset; + pp->first = ff; + n -= FRAGSIZE(ff); + pp->asize += FRAGASIZE(ff); + + /* the remaining */ + while(n > 0) { + f = f->next; + ff->next = fragDup(pp, f); + ff = ff->next; + n -= FRAGSIZE(ff); + pp->asize += FRAGASIZE(ff); + } + + /* fix up last frag: note n <= 0 */ + ff->wp += n; + ff->next = nil; + pp->last = ff; + + return pp; +} + +Packet * +packetSplit(Packet *p, int n) +{ + Packet *pp; + Frag *f, *ff; + + NOTFREE(p); + if(n < 0 || n > p->size) { + vtSetError(EPacketSize); + return nil; + } + + pp = packetAlloc(); + if(n == 0) + return pp; + + pp->size = n; + p->size -= n; + ff = nil; + for(f=p->first; n > 0 && n >= FRAGSIZE(f); f=f->next) { + n -= FRAGSIZE(f); + p->asize -= FRAGASIZE(f); + pp->asize += FRAGASIZE(f); + ff = f; + } + + /* split shared frag */ + if(n > 0) { + ff = f; + f = fragDup(pp, ff); + pp->asize += FRAGASIZE(ff); + ff->next = nil; + ff->wp = ff->rp + n; + f->rp += n; + } + + pp->first = p->first; + pp->last = ff; + p->first = f; + return pp; +} + +int +packetConsume(Packet *p, uchar *buf, int n) +{ + NOTFREE(p); + if(buf && !packetCopy(p, buf, 0, n)) + return 0; + return packetTrim(p, n, p->size-n); +} + +int +packetTrim(Packet *p, int offset, int n) +{ + Frag *f, *ff; + + NOTFREE(p); + if(offset < 0 || offset > p->size) { + vtSetError(EPacketOffset); + return 0; + } + + if(n < 0 || offset + n > p->size) { + vtSetError(EPacketOffset); + return 0; + } + + p->size = n; + + /* easy case */ + if(n == 0) { + for(f=p->first; f != nil; f=ff) { + ff = f->next; + fragFree(f); + } + p->first = p->last = nil; + p->asize = 0; + return 1; + } + + /* free before offset */ + for(f=p->first; offset >= FRAGSIZE(f); f=ff) { + p->asize -= FRAGASIZE(f); + offset -= FRAGSIZE(f); + ff = f->next; + fragFree(f); + } + + /* adjust frag */ + f->rp += offset; + p->first = f; + + /* skip middle */ + for(; n > 0 && n > FRAGSIZE(f); f=f->next) + n -= FRAGSIZE(f); + + /* adjust end */ + f->wp = f->rp + n; + p->last = f; + ff = f->next; + f->next = nil; + + /* free after */ + for(f=ff; f != nil; f=ff) { + p->asize -= FRAGASIZE(f); + ff = f->next; + fragFree(f); + } + return 1; +} + +uchar * +packetHeader(Packet *p, int n) +{ + Frag *f; + Mem *m; + + NOTFREE(p); + if(n <= 0 || n > MaxFragSize) { + vtSetError(EPacketSize); + return 0; + } + + p->size += n; + + /* try and fix in current frag */ + f = p->first; + if(f != nil) { + m = f->mem; + if(n <= f->rp - m->bp) + if(m->ref == 1 || memHead(m, f->rp, n)) { + f->rp -= n; + return f->rp; + } + } + + /* add frag to front */ + f = fragAlloc(p, n, PEnd, p->first); + p->asize += FRAGASIZE(f); + if(p->first == nil) + p->last = f; + p->first = f; + return f->rp; +} + +uchar * +packetTrailer(Packet *p, int n) +{ + Mem *m; + Frag *f; + + NOTFREE(p); + if(n <= 0 || n > MaxFragSize) { + vtSetError(EPacketSize); + return 0; + } + + p->size += n; + + /* try and fix in current frag */ + if(p->first != nil) { + f = p->last; + m = f->mem; + if(n <= m->ep - f->wp) + if(m->ref == 1 || memTail(m, f->wp, n)) { + f->wp += n; + return f->wp - n; + } + } + + /* add frag to end */ + f = fragAlloc(p, n, (p->first == nil)?PMiddle:PFront, nil); + p->asize += FRAGASIZE(f); + if(p->first == nil) + p->first = f; + else + p->last->next = f; + p->last = f; + return f->rp; +} + +int +packetPrefix(Packet *p, uchar *buf, int n) +{ + Frag *f; + int nn; + Mem *m; + + NOTFREE(p); + if(n <= 0) + return 1; + + p->size += n; + + /* try and fix in current frag */ + f = p->first; + if(f != nil) { + m = f->mem; + nn = f->rp - m->bp; + if(nn > n) + nn = n; + if(m->ref == 1 || memHead(m, f->rp, nn)) { + f->rp -= nn; + n -= nn; + memmove(f->rp, buf+n, nn); + } + } + + while(n > 0) { + nn = n; + if(nn > MaxFragSize) + nn = MaxFragSize; + f = fragAlloc(p, nn, PEnd, p->first); + p->asize += FRAGASIZE(f); + if(p->first == nil) + p->last = f; + p->first = f; + n -= nn; + memmove(f->rp, buf+n, nn); + } + return 1; +} + +int +packetAppend(Packet *p, uchar *buf, int n) +{ + Frag *f; + int nn; + Mem *m; + + NOTFREE(p); + if(n <= 0) + return 1; + + p->size += n; + /* try and fix in current frag */ + if(p->first != nil) { + f = p->last; + m = f->mem; + nn = m->ep - f->wp; + if(nn > n) + nn = n; + if(m->ref == 1 || memTail(m, f->wp, nn)) { + memmove(f->wp, buf, nn); + f->wp += nn; + buf += nn; + n -= nn; + } + } + + while(n > 0) { + nn = n; + if(nn > MaxFragSize) + nn = MaxFragSize; + f = fragAlloc(p, nn, (p->first == nil)?PMiddle:PFront, nil); + p->asize += FRAGASIZE(f); + if(p->first == nil) + p->first = f; + else + p->last->next = f; + p->last = f; + memmove(f->rp, buf, nn); + buf += nn; + n -= nn; + } + return 1; +} + +int +packetConcat(Packet *p, Packet *pp) +{ + NOTFREE(p); + NOTFREE(pp); + if(pp->size == 0) + return 1; + p->size += pp->size; + p->asize += pp->asize; + + if(p->first != nil) + p->last->next = pp->first; + else + p->first = pp->first; + p->last = pp->last; + pp->size = 0; + pp->asize = 0; + pp->first = nil; + pp->last = nil; + return 1; +} + +uchar * +packetPeek(Packet *p, uchar *buf, int offset, int n) +{ + Frag *f; + int nn; + uchar *b; + + NOTFREE(p); + if(n == 0) + return buf; + + if(offset < 0 || offset >= p->size) { + vtSetError(EPacketOffset); + return 0; + } + + if(n < 0 || offset + n > p->size) { + vtSetError(EPacketSize); + return 0; + } + + /* skip up to offset */ + for(f=p->first; offset >= FRAGSIZE(f); f=f->next) + offset -= FRAGSIZE(f); + + /* easy case */ + if(offset + n <= FRAGSIZE(f)) + return f->rp + offset; + + for(b=buf; n>0; n -= nn) { + nn = FRAGSIZE(f) - offset; + if(nn > n) + nn = n; + memmove(b, f->rp+offset, nn); + offset = 0; + f = f->next; + b += nn; + } + + return buf; +} + +int +packetCopy(Packet *p, uchar *buf, int offset, int n) +{ + uchar *b; + + NOTFREE(p); + b = packetPeek(p, buf, offset, n); + if(b == nil) + return 0; + if(b != buf) + memmove(buf, b, n); + return 1; +} + +int +packetFragments(Packet *p, IOchunk *io, int nio, int offset) +{ + Frag *f; + int size; + IOchunk *eio; + + NOTFREE(p); + if(p->size == 0 || nio <= 0) + return 0; + + if(offset < 0 || offset > p->size) { + vtSetError(EPacketOffset); + return -1; + } + + for(f=p->first; offset >= FRAGSIZE(f); f=f->next) + offset -= FRAGSIZE(f); + + size = 0; + eio = io + nio; + for(; f != nil && io < eio; f=f->next) { + io->addr = f->rp + offset; + io->len = f->wp - (f->rp + offset); + offset = 0; + size += io->len; + io++; + } + + return size; +} + +void +packetStats(void) +{ + Packet *p; + Frag *f; + Mem *m; + + int np, nf, nsm, nbm; + + lock(&freeList.lk); + np = 0; + for(p=freeList.packet; p; p=p->next) + np++; + nf = 0; + for(f=freeList.frag; f; f=f->next) + nf++; + nsm = 0; + for(m=freeList.smallMem; m; m=m->next) + nsm++; + nbm = 0; + for(m=freeList.bigMem; m; m=m->next) + nbm++; + + fprint(2, "packet: %d/%d frag: %d/%d small mem: %d/%d big mem: %d/%d\n", + np, freeList.npacket, + nf, freeList.nfrag, + nsm, freeList.nsmallMem, + nbm, freeList.nbigMem); + + unlock(&freeList.lk); +} + + +int +packetSize(Packet *p) +{ + NOTFREE(p); + if(0) { + Frag *f; + int size = 0; + + for(f=p->first; f; f=f->next) + size += FRAGSIZE(f); + if(size != p->size) + fprint(2, "packetSize %d %d\n", size, p->size); + assert(size == p->size); + } + return p->size; +} + +int +packetAllocatedSize(Packet *p) +{ + NOTFREE(p); + if(0) { + Frag *f; + int asize = 0; + + for(f=p->first; f; f=f->next) + asize += FRAGASIZE(f); + if(asize != p->asize) + fprint(2, "packetAllocatedSize %d %d\n", asize, p->asize); + assert(asize == p->asize); + } + return p->asize; +} + +void +packetSha1(Packet *p, uchar sha1[VtScoreSize]) +{ + Frag *f; + VtSha1 *s; + int size; + + NOTFREE(p); + s = vtSha1Alloc(); + size = p->size; + for(f=p->first; f; f=f->next) { + vtSha1Update(s, f->rp, FRAGSIZE(f)); + size -= FRAGSIZE(f); + } + assert(size == 0); + vtSha1Final(s, sha1); + vtSha1Free(s); +} + +int +packetCmp(Packet *pkt0, Packet *pkt1) +{ + Frag *f0, *f1; + int n0, n1, x; + + NOTFREE(pkt0); + NOTFREE(pkt1); + f0 = pkt0->first; + f1 = pkt1->first; + + if(f0 == nil) + return (f1 == nil)?0:-1; + if(f1 == nil) + return 1; + n0 = FRAGSIZE(f0); + n1 = FRAGSIZE(f1); + + for(;;) { + if(n0 < n1) { + x = memcmp(f0->wp - n0, f1->wp - n1, n0); + if(x != 0) + return x; + n1 -= n0; + f0 = f0->next; + if(f0 == nil) + return -1; + n0 = FRAGSIZE(f0); + } else if (n0 > n1) { + x = memcmp(f0->wp - n0, f1->wp - n1, n1); + if(x != 0) + return x; + n0 -= n1; + f1 = f1->next; + if(f1 == nil) + return 1; + n1 = FRAGSIZE(f1); + } else { /* n0 == n1 */ + x = memcmp(f0->wp - n0, f1->wp - n1, n0); + if(x != 0) + return x; + f0 = f0->next; + f1 = f1->next; + if(f0 == nil) + return (f1 == nil)?0:-1; + if(f1 == nil) + return 1; + n0 = FRAGSIZE(f0); + n1 = FRAGSIZE(f1); + } + } +} + + +static Frag * +fragAlloc(Packet *p, int n, int pos, Frag *next) +{ + Frag *f, *ef; + Mem *m; + + /* look for local frag */ + f = &p->local[0]; + ef = &p->local[NLocalFrag]; + for(;f<ef; f++) { + if(f->state == FragLocalFree) { + f->state = FragLocalAlloc; + goto Found; + } + } + lock(&freeList.lk); + f = freeList.frag; + if(f != nil) + freeList.frag = f->next; + else + freeList.nfrag++; + unlock(&freeList.lk); + + if(f == nil) { + f = vtMemBrk(sizeof(Frag)); + f->state = FragGlobal; + } + +Found: + if(n == 0) + return f; + + if(pos == PEnd && next == nil) + pos = PMiddle; + m = memAlloc(n, pos); + f->mem = m; + f->rp = m->rp; + f->wp = m->wp; + f->next = next; + + return f; +} + +static Frag * +fragDup(Packet *p, Frag *f) +{ + Frag *ff; + Mem *m; + + m = f->mem; + + /* + * m->rp && m->wp can be out of date when ref == 1 + * also, potentially reclaims space from previous frags + */ + if(m->ref == 1) { + m->rp = f->rp; + m->wp = f->wp; + } + + ff = fragAlloc(p, 0, 0, nil); + *ff = *f; + lock(&m->lk); + m->ref++; + unlock(&m->lk); + return ff; +} + + +static void +fragFree(Frag *f) +{ + memFree(f->mem); + + if(f->state == FragLocalAlloc) { + f->state = FragLocalFree; + return; + } + + lock(&freeList.lk); + f->next = freeList.frag; + freeList.frag = f; + unlock(&freeList.lk); +} + +static Mem * +memAlloc(int n, int pos) +{ + Mem *m; + int nn; + + if(n < 0 || n > MaxFragSize) { + vtSetError(EPacketSize); + return 0; + } + if(n <= SmallMemSize) { + lock(&freeList.lk); + m = freeList.smallMem; + if(m != nil) + freeList.smallMem = m->next; + else + freeList.nsmallMem++; + unlock(&freeList.lk); + nn = SmallMemSize; + } else { + lock(&freeList.lk); + m = freeList.bigMem; + if(m != nil) + freeList.bigMem = m->next; + else + freeList.nbigMem++; + unlock(&freeList.lk); + nn = BigMemSize; + } + + if(m == nil) { + m = vtMemBrk(sizeof(Mem)); + m->bp = vtMemBrk(nn); + m->ep = m->bp + nn; + } + assert(m->ref == 0); + m->ref = 1; + + switch(pos) { + default: + assert(0); + case PFront: + m->rp = m->bp; + break; + case PMiddle: + /* leave a little bit at end */ + m->rp = m->ep - n - 32; + break; + case PEnd: + m->rp = m->ep - n; + break; + } + /* check we did not blow it */ + if(m->rp < m->bp) + m->rp = m->bp; + m->wp = m->rp + n; + assert(m->rp >= m->bp && m->wp <= m->ep); + return m; +} + +static void +memFree(Mem *m) +{ + lock(&m->lk); + m->ref--; + if(m->ref > 0) { + unlock(&m->lk); + return; + } + unlock(&m->lk); + assert(m->ref == 0); + + switch(m->ep - m->bp) { + default: + assert(0); + case SmallMemSize: + lock(&freeList.lk); + m->next = freeList.smallMem; + freeList.smallMem = m; + unlock(&freeList.lk); + break; + case BigMemSize: + lock(&freeList.lk); + m->next = freeList.bigMem; + freeList.bigMem = m; + unlock(&freeList.lk); + break; + } +} + +static int +memHead(Mem *m, uchar *rp, int n) +{ + lock(&m->lk); + if(m->rp != rp) { + unlock(&m->lk); + return 0; + } + m->rp -= n; + unlock(&m->lk); + return 1; +} + +static int +memTail(Mem *m, uchar *wp, int n) +{ + lock(&m->lk); + if(m->wp != wp) { + unlock(&m->lk); + return 0; + } + m->wp += n; + unlock(&m->lk); + return 1; +} diff --git a/sys/src/liboventi/packet.h b/sys/src/liboventi/packet.h new file mode 100755 index 000000000..55b8eabf6 --- /dev/null +++ b/sys/src/liboventi/packet.h @@ -0,0 +1,56 @@ +typedef struct Packet Packet; +typedef struct Mem Mem; +typedef struct Frag Frag; + +enum { + BigMemSize = MaxFragSize, + SmallMemSize = BigMemSize/8, + NLocalFrag = 2, +}; + +/* position to carve out of a Mem */ +enum { + PFront, + PMiddle, + PEnd, +}; + +struct Mem +{ + Lock lk; + int ref; + uchar *bp; + uchar *ep; + uchar *rp; + uchar *wp; + Mem *next; +}; + +enum { + FragLocalFree, + FragLocalAlloc, + FragGlobal, +}; + +struct Frag +{ + int state; + Mem *mem; + uchar *rp; + uchar *wp; + Frag *next; +}; + +struct Packet +{ + int size; + int asize; /* allocated memmory - always greater than size */ + + Packet *next; + + Frag *first; + Frag *last; + + Frag local[NLocalFrag]; +}; + diff --git a/sys/src/liboventi/parsescore.c b/sys/src/liboventi/parsescore.c new file mode 100755 index 000000000..0f02610fd --- /dev/null +++ b/sys/src/liboventi/parsescore.c @@ -0,0 +1,31 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +int +vtParseScore(char *buf, uint n, uchar score[VtScoreSize]) +{ + int i, c; + + memset(score, 0, VtScoreSize); + + if(n != VtScoreSize*2) + return 0; + for(i=0; i<VtScoreSize*2; i++){ + if(buf[i] >= '0' && buf[i] <= '9') + c = buf[i] - '0'; + else if(buf[i] >= 'a' && buf[i] <= 'f') + c = buf[i] - 'a' + 10; + else if(buf[i] >= 'A' && buf[i] <= 'F') + c = buf[i] - 'A' + 10; + else + return 0; + + if((i & 1) == 0) + c <<= 4; + + score[i>>1] |= c; + } + return 1; +} + diff --git a/sys/src/liboventi/plan9-io.c b/sys/src/liboventi/plan9-io.c new file mode 100755 index 000000000..e34a8d375 --- /dev/null +++ b/sys/src/liboventi/plan9-io.c @@ -0,0 +1,146 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +enum { + IdealAlignment = 32, + ChunkSize = 128*1024, +}; + + +void +vtMemFree(void *p) +{ + if(p == 0) + return; + free(p); +} + + +void * +vtMemAlloc(int size) +{ + void *p; + + p = malloc(size); + if(p == 0) + vtFatal("vtMemAlloc: out of memory"); + setmalloctag(p, getcallerpc(&size)); + return p; +} + +void * +vtMemAllocZ(int size) +{ + void *p = vtMemAlloc(size); + memset(p, 0, size); + setmalloctag(p, getcallerpc(&size)); + return p; +} + +void * +vtMemRealloc(void *p, int size) +{ + if(p == nil) + return vtMemAlloc(size); + p = realloc(p, size); + if(p == 0) + vtFatal("vtRealloc: out of memory"); + setrealloctag(p, getcallerpc(&size)); + return p; +} + + +void * +vtMemBrk(int n) +{ + static Lock lk; + static uchar *buf; + static int nbuf; + static int nchunk; + int align, pad; + void *p; + + if(n >= IdealAlignment) + align = IdealAlignment; + else if(n > 8) + align = 8; + else + align = 4; + + lock(&lk); + pad = (align - (uintptr)buf) & (align-1); + if(n + pad > nbuf) { + buf = vtMemAllocZ(ChunkSize); + setmalloctag(buf, getcallerpc(&n)); + nbuf = ChunkSize; + pad = (align - (uintptr)buf) & (align-1); + nchunk++; + } + + assert(n + pad <= nbuf); + + p = buf + pad; + buf += pad + n; + nbuf -= pad + n; + unlock(&lk); + + return p; +} + +void +vtThreadSetName(char *name) +{ + int fd; + char buf[32]; + + sprint(buf, "/proc/%d/args", getpid()); + if((fd = open(buf, OWRITE)) >= 0){ + write(fd, name, strlen(name)); + close(fd); + } +} + +int +vtFdRead(int fd, uchar *buf, int n) +{ + n = read(fd, buf, n); + if(n < 0) { + vtOSError(); + return -1; + } + if(n == 0) { + vtSetError("unexpected EOF"); + return 0; + } + return n; +} + +int +vtFdWrite(int fd, uchar *buf, int n) +{ + int nn; + + nn = write(fd, buf, n); + if(nn < 0) { + vtOSError(); + return 0; + } + if(n != nn) { + vtSetError("truncated write"); + return 0; + } + return 1; +} + +void +vtFdClose(int fd) +{ + close(fd); +} + +char * +vtOSError(void) +{ + return vtSetError("%r"); +} diff --git a/sys/src/liboventi/plan9-sha1.c b/sys/src/liboventi/plan9-sha1.c new file mode 100755 index 000000000..c602efed8 --- /dev/null +++ b/sys/src/liboventi/plan9-sha1.c @@ -0,0 +1,78 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> +#include <libsec.h> + +static void encode(uchar*, u32int*, ulong); +extern void vtSha1Block(u32int *s, uchar *p, ulong len); + +struct VtSha1 +{ + DigestState *s; +}; + +VtSha1 * +vtSha1Alloc(void) +{ + VtSha1 *s; + + s = vtMemAlloc(sizeof(VtSha1)); + vtSha1Init(s); + return s; +} + +void +vtSha1Free(VtSha1 *s) +{ + if(s == nil) + return; + if(s->s != nil) + free(s->s); + vtMemFree(s); +} + +void +vtSha1Init(VtSha1 *s) +{ + s->s = nil; +} + +void +vtSha1Update(VtSha1 *s, uchar *p, int len) +{ + s->s = sha1(p, len, nil, s->s); +} + +void +vtSha1Final(VtSha1 *s, uchar *digest) +{ + sha1(nil, 0, digest, s->s); + s->s = nil; +} + +void +vtSha1(uchar sha1[VtScoreSize], uchar *p, int n) +{ + VtSha1 s; + + vtSha1Init(&s); + vtSha1Update(&s, p, n); + vtSha1Final(&s, sha1); +} + +int +vtSha1Check(uchar score[VtScoreSize], uchar *p, int n) +{ + VtSha1 s; + uchar score2[VtScoreSize]; + + vtSha1Init(&s); + vtSha1Update(&s, p, n); + vtSha1Final(&s, score2); + + if(memcmp(score, score2, VtScoreSize) != 0) { + vtSetError("vtSha1Check failed"); + return 0; + } + return 1; +} diff --git a/sys/src/liboventi/plan9-thread.acid b/sys/src/liboventi/plan9-thread.acid new file mode 100755 index 000000000..f581a9bbf --- /dev/null +++ b/sys/src/liboventi/plan9-thread.acid @@ -0,0 +1,531 @@ +sizeof_1_ = 8; +aggr _1_ +{ + 'U' 0 lo; + 'U' 4 hi; +}; + +defn +_1_(addr) { + complex _1_ addr; + print(" lo ", addr.lo, "\n"); + print(" hi ", addr.hi, "\n"); +}; + +sizeofFPdbleword = 8; +aggr FPdbleword +{ + 'F' 0 x; + { + 'U' 0 lo; + 'U' 4 hi; + }; +}; + +defn +FPdbleword(addr) { + complex FPdbleword addr; + print(" x ", addr.x, "\n"); + print("_1_ {\n"); + _1_(addr+0); + print("}\n"); +}; + +UTFmax = 3; +Runesync = 128; +Runeself = 128; +Runeerror = 65533; +sizeofFmt = 48; +aggr Fmt +{ + 'b' 0 runes; + 'X' 4 start; + 'X' 8 to; + 'X' 12 stop; + 'X' 16 flush; + 'X' 20 farg; + 'D' 24 nfmt; + 'X' 28 args; + 'D' 32 r; + 'D' 36 width; + 'D' 40 prec; + 'U' 44 flags; +}; + +defn +Fmt(addr) { + complex Fmt addr; + print(" runes ", addr.runes, "\n"); + print(" start ", addr.start\X, "\n"); + print(" to ", addr.to\X, "\n"); + print(" stop ", addr.stop\X, "\n"); + print(" flush ", addr.flush\X, "\n"); + print(" farg ", addr.farg\X, "\n"); + print(" nfmt ", addr.nfmt, "\n"); + print(" args ", addr.args\X, "\n"); + print(" r ", addr.r, "\n"); + print(" width ", addr.width, "\n"); + print(" prec ", addr.prec, "\n"); + print(" flags ", addr.flags, "\n"); +}; + +FmtWidth = 1; +FmtLeft = 2; +FmtPrec = 4; +FmtSharp = 8; +FmtSpace = 16; +FmtSign = 32; +FmtZero = 64; +FmtUnsigned = 128; +FmtShort = 256; +FmtLong = 512; +FmtVLong = 1024; +FmtComma = 2048; +FmtByte = 4096; +FmtFlag = 8192; +sizeofTm = 40; +aggr Tm +{ + 'D' 0 sec; + 'D' 4 min; + 'D' 8 hour; + 'D' 12 mday; + 'D' 16 mon; + 'D' 20 year; + 'D' 24 wday; + 'D' 28 yday; + 'a' 32 zone; + 'D' 36 tzoff; +}; + +defn +Tm(addr) { + complex Tm addr; + print(" sec ", addr.sec, "\n"); + print(" min ", addr.min, "\n"); + print(" hour ", addr.hour, "\n"); + print(" mday ", addr.mday, "\n"); + print(" mon ", addr.mon, "\n"); + print(" year ", addr.year, "\n"); + print(" wday ", addr.wday, "\n"); + print(" yday ", addr.yday, "\n"); + print(" zone ", addr.zone, "\n"); + print(" tzoff ", addr.tzoff, "\n"); +}; + +PNPROC = 1; +PNGROUP = 2; +Profoff = 0; +Profuser = 1; +Profkernel = 2; +Proftime = 3; +Profsample = 4; +sizeofLock = 4; +aggr Lock +{ + 'D' 0 val; +}; + +defn +Lock(addr) { + complex Lock addr; + print(" val ", addr.val, "\n"); +}; + +sizeofQLp = 12; +aggr QLp +{ + 'D' 0 inuse; + 'A' QLp 4 next; + 'C' 8 state; +}; + +defn +QLp(addr) { + complex QLp addr; + print(" inuse ", addr.inuse, "\n"); + print(" next ", addr.next\X, "\n"); + print(" state ", addr.state, "\n"); +}; + +sizeofQLock = 16; +aggr QLock +{ + Lock 0 lock; + 'D' 4 locked; + 'A' QLp 8 $head; + 'A' QLp 12 $tail; +}; + +defn +QLock(addr) { + complex QLock addr; + print("Lock lock {\n"); + Lock(addr.lock); + print("}\n"); + print(" locked ", addr.locked, "\n"); + print(" $head ", addr.$head\X, "\n"); + print(" $tail ", addr.$tail\X, "\n"); +}; + +sizeofRWLock = 20; +aggr RWLock +{ + Lock 0 lock; + 'D' 4 readers; + 'D' 8 writer; + 'A' QLp 12 $head; + 'A' QLp 16 $tail; +}; + +defn +RWLock(addr) { + complex RWLock addr; + print("Lock lock {\n"); + Lock(addr.lock); + print("}\n"); + print(" readers ", addr.readers, "\n"); + print(" writer ", addr.writer, "\n"); + print(" $head ", addr.$head\X, "\n"); + print(" $tail ", addr.$tail\X, "\n"); +}; + +sizeofRendez = 12; +aggr Rendez +{ + 'A' QLock 0 l; + 'A' QLp 4 $head; + 'A' QLp 8 $tail; +}; + +defn +Rendez(addr) { + complex Rendez addr; + print(" l ", addr.l\X, "\n"); + print(" $head ", addr.$head\X, "\n"); + print(" $tail ", addr.$tail\X, "\n"); +}; + +sizeofNetConnInfo = 36; +aggr NetConnInfo +{ + 'X' 0 dir; + 'X' 4 root; + 'X' 8 spec; + 'X' 12 lsys; + 'X' 16 lserv; + 'X' 20 rsys; + 'X' 24 rserv; + 'X' 28 laddr; + 'X' 32 raddr; +}; + +defn +NetConnInfo(addr) { + complex NetConnInfo addr; + print(" dir ", addr.dir\X, "\n"); + print(" root ", addr.root\X, "\n"); + print(" spec ", addr.spec\X, "\n"); + print(" lsys ", addr.lsys\X, "\n"); + print(" lserv ", addr.lserv\X, "\n"); + print(" rsys ", addr.rsys\X, "\n"); + print(" rserv ", addr.rserv\X, "\n"); + print(" laddr ", addr.laddr\X, "\n"); + print(" raddr ", addr.raddr\X, "\n"); +}; + +RFNAMEG = 1; +RFENVG = 2; +RFFDG = 4; +RFNOTEG = 8; +RFPROC = 16; +RFMEM = 32; +RFNOWAIT = 64; +RFCNAMEG = 1024; +RFCENVG = 2048; +RFCFDG = 4096; +RFREND = 8192; +RFNOMNT = 16384; +sizeofQid = 16; +aggr Qid +{ + 'W' 0 path; + 'U' 8 vers; + 'b' 12 type; +}; + +defn +Qid(addr) { + complex Qid addr; + print(" path ", addr.path, "\n"); + print(" vers ", addr.vers, "\n"); + print(" type ", addr.type, "\n"); +}; + +sizeofDir = 60; +aggr Dir +{ + 'u' 0 type; + 'U' 4 dev; + Qid 8 qid; + 'U' 24 mode; + 'U' 28 atime; + 'U' 32 mtime; + 'V' 36 length; + 'X' 44 name; + 'X' 48 uid; + 'X' 52 gid; + 'X' 56 muid; +}; + +defn +Dir(addr) { + complex Dir addr; + print(" type ", addr.type, "\n"); + print(" dev ", addr.dev, "\n"); + print("Qid qid {\n"); + Qid(addr.qid); + print("}\n"); + print(" mode ", addr.mode, "\n"); + print(" atime ", addr.atime, "\n"); + print(" mtime ", addr.mtime, "\n"); + print(" length ", addr.length, "\n"); + print(" name ", addr.name\X, "\n"); + print(" uid ", addr.uid\X, "\n"); + print(" gid ", addr.gid\X, "\n"); + print(" muid ", addr.muid\X, "\n"); +}; + +sizeofWaitmsg = 20; +aggr Waitmsg +{ + 'D' 0 pid; + 'a' 4 time; + 'X' 16 msg; +}; + +defn +Waitmsg(addr) { + complex Waitmsg addr; + print(" pid ", addr.pid, "\n"); + print(" time ", addr.time, "\n"); + print(" msg ", addr.msg\X, "\n"); +}; + +sizeofIOchunk = 8; +aggr IOchunk +{ + 'X' 0 addr; + 'U' 4 len; +}; + +defn +IOchunk(addr) { + complex IOchunk addr; + print(" addr ", addr.addr\X, "\n"); + print(" len ", addr.len, "\n"); +}; + +VtScoreSize = 20; +VtMaxLumpSize = 57344; +VtPointerDepth = 7; +VtEntrySize = 40; +VtRootSize = 300; +VtMaxStringSize = 1000; +VtAuthSize = 1024; +MaxFragSize = 9216; +VtMaxFileSize = 281474976710655; +VtRootVersion = 2; +VtCryptoStrengthNone = 0; +VtCryptoStrengthAuth = 1; +VtCryptoStrengthWeak = 2; +VtCryptoStrengthStrong = 3; +VtCryptoNone = 0; +VtCryptoSSL3 = 1; +VtCryptoTLS1 = 2; +VtCryptoMax = 3; +VtCodecNone = 0; +VtCodecDeflate = 1; +VtCodecThwack = 2; +VtCodecMax = 3; +VtErrType = 0; +VtRootType = 1; +VtDirType = 2; +VtPointerType0 = 3; +VtPointerType1 = 4; +VtPointerType2 = 5; +VtPointerType3 = 6; +VtPointerType4 = 7; +VtPointerType5 = 8; +VtPointerType6 = 9; +VtPointerType7 = 10; +VtPointerType8 = 11; +VtPointerType9 = 12; +VtDataType = 13; +VtMaxType = 14; +VtEntryActive = 1; +VtEntryDir = 2; +VtEntryDepthShift = 2; +VtEntryDepthMask = 28; +VtEntryLocal = 32; +VtEntryNoArchive = 64; +sizeofVtRoot = 300; +aggr VtRoot +{ + 'u' 0 version; + 'a' 2 name; + 'a' 130 type; + 'a' 258 score; + 'u' 278 blockSize; + 'a' 280 prev; +}; + +defn +VtRoot(addr) { + complex VtRoot addr; + print(" version ", addr.version, "\n"); + print(" name ", addr.name, "\n"); + print(" type ", addr.type, "\n"); + print(" score ", addr.score, "\n"); + print(" blockSize ", addr.blockSize, "\n"); + print(" prev ", addr.prev, "\n"); +}; + +sizeofVtEntry = 40; +aggr VtEntry +{ + 'U' 0 gen; + 'u' 4 psize; + 'u' 6 dsize; + 'b' 8 depth; + 'b' 9 flags; + 'W' 12 size; + 'a' 20 score; +}; + +defn +VtEntry(addr) { + complex VtEntry addr; + print(" gen ", addr.gen, "\n"); + print(" psize ", addr.psize, "\n"); + print(" dsize ", addr.dsize, "\n"); + print(" depth ", addr.depth, "\n"); + print(" flags ", addr.flags, "\n"); + print(" size ", addr.size, "\n"); + print(" score ", addr.score, "\n"); +}; + +sizeofVtServerVtbl = 16; +aggr VtServerVtbl +{ + 'X' 0 read; + 'X' 4 write; + 'X' 8 closing; + 'X' 12 sync; +}; + +defn +VtServerVtbl(addr) { + complex VtServerVtbl addr; + print(" read ", addr.read\X, "\n"); + print(" write ", addr.write\X, "\n"); + print(" closing ", addr.closing\X, "\n"); + print(" sync ", addr.sync\X, "\n"); +}; + +VtVersion01 = 1; +VtVersion02 = 2; +QueuingW = 0; +QueuingR = 1; +sizeofThread = 20; +aggr Thread +{ + 'D' 0 pid; + 'D' 4 ref; + 'X' 8 error; + 'D' 12 state; + 'A' Thread 16 next; +}; + +defn +Thread(addr) { + complex Thread addr; + print(" pid ", addr.pid, "\n"); + print(" ref ", addr.ref, "\n"); + print(" error ", addr.error\X, "\n"); + print(" state ", addr.state, "\n"); + print(" next ", addr.next\X, "\n"); +}; + +sizeofVtLock = 20; +aggr VtLock +{ + Lock 0 lk; + 'A' Thread 4 writer; + 'D' 8 readers; + 'A' Thread 12 qfirst; + 'A' Thread 16 qlast; +}; + +defn +VtLock(addr) { + complex VtLock addr; + print("Lock lk {\n"); + Lock(addr.lk); + print("}\n"); + print(" writer ", addr.writer\X, "\n"); + print(" readers ", addr.readers, "\n"); + print(" qfirst ", addr.qfirst\X, "\n"); + print(" qlast ", addr.qlast\X, "\n"); +}; + +sizeofVtRendez = 12; +aggr VtRendez +{ + 'A' VtLock 0 lk; + 'A' Thread 4 wfirst; + 'A' Thread 8 wlast; +}; + +defn +VtRendez(addr) { + complex VtRendez addr; + print(" lk ", addr.lk\X, "\n"); + print(" wfirst ", addr.wfirst\X, "\n"); + print(" wlast ", addr.wlast\X, "\n"); +}; + +ERROR = 0; +complex Thread vtRock; +complex Thread vtAttach:p; +complex Lock lk$4; +complex Thread vtDetach:p; +complex Thread vtSetError:p; +complex Lock lk$12; +complex VtLock vtLockFree:p; +complex VtLock vtRendezAlloc:p; +complex VtRendez vtRendezAlloc:q; +complex VtRendez vtRendezFree:q; +complex VtLock vtCanLock:p; +complex Thread vtCanLock:t; +complex VtLock vtLock:p; +complex Thread vtLock:t; +complex VtLock vtCanRLock:p; +complex VtLock vtRLock:p; +complex Thread vtRLock:t; +complex VtLock vtUnlock:p; +complex Thread vtUnlock:t; +complex Thread vtUnlock:tt; +complex VtLock vtRUnlock:p; +complex Thread vtRUnlock:t; +complex VtRendez vtSleep:q; +complex Thread vtSleep:s; +complex Thread vtSleep:t; +complex Thread vtSleep:tt; +complex VtLock vtSleep:p; +complex VtRendez vtWakeup:q; +complex Thread vtWakeup:t; +complex VtLock vtWakeup:p; +complex VtRendez vtWakeupAll:q; +complex Thread threadSleep:t; +complex Thread threadWakeup:t; diff --git a/sys/src/liboventi/plan9-thread.c b/sys/src/liboventi/plan9-thread.c new file mode 100755 index 000000000..8b158d519 --- /dev/null +++ b/sys/src/liboventi/plan9-thread.c @@ -0,0 +1,468 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +enum +{ + QueuingW, /* queuing for write lock */ + QueuingR, /* queuing for read lock */ +}; + + +typedef struct Thread Thread; + +struct Thread { + int pid; + int ref; + char *error; + int state; + Thread *next; +}; + +struct VtLock { + Lock lk; + Thread *writer; /* thread writering write lock */ + int readers; /* number writering read lock */ + Thread *qfirst; + Thread *qlast; +}; + +struct VtRendez { + VtLock *lk; + Thread *wfirst; + Thread *wlast; +}; + +enum { + ERROR = 0, +}; + +static Thread **vtRock; + +static void vtThreadInit(void); +static void threadSleep(Thread*); +static void threadWakeup(Thread*); + +int +vtThread(void (*f)(void*), void *rock) +{ + int tid; + + tid = rfork(RFNOWAIT|RFMEM|RFPROC); + switch(tid){ + case -1: + vtOSError(); + return -1; + case 0: + break; + default: + return tid; + } + vtAttach(); + (*f)(rock); + vtDetach(); + _exits(0); + return 0; +} + +static Thread * +threadLookup(void) +{ + return *vtRock; +} + +void +vtAttach(void) +{ + int pid; + Thread *p; + static int init; + static Lock lk; + + lock(&lk); + if(!init) { + rfork(RFREND); + vtThreadInit(); + init = 1; + } + unlock(&lk); + + pid = getpid(); + p = *vtRock; + if(p != nil && p->pid == pid) { + p->ref++; + return; + } + p = vtMemAllocZ(sizeof(Thread)); + p->ref = 1; + p->pid = pid; + *vtRock = p; +} + +void +vtDetach(void) +{ + Thread *p; + + p = *vtRock; + assert(p != nil); + p->ref--; + if(p->ref == 0) { + vtMemFree(p->error); + vtMemFree(p); + *vtRock = nil; + } +} + +char * +vtGetError(void) +{ + char *s; + + if(ERROR) + fprint(2, "vtGetError: %s\n", threadLookup()->error); + s = threadLookup()->error; + if(s == nil) + return "unknown error"; + return s; +} + +char* +vtSetError(char* fmt, ...) +{ + Thread *p; + char *s; + va_list args; + + p = threadLookup(); + + va_start(args, fmt); + s = vsmprint(fmt, args); + vtMemFree(p->error); + p->error = s; + va_end(args); + if(ERROR) + fprint(2, "vtSetError: %s\n", p->error); + werrstr("%s", p->error); + return p->error; +} + +static void +vtThreadInit(void) +{ + static Lock lk; + + lock(&lk); + if(vtRock != nil) { + unlock(&lk); + return; + } + vtRock = privalloc(); + if(vtRock == nil) + vtFatal("can't allocate thread-private storage"); + unlock(&lk); +} + +VtLock* +vtLockAlloc(void) +{ + return vtMemAllocZ(sizeof(VtLock)); +} + +/* + * RSC: I think the test is backward. Let's see who uses it. + * +void +vtLockInit(VtLock **p) +{ + static Lock lk; + + lock(&lk); + if(*p != nil) + *p = vtLockAlloc(); + unlock(&lk); +} + */ + +void +vtLockFree(VtLock *p) +{ + if(p == nil) + return; + assert(p->writer == nil); + assert(p->readers == 0); + assert(p->qfirst == nil); + vtMemFree(p); +} + +VtRendez* +vtRendezAlloc(VtLock *p) +{ + VtRendez *q; + + q = vtMemAllocZ(sizeof(VtRendez)); + q->lk = p; + setmalloctag(q, getcallerpc(&p)); + return q; +} + +void +vtRendezFree(VtRendez *q) +{ + if(q == nil) + return; + assert(q->wfirst == nil); + vtMemFree(q); +} + +int +vtCanLock(VtLock *p) +{ + Thread *t; + + lock(&p->lk); + t = *vtRock; + if(p->writer == nil && p->readers == 0) { + p->writer = t; + unlock(&p->lk); + return 1; + } + unlock(&p->lk); + return 0; +} + + +void +vtLock(VtLock *p) +{ + Thread *t; + + lock(&p->lk); + t = *vtRock; + if(p->writer == nil && p->readers == 0) { + p->writer = t; + unlock(&p->lk); + return; + } + + /* + * venti currently contains code that assume locks can be passed between threads :-( + * assert(p->writer != t); + */ + + if(p->qfirst == nil) + p->qfirst = t; + else + p->qlast->next = t; + p->qlast = t; + t->next = nil; + t->state = QueuingW; + unlock(&p->lk); + + threadSleep(t); + assert(p->writer == t && p->readers == 0); +} + +int +vtCanRLock(VtLock *p) +{ + lock(&p->lk); + if(p->writer == nil && p->qfirst == nil) { + p->readers++; + unlock(&p->lk); + return 1; + } + unlock(&p->lk); + return 0; +} + +void +vtRLock(VtLock *p) +{ + Thread *t; + + lock(&p->lk); + t = *vtRock; + if(p->writer == nil && p->qfirst == nil) { + p->readers++; + unlock(&p->lk); + return; + } + + /* + * venti currently contains code that assumes locks can be passed between threads + * assert(p->writer != t); + */ + if(p->qfirst == nil) + p->qfirst = t; + else + p->qlast->next = t; + p->qlast = t; + t->next = nil; + t->state = QueuingR; + unlock(&p->lk); + + threadSleep(t); + assert(p->writer == nil && p->readers > 0); +} + +void +vtUnlock(VtLock *p) +{ + Thread *t, *tt; + + lock(&p->lk); + /* + * venti currently has code that assumes lock can be passed between threads :-) + * assert(p->writer == *vtRock); + */ + assert(p->writer != nil); + assert(p->readers == 0); + t = p->qfirst; + if(t == nil) { + p->writer = nil; + unlock(&p->lk); + return; + } + if(t->state == QueuingW) { + p->qfirst = t->next; + p->writer = t; + unlock(&p->lk); + threadWakeup(t); + return; + } + + p->writer = nil; + while(t != nil && t->state == QueuingR) { + tt = t; + t = t->next; + p->readers++; + threadWakeup(tt); + } + p->qfirst = t; + unlock(&p->lk); +} + +void +vtRUnlock(VtLock *p) +{ + Thread *t; + + lock(&p->lk); + assert(p->writer == nil && p->readers > 0); + p->readers--; + t = p->qfirst; + if(p->readers > 0 || t == nil) { + unlock(&p->lk); + return; + } + assert(t->state == QueuingW); + + p->qfirst = t->next; + p->writer = t; + unlock(&p->lk); + + threadWakeup(t); +} + +int +vtSleep(VtRendez *q) +{ + Thread *s, *t, *tt; + VtLock *p; + + p = q->lk; + lock(&p->lk); + s = *vtRock; + /* + * venti currently contains code that assume locks can be passed between threads :-( + * assert(p->writer != s); + */ + assert(p->writer != nil); + assert(p->readers == 0); + t = p->qfirst; + if(t == nil) { + p->writer = nil; + } else if(t->state == QueuingW) { + p->qfirst = t->next; + p->writer = t; + threadWakeup(t); + } else { + p->writer = nil; + while(t != nil && t->state == QueuingR) { + tt = t; + t = t->next; + p->readers++; + threadWakeup(tt); + } + } + + if(q->wfirst == nil) + q->wfirst = s; + else + q->wlast->next = s; + q->wlast = s; + s->next = nil; + unlock(&p->lk); + + threadSleep(s); + assert(p->writer == s); + return 1; +} + +int +vtWakeup(VtRendez *q) +{ + Thread *t; + VtLock *p; + + /* + * take off wait and put on front of queue + * put on front so guys that have been waiting will not get starved + */ + p = q->lk; + lock(&p->lk); + /* + * venti currently has code that assumes lock can be passed between threads :-) + * assert(p->writer == *vtRock); + */ + assert(p->writer != nil); + t = q->wfirst; + if(t == nil) { + unlock(&p->lk); + return 0; + } + q->wfirst = t->next; + if(p->qfirst == nil) + p->qlast = t; + t->next = p->qfirst; + p->qfirst = t; + t->state = QueuingW; + unlock(&p->lk); + + return 1; +} + +int +vtWakeupAll(VtRendez *q) +{ + int i; + + for(i=0; vtWakeup(q); i++) + ; + return i; +} + +static void +threadSleep(Thread *t) +{ + if(rendezvous(t, (void*)0x22bbdfd6) != (void*)0x44391f14) + sysfatal("threadSleep: rendezvous failed: %r"); +} + +static void +threadWakeup(Thread *t) +{ + if(rendezvous(t, (void*)0x44391f14) != (void*)0x22bbdfd6) + sysfatal("threadWakeup: rendezvous failed: %r"); +} diff --git a/sys/src/liboventi/readfully.c b/sys/src/liboventi/readfully.c new file mode 100755 index 000000000..356a24123 --- /dev/null +++ b/sys/src/liboventi/readfully.c @@ -0,0 +1,19 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> +#include "session.h" + +int +vtFdReadFully(int fd, uchar *p, int n) +{ + int nn; + + while(n > 0) { + nn = vtFdRead(fd, p, n); + if(nn <= 0) + return 0; + n -= nn; + p += nn; + } + return 1; +} diff --git a/sys/src/liboventi/rpc.c b/sys/src/liboventi/rpc.c new file mode 100755 index 000000000..b7153dc3b --- /dev/null +++ b/sys/src/liboventi/rpc.c @@ -0,0 +1,471 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> +#include "session.h" + +struct { + int version; + char *s; +} vtVersions[] = { + VtVersion02, "02", + 0, 0, +}; + +static char EBigString[] = "string too long"; +static char EBigPacket[] = "packet too long"; +static char ENullString[] = "missing string"; +static char EBadVersion[] = "bad format in version string"; + +static Packet *vtRPC(VtSession *z, int op, Packet *p); + + +VtSession * +vtAlloc(void) +{ + VtSession *z; + + z = vtMemAllocZ(sizeof(VtSession)); + z->lk = vtLockAlloc(); +// z->inHash = vtSha1Alloc(); + z->inLock = vtLockAlloc(); + z->part = packetAlloc(); +// z->outHash = vtSha1Alloc(); + z->outLock = vtLockAlloc(); + z->fd = -1; + z->uid = vtStrDup("anonymous"); + z->sid = vtStrDup("anonymous"); + return z; +} + +void +vtReset(VtSession *z) +{ + vtLock(z->lk); + z->cstate = VtStateAlloc; + if(z->fd >= 0){ + vtFdClose(z->fd); + z->fd = -1; + } + vtUnlock(z->lk); +} + +int +vtConnected(VtSession *z) +{ + return z->cstate == VtStateConnected; +} + +void +vtDisconnect(VtSession *z, int error) +{ + Packet *p; + uchar *b; + +vtDebug(z, "vtDisconnect\n"); + vtLock(z->lk); + if(z->cstate == VtStateConnected && !error && z->vtbl == nil) { + /* clean shutdown */ + p = packetAlloc(); + b = packetHeader(p, 2); + b[0] = VtQGoodbye; + b[1] = 0; + vtSendPacket(z, p); + } + if(z->fd >= 0) + vtFdClose(z->fd); + z->fd = -1; + z->cstate = VtStateClosed; + vtUnlock(z->lk); +} + +void +vtClose(VtSession *z) +{ + vtDisconnect(z, 0); +} + +void +vtFree(VtSession *z) +{ + if(z == nil) + return; + vtLockFree(z->lk); + vtSha1Free(z->inHash); + vtLockFree(z->inLock); + packetFree(z->part); + vtSha1Free(z->outHash); + vtLockFree(z->outLock); + vtMemFree(z->uid); + vtMemFree(z->sid); + vtMemFree(z->vtbl); + + memset(z, 0, sizeof(VtSession)); + z->fd = -1; + + vtMemFree(z); +} + +char * +vtGetUid(VtSession *s) +{ + return s->uid; +} + +char * +vtGetSid(VtSession *z) +{ + return z->sid; +} + +int +vtSetDebug(VtSession *z, int debug) +{ + int old; + vtLock(z->lk); + old = z->debug; + z->debug = debug; + vtUnlock(z->lk); + return old; +} + +int +vtSetFd(VtSession *z, int fd) +{ + vtLock(z->lk); + if(z->cstate != VtStateAlloc) { + vtSetError("bad state"); + vtUnlock(z->lk); + return 0; + } + if(z->fd >= 0) + vtFdClose(z->fd); + z->fd = fd; + vtUnlock(z->lk); + return 1; +} + +int +vtGetFd(VtSession *z) +{ + return z->fd; +} + +int +vtSetCryptoStrength(VtSession *z, int c) +{ + if(z->cstate != VtStateAlloc) { + vtSetError("bad state"); + return 0; + } + if(c != VtCryptoStrengthNone) { + vtSetError("not supported yet"); + return 0; + } + return 1; +} + +int +vtGetCryptoStrength(VtSession *s) +{ + return s->cryptoStrength; +} + +int +vtSetCompression(VtSession *z, int fd) +{ + vtLock(z->lk); + if(z->cstate != VtStateAlloc) { + vtSetError("bad state"); + vtUnlock(z->lk); + return 0; + } + z->fd = fd; + vtUnlock(z->lk); + return 1; +} + +int +vtGetCompression(VtSession *s) +{ + return s->compression; +} + +int +vtGetCrypto(VtSession *s) +{ + return s->crypto; +} + +int +vtGetCodec(VtSession *s) +{ + return s->codec; +} + +char * +vtGetVersion(VtSession *z) +{ + int v, i; + + v = z->version; + if(v == 0) + return "unknown"; + for(i=0; vtVersions[i].version; i++) + if(vtVersions[i].version == v) + return vtVersions[i].s; + assert(0); + return 0; +} + +/* hold z->inLock */ +static int +vtVersionRead(VtSession *z, char *prefix, int *ret) +{ + char c; + char buf[VtMaxStringSize]; + char *q, *p, *pp; + int i; + + q = prefix; + p = buf; + for(;;) { + if(p >= buf + sizeof(buf)) { + vtSetError(EBadVersion); + return 0; + } + if(!vtFdReadFully(z->fd, (uchar*)&c, 1)) + return 0; + if(z->inHash) + vtSha1Update(z->inHash, (uchar*)&c, 1); + if(c == '\n') { + *p = 0; + break; + } + if(c < ' ' || *q && c != *q) { + vtSetError(EBadVersion); + return 0; + } + *p++ = c; + if(*q) + q++; + } + + vtDebug(z, "version string in: %s\n", buf); + + p = buf + strlen(prefix); + for(;;) { + for(pp=p; *pp && *pp != ':' && *pp != '-'; pp++) + ; + for(i=0; vtVersions[i].version; i++) { + if(strlen(vtVersions[i].s) != pp-p) + continue; + if(memcmp(vtVersions[i].s, p, pp-p) == 0) { + *ret = vtVersions[i].version; + return 1; + } + } + p = pp; + if(*p != ':') + return 0; + p++; + } +} + +Packet* +vtRecvPacket(VtSession *z) +{ + uchar buf[10], *b; + int n; + Packet *p; + int size, len; + + if(z->cstate != VtStateConnected) { + vtSetError("session not connected"); + return 0; + } + + vtLock(z->inLock); + p = z->part; + /* get enough for head size */ + size = packetSize(p); + while(size < 2) { + b = packetTrailer(p, MaxFragSize); + assert(b != nil); + n = vtFdRead(z->fd, b, MaxFragSize); + if(n <= 0) + goto Err; + size += n; + packetTrim(p, 0, size); + } + + if(!packetConsume(p, buf, 2)) + goto Err; + len = (buf[0] << 8) | buf[1]; + size -= 2; + + while(size < len) { + n = len - size; + if(n > MaxFragSize) + n = MaxFragSize; + b = packetTrailer(p, n); + if(!vtFdReadFully(z->fd, b, n)) + goto Err; + size += n; + } + p = packetSplit(p, len); + vtUnlock(z->inLock); + return p; +Err: + vtUnlock(z->inLock); + return nil; +} + +int +vtSendPacket(VtSession *z, Packet *p) +{ + IOchunk ioc; + int n; + uchar buf[2]; + + /* add framing */ + n = packetSize(p); + if(n >= (1<<16)) { + vtSetError(EBigPacket); + packetFree(p); + return 0; + } + buf[0] = n>>8; + buf[1] = n; + packetPrefix(p, buf, 2); + + for(;;) { + n = packetFragments(p, &ioc, 1, 0); + if(n == 0) + break; + if(!vtFdWrite(z->fd, ioc.addr, ioc.len)) { + packetFree(p); + return 0; + } + packetConsume(p, nil, n); + } + packetFree(p); + return 1; +} + + +int +vtGetString(Packet *p, char **ret) +{ + uchar buf[2]; + int n; + char *s; + + if(!packetConsume(p, buf, 2)) + return 0; + n = (buf[0]<<8) + buf[1]; + if(n > VtMaxStringSize) { + vtSetError(EBigString); + return 0; + } + s = vtMemAlloc(n+1); + setmalloctag(s, getcallerpc(&p)); + if(!packetConsume(p, (uchar*)s, n)) { + vtMemFree(s); + return 0; + } + s[n] = 0; + *ret = s; + return 1; +} + +int +vtAddString(Packet *p, char *s) +{ + uchar buf[2]; + int n; + + if(s == nil) { + vtSetError(ENullString); + return 0; + } + n = strlen(s); + if(n > VtMaxStringSize) { + vtSetError(EBigString); + return 0; + } + buf[0] = n>>8; + buf[1] = n; + packetAppend(p, buf, 2); + packetAppend(p, (uchar*)s, n); + return 1; +} + +int +vtConnect(VtSession *z, char *password) +{ + char buf[VtMaxStringSize], *p, *ep, *prefix; + int i; + + USED(password); + vtLock(z->lk); + if(z->cstate != VtStateAlloc) { + vtSetError("bad session state"); + vtUnlock(z->lk); + return 0; + } + if(z->fd < 0){ + vtSetError("%s", z->fderror); + vtUnlock(z->lk); + return 0; + } + + /* be a little anal */ + vtLock(z->inLock); + vtLock(z->outLock); + + prefix = "venti-"; + p = buf; + ep = buf + sizeof(buf); + p = seprint(p, ep, "%s", prefix); + p += strlen(p); + for(i=0; vtVersions[i].version; i++) { + if(i != 0) + *p++ = ':'; + p = seprint(p, ep, "%s", vtVersions[i].s); + } + p = seprint(p, ep, "-libventi\n"); + assert(p-buf < sizeof(buf)); + if(z->outHash) + vtSha1Update(z->outHash, (uchar*)buf, p-buf); + if(!vtFdWrite(z->fd, (uchar*)buf, p-buf)) + goto Err; + + vtDebug(z, "version string out: %s", buf); + + if(!vtVersionRead(z, prefix, &z->version)) + goto Err; + + vtDebug(z, "version = %d: %s\n", z->version, vtGetVersion(z)); + + vtUnlock(z->inLock); + vtUnlock(z->outLock); + z->cstate = VtStateConnected; + vtUnlock(z->lk); + + if(z->vtbl) + return 1; + + if(!vtHello(z)) + goto Err; + return 1; +Err: + if(z->fd >= 0) + vtFdClose(z->fd); + z->fd = -1; + vtUnlock(z->inLock); + vtUnlock(z->outLock); + z->cstate = VtStateClosed; + vtUnlock(z->lk); + return 0; +} + diff --git a/sys/src/liboventi/scorefmt.c b/sys/src/liboventi/scorefmt.c new file mode 100755 index 000000000..4e9eb12ba --- /dev/null +++ b/sys/src/liboventi/scorefmt.c @@ -0,0 +1,20 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +int +vtScoreFmt(Fmt *f) +{ + uchar *v; + int i; + + v = va_arg(f->args, uchar*); + if(v == nil){ + fmtprint(f, "*"); + }else{ + for(i = 0; i < VtScoreSize; i++) + fmtprint(f, "%2.2ux", v[i]); + } + + return 0; +} diff --git a/sys/src/liboventi/server.c b/sys/src/liboventi/server.c new file mode 100755 index 000000000..2bc2beddd --- /dev/null +++ b/sys/src/liboventi/server.c @@ -0,0 +1,265 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> +#include "session.h" + +static char EAuthState[] = "bad authentication state"; +static char ENotServer[] = "not a server session"; +static char EVersion[] = "incorrect version number"; +static char EProtocolBotch[] = "venti protocol botch"; + +VtSession * +vtServerAlloc(VtServerVtbl *vtbl) +{ + VtSession *z = vtAlloc(); + z->vtbl = vtMemAlloc(sizeof(VtServerVtbl)); + setmalloctag(z->vtbl, getcallerpc(&vtbl)); + *z->vtbl = *vtbl; + return z; +} + +static int +srvHello(VtSession *z, char *version, char *uid, int , uchar *, int , uchar *, int ) +{ + vtLock(z->lk); + if(z->auth.state != VtAuthHello) { + vtSetError(EAuthState); + goto Err; + } + if(strcmp(version, vtGetVersion(z)) != 0) { + vtSetError(EVersion); + goto Err; + } + vtMemFree(z->uid); + z->uid = vtStrDup(uid); + z->auth.state = VtAuthOK; + vtUnlock(z->lk); + return 1; +Err: + z->auth.state = VtAuthFailed; + vtUnlock(z->lk); + return 0; +} + + +static int +dispatchHello(VtSession *z, Packet **pkt) +{ + char *version, *uid; + uchar *crypto, *codec; + uchar buf[10]; + int ncrypto, ncodec, cryptoStrength; + int ret; + Packet *p; + + p = *pkt; + + version = nil; + uid = nil; + crypto = nil; + codec = nil; + + ret = 0; + if(!vtGetString(p, &version)) + goto Err; + if(!vtGetString(p, &uid)) + goto Err; + if(!packetConsume(p, buf, 2)) + goto Err; + cryptoStrength = buf[0]; + ncrypto = buf[1]; + crypto = vtMemAlloc(ncrypto); + if(!packetConsume(p, crypto, ncrypto)) + goto Err; + + if(!packetConsume(p, buf, 1)) + goto Err; + ncodec = buf[0]; + codec = vtMemAlloc(ncodec); + if(!packetConsume(p, codec, ncodec)) + goto Err; + + if(packetSize(p) != 0) { + vtSetError(EProtocolBotch); + goto Err; + } + if(!srvHello(z, version, uid, cryptoStrength, crypto, ncrypto, codec, ncodec)) { + packetFree(p); + *pkt = nil; + } else { + if(!vtAddString(p, vtGetSid(z))) + goto Err; + buf[0] = vtGetCrypto(z); + buf[1] = vtGetCodec(z); + packetAppend(p, buf, 2); + } + ret = 1; +Err: + vtMemFree(version); + vtMemFree(uid); + vtMemFree(crypto); + vtMemFree(codec); + return ret; +} + +static int +dispatchRead(VtSession *z, Packet **pkt) +{ + Packet *p; + int type, n; + uchar score[VtScoreSize], buf[4]; + + p = *pkt; + if(!packetConsume(p, score, VtScoreSize)) + return 0; + if(!packetConsume(p, buf, 4)) + return 0; + type = buf[0]; + n = (buf[2]<<8) | buf[3]; + if(packetSize(p) != 0) { + vtSetError(EProtocolBotch); + return 0; + } + packetFree(p); + *pkt = (*z->vtbl->read)(z, score, type, n); + return 1; +} + +static int +dispatchWrite(VtSession *z, Packet **pkt) +{ + Packet *p; + int type; + uchar score[VtScoreSize], buf[4]; + + p = *pkt; + if(!packetConsume(p, buf, 4)) + return 0; + type = buf[0]; + if(!(z->vtbl->write)(z, score, type, p)) { + *pkt = 0; + } else { + *pkt = packetAlloc(); + packetAppend(*pkt, score, VtScoreSize); + } + return 1; +} + +static int +dispatchSync(VtSession *z, Packet **pkt) +{ + (z->vtbl->sync)(z); + if(packetSize(*pkt) != 0) { + vtSetError(EProtocolBotch); + return 0; + } + return 1; +} + +int +vtExport(VtSession *z) +{ + Packet *p; + uchar buf[10], *hdr; + int op, tid, clean; + + if(z->vtbl == nil) { + vtSetError(ENotServer); + return 0; + } + + /* fork off slave */ + switch(rfork(RFNOWAIT|RFMEM|RFPROC)){ + case -1: + vtOSError(); + return 0; + case 0: + break; + default: + return 1; + } + + + p = nil; + clean = 0; + vtAttach(); + if(!vtConnect(z, nil)) + goto Exit; + + vtDebug(z, "server connected!\n"); +if(0) vtSetDebug(z, 1); + + for(;;) { + p = vtRecvPacket(z); + if(p == nil) { + break; + } + vtDebug(z, "server recv: "); + vtDebugMesg(z, p, "\n"); + + if(!packetConsume(p, buf, 2)) { + vtSetError(EProtocolBotch); + break; + } + op = buf[0]; + tid = buf[1]; + switch(op) { + default: + vtSetError(EProtocolBotch); + goto Exit; + case VtQPing: + break; + case VtQGoodbye: + clean = 1; + goto Exit; + case VtQHello: + if(!dispatchHello(z, &p)) + goto Exit; + break; + case VtQRead: + if(!dispatchRead(z, &p)) + goto Exit; + break; + case VtQWrite: + if(!dispatchWrite(z, &p)) + goto Exit; + break; + case VtQSync: + if(!dispatchSync(z, &p)) + goto Exit; + break; + } + if(p != nil) { + hdr = packetHeader(p, 2); + hdr[0] = op+1; + hdr[1] = tid; + } else { + p = packetAlloc(); + hdr = packetHeader(p, 2); + hdr[0] = VtRError; + hdr[1] = tid; + if(!vtAddString(p, vtGetError())) + goto Exit; + } + + vtDebug(z, "server send: "); + vtDebugMesg(z, p, "\n"); + + if(!vtSendPacket(z, p)) { + p = nil; + goto Exit; + } + } +Exit: + if(p != nil) + packetFree(p); + if(z->vtbl->closing) + z->vtbl->closing(z, clean); + vtClose(z); + vtFree(z); + vtDetach(); + + exits(0); + return 0; /* never gets here */ +} + diff --git a/sys/src/liboventi/session.h b/sys/src/liboventi/session.h new file mode 100755 index 000000000..8df857f4d --- /dev/null +++ b/sys/src/liboventi/session.h @@ -0,0 +1,74 @@ +typedef struct VtAuth VtAuth; + +/* op codes */ +enum { + VtRError = 1, + VtQPing, + VtRPing, + VtQHello, + VtRHello, + VtQGoodbye, + VtRGoodbye, /* not used */ + VtQAuth0, + VtRAuth0, + VtQAuth1, + VtRAuth1, + VtQRead, + VtRRead, + VtQWrite, + VtRWrite, + VtQSync, + VtRSync, + + VtMaxOp +}; + +/* connection state */ +enum { + VtStateAlloc, + VtStateConnected, + VtStateClosed, +}; + +/* auth state */ +enum { + VtAuthHello, + VtAuth0, + VtAuth1, + VtAuthOK, + VtAuthFailed, +}; + +struct VtAuth { + int state; + uchar client[VtScoreSize]; + uchar sever[VtScoreSize]; +}; + +struct VtSession { + VtLock *lk; + VtServerVtbl *vtbl; /* == nil means client side */ + int cstate; /* connection state */ + int fd; + char fderror[ERRMAX]; + + VtAuth auth; + + VtSha1 *inHash; + VtLock *inLock; + Packet *part; /* partial packet */ + + VtSha1 *outHash; + VtLock *outLock; + + int debug; + int version; + int ref; + char *uid; + char *sid; + int cryptoStrength; + int compression; + int crypto; + int codec; +}; + diff --git a/sys/src/liboventi/strdup.c b/sys/src/liboventi/strdup.c new file mode 100755 index 000000000..a19648eb2 --- /dev/null +++ b/sys/src/liboventi/strdup.c @@ -0,0 +1,19 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +char* +vtStrDup(char *s) +{ + int n; + char *ss; + + if(s == nil) + return nil; + n = strlen(s) + 1; + ss = vtMemAlloc(n); + memmove(ss, s, n); + setmalloctag(ss, getcallerpc(&s)); + return ss; +} + diff --git a/sys/src/liboventi/venti.txt b/sys/src/liboventi/venti.txt new file mode 100755 index 000000000..3e1a8c785 --- /dev/null +++ b/sys/src/liboventi/venti.txt @@ -0,0 +1,267 @@ + +protocol version id + +both client and server send '\n' terminated ascii of the form + +venti-<versions>-<software> + + +<software> = is a software id which is ignored but may be useful for debugging + +If the server and client have the same version number then this +version is used. Otherwise backup to the greatest common +major version number, e.g. 1.00 2.00 etc. If no version in +common then abort. The idea is both client and server should +support a continusous range of major versions. The minor version +numbers are used for development purposes. + +After protocol negotiation switch to binary format + +all numbers sent big endian + +strings are sent with 2 byte length without nulls and in utf-8 max size of 1K + +RPC protocol + +header + op[1] byte op + tid[2] transation id + +client manages the tid space. Until session is established tid must equal 0. +i.e. only one outstanding op. + +CipherStrength + None, + Auth, + Weak, + Strong +CipherAlgorithms + None + SSL3 + TLS1 +CompressionAlgorithms + None + Deflate + Thwack? + +BlockTypes + Root + Pointer + Data + +============================================== + +TPing +RPing + +RError + error: string + +THello + version:string; know to be supported by both + uid: string + uhash[20] use for cipher boot strapping + cipherstrength[1] + ncipher[1]; + cipher[ncipher]; + ncompressor[1]; + compressor[ncompressor]; +RHello + sid: string + shash[20]; use for cipher bott strapping + cipher[1]; + compressor[1]; + +use srp style authentication + g=2 + N is safe prime 1024 bits - find a good prime! + + x = H("venti" H(sid) H(uid) H(password)) + v = g^x + a = random + b = random + B = (v + g^b) + A = g^a + u = first four bytes of H(B) + S = (B - g^x) ^ (a + u*x) = (A * v^u) ^ b + K = H(S) + M1 = H(H(versions) H(THello) H(RHello) A B K) + M2 = H(A M1 K) + +TAuth0 + A[128] +RAuth0 + B[128] +TAuth1 + M1[20] +RAuth1 + M2[20] + +push cipher +push compression + +TWrite + length[2] max 56*1024 + type[1] + data[length] +RWrite + hash[20] + +TRead + hash[20] + type[1] + length[2] +RRead + length[2] + data[length] + +============================================================ + +simplified access when trusting the server +reduces network bandwidth since pointer blocks are not +sent to the client - can also enable permission checking + +RReadRoot + root[20] +TReadRoot + name: string + type: string + blocksize[2] + nblock[8] + time[4] + uid: string; + gid: string + +RReadData + root[20] + block[8] + length[2] +TReadData + length[2] + collision[1] + data[length] + +============================================== + +maybe some clients should be required to navigate to block via root nodes. +This would enable permission checking via access to the root node. + +RTagOpen + tag[2] + root[20] +TTagOpen + +QTagRead + tag[2] + type[1] + length[2] +RTagRead + length[2] + data[length] + +QTagWalkRead + tag[2] + ntag[2] can reuse a tag to do an implict clunk + index[2] + type[1] + length[2] +RTagWalkRead + length[2] + data[length] + +RTagClunk + tag[2] +TTagClunk + +============================ + +Types of blocks + +Data + +Root + name[128] + type[128] + score[20] - DirBlock + +Pointer + score[20]* the number of hashes can be less than fanout when the + tree is not full +DirBlock + DirEntry[32]* + +DirEntry + pointersize[2] - pointer block size + datasize[2] - data blocks size + flag[1] directory + size[7] in bytes - determines pointer depth - intermidate truncated block count as full + score[20] root of pointer blocks or data block + +============================ + +mode flags + (1<<0) ModeOtherExec + (1<<1) ModeOtherWrite + (1<<2) ModeOtherRead + (1<<3) ModeGroupExec + (1<<4) ModeGroupWrite + (1<<5) ModeGroupRead + (1<<6) ModeOwnerExec + (1<<7) ModeOwnerWrite + (1<<8) ModeOwnerRead + (1<<9) ModeSticky + (1<<10) ModeSetUid + (1<<11) ModeSetGid + (1<<12) ModeAppend + (1<<13) ModeExclusive + (1<<14) ModeLink + (1<<15) ModeDir - duplicates dir entry + (1<<16) ModeHidden + (1<<17) ModeSystem + (1<<18) ModeArchive + (1<<19) ModeTemporary + (1<<18) ModeCompressed + (1<<19) ModeEncrypted + +extraType + 2 Plan9 + version[4] + muid: string + + 3 NT Time + createTime[8]; + modifyTime[8]; + accessTime[8] + + +MetaEntry + name: string /* must be first */ + direntry[4] + + id[8] + + uid: string + gui: string + mtime[4] + ctime[4] + atime[4] + + mode[4] + + extratype; + extrasize[2] + extra:[nextra] + +MetaEntry Block + magic[4] + size[2] + free[2] - used to determine if work compacting + maxindex[2] - size of index table in bytes + nindex[2] + index[2*nindex] + +per OS directory entries? + +inode... + + diff --git a/sys/src/liboventi/zero.c b/sys/src/liboventi/zero.c new file mode 100755 index 000000000..75acf3bd7 --- /dev/null +++ b/sys/src/liboventi/zero.c @@ -0,0 +1,79 @@ +#include <u.h> +#include <libc.h> +#include <oventi.h> + +/* score of a zero length block */ +uchar vtZeroScore[VtScoreSize] = { + 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 +}; + + +int +vtZeroExtend(int type, uchar *buf, int n, int nn) +{ + uchar *p, *ep; + + switch(type) { + default: + memset(buf+n, 0, nn-n); + break; + case VtPointerType0: + case VtPointerType1: + case VtPointerType2: + case VtPointerType3: + case VtPointerType4: + case VtPointerType5: + case VtPointerType6: + case VtPointerType7: + case VtPointerType8: + case VtPointerType9: + p = buf + (n/VtScoreSize)*VtScoreSize; + ep = buf + (nn/VtScoreSize)*VtScoreSize; + while(p < ep) { + memmove(p, vtZeroScore, VtScoreSize); + p += VtScoreSize; + } + memset(p, 0, buf+nn-p); + break; + } + return 1; +} + +int +vtZeroTruncate(int type, uchar *buf, int n) +{ + uchar *p; + + switch(type) { + default: + for(p = buf + n; p > buf; p--) { + if(p[-1] != 0) + break; + } + return p - buf; + case VtRootType: + if(n < VtRootSize) + return n; + return VtRootSize; + case VtPointerType0: + case VtPointerType1: + case VtPointerType2: + case VtPointerType3: + case VtPointerType4: + case VtPointerType5: + case VtPointerType6: + case VtPointerType7: + case VtPointerType8: + case VtPointerType9: + /* ignore slop at end of block */ + p = buf + (n/VtScoreSize)*VtScoreSize; + + while(p > buf) { + if(memcmp(p - VtScoreSize, vtZeroScore, VtScoreSize) != 0) + break; + p -= VtScoreSize; + } + return p - buf; + } +} |