summaryrefslogtreecommitdiff
path: root/sys/src/liboventi
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/liboventi
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/liboventi')
-rwxr-xr-xsys/src/liboventi/client.c346
-rwxr-xr-xsys/src/liboventi/debug.c78
-rwxr-xr-xsys/src/liboventi/errfmt.c12
-rwxr-xr-xsys/src/liboventi/fatal.c16
-rwxr-xr-xsys/src/liboventi/mkfile46
-rwxr-xr-xsys/src/liboventi/pack.c147
-rwxr-xr-xsys/src/liboventi/packet.c848
-rwxr-xr-xsys/src/liboventi/packet.h56
-rwxr-xr-xsys/src/liboventi/parsescore.c31
-rwxr-xr-xsys/src/liboventi/plan9-io.c146
-rwxr-xr-xsys/src/liboventi/plan9-sha1.c78
-rwxr-xr-xsys/src/liboventi/plan9-thread.acid531
-rwxr-xr-xsys/src/liboventi/plan9-thread.c468
-rwxr-xr-xsys/src/liboventi/readfully.c19
-rwxr-xr-xsys/src/liboventi/rpc.c471
-rwxr-xr-xsys/src/liboventi/scorefmt.c20
-rwxr-xr-xsys/src/liboventi/server.c265
-rwxr-xr-xsys/src/liboventi/session.h74
-rwxr-xr-xsys/src/liboventi/strdup.c19
-rwxr-xr-xsys/src/liboventi/venti.txt267
-rwxr-xr-xsys/src/liboventi/zero.c79
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;
+ }
+}