summaryrefslogtreecommitdiff
path: root/sys/src/libventi
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/libventi
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/libventi')
-rwxr-xr-xsys/src/libventi/cache.acid644
-rwxr-xr-xsys/src/libventi/cache.c599
-rwxr-xr-xsys/src/libventi/client.c180
-rwxr-xr-xsys/src/libventi/conn.c51
-rwxr-xr-xsys/src/libventi/cvt.h15
-rwxr-xr-xsys/src/libventi/debug.c17
-rwxr-xr-xsys/src/libventi/debugpacket.c265
-rwxr-xr-xsys/src/libventi/dial.c25
-rwxr-xr-xsys/src/libventi/dtype.c78
-rwxr-xr-xsys/src/libventi/entry.c95
-rwxr-xr-xsys/src/libventi/fcall.c231
-rwxr-xr-xsys/src/libventi/fcallfmt.c55
-rwxr-xr-xsys/src/libventi/file.c1282
-rwxr-xr-xsys/src/libventi/hangup.c31
-rwxr-xr-xsys/src/libventi/log.c256
-rwxr-xr-xsys/src/libventi/mem.c86
-rwxr-xr-xsys/src/libventi/mkfile45
-rwxr-xr-xsys/src/libventi/packet.acid1194
-rwxr-xr-xsys/src/libventi/packet.c1025
-rwxr-xr-xsys/src/libventi/parsescore.c43
-rwxr-xr-xsys/src/libventi/queue.c136
-rwxr-xr-xsys/src/libventi/queue.h9
-rwxr-xr-xsys/src/libventi/root.c67
-rwxr-xr-xsys/src/libventi/rpc.acid710
-rwxr-xr-xsys/src/libventi/rpc.c173
-rwxr-xr-xsys/src/libventi/scorefmt.c18
-rwxr-xr-xsys/src/libventi/send.c251
-rwxr-xr-xsys/src/libventi/server.c215
-rwxr-xr-xsys/src/libventi/srvhello.c50
-rwxr-xr-xsys/src/libventi/strdup.c18
-rwxr-xr-xsys/src/libventi/string.c50
-rwxr-xr-xsys/src/libventi/time.c25
-rwxr-xr-xsys/src/libventi/version.c120
-rwxr-xr-xsys/src/libventi/zero.c55
-rwxr-xr-xsys/src/libventi/zeroscore.c10
35 files changed, 8124 insertions, 0 deletions
diff --git a/sys/src/libventi/cache.acid b/sys/src/libventi/cache.acid
new file mode 100755
index 000000000..45b28466c
--- /dev/null
+++ b/sys/src/libventi/cache.acid
@@ -0,0 +1,644 @@
+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 = 128;
+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;
+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 = 28;
+aggr NetConnInfo
+{
+ 'X' 0 dir;
+ 'X' 4 root;
+ 'X' 8 spec;
+ 'X' 12 lsys;
+ 'X' 16 lserv;
+ 'X' 20 rsys;
+ 'X' 24 rserv;
+};
+
+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");
+};
+
+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");
+};
+
+MaxFragSize = 9216;
+VtScoreSize = 20;
+VtMaxStringSize = 1024;
+VtMaxFileSize = 281474976710655;
+VtMaxLumpSize = 57344;
+VtPointerDepth = 7;
+VtDataType = 0;
+VtDirType = 8;
+VtRootType = 16;
+VtMaxType = 17;
+VtTypeDepthMask = 7;
+VtEntryActive = 1;
+VtEntryDir = 2;
+VtEntryDepthShift = 2;
+VtEntryDepthMask = 28;
+VtEntryLocal = 32;
+VtEntrySize = 40;
+sizeofVtEntry = 40;
+aggr VtEntry
+{
+ 'U' 0 gen;
+ 'u' 4 psize;
+ 'u' 6 dsize;
+ 'b' 8 type;
+ '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(" type ", addr.type, "\n");
+ print(" flags ", addr.flags, "\n");
+ print(" size ", addr.size, "\n");
+ print(" score ", addr.score, "\n");
+};
+
+sizeofVtRoot = 300;
+aggr VtRoot
+{
+ 'a' 0 name;
+ 'a' 128 type;
+ 'a' 256 score;
+ 'u' 276 blocksize;
+ 'a' 278 prev;
+};
+
+defn
+VtRoot(addr) {
+ complex VtRoot addr;
+ 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");
+};
+
+VtRootSize = 300;
+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;
+VtRerror = 1;
+VtTping = 2;
+VtRping = 3;
+VtThello = 4;
+VtRhello = 5;
+VtTgoodbye = 6;
+VtRgoodbye = 7;
+VtTauth0 = 8;
+VtRauth0 = 9;
+VtTauth1 = 10;
+VtRauth1 = 11;
+VtTread = 12;
+VtRread = 13;
+VtTwrite = 14;
+VtRwrite = 15;
+VtTsync = 16;
+VtRsync = 17;
+VtTmax = 18;
+sizeofVtFcall = 80;
+aggr VtFcall
+{
+ 'b' 0 type;
+ 'b' 1 tag;
+ 'X' 4 error;
+ 'X' 8 version;
+ 'X' 12 uid;
+ 'b' 16 strength;
+ 'X' 20 crypto;
+ 'U' 24 ncrypto;
+ 'X' 28 codec;
+ 'U' 32 ncodec;
+ 'X' 36 sid;
+ 'b' 40 rcrypto;
+ 'b' 41 rcodec;
+ 'X' 44 auth;
+ 'U' 48 nauth;
+ 'a' 52 score;
+ 'b' 72 dtype;
+ 'u' 74 count;
+ 'X' 76 data;
+};
+
+defn
+VtFcall(addr) {
+ complex VtFcall addr;
+ print(" type ", addr.type, "\n");
+ print(" tag ", addr.tag, "\n");
+ print(" error ", addr.error\X, "\n");
+ print(" version ", addr.version\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" strength ", addr.strength, "\n");
+ print(" crypto ", addr.crypto\X, "\n");
+ print(" ncrypto ", addr.ncrypto, "\n");
+ print(" codec ", addr.codec\X, "\n");
+ print(" ncodec ", addr.ncodec, "\n");
+ print(" sid ", addr.sid\X, "\n");
+ print(" rcrypto ", addr.rcrypto, "\n");
+ print(" rcodec ", addr.rcodec, "\n");
+ print(" auth ", addr.auth\X, "\n");
+ print(" nauth ", addr.nauth, "\n");
+ print(" score ", addr.score, "\n");
+ print(" dtype ", addr.dtype, "\n");
+ print(" count ", addr.count, "\n");
+ print(" data ", addr.data\X, "\n");
+};
+
+VtStateAlloc = 0;
+VtStateConnected = 1;
+VtStateClosed = 2;
+sizeofVtConn = 1148;
+aggr VtConn
+{
+ QLock 0 lk;
+ QLock 16 inlk;
+ QLock 32 outlk;
+ 'D' 48 debug;
+ 'D' 52 infd;
+ 'D' 56 outfd;
+ 'D' 60 muxer;
+ 'X' 64 writeq;
+ 'X' 68 readq;
+ 'D' 72 state;
+ 'a' 76 wait;
+ 'U' 1100 ntag;
+ 'U' 1104 nsleep;
+ 'X' 1108 part;
+ Rendez 1112 tagrend;
+ Rendez 1124 rpcfork;
+ 'X' 1136 version;
+ 'X' 1140 uid;
+ 'X' 1144 sid;
+};
+
+defn
+VtConn(addr) {
+ complex VtConn addr;
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print("QLock inlk {\n");
+ QLock(addr.inlk);
+ print("}\n");
+ print("QLock outlk {\n");
+ QLock(addr.outlk);
+ print("}\n");
+ print(" debug ", addr.debug, "\n");
+ print(" infd ", addr.infd, "\n");
+ print(" outfd ", addr.outfd, "\n");
+ print(" muxer ", addr.muxer, "\n");
+ print(" writeq ", addr.writeq\X, "\n");
+ print(" readq ", addr.readq\X, "\n");
+ print(" state ", addr.state, "\n");
+ print(" wait ", addr.wait, "\n");
+ print(" ntag ", addr.ntag, "\n");
+ print(" nsleep ", addr.nsleep, "\n");
+ print(" part ", addr.part\X, "\n");
+ print("Rendez tagrend {\n");
+ Rendez(addr.tagrend);
+ print("}\n");
+ print("Rendez rpcfork {\n");
+ Rendez(addr.rpcfork);
+ print("}\n");
+ print(" version ", addr.version\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" sid ", addr.sid\X, "\n");
+};
+
+NilBlock = -1;
+sizeofVtBlock = 88;
+aggr VtBlock
+{
+ 'X' 0 c;
+ QLock 4 lk;
+ 'X' 20 data;
+ 'a' 24 score;
+ 'b' 44 type;
+ 'D' 48 nlock;
+ 'D' 52 iostate;
+ 'D' 56 ref;
+ 'U' 60 heap;
+ 'A' VtBlock 64 next;
+ 'A' VtBlock 68 prev;
+ 'U' 72 used;
+ 'U' 76 used2;
+ 'U' 80 addr;
+ 'D' 84 decrypted;
+};
+
+defn
+VtBlock(addr) {
+ complex VtBlock addr;
+ print(" c ", addr.c\X, "\n");
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print(" data ", addr.data\X, "\n");
+ print(" score ", addr.score, "\n");
+ print(" type ", addr.type, "\n");
+ print(" nlock ", addr.nlock, "\n");
+ print(" iostate ", addr.iostate, "\n");
+ print(" ref ", addr.ref, "\n");
+ print(" heap ", addr.heap, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" prev ", addr.prev\X, "\n");
+ print(" used ", addr.used, "\n");
+ print(" used2 ", addr.used2, "\n");
+ print(" addr ", addr.addr, "\n");
+ print(" decrypted ", addr.decrypted, "\n");
+};
+
+VtOREAD = 0;
+VtOWRITE = 1;
+VtORDWR = 2;
+VtOCREATE = 256;
+BioLocal = 1;
+BioVenti = 2;
+BioReading = 3;
+BioWriting = 4;
+BioEmpty = 5;
+BioVentiError = 6;
+BadHeap = -1;
+sizeofVtCache = 60;
+aggr VtCache
+{
+ QLock 0 lk;
+ 'A' VtConn 16 z;
+ 'U' 20 blocksize;
+ 'U' 24 now;
+ 'A' VtBlock 28 hash;
+ 'D' 32 nhash;
+ 'A' VtBlock 36 heap;
+ 'D' 40 nheap;
+ 'A' VtBlock 44 block;
+ 'D' 48 nblock;
+ 'X' 52 mem;
+ 'D' 56 mode;
+};
+
+defn
+VtCache(addr) {
+ complex VtCache addr;
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print(" z ", addr.z\X, "\n");
+ print(" blocksize ", addr.blocksize, "\n");
+ print(" now ", addr.now, "\n");
+ print(" hash ", addr.hash\X, "\n");
+ print(" nhash ", addr.nhash, "\n");
+ print(" heap ", addr.heap\X, "\n");
+ print(" nheap ", addr.nheap, "\n");
+ print(" block ", addr.block\X, "\n");
+ print(" nblock ", addr.nblock, "\n");
+ print(" mem ", addr.mem\X, "\n");
+ print(" mode ", addr.mode, "\n");
+};
+
+complex VtConn vtcachealloc:z;
+complex VtCache vtcachealloc:c;
+complex VtBlock vtcachealloc:b;
+complex VtCache vtcachefree:c;
+complex VtCache vtcachedump:c;
+complex VtBlock vtcachedump:b;
+complex VtCache cachecheck:c;
+complex VtBlock cachecheck:b;
+complex VtBlock upheap:b;
+complex VtBlock upheap:bb;
+complex VtCache upheap:c;
+complex VtBlock downheap:b;
+complex VtBlock downheap:bb;
+complex VtCache downheap:c;
+complex VtBlock heapdel:b;
+complex VtCache heapdel:c;
+complex VtBlock heapins:b;
+complex VtCache vtcachebumpblock:c;
+complex VtBlock vtcachebumpblock:b;
+complex VtCache vtcachelocal:c;
+complex VtBlock vtcachelocal:b;
+complex VtCache vtcacheallocblock:c;
+complex VtBlock vtcacheallocblock:b;
+complex VtCache vtcacheglobal:c;
+complex VtBlock vtcacheglobal:b;
+complex VtBlock vtblockduplock:b;
+complex VtBlock vtblockput:b;
+complex VtCache vtblockput:c;
+complex VtBlock vtblockwrite:b;
+complex VtCache vtblockwrite:c;
+complex VtCache vtcacheblocksize:c;
+complex VtBlock vtblockcopy:b;
+complex VtBlock vtblockcopy:bb;
diff --git a/sys/src/libventi/cache.c b/sys/src/libventi/cache.c
new file mode 100755
index 000000000..65e0e8c45
--- /dev/null
+++ b/sys/src/libventi/cache.c
@@ -0,0 +1,599 @@
+/*
+ * Memory-only VtBlock cache.
+ *
+ * The cached Venti blocks are in the hash chains.
+ * The cached local blocks are only in the blocks array.
+ * The free blocks are in the heap, which is supposed to
+ * be indexed by second-to-last use but actually
+ * appears to be last use.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+int vtcachenread;
+int vtcachencopy;
+int vtcachenwrite;
+int vttracelevel;
+
+enum {
+ BioLocal = 1,
+ BioVenti,
+ BioReading,
+ BioWriting,
+ BioEmpty,
+ BioVentiError
+};
+enum {
+ BadHeap = ~0
+};
+struct VtCache
+{
+ QLock lk;
+ VtConn *z;
+ u32int blocksize;
+ u32int now; /* ticks for usage time stamps */
+ VtBlock **hash; /* hash table for finding addresses */
+ int nhash;
+ VtBlock **heap; /* heap for finding victims */
+ int nheap;
+ VtBlock *block; /* all allocated blocks */
+ int nblock;
+ uchar *mem; /* memory for all blocks and data */
+ int (*write)(VtConn*, uchar[VtScoreSize], uint, uchar*, int);
+};
+
+static void cachecheck(VtCache*);
+
+VtCache*
+vtcachealloc(VtConn *z, int blocksize, ulong nblock)
+{
+ uchar *p;
+ VtCache *c;
+ int i;
+ VtBlock *b;
+
+ c = vtmallocz(sizeof(VtCache));
+
+ c->z = z;
+ c->blocksize = (blocksize + 127) & ~127;
+ c->nblock = nblock;
+ c->nhash = nblock;
+ c->hash = vtmallocz(nblock*sizeof(VtBlock*));
+ c->heap = vtmallocz(nblock*sizeof(VtBlock*));
+ c->block = vtmallocz(nblock*sizeof(VtBlock));
+ c->mem = vtmallocz(nblock*c->blocksize);
+ c->write = vtwrite;
+
+ p = c->mem;
+ for(i=0; i<nblock; i++){
+ b = &c->block[i];
+ b->addr = NilBlock;
+ b->c = c;
+ b->data = p;
+ b->heap = i;
+ c->heap[i] = b;
+ p += c->blocksize;
+ }
+ c->nheap = nblock;
+ cachecheck(c);
+ return c;
+}
+
+/*
+ * BUG This is here so that vbackup can override it and do some
+ * pipelining of writes. Arguably vtwrite or vtwritepacket or the
+ * cache itself should be providing this functionality.
+ */
+void
+vtcachesetwrite(VtCache *c, int (*write)(VtConn*, uchar[VtScoreSize], uint, uchar*, int))
+{
+ if(write == nil)
+ write = vtwrite;
+ c->write = write;
+}
+
+void
+vtcachefree(VtCache *c)
+{
+ int i;
+
+ qlock(&c->lk);
+
+ cachecheck(c);
+ for(i=0; i<c->nblock; i++)
+ assert(c->block[i].ref == 0);
+
+ vtfree(c->hash);
+ vtfree(c->heap);
+ vtfree(c->block);
+ vtfree(c->mem);
+ vtfree(c);
+}
+
+static void
+vtcachedump(VtCache *c)
+{
+ int i;
+ VtBlock *b;
+
+ for(i=0; i<c->nblock; i++){
+ b = &c->block[i];
+ print("cache block %d: type %d score %V iostate %d addr %d ref %d nlock %d\n",
+ i, b->type, b->score, b->iostate, b->addr, b->ref, b->nlock);
+ }
+}
+
+static void
+cachecheck(VtCache *c)
+{
+ u32int size, now;
+ int i, k, refed;
+ VtBlock *b;
+
+ size = c->blocksize;
+ now = c->now;
+
+ for(i = 0; i < c->nheap; i++){
+ if(c->heap[i]->heap != i)
+ sysfatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
+ if(i > 0 && c->heap[(i - 1) >> 1]->used - now > c->heap[i]->used - now)
+ sysfatal("bad heap ordering");
+ k = (i << 1) + 1;
+ if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
+ sysfatal("bad heap ordering");
+ k++;
+ if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used - now)
+ sysfatal("bad heap ordering");
+ }
+
+ refed = 0;
+ for(i = 0; i < c->nblock; i++){
+ b = &c->block[i];
+ if(b->data != &c->mem[i * size])
+ sysfatal("mis-blocked at %d", i);
+ if(b->ref && b->heap == BadHeap)
+ refed++;
+ else if(b->addr != NilBlock)
+ refed++;
+ }
+ assert(c->nheap + refed == c->nblock);
+ refed = 0;
+ for(i = 0; i < c->nblock; i++){
+ b = &c->block[i];
+ if(b->ref){
+ refed++;
+ }
+ }
+}
+
+static int
+upheap(int i, VtBlock *b)
+{
+ VtBlock *bb;
+ u32int now;
+ int p;
+ VtCache *c;
+
+ c = b->c;
+ now = c->now;
+ for(; i != 0; i = p){
+ p = (i - 1) >> 1;
+ bb = c->heap[p];
+ if(b->used - now >= bb->used - now)
+ break;
+ c->heap[i] = bb;
+ bb->heap = i;
+ }
+ c->heap[i] = b;
+ b->heap = i;
+
+ return i;
+}
+
+static int
+downheap(int i, VtBlock *b)
+{
+ VtBlock *bb;
+ u32int now;
+ int k;
+ VtCache *c;
+
+ c = b->c;
+ now = c->now;
+ for(; ; i = k){
+ k = (i << 1) + 1;
+ if(k >= c->nheap)
+ break;
+ if(k + 1 < c->nheap && c->heap[k]->used - now > c->heap[k + 1]->used - now)
+ k++;
+ bb = c->heap[k];
+ if(b->used - now <= bb->used - now)
+ break;
+ c->heap[i] = bb;
+ bb->heap = i;
+ }
+ c->heap[i] = b;
+ b->heap = i;
+ return i;
+}
+
+/*
+ * Delete a block from the heap.
+ * Called with c->lk held.
+ */
+static void
+heapdel(VtBlock *b)
+{
+ int i, si;
+ VtCache *c;
+
+ c = b->c;
+
+ si = b->heap;
+ if(si == BadHeap)
+ return;
+ b->heap = BadHeap;
+ c->nheap--;
+ if(si == c->nheap)
+ return;
+ b = c->heap[c->nheap];
+ i = upheap(si, b);
+ if(i == si)
+ downheap(i, b);
+}
+
+/*
+ * Insert a block into the heap.
+ * Called with c->lk held.
+ */
+static void
+heapins(VtBlock *b)
+{
+ assert(b->heap == BadHeap);
+ upheap(b->c->nheap++, b);
+}
+
+/*
+ * locate the vtBlock with the oldest second to last use.
+ * remove it from the heap, and fix up the heap.
+ */
+/* called with c->lk held */
+static VtBlock*
+vtcachebumpblock(VtCache *c)
+{
+ VtBlock *b;
+
+ /*
+ * locate the vtBlock with the oldest second to last use.
+ * remove it from the heap, and fix up the heap.
+ */
+ if(c->nheap == 0){
+ vtcachedump(c);
+ fprint(2, "vtcachebumpblock: no free blocks in vtCache");
+ abort();
+ }
+ b = c->heap[0];
+ heapdel(b);
+
+ assert(b->heap == BadHeap);
+ assert(b->ref == 0);
+
+ /*
+ * unchain the vtBlock from hash chain if any
+ */
+ if(b->prev){
+ *(b->prev) = b->next;
+ if(b->next)
+ b->next->prev = b->prev;
+ b->prev = nil;
+ }
+
+
+if(0)fprint(2, "droping %x:%V\n", b->addr, b->score);
+ /* set vtBlock to a reasonable state */
+ b->ref = 1;
+ b->iostate = BioEmpty;
+ return b;
+}
+
+/*
+ * fetch a local block from the memory cache.
+ * if it's not there, load it, bumping some other Block.
+ * if we're out of free blocks, we're screwed.
+ */
+VtBlock*
+vtcachelocal(VtCache *c, u32int addr, int type)
+{
+ VtBlock *b;
+
+ if(addr == 0)
+ sysfatal("vtcachelocal: asked for nonexistent block 0");
+ if(addr > c->nblock)
+ sysfatal("vtcachelocal: asked for block #%ud; only %d blocks",
+ addr, c->nblock);
+
+ b = &c->block[addr-1];
+ if(b->addr == NilBlock || b->iostate != BioLocal)
+ sysfatal("vtcachelocal: block is not local");
+
+ if(b->type != type)
+ sysfatal("vtcachelocal: block has wrong type %d != %d", b->type, type);
+
+ qlock(&c->lk);
+ b->ref++;
+ qunlock(&c->lk);
+
+ qlock(&b->lk);
+ b->nlock = 1;
+ b->pc = getcallerpc(&c);
+ return b;
+}
+
+VtBlock*
+vtcacheallocblock(VtCache *c, int type)
+{
+ VtBlock *b;
+
+ qlock(&c->lk);
+ b = vtcachebumpblock(c);
+ b->iostate = BioLocal;
+ b->type = type;
+ b->addr = (b - c->block)+1;
+ vtzeroextend(type, b->data, 0, c->blocksize);
+ vtlocaltoglobal(b->addr, b->score);
+ qunlock(&c->lk);
+
+ qlock(&b->lk);
+ b->nlock = 1;
+ b->pc = getcallerpc(&c);
+ return b;
+}
+
+/*
+ * fetch a global (Venti) block from the memory cache.
+ * if it's not there, load it, bumping some other block.
+ */
+VtBlock*
+vtcacheglobal(VtCache *c, uchar score[VtScoreSize], int type)
+{
+ VtBlock *b;
+ ulong h;
+ int n;
+ u32int addr;
+
+ if(vttracelevel)
+ fprint(2, "vtcacheglobal %V %d from %p\n", score, type, getcallerpc(&c));
+ addr = vtglobaltolocal(score);
+ if(addr != NilBlock){
+ if(vttracelevel)
+ fprint(2, "vtcacheglobal %V %d => local\n", score, type);
+ b = vtcachelocal(c, addr, type);
+ if(b)
+ b->pc = getcallerpc(&c);
+ return b;
+ }
+
+ h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
+
+ /*
+ * look for the block in the cache
+ */
+ qlock(&c->lk);
+ for(b = c->hash[h]; b != nil; b = b->next){
+ if(b->addr != NilBlock || memcmp(b->score, score, VtScoreSize) != 0 || b->type != type)
+ continue;
+ heapdel(b);
+ b->ref++;
+ qunlock(&c->lk);
+ if(vttracelevel)
+ fprint(2, "vtcacheglobal %V %d => found in cache %p; locking\n", score, type, b);
+ qlock(&b->lk);
+ b->nlock = 1;
+ if(b->iostate == BioVentiError){
+ if(chattyventi)
+ fprint(2, "cached read error for %V\n", score);
+ if(vttracelevel)
+ fprint(2, "vtcacheglobal %V %d => cache read error\n", score, type);
+ vtblockput(b);
+ werrstr("venti i/o error");
+ return nil;
+ }
+ if(vttracelevel)
+ fprint(2, "vtcacheglobal %V %d => found in cache; returning\n", score, type);
+ b->pc = getcallerpc(&c);
+ return b;
+ }
+
+ /*
+ * not found
+ */
+ b = vtcachebumpblock(c);
+ b->addr = NilBlock;
+ b->type = type;
+ memmove(b->score, score, VtScoreSize);
+ /* chain onto correct hash */
+ b->next = c->hash[h];
+ c->hash[h] = b;
+ if(b->next != nil)
+ b->next->prev = &b->next;
+ b->prev = &c->hash[h];
+
+ /*
+ * Lock b before unlocking c, so that others wait while we read.
+ *
+ * You might think there is a race between this qlock(b) before qunlock(c)
+ * and the qlock(c) while holding a qlock(b) in vtblockwrite. However,
+ * the block here can never be the block in a vtblockwrite, so we're safe.
+ * We're certainly living on the edge.
+ */
+ if(vttracelevel)
+ fprint(2, "vtcacheglobal %V %d => bumped; locking %p\n", score, type, b);
+ qlock(&b->lk);
+ b->nlock = 1;
+ qunlock(&c->lk);
+
+ vtcachenread++;
+ n = vtread(c->z, score, type, b->data, c->blocksize);
+ if(n < 0){
+ if(chattyventi)
+ fprint(2, "read %V: %r\n", score);
+ if(vttracelevel)
+ fprint(2, "vtcacheglobal %V %d => bumped; read error\n", score, type);
+ b->iostate = BioVentiError;
+ vtblockput(b);
+ return nil;
+ }
+ vtzeroextend(type, b->data, n, c->blocksize);
+ b->iostate = BioVenti;
+ b->nlock = 1;
+ if(vttracelevel)
+ fprint(2, "vtcacheglobal %V %d => loaded into cache; returning\n", score, type);
+ b->pc = getcallerpc(&b);
+ return b;
+}
+
+/*
+ * The thread that has locked b may refer to it by
+ * multiple names. Nlock counts the number of
+ * references the locking thread holds. It will call
+ * vtblockput once per reference.
+ */
+void
+vtblockduplock(VtBlock *b)
+{
+ assert(b->nlock > 0);
+ b->nlock++;
+}
+
+/*
+ * we're done with the block.
+ * unlock it. can't use it after calling this.
+ */
+void
+vtblockput(VtBlock* b)
+{
+ VtCache *c;
+
+ if(b == nil)
+ return;
+
+if(0)fprint(2, "vtblockput: %d: %x %d %d\n", getpid(), b->addr, c->nheap, b->iostate);
+ if(vttracelevel)
+ fprint(2, "vtblockput %p from %p\n", b, getcallerpc(&b));
+
+ if(--b->nlock > 0)
+ return;
+
+ /*
+ * b->nlock should probably stay at zero while
+ * the vtBlock is unlocked, but diskThread and vtSleep
+ * conspire to assume that they can just qlock(&b->lk); vtblockput(b),
+ * so we have to keep b->nlock set to 1 even
+ * when the vtBlock is unlocked.
+ */
+ assert(b->nlock == 0);
+ b->nlock = 1;
+
+ qunlock(&b->lk);
+ c = b->c;
+ qlock(&c->lk);
+
+ if(--b->ref > 0){
+ qunlock(&c->lk);
+ return;
+ }
+
+ assert(b->ref == 0);
+ switch(b->iostate){
+ case BioVenti:
+/*if(b->addr != NilBlock) print("blockput %d\n", b->addr); */
+ b->used = c->now++;
+ /* fall through */
+ case BioVentiError:
+ heapins(b);
+ break;
+ case BioLocal:
+ break;
+ }
+ qunlock(&c->lk);
+}
+
+int
+vtblockwrite(VtBlock *b)
+{
+ uchar score[VtScoreSize];
+ VtCache *c;
+ uint h;
+ int n;
+
+ if(b->iostate != BioLocal){
+ werrstr("vtblockwrite: not a local block");
+ return -1;
+ }
+
+ c = b->c;
+ n = vtzerotruncate(b->type, b->data, c->blocksize);
+ vtcachenwrite++;
+ if(c->write(c->z, score, b->type, b->data, n) < 0)
+ return -1;
+
+ memmove(b->score, score, VtScoreSize);
+
+ qlock(&c->lk);
+ b->addr = NilBlock; /* now on venti */
+ b->iostate = BioVenti;
+ h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c->nhash;
+ b->next = c->hash[h];
+ c->hash[h] = b;
+ if(b->next != nil)
+ b->next->prev = &b->next;
+ b->prev = &c->hash[h];
+ qunlock(&c->lk);
+ return 0;
+}
+
+uint
+vtcacheblocksize(VtCache *c)
+{
+ return c->blocksize;
+}
+
+VtBlock*
+vtblockcopy(VtBlock *b)
+{
+ VtBlock *bb;
+
+ vtcachencopy++;
+ bb = vtcacheallocblock(b->c, b->type);
+ if(bb == nil){
+ vtblockput(b);
+ return nil;
+ }
+ memmove(bb->data, b->data, b->c->blocksize);
+ vtblockput(b);
+ bb->pc = getcallerpc(&b);
+ return bb;
+}
+
+void
+vtlocaltoglobal(u32int addr, uchar score[VtScoreSize])
+{
+ memset(score, 0, 16);
+ score[16] = addr>>24;
+ score[17] = addr>>16;
+ score[18] = addr>>8;
+ score[19] = addr;
+}
+
+
+u32int
+vtglobaltolocal(uchar score[VtScoreSize])
+{
+ static uchar zero[16];
+ if(memcmp(score, zero, 16) != 0)
+ return NilBlock;
+ return (score[16]<<24)|(score[17]<<16)|(score[18]<<8)|score[19];
+}
+
diff --git a/sys/src/libventi/client.c b/sys/src/libventi/client.c
new file mode 100755
index 000000000..40ee85175
--- /dev/null
+++ b/sys/src/libventi/client.c
@@ -0,0 +1,180 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+int ventidoublechecksha1 = 1;
+
+static int
+vtfcallrpc(VtConn *z, VtFcall *ou, VtFcall *in)
+{
+ Packet *p;
+
+ p = vtfcallpack(ou);
+ if(p == nil)
+ return -1;
+ if((p = _vtrpc(z, p, ou)) == nil)
+ return -1;
+ if(vtfcallunpack(in, p) < 0){
+ packetfree(p);
+ return -1;
+ }
+ if(chattyventi)
+ fprint(2, "%s <- %F\n", argv0, in);
+ if(in->msgtype == VtRerror){
+ werrstr(in->error);
+ vtfcallclear(in);
+ packetfree(p);
+ return -1;
+ }
+ if(in->msgtype != ou->msgtype+1){
+ werrstr("type mismatch: sent %c%d got %c%d",
+ "TR"[ou->msgtype&1], ou->msgtype>>1,
+ "TR"[in->msgtype&1], in->msgtype>>1);
+ vtfcallclear(in);
+ packetfree(p);
+ return -1;
+ }
+ packetfree(p);
+ return 0;
+}
+
+int
+vthello(VtConn *z)
+{
+ VtFcall tx, rx;
+
+ memset(&tx, 0, sizeof tx);
+ tx.msgtype = VtThello;
+ tx.version = z->version;
+ tx.uid = z->uid;
+ if(tx.uid == nil)
+ tx.uid = "anonymous";
+ if(vtfcallrpc(z, &tx, &rx) < 0)
+ return -1;
+ z->sid = rx.sid;
+ rx.sid = 0;
+ vtfcallclear(&rx);
+ return 0;
+}
+
+Packet*
+vtreadpacket(VtConn *z, uchar score[VtScoreSize], uint type, int n)
+{
+ VtFcall tx, rx;
+
+ if(memcmp(score, vtzeroscore, VtScoreSize) == 0)
+ return packetalloc();
+
+ memset(&tx, 0, sizeof tx);
+ tx.msgtype = VtTread;
+ tx.blocktype = type;
+ tx.count = n;
+ memmove(tx.score, score, VtScoreSize);
+ if(vtfcallrpc(z, &tx, &rx) < 0)
+ return nil;
+ if(packetsize(rx.data) > n){
+ werrstr("read returned too much data");
+ packetfree(rx.data);
+ return nil;
+ }
+ if(ventidoublechecksha1){
+ packetsha1(rx.data, tx.score);
+ if(memcmp(score, tx.score, VtScoreSize) != 0){
+ werrstr("read asked for %V got %V", score, tx.score);
+ packetfree(rx.data);
+ return nil;
+ }
+ }
+ return rx.data;
+}
+
+int
+vtread(VtConn *z, uchar score[VtScoreSize], uint type, uchar *buf, int n)
+{
+ int nn;
+ Packet *p;
+
+ if((p = vtreadpacket(z, score, type, n)) == nil)
+ return -1;
+ nn = packetsize(p);
+ if(packetconsume(p, buf, nn) < 0)
+ abort();
+ packetfree(p);
+ return nn;
+}
+
+int
+vtwritepacket(VtConn *z, uchar score[VtScoreSize], uint type, Packet *p)
+{
+ VtFcall tx, rx;
+
+ if(packetsize(p) == 0){
+ memmove(score, vtzeroscore, VtScoreSize);
+ return 0;
+ }
+ tx.msgtype = VtTwrite;
+ tx.blocktype = type;
+ tx.data = p;
+ if(ventidoublechecksha1)
+ packetsha1(p, score);
+ if(vtfcallrpc(z, &tx, &rx) < 0)
+ return -1;
+ if(ventidoublechecksha1){
+ if(memcmp(score, rx.score, VtScoreSize) != 0){
+ werrstr("sha1 hash mismatch: want %V got %V", score, rx.score);
+ return -1;
+ }
+ }else
+ memmove(score, rx.score, VtScoreSize);
+ return 0;
+}
+
+int
+vtwrite(VtConn *z, uchar score[VtScoreSize], uint type, uchar *buf, int n)
+{
+ Packet *p;
+ int nn;
+
+ p = packetforeign(buf, n, 0, nil);
+ nn = vtwritepacket(z, score, type, p);
+ packetfree(p);
+ return nn;
+}
+
+int
+vtsync(VtConn *z)
+{
+ VtFcall tx, rx;
+
+ tx.msgtype = VtTsync;
+ return vtfcallrpc(z, &tx, &rx);
+}
+
+int
+vtping(VtConn *z)
+{
+ VtFcall tx, rx;
+
+ tx.msgtype = VtTping;
+ return vtfcallrpc(z, &tx, &rx);
+}
+
+int
+vtconnect(VtConn *z)
+{
+ if(vtversion(z) < 0)
+ return -1;
+ if(vthello(z) < 0)
+ return -1;
+ return 0;
+}
+
+int
+vtgoodbye(VtConn *z)
+{
+ VtFcall tx, rx;
+
+ tx.msgtype = VtTgoodbye;
+ vtfcallrpc(z, &tx, &rx); /* always fails: no VtRgoodbye */
+ return 0;
+}
diff --git a/sys/src/libventi/conn.c b/sys/src/libventi/conn.c
new file mode 100755
index 000000000..e488c6db6
--- /dev/null
+++ b/sys/src/libventi/conn.c
@@ -0,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+#include "queue.h"
+
+int chattyventi;
+
+VtConn*
+vtconn(int infd, int outfd)
+{
+ VtConn *z;
+ NetConnInfo *nci;
+
+ z = vtmallocz(sizeof(VtConn));
+ z->tagrend.l = &z->lk;
+ z->rpcfork.l = &z->lk;
+ z->infd = infd;
+ z->outfd = outfd;
+ z->part = packetalloc();
+ nci = getnetconninfo(nil, infd);
+ if(nci == nil)
+ snprint(z->addr, sizeof z->addr, "/dev/fd/%d", infd);
+ else{
+ strecpy(z->addr, z->addr+sizeof z->addr, nci->raddr);
+ freenetconninfo(nci);
+ }
+ return z;
+}
+
+void
+vtfreeconn(VtConn *z)
+{
+ vthangup(z);
+ qlock(&z->lk);
+ /*
+ * Wait for send and recv procs to notice
+ * the hangup and clear out the queues.
+ */
+ while(z->readq || z->writeq){
+ if(z->readq)
+ _vtqhangup(z->readq);
+ if(z->writeq)
+ _vtqhangup(z->writeq);
+ rsleep(&z->rpcfork);
+ }
+ packetfree(z->part);
+ vtfree(z->version);
+ vtfree(z->sid);
+ qunlock(&z->lk);
+ vtfree(z);
+}
diff --git a/sys/src/libventi/cvt.h b/sys/src/libventi/cvt.h
new file mode 100755
index 000000000..ef4c2f51d
--- /dev/null
+++ b/sys/src/libventi/cvt.h
@@ -0,0 +1,15 @@
+/*
+ * 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)
+
diff --git a/sys/src/libventi/debug.c b/sys/src/libventi/debug.c
new file mode 100755
index 000000000..e0452e48d
--- /dev/null
+++ b/sys/src/libventi/debug.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+void
+vtdebug(VtConn *z, char *fmt, ...)
+{
+ va_list arg;
+
+ if(z->debug == 0)
+ return;
+
+ va_start(arg, fmt);
+ vfprint(2, fmt, arg);
+ va_end(arg);
+}
+
diff --git a/sys/src/libventi/debugpacket.c b/sys/src/libventi/debugpacket.c
new file mode 100755
index 000000000..4c13bc87e
--- /dev/null
+++ b/sys/src/libventi/debugpacket.c
@@ -0,0 +1,265 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+#include <libsec.h>
+
+#define MAGIC 0x54798314
+#define NOTFREE(p) assert((p)->magic == MAGIC)
+
+struct Packet
+{
+ char *data;
+ int len;
+ void (*free)(void*);
+ void *arg;
+ int magic;
+};
+
+Packet*
+packetalloc(void)
+{
+ Packet *p;
+
+ p = vtmallocz(sizeof *p);
+ p->free = vtfree;
+ p->arg = nil;
+ p->magic = MAGIC;
+ return p;
+}
+
+void
+packetappend(Packet *p, uchar *buf, int n)
+{
+ NOTFREE(p);
+ if(n < 0)
+ abort();
+ if(p->free != vtfree)
+ sysfatal("packetappend");
+ p->data = vtrealloc(p->data, p->len+n);
+ p->arg = p->data;
+ memmove(p->data+p->len, buf, n);
+ p->len += n;
+}
+
+uint
+packetasize(Packet *p)
+{
+ NOTFREE(p);
+ return p->len;
+}
+
+int
+packetcmp(Packet *p, Packet *q)
+{
+ int i, len;
+
+ NOTFREE(p);
+ NOTFREE(q);
+ len = p->len;
+ if(len > q->len)
+ len = q->len;
+ if(len && (i=memcmp(p->data, q->data, len)) != 0)
+ return i;
+ if(p->len > len)
+ return 1;
+ if(q->len > len)
+ return -1;
+ return 0;
+}
+
+void
+packetconcat(Packet *p, Packet *q)
+{
+ NOTFREE(p);
+ NOTFREE(q);
+ packetappend(p, q->data, q->len);
+ if(q->free == vtfree)
+ memset(q->data, 0xFE, q->len);
+ q->free(q->arg);
+ q->data = nil;
+ q->len = 0;
+}
+
+int
+packetconsume(Packet *p, uchar *buf, int n)
+{
+ NOTFREE(p);
+ if(n < 0)
+ abort();
+ if(p->len < n)
+ abort();
+ memmove(buf, p->data, n);
+ p->len -= n;
+ memmove(p->data, p->data+n, p->len);
+ return 0;
+}
+
+int
+packetcopy(Packet *p, uchar *buf, int offset, int n)
+{
+ NOTFREE(p);
+ if(offset < 0 || n < 0)
+ abort();
+ if(offset > p->len)
+ abort();
+ if(offset+n > p->len)
+ n = p->len - offset;
+ memmove(buf, p->data+offset, n);
+ return 0;
+}
+
+Packet*
+packetdup(Packet *p, int offset, int n)
+{
+ Packet *q;
+
+ NOTFREE(p);
+ if(offset < 0 || n < 0)
+ abort();
+ if(offset > p->len)
+ abort();
+ if(offset+n > p->len)
+ n = p->len - offset;
+ q = packetalloc();
+ packetappend(q, p->data+offset, n);
+ return q;
+}
+
+Packet*
+packetforeign(uchar *buf, int n, void (*free)(void*), void *a)
+{
+ Packet *p;
+
+ if(n < 0)
+ abort();
+ p = packetalloc();
+ p->data = (char*)buf;
+ p->len = n;
+ p->free = free;
+ p->arg = a;
+ return p;
+}
+
+int
+packetfragments(Packet *p, IOchunk *io, int nio, int offset)
+{
+ NOTFREE(p);
+ if(offset < 0)
+ abort();
+ if(nio == 0)
+ return 0;
+ memset(io, 0, sizeof(io[0])*nio);
+ if(offset >= p->len)
+ return 0;
+ io[0].addr = p->data + offset;
+ io[0].len = p->len - offset;
+ return p->len;
+}
+
+void
+packetfree(Packet *p)
+{
+ NOTFREE(p);
+ if(p->free == free)
+ memset(p->data, 0xFE, p->len);
+ p->free(p->arg);
+ p->data = nil;
+ p->len = 0;
+ memset(p, 0xFB, sizeof *p);
+ free(p);
+}
+
+uchar*
+packetheader(Packet *p, int n)
+{
+ NOTFREE(p);
+ if(n < 0)
+ abort();
+ if(n > p->len)
+ abort();
+ return p->data;
+}
+
+uchar*
+packetpeek(Packet *p, uchar *buf, int offset, int n)
+{
+ NOTFREE(p);
+ if(offset < 0 || n < 0)
+ abort();
+ if(offset+n > p->len)
+ abort();
+ return p->data+offset;
+}
+
+void
+packetprefix(Packet *p, uchar *buf, int n)
+{
+ NOTFREE(p);
+ if(n < 0)
+ abort();
+ if(p->free != free)
+ sysfatal("packetappend");
+ p->data = vtrealloc(p->data, p->len+n);
+ p->arg = p->data;
+ memmove(p->data+n, p->data, p->len);
+ memmove(p->data, buf, n);
+ p->len += n;
+}
+
+void
+packetsha1(Packet *p, uchar d[20])
+{
+ NOTFREE(p);
+ sha1((uchar*)p->data, p->len, d, nil);
+}
+
+uint
+packetsize(Packet *p)
+{
+ NOTFREE(p);
+ return p->len;
+}
+
+Packet*
+packetsplit(Packet *p, int n)
+{
+ Packet *q;
+
+ NOTFREE(p);
+ q = packetalloc();
+ q->data = vtmalloc(n);
+ q->arg = q->data;
+ q->free = vtfree;
+ packetconsume(p, q->data, n);
+ return q;
+}
+
+void
+packetstats(void)
+{
+}
+
+uchar*
+packettrailer(Packet *p, int n)
+{
+ NOTFREE(p);
+ if(n < 0)
+ abort();
+ if(n > p->len)
+ abort();
+ return p->data + p->len - n;
+}
+
+int
+packettrim(Packet *p, int offset, int n)
+{
+ NOTFREE(p);
+ if(offset < 0 || n < 0)
+ abort();
+ if(offset+n > p->len)
+ abort();
+ memmove(p->data+offset, p->data+offset+n, p->len-offset-n);
+ p->len -= n;
+ return 0;
+}
+
diff --git a/sys/src/libventi/dial.c b/sys/src/libventi/dial.c
new file mode 100755
index 000000000..060b93f61
--- /dev/null
+++ b/sys/src/libventi/dial.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+VtConn*
+vtdial(char *addr)
+{
+ char *na;
+ int fd;
+ VtConn *z;
+
+ if(addr == nil)
+ addr = getenv("venti");
+ if(addr == nil)
+ addr = "$venti";
+
+ na = netmkaddr(addr, "tcp", "venti");
+ if((fd = dial(na, nil, nil, nil)) < 0)
+ return nil;
+
+ z = vtconn(fd, fd);
+ if(z)
+ strecpy(z->addr, z->addr+sizeof z->addr, na);
+ return z;
+}
diff --git a/sys/src/libventi/dtype.c b/sys/src/libventi/dtype.c
new file mode 100755
index 000000000..7886518e2
--- /dev/null
+++ b/sys/src/libventi/dtype.c
@@ -0,0 +1,78 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+enum {
+ OVtErrType, /* illegal */
+
+ OVtRootType,
+ OVtDirType,
+ OVtPointerType0,
+ OVtPointerType1,
+ OVtPointerType2,
+ OVtPointerType3,
+ OVtPointerType4,
+ OVtPointerType5,
+ OVtPointerType6,
+ OVtPointerType7, /* not used */
+ OVtPointerType8, /* not used */
+ OVtPointerType9, /* not used */
+ OVtDataType,
+
+ OVtMaxType
+};
+
+
+uint todisk[] = {
+ OVtDataType,
+ OVtPointerType0,
+ OVtPointerType1,
+ OVtPointerType2,
+ OVtPointerType3,
+ OVtPointerType4,
+ OVtPointerType5,
+ OVtPointerType6,
+ OVtDirType,
+ OVtPointerType0,
+ OVtPointerType1,
+ OVtPointerType2,
+ OVtPointerType3,
+ OVtPointerType4,
+ OVtPointerType5,
+ OVtPointerType6,
+ OVtRootType,
+};
+
+uint fromdisk[] = {
+ VtCorruptType,
+ VtRootType,
+ VtDirType,
+ VtDirType+1,
+ VtDirType+2,
+ VtDirType+3,
+ VtDirType+4,
+ VtDirType+5,
+ VtDirType+6,
+ VtDirType+7,
+ VtCorruptType,
+ VtCorruptType,
+ VtCorruptType,
+ VtDataType,
+};
+
+uint
+vttodisktype(uint n)
+{
+ if(n >= nelem(todisk))
+ return VtCorruptType;
+ return todisk[n];
+}
+
+uint
+vtfromdisktype(uint n)
+{
+ if(n >= nelem(fromdisk))
+ return VtCorruptType;
+ return fromdisk[n];
+}
+
diff --git a/sys/src/libventi/entry.c b/sys/src/libventi/entry.c
new file mode 100755
index 000000000..aeb8dada3
--- /dev/null
+++ b/sys/src/libventi/entry.c
@@ -0,0 +1,95 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+#include "cvt.h"
+
+static int
+checksize(int n)
+{
+ if(n < 256 || n > VtMaxLumpSize) {
+ werrstr("bad block size %#ux", n);
+ return -1;
+ }
+ return 0;
+}
+
+void
+vtentrypack(VtEntry *e, uchar *p, int index)
+{
+ ulong t32;
+ int flags;
+ uchar *op;
+ int depth;
+
+ p += index * VtEntrySize;
+ op = p;
+
+ U32PUT(p, e->gen);
+ p += 4;
+ U16PUT(p, e->psize);
+ p += 2;
+ U16PUT(p, e->dsize);
+ p += 2;
+ depth = e->type&VtTypeDepthMask;
+ flags = (e->flags&~(_VtEntryDir|_VtEntryDepthMask));
+ flags |= depth << _VtEntryDepthShift;
+ if(e->type - depth == VtDirType)
+ flags |= _VtEntryDir;
+ 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->type = (e->flags&_VtEntryDir) ? VtDirType : VtDataType;
+ e->type += (e->flags & _VtEntryDepthMask) >> _VtEntryDepthShift;
+ e->flags &= ~(_VtEntryDir|_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 0;
+
+ /*
+ * Some old vac files use psize==0 and dsize==0 when the
+ * file itself has size 0 or is zeros. Just to make programs not
+ * have to figure out what block sizes of 0 means, rewrite them.
+ */
+ if(e->psize == 0 && e->dsize == 0
+ && memcmp(e->score, vtzeroscore, VtScoreSize) == 0){
+ e->psize = 4096;
+ e->dsize = 4096;
+ }
+ if(checksize(e->psize) < 0 || checksize(e->dsize) < 0)
+ return -1;
+
+ return 0;
+}
+
diff --git a/sys/src/libventi/fcall.c b/sys/src/libventi/fcall.c
new file mode 100755
index 000000000..65ee2d178
--- /dev/null
+++ b/sys/src/libventi/fcall.c
@@ -0,0 +1,231 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+Packet*
+vtfcallpack(VtFcall *f)
+{
+ uchar buf[4];
+ Packet *p;
+
+ p = packetalloc();
+
+ buf[0] = f->msgtype;
+ buf[1] = f->tag;
+ packetappend(p, buf, 2);
+
+ switch(f->msgtype){
+ default:
+ werrstr("vtfcallpack: unknown packet type %d", f->msgtype);
+ goto Err;
+
+ case VtRerror:
+ if(vtputstring(p, f->error) < 0)
+ goto Err;
+ break;
+
+ case VtTping:
+ break;
+
+ case VtRping:
+ break;
+
+ case VtThello:
+ if(vtputstring(p, f->version) < 0
+ || vtputstring(p, f->uid) < 0)
+ goto Err;
+ buf[0] = f->strength;
+ buf[1] = f->ncrypto;
+ packetappend(p, buf, 2);
+ packetappend(p, f->crypto, f->ncrypto);
+ buf[0] = f->ncodec;
+ packetappend(p, buf, 1);
+ packetappend(p, f->codec, f->ncodec);
+ break;
+
+ case VtRhello:
+ if(vtputstring(p, f->sid) < 0)
+ goto Err;
+ buf[0] = f->rcrypto;
+ buf[1] = f->rcodec;
+ packetappend(p, buf, 2);
+ break;
+
+ case VtTgoodbye:
+ break;
+
+ case VtTread:
+ packetappend(p, f->score, VtScoreSize);
+ buf[0] = vttodisktype(f->blocktype);
+ if(~buf[0] == 0)
+ goto Err;
+ buf[1] = 0;
+ buf[2] = f->count >> 8;
+ buf[3] = f->count;
+ packetappend(p, buf, 4);
+ break;
+
+ case VtRread:
+ packetconcat(p, f->data);
+ break;
+
+ case VtTwrite:
+ buf[0] = vttodisktype(f->blocktype);
+ if(~buf[0] == 0)
+ goto Err;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ packetappend(p, buf, 4);
+ packetconcat(p, f->data);
+ break;
+
+ case VtRwrite:
+ packetappend(p, f->score, VtScoreSize);
+ break;
+
+ case VtTsync:
+ break;
+
+ case VtRsync:
+ break;
+ }
+
+ return p;
+
+Err:
+ packetfree(p);
+ return nil;
+}
+
+int
+vtfcallunpack(VtFcall *f, Packet *p)
+{
+ uchar buf[4];
+
+ memset(f, 0, sizeof *f);
+
+ if(packetconsume(p, buf, 2) < 0)
+ return -1;
+
+ f->msgtype = buf[0];
+ f->tag = buf[1];
+
+ switch(f->msgtype){
+ default:
+ werrstr("vtfcallunpack: unknown bad packet type %d", f->msgtype);
+ vtfcallclear(f);
+ return -1;
+
+ case VtRerror:
+ if(vtgetstring(p, &f->error) < 0)
+ goto Err;
+ break;
+
+ case VtTping:
+ break;
+
+ case VtRping:
+ break;
+
+ case VtThello:
+ if(vtgetstring(p, &f->version) < 0
+ || vtgetstring(p, &f->uid) < 0
+ || packetconsume(p, buf, 2) < 0)
+ goto Err;
+ f->strength = buf[0];
+ f->ncrypto = buf[1];
+ if(f->ncrypto){
+ f->crypto = vtmalloc(f->ncrypto);
+ if(packetconsume(p, buf, f->ncrypto) < 0)
+ goto Err;
+ }
+ if(packetconsume(p, buf, 1) < 0)
+ goto Err;
+ f->ncodec = buf[0];
+ if(f->ncodec){
+ f->codec = vtmalloc(f->ncodec);
+ if(packetconsume(p, buf, f->ncodec) < 0)
+ goto Err;
+ }
+ break;
+
+ case VtRhello:
+ if(vtgetstring(p, &f->sid) < 0
+ || packetconsume(p, buf, 2) < 0)
+ goto Err;
+ f->rcrypto = buf[0];
+ f->rcodec = buf[1];
+ break;
+
+ case VtTgoodbye:
+ break;
+
+ case VtTread:
+ if(packetconsume(p, f->score, VtScoreSize) < 0
+ || packetconsume(p, buf, 4) < 0)
+ goto Err;
+ f->blocktype = vtfromdisktype(buf[0]);
+ if(~f->blocktype == 0)
+ goto Err;
+ f->count = (buf[2] << 8) | buf[3];
+ break;
+
+ case VtRread:
+ f->data = packetalloc();
+ packetconcat(f->data, p);
+ break;
+
+ case VtTwrite:
+ if(packetconsume(p, buf, 4) < 0)
+ goto Err;
+ f->blocktype = vtfromdisktype(buf[0]);
+ if(~f->blocktype == 0)
+ goto Err;
+ f->data = packetalloc();
+ packetconcat(f->data, p);
+ break;
+
+ case VtRwrite:
+ if(packetconsume(p, f->score, VtScoreSize) < 0)
+ goto Err;
+ break;
+
+ case VtTsync:
+ break;
+
+ case VtRsync:
+ break;
+ }
+
+ if(packetsize(p) != 0)
+ goto Err;
+
+ return 0;
+
+Err:
+ werrstr("bad packet");
+ vtfcallclear(f);
+ return -1;
+}
+
+void
+vtfcallclear(VtFcall *f)
+{
+ vtfree(f->error);
+ f->error = nil;
+ vtfree(f->uid);
+ f->uid = nil;
+ vtfree(f->sid);
+ f->sid = nil;
+ vtfree(f->version);
+ f->version = nil;
+ vtfree(f->crypto);
+ f->crypto = nil;
+ vtfree(f->codec);
+ f->codec = nil;
+ vtfree(f->auth);
+ f->auth = nil;
+ packetfree(f->data);
+ f->data = nil;
+}
diff --git a/sys/src/libventi/fcallfmt.c b/sys/src/libventi/fcallfmt.c
new file mode 100755
index 000000000..9b493f9aa
--- /dev/null
+++ b/sys/src/libventi/fcallfmt.c
@@ -0,0 +1,55 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+int
+vtfcallfmt(Fmt *f)
+{
+ VtFcall *t;
+
+ t = va_arg(f->args, VtFcall*);
+ if(t == nil){
+ fmtprint(f, "<nil fcall>");
+ return 0;
+ }
+ switch(t->msgtype){
+ default:
+ return fmtprint(f, "%c%d tag %ud", "TR"[t->msgtype&1], t->msgtype>>1, t->tag);
+ case VtRerror:
+ return fmtprint(f, "Rerror tag %ud error %s", t->tag, t->error);
+ case VtTping:
+ return fmtprint(f, "Tping tag %ud", t->tag);
+ case VtRping:
+ return fmtprint(f, "Rping tag %ud", t->tag);
+ case VtThello:
+ return fmtprint(f, "Thello tag %ud vers %s uid %s strength %d crypto %d:%.*H codec %d:%.*H", t->tag,
+ t->version, t->uid, t->strength, t->ncrypto, t->ncrypto, t->crypto,
+ t->ncodec, t->ncodec, t->codec);
+ case VtRhello:
+ return fmtprint(f, "Rhello tag %ud sid %s rcrypto %d rcodec %d", t->tag, t->sid, t->rcrypto, t->rcodec);
+ case VtTgoodbye:
+ return fmtprint(f, "Tgoodbye tag %ud", t->tag);
+ case VtRgoodbye:
+ return fmtprint(f, "Rgoodbye tag %ud", t->tag);
+ case VtTauth0:
+ return fmtprint(f, "Tauth0 tag %ud auth %.*H", t->tag, t->nauth, t->auth);
+ case VtRauth0:
+ return fmtprint(f, "Rauth0 tag %ud auth %.*H", t->tag, t->nauth, t->auth);
+ case VtTauth1:
+ return fmtprint(f, "Tauth1 tag %ud auth %.*H", t->tag, t->nauth, t->auth);
+ case VtRauth1:
+ return fmtprint(f, "Rauth1 tag %ud auth %.*H", t->tag, t->nauth, t->auth);
+ case VtTread:
+ return fmtprint(f, "Tread tag %ud score %V blocktype %d count %d", t->tag, t->score, t->blocktype, t->count);
+ case VtRread:
+ return fmtprint(f, "Rread tag %ud count %d", t->tag, packetsize(t->data));
+ case VtTwrite:
+ return fmtprint(f, "Twrite tag %ud blocktype %d count %d", t->tag, t->blocktype, packetsize(t->data));
+ case VtRwrite:
+ return fmtprint(f, "Rwrite tag %ud score %V", t->tag, t->score);
+ case VtTsync:
+ return fmtprint(f, "Tsync tag %ud", t->tag);
+ case VtRsync:
+ return fmtprint(f, "Rsync tag %ud", t->tag);
+ }
+}
diff --git a/sys/src/libventi/file.c b/sys/src/libventi/file.c
new file mode 100755
index 000000000..b6887dec7
--- /dev/null
+++ b/sys/src/libventi/file.c
@@ -0,0 +1,1282 @@
+/*
+ * Manage tree of VtFiles stored in the block cache.
+ *
+ * The single point of truth for the info about the VtFiles themselves
+ * is the block data. Because of this, there is no explicit locking of
+ * VtFile structures, and indeed there may be more than one VtFile
+ * structure for a given Venti file. They synchronize through the
+ * block cache.
+ *
+ * This is a bit simpler than fossil because there are no epochs
+ * or tags or anything else. Just mutable local blocks and immutable
+ * Venti blocks.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+#define MaxBlock (1UL<<31)
+
+static char ENotDir[] = "walk in non-directory";
+static char ETooBig[] = "file too big";
+/* static char EBadAddr[] = "bad address"; */
+static char ELabelMismatch[] = "label mismatch";
+
+static int sizetodepth(uvlong s, int psize, int dsize);
+static VtBlock *fileload(VtFile *r, VtEntry *e);
+static int shrinkdepth(VtFile*, VtBlock*, VtEntry*, int);
+static int shrinksize(VtFile*, VtEntry*, uvlong);
+static int growdepth(VtFile*, VtBlock*, VtEntry*, int);
+
+#define ISLOCKED(r) ((r)->b != nil)
+#define DEPTH(t) ((t)&VtTypeDepthMask)
+
+static VtFile *
+vtfilealloc(VtCache *c, VtBlock *b, VtFile *p, u32int offset, int mode)
+{
+ int epb;
+ u32int size;
+ VtEntry e;
+ VtFile *r;
+
+ assert(p==nil || ISLOCKED(p));
+
+ if(p == nil){
+ assert(offset == 0);
+ epb = 1;
+ }else
+ epb = p->dsize / VtEntrySize;
+
+ if(b->type != VtDirType){
+ werrstr("bad block type %#uo", b->type);
+ return nil;
+ }
+
+ /*
+ * a non-active entry is the only thing that
+ * can legitimately happen here. all the others
+ * get prints.
+ */
+ if(vtentryunpack(&e, b->data, offset % epb) < 0){
+ fprint(2, "vtentryunpack failed: %r (%.*H)\n", VtEntrySize, b->data+VtEntrySize*(offset%epb));
+ return nil;
+ }
+ if(!(e.flags & VtEntryActive)){
+ werrstr("entry not active");
+ return nil;
+ }
+
+ if(DEPTH(e.type) < sizetodepth(e.size, e.psize, e.dsize)){
+ fprint(2, "depth %ud size %llud psize %ud dsize %ud\n",
+ DEPTH(e.type), e.size, e.psize, e.dsize);
+ werrstr("bad depth");
+ return nil;
+ }
+
+ size = vtcacheblocksize(c);
+ if(e.dsize > size || e.psize > size){
+ werrstr("block sizes %ud, %ud bigger than cache block size %ud",
+ e.psize, e.dsize, size);
+ return nil;
+ }
+
+ r = vtmallocz(sizeof(VtFile));
+ r->c = c;
+ r->mode = mode;
+ r->dsize = e.dsize;
+ r->psize = e.psize;
+ r->gen = e.gen;
+ r->dir = (e.type & VtTypeBaseMask) == VtDirType;
+ r->ref = 1;
+ r->parent = p;
+ if(p){
+ qlock(&p->lk);
+ assert(mode == VtOREAD || p->mode == VtORDWR);
+ p->ref++;
+ qunlock(&p->lk);
+ }else{
+ assert(b->addr != NilBlock);
+ r->local = 1;
+ }
+ memmove(r->score, b->score, VtScoreSize);
+ r->offset = offset;
+ r->epb = epb;
+
+ return r;
+}
+
+VtFile *
+vtfileroot(VtCache *c, u32int addr, int mode)
+{
+ VtFile *r;
+ VtBlock *b;
+
+ b = vtcachelocal(c, addr, VtDirType);
+ if(b == nil)
+ return nil;
+ r = vtfilealloc(c, b, nil, 0, mode);
+ vtblockput(b);
+ return r;
+}
+
+VtFile*
+vtfileopenroot(VtCache *c, VtEntry *e)
+{
+ VtBlock *b;
+ VtFile *f;
+
+ b = vtcacheallocblock(c, VtDirType);
+ if(b == nil)
+ return nil;
+
+ vtentrypack(e, b->data, 0);
+ f = vtfilealloc(c, b, nil, 0, VtORDWR);
+ vtblockput(b);
+ return f;
+}
+
+VtFile *
+vtfilecreateroot(VtCache *c, int psize, int dsize, int type)
+{
+ VtEntry e;
+
+ memset(&e, 0, sizeof e);
+ e.flags = VtEntryActive;
+ e.psize = psize;
+ e.dsize = dsize;
+ e.type = type;
+ memmove(e.score, vtzeroscore, VtScoreSize);
+
+ return vtfileopenroot(c, &e);
+}
+
+VtFile *
+vtfileopen(VtFile *r, u32int offset, int mode)
+{
+ ulong bn;
+ VtBlock *b;
+
+ assert(ISLOCKED(r));
+ if(!r->dir){
+ werrstr(ENotDir);
+ return nil;
+ }
+
+ bn = offset/(r->dsize/VtEntrySize);
+
+ b = vtfileblock(r, bn, mode);
+ if(b == nil)
+ return nil;
+ r = vtfilealloc(r->c, b, r, offset, mode);
+ vtblockput(b);
+ return r;
+}
+
+VtFile*
+vtfilecreate(VtFile *r, int psize, int dsize, int type)
+{
+ return _vtfilecreate(r, -1, psize, dsize, type);
+}
+
+VtFile*
+_vtfilecreate(VtFile *r, int o, int psize, int dsize, int type)
+{
+ int i;
+ VtBlock *b;
+ u32int bn, size;
+ VtEntry e;
+ int epb;
+ VtFile *rr;
+ u32int offset;
+
+ assert(ISLOCKED(r));
+ assert(psize <= VtMaxLumpSize);
+ assert(dsize <= VtMaxLumpSize);
+ assert(type == VtDirType || type == VtDataType);
+
+ if(!r->dir){
+ werrstr(ENotDir);
+ return nil;
+ }
+
+ epb = r->dsize/VtEntrySize;
+
+ size = vtfilegetdirsize(r);
+ /*
+ * look at a random block to see if we can find an empty entry
+ */
+ if(o == -1){
+ offset = lnrand(size+1);
+ offset -= offset % epb;
+ }else
+ offset = o;
+
+ /* try the given block and then try the last block */
+ for(;;){
+ bn = offset/epb;
+ b = vtfileblock(r, bn, VtORDWR);
+ if(b == nil)
+ return nil;
+ for(i=offset%r->epb; i<epb; i++){
+ if(vtentryunpack(&e, b->data, i) < 0)
+ continue;
+ if((e.flags&VtEntryActive) == 0 && e.gen != ~0)
+ goto Found;
+ }
+ vtblockput(b);
+ if(offset == size){
+ fprint(2, "vtfilecreate: cannot happen\n");
+ werrstr("vtfilecreate: cannot happen");
+ return nil;
+ }
+ offset = size;
+ }
+
+Found:
+ /* found an entry - gen already set */
+ e.psize = psize;
+ e.dsize = dsize;
+ e.flags = VtEntryActive;
+ e.type = type;
+ e.size = 0;
+ memmove(e.score, vtzeroscore, VtScoreSize);
+ vtentrypack(&e, b->data, i);
+
+ offset = bn*epb + i;
+ if(offset+1 > size){
+ if(vtfilesetdirsize(r, offset+1) < 0){
+ vtblockput(b);
+ return nil;
+ }
+ }
+
+ rr = vtfilealloc(r->c, b, r, offset, VtORDWR);
+ vtblockput(b);
+ return rr;
+}
+
+static int
+vtfilekill(VtFile *r, int doremove)
+{
+ VtEntry e;
+ VtBlock *b;
+
+ assert(ISLOCKED(r));
+ b = fileload(r, &e);
+ if(b == nil)
+ return -1;
+
+ if(doremove==0 && e.size == 0){
+ /* already truncated */
+ vtblockput(b);
+ return 0;
+ }
+
+ if(doremove){
+ if(e.gen != ~0)
+ e.gen++;
+ e.dsize = 0;
+ e.psize = 0;
+ e.flags = 0;
+ }else
+ e.flags &= ~VtEntryLocal;
+ e.type = 0;
+ e.size = 0;
+ memmove(e.score, vtzeroscore, VtScoreSize);
+ vtentrypack(&e, b->data, r->offset % r->epb);
+ vtblockput(b);
+
+ if(doremove){
+ vtfileunlock(r);
+ vtfileclose(r);
+ }
+
+ return 0;
+}
+
+int
+vtfileremove(VtFile *r)
+{
+ return vtfilekill(r, 1);
+}
+
+int
+vtfiletruncate(VtFile *r)
+{
+ return vtfilekill(r, 0);
+}
+
+uvlong
+vtfilegetsize(VtFile *r)
+{
+ VtEntry e;
+ VtBlock *b;
+
+ assert(ISLOCKED(r));
+ b = fileload(r, &e);
+ if(b == nil)
+ return ~(uvlong)0;
+ vtblockput(b);
+
+ return e.size;
+}
+
+static int
+shrinksize(VtFile *r, VtEntry *e, uvlong size)
+{
+ int i, depth, type, isdir, ppb;
+ uvlong ptrsz;
+ uchar score[VtScoreSize];
+ VtBlock *b;
+
+ b = vtcacheglobal(r->c, e->score, e->type);
+ if(b == nil)
+ return -1;
+
+ ptrsz = e->dsize;
+ ppb = e->psize/VtScoreSize;
+ type = e->type;
+ depth = DEPTH(type);
+ for(i=0; i+1<depth; i++)
+ ptrsz *= ppb;
+
+ isdir = r->dir;
+ while(depth > 0){
+ if(b->addr == NilBlock){
+ /* not worth copying the block just so we can zero some of it */
+ vtblockput(b);
+ return -1;
+ }
+
+ /*
+ * invariant: each pointer in the tree rooted at b accounts for ptrsz bytes
+ */
+
+ /* zero the pointers to unnecessary blocks */
+ i = (size+ptrsz-1)/ptrsz;
+ for(; i<ppb; i++)
+ memmove(b->data+i*VtScoreSize, vtzeroscore, VtScoreSize);
+
+ /* recurse (go around again) on the partially necessary block */
+ i = size/ptrsz;
+ size = size%ptrsz;
+ if(size == 0){
+ vtblockput(b);
+ return 0;
+ }
+ ptrsz /= ppb;
+ type--;
+ memmove(score, b->data+i*VtScoreSize, VtScoreSize);
+ vtblockput(b);
+ b = vtcacheglobal(r->c, score, type);
+ if(b == nil)
+ return -1;
+ }
+
+ if(b->addr == NilBlock){
+ vtblockput(b);
+ return -1;
+ }
+
+ /*
+ * No one ever truncates BtDir blocks.
+ */
+ if(depth==0 && !isdir && e->dsize > size)
+ memset(b->data+size, 0, e->dsize-size);
+ vtblockput(b);
+ return 0;
+}
+
+int
+vtfilesetsize(VtFile *r, u64int size)
+{
+ int depth, edepth;
+ VtEntry e;
+ VtBlock *b;
+
+ assert(ISLOCKED(r));
+ if(size == 0)
+ return vtfiletruncate(r);
+
+ if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){
+ werrstr(ETooBig);
+ return -1;
+ }
+
+ b = fileload(r, &e);
+ if(b == nil)
+ return -1;
+
+ /* quick out */
+ if(e.size == size){
+ vtblockput(b);
+ return 0;
+ }
+
+ depth = sizetodepth(size, e.psize, e.dsize);
+ edepth = DEPTH(e.type);
+ if(depth < edepth){
+ if(shrinkdepth(r, b, &e, depth) < 0){
+ vtblockput(b);
+ return -1;
+ }
+ }else if(depth > edepth){
+ if(growdepth(r, b, &e, depth) < 0){
+ vtblockput(b);
+ return -1;
+ }
+ }
+
+ if(size < e.size)
+ shrinksize(r, &e, size);
+
+ e.size = size;
+ vtentrypack(&e, b->data, r->offset % r->epb);
+ vtblockput(b);
+
+ return 0;
+}
+
+int
+vtfilesetdirsize(VtFile *r, u32int ds)
+{
+ uvlong size;
+ int epb;
+
+ assert(ISLOCKED(r));
+ epb = r->dsize/VtEntrySize;
+
+ size = (uvlong)r->dsize*(ds/epb);
+ size += VtEntrySize*(ds%epb);
+ return vtfilesetsize(r, size);
+}
+
+u32int
+vtfilegetdirsize(VtFile *r)
+{
+ ulong ds;
+ uvlong size;
+ int epb;
+
+ assert(ISLOCKED(r));
+ epb = r->dsize/VtEntrySize;
+
+ size = vtfilegetsize(r);
+ ds = epb*(size/r->dsize);
+ ds += (size%r->dsize)/VtEntrySize;
+ return ds;
+}
+
+int
+vtfilegetentry(VtFile *r, VtEntry *e)
+{
+ VtBlock *b;
+
+ assert(ISLOCKED(r));
+ b = fileload(r, e);
+ if(b == nil)
+ return -1;
+ vtblockput(b);
+
+ return 0;
+}
+
+int
+vtfilesetentry(VtFile *r, VtEntry *e)
+{
+ VtBlock *b;
+ VtEntry ee;
+
+ assert(ISLOCKED(r));
+ b = fileload(r, &ee);
+ if(b == nil)
+ return -1;
+ vtentrypack(e, b->data, r->offset % r->epb);
+ vtblockput(b);
+ return 0;
+}
+
+static VtBlock *
+blockwalk(VtBlock *p, int index, VtCache *c, int mode, VtEntry *e)
+{
+ VtBlock *b;
+ int type;
+ uchar *score;
+ VtEntry oe;
+
+ switch(p->type){
+ case VtDataType:
+ assert(0);
+ case VtDirType:
+ type = e->type;
+ score = e->score;
+ break;
+ default:
+ type = p->type - 1;
+ score = p->data+index*VtScoreSize;
+ break;
+ }
+/*print("walk from %V/%d ty %d to %V ty %d\n", p->score, index, p->type, score, type); */
+
+ if(mode == VtOWRITE && vtglobaltolocal(score) == NilBlock){
+ b = vtcacheallocblock(c, type);
+ if(b)
+ goto HaveCopy;
+ }else
+ b = vtcacheglobal(c, score, type);
+
+ if(b == nil || mode == VtOREAD)
+ return b;
+
+ if(vtglobaltolocal(b->score) != NilBlock)
+ return b;
+
+ oe = *e;
+
+ /*
+ * Copy on write.
+ */
+ e->flags |= VtEntryLocal;
+
+ b = vtblockcopy(b/*, e->tag, fs->ehi, fs->elo*/);
+ if(b == nil)
+ return nil;
+
+HaveCopy:
+ if(p->type == VtDirType){
+ memmove(e->score, b->score, VtScoreSize);
+ vtentrypack(e, p->data, index);
+ }else{
+ memmove(p->data+index*VtScoreSize, b->score, VtScoreSize);
+ }
+ return b;
+}
+
+/*
+ * Change the depth of the VtFile r.
+ * The entry e for r is contained in block p.
+ */
+static int
+growdepth(VtFile *r, VtBlock *p, VtEntry *e, int depth)
+{
+ VtBlock *b, *bb;
+ VtEntry oe;
+
+ assert(ISLOCKED(r));
+ assert(depth <= VtPointerDepth);
+
+ b = vtcacheglobal(r->c, e->score, e->type);
+ if(b == nil)
+ return -1;
+
+ oe = *e;
+
+ /*
+ * Keep adding layers until we get to the right depth
+ * or an error occurs.
+ */
+ while(DEPTH(e->type) < depth){
+ bb = vtcacheallocblock(r->c, e->type+1);
+ if(bb == nil)
+ break;
+ memmove(bb->data, b->score, VtScoreSize);
+ memmove(e->score, bb->score, VtScoreSize);
+ e->type++;
+ e->flags |= VtEntryLocal;
+ vtblockput(b);
+ b = bb;
+ }
+
+ vtentrypack(e, p->data, r->offset % r->epb);
+ vtblockput(b);
+
+ if(DEPTH(e->type) == depth)
+ return 0;
+ return -1;
+}
+
+static int
+shrinkdepth(VtFile *r, VtBlock *p, VtEntry *e, int depth)
+{
+ VtBlock *b, *nb, *ob, *rb;
+ VtEntry oe;
+
+ assert(ISLOCKED(r));
+ assert(depth <= VtPointerDepth);
+
+ rb = vtcacheglobal(r->c, e->score, e->type);
+ if(rb == nil)
+ return -1;
+
+ /*
+ * Walk down to the new root block.
+ * We may stop early, but something is better than nothing.
+ */
+ oe = *e;
+
+ ob = nil;
+ b = rb;
+ for(; DEPTH(e->type) > depth; e->type--){
+ nb = vtcacheglobal(r->c, b->data, e->type-1);
+ if(nb == nil)
+ break;
+ if(ob!=nil && ob!=rb)
+ vtblockput(ob);
+ ob = b;
+ b = nb;
+ }
+
+ if(b == rb){
+ vtblockput(rb);
+ return 0;
+ }
+
+ /*
+ * Right now, e points at the root block rb, b is the new root block,
+ * and ob points at b. To update:
+ *
+ * (i) change e to point at b
+ * (ii) zero the pointer ob -> b
+ * (iii) free the root block
+ *
+ * p (the block containing e) must be written before
+ * anything else.
+ */
+
+ /* (i) */
+ memmove(e->score, b->score, VtScoreSize);
+ vtentrypack(e, p->data, r->offset % r->epb);
+
+ /* (ii) */
+ memmove(ob->data, vtzeroscore, VtScoreSize);
+
+ /* (iii) */
+ vtblockput(rb);
+ if(ob!=nil && ob!=rb)
+ vtblockput(ob);
+ vtblockput(b);
+
+ if(DEPTH(e->type) == depth)
+ return 0;
+ return -1;
+}
+
+static int
+mkindices(VtEntry *e, u32int bn, int *index)
+{
+ int i, np;
+
+ memset(index, 0, (VtPointerDepth+1)*sizeof(int));
+
+ np = e->psize/VtScoreSize;
+ for(i=0; bn > 0; i++){
+ if(i >= VtPointerDepth){
+ werrstr("bad address 0x%lux", (ulong)bn);
+ return -1;
+ }
+ index[i] = bn % np;
+ bn /= np;
+ }
+ return i;
+}
+
+VtBlock *
+vtfileblock(VtFile *r, u32int bn, int mode)
+{
+ VtBlock *b, *bb;
+ int index[VtPointerDepth+1];
+ VtEntry e;
+ int i;
+ int m;
+
+ assert(ISLOCKED(r));
+ assert(bn != NilBlock);
+
+ b = fileload(r, &e);
+ if(b == nil)
+ return nil;
+
+ i = mkindices(&e, bn, index);
+ if(i < 0)
+ goto Err;
+ if(i > DEPTH(e.type)){
+ if(mode == VtOREAD){
+ werrstr("bad address 0x%lux", (ulong)bn);
+ goto Err;
+ }
+ index[i] = 0;
+ if(growdepth(r, b, &e, i) < 0)
+ goto Err;
+ }
+
+assert(b->type == VtDirType);
+
+ index[DEPTH(e.type)] = r->offset % r->epb;
+
+ /* mode for intermediate block */
+ m = mode;
+ if(m == VtOWRITE)
+ m = VtORDWR;
+
+ for(i=DEPTH(e.type); i>=0; i--){
+ bb = blockwalk(b, index[i], r->c, i==0 ? mode : m, &e);
+ if(bb == nil)
+ goto Err;
+ vtblockput(b);
+ b = bb;
+ }
+ b->pc = getcallerpc(&r);
+ return b;
+Err:
+ vtblockput(b);
+ return nil;
+}
+
+int
+vtfileblockscore(VtFile *r, u32int bn, uchar score[VtScoreSize])
+{
+ VtBlock *b, *bb;
+ int index[VtPointerDepth+1];
+ VtEntry e;
+ int i;
+
+ assert(ISLOCKED(r));
+ assert(bn != NilBlock);
+
+ b = fileload(r, &e);
+ if(b == nil)
+ return -1;
+
+ if(DEPTH(e.type) == 0){
+ memmove(score, e.score, VtScoreSize);
+ vtblockput(b);
+ return 0;
+ }
+
+ i = mkindices(&e, bn, index);
+ if(i < 0){
+ vtblockput(b);
+ return -1;
+ }
+ if(i > DEPTH(e.type)){
+ memmove(score, vtzeroscore, VtScoreSize);
+ vtblockput(b);
+ return 0;
+ }
+
+ index[DEPTH(e.type)] = r->offset % r->epb;
+
+ for(i=DEPTH(e.type); i>=1; i--){
+ bb = blockwalk(b, index[i], r->c, VtOREAD, &e);
+ if(bb == nil)
+ goto Err;
+ vtblockput(b);
+ b = bb;
+ if(memcmp(b->score, vtzeroscore, VtScoreSize) == 0)
+ break;
+ }
+
+ memmove(score, b->data+index[0]*VtScoreSize, VtScoreSize);
+ vtblockput(b);
+ return 0;
+
+Err:
+ vtblockput(b);
+ return -1;
+}
+
+void
+vtfileincref(VtFile *r)
+{
+ qlock(&r->lk);
+ r->ref++;
+ qunlock(&r->lk);
+}
+
+void
+vtfileclose(VtFile *r)
+{
+ if(r == nil)
+ return;
+ qlock(&r->lk);
+ r->ref--;
+ if(r->ref){
+ qunlock(&r->lk);
+ return;
+ }
+ assert(r->ref == 0);
+ qunlock(&r->lk);
+ if(r->parent)
+ vtfileclose(r->parent);
+ memset(r, ~0, sizeof(*r));
+ vtfree(r);
+}
+
+/*
+ * Retrieve the block containing the entry for r.
+ * If a snapshot has happened, we might need
+ * to get a new copy of the block. We avoid this
+ * in the common case by caching the score for
+ * the block and the last epoch in which it was valid.
+ *
+ * We use r->mode to tell the difference between active
+ * file system VtFiles (VtORDWR) and VtFiles for the
+ * snapshot file system (VtOREAD).
+ */
+static VtBlock*
+fileloadblock(VtFile *r, int mode)
+{
+ char e[ERRMAX];
+ u32int addr;
+ VtBlock *b;
+
+ switch(r->mode){
+ default:
+ assert(0);
+ case VtORDWR:
+ assert(r->mode == VtORDWR);
+ if(r->local == 1){
+ b = vtcacheglobal(r->c, r->score, VtDirType);
+ if(b == nil)
+ return nil;
+ b->pc = getcallerpc(&r);
+ return b;
+ }
+ assert(r->parent != nil);
+ if(vtfilelock(r->parent, VtORDWR) < 0)
+ return nil;
+ b = vtfileblock(r->parent, r->offset/r->epb, VtORDWR);
+ vtfileunlock(r->parent);
+ if(b == nil)
+ return nil;
+ memmove(r->score, b->score, VtScoreSize);
+ r->local = 1;
+ return b;
+
+ case VtOREAD:
+ if(mode == VtORDWR){
+ werrstr("read/write lock of read-only file");
+ return nil;
+ }
+ addr = vtglobaltolocal(r->score);
+ if(addr == NilBlock)
+ return vtcacheglobal(r->c, r->score, VtDirType);
+
+ b = vtcachelocal(r->c, addr, VtDirType);
+ if(b)
+ return b;
+
+ /*
+ * If it failed because the epochs don't match, the block has been
+ * archived and reclaimed. Rewalk from the parent and get the
+ * new pointer. This can't happen in the VtORDWR case
+ * above because blocks in the current epoch don't get
+ * reclaimed. The fact that we're VtOREAD means we're
+ * a snapshot. (Or else the file system is read-only, but then
+ * the archiver isn't going around deleting blocks.)
+ */
+ rerrstr(e, sizeof e);
+ if(strcmp(e, ELabelMismatch) == 0){
+ if(vtfilelock(r->parent, VtOREAD) < 0)
+ return nil;
+ b = vtfileblock(r->parent, r->offset/r->epb, VtOREAD);
+ vtfileunlock(r->parent);
+ if(b){
+ fprint(2, "vtfilealloc: lost %V found %V\n",
+ r->score, b->score);
+ memmove(r->score, b->score, VtScoreSize);
+ return b;
+ }
+ }
+ return nil;
+ }
+}
+
+int
+vtfilelock(VtFile *r, int mode)
+{
+ VtBlock *b;
+
+ if(mode == -1)
+ mode = r->mode;
+
+ b = fileloadblock(r, mode);
+ if(b == nil)
+ return -1;
+ /*
+ * The fact that we are holding b serves as the
+ * lock entitling us to write to r->b.
+ */
+ assert(r->b == nil);
+ r->b = b;
+ b->pc = getcallerpc(&r);
+ return 0;
+}
+
+/*
+ * Lock two (usually sibling) VtFiles. This needs special care
+ * because the Entries for both vtFiles might be in the same block.
+ * We also try to lock blocks in left-to-right order within the tree.
+ */
+int
+vtfilelock2(VtFile *r, VtFile *rr, int mode)
+{
+ VtBlock *b, *bb;
+
+ if(rr == nil)
+ return vtfilelock(r, mode);
+
+ if(mode == -1)
+ mode = r->mode;
+
+ if(r->parent==rr->parent && r->offset/r->epb == rr->offset/rr->epb){
+ b = fileloadblock(r, mode);
+ if(b == nil)
+ return -1;
+ vtblockduplock(b);
+ bb = b;
+ }else if(r->parent==rr->parent || r->offset > rr->offset){
+ bb = fileloadblock(rr, mode);
+ b = fileloadblock(r, mode);
+ }else{
+ b = fileloadblock(r, mode);
+ bb = fileloadblock(rr, mode);
+ }
+ if(b == nil || bb == nil){
+ if(b)
+ vtblockput(b);
+ if(bb)
+ vtblockput(bb);
+ return -1;
+ }
+
+ /*
+ * The fact that we are holding b and bb serves
+ * as the lock entitling us to write to r->b and rr->b.
+ */
+ r->b = b;
+ rr->b = bb;
+ b->pc = getcallerpc(&r);
+ bb->pc = getcallerpc(&r);
+ return 0;
+}
+
+void
+vtfileunlock(VtFile *r)
+{
+ VtBlock *b;
+
+ if(r->b == nil){
+ fprint(2, "vtfileunlock: already unlocked\n");
+ abort();
+ }
+ b = r->b;
+ r->b = nil;
+ vtblockput(b);
+}
+
+static VtBlock*
+fileload(VtFile *r, VtEntry *e)
+{
+ VtBlock *b;
+
+ assert(ISLOCKED(r));
+ b = r->b;
+ if(vtentryunpack(e, b->data, r->offset % r->epb) < 0)
+ return nil;
+ vtblockduplock(b);
+ return b;
+}
+
+static int
+sizetodepth(uvlong s, int psize, int dsize)
+{
+ int np;
+ int d;
+
+ /* determine pointer depth */
+ np = psize/VtScoreSize;
+ s = (s + dsize - 1)/dsize;
+ for(d = 0; s > 1; d++)
+ s = (s + np - 1)/np;
+ return d;
+}
+
+long
+vtfileread(VtFile *f, void *data, long count, vlong offset)
+{
+ int frag;
+ VtBlock *b;
+ VtEntry e;
+
+ assert(ISLOCKED(f));
+
+ vtfilegetentry(f, &e);
+ if(count == 0)
+ return 0;
+ if(count < 0 || offset < 0){
+ werrstr("vtfileread: bad offset or count");
+ return -1;
+ }
+ if(offset >= e.size)
+ return 0;
+
+ if(offset+count > e.size)
+ count = e.size - offset;
+
+ frag = offset % e.dsize;
+ if(frag+count > e.dsize)
+ count = e.dsize - frag;
+
+ b = vtfileblock(f, offset/e.dsize, VtOREAD);
+ if(b == nil)
+ return -1;
+
+ memmove(data, b->data+frag, count);
+ vtblockput(b);
+ return count;
+}
+
+static long
+filewrite1(VtFile *f, void *data, long count, vlong offset)
+{
+ int frag, m;
+ VtBlock *b;
+ VtEntry e;
+
+ vtfilegetentry(f, &e);
+ if(count < 0 || offset < 0){
+ werrstr("vtfilewrite: bad offset or count");
+ return -1;
+ }
+
+ frag = offset % e.dsize;
+ if(frag+count > e.dsize)
+ count = e.dsize - frag;
+
+ m = VtORDWR;
+ if(frag == 0 && count == e.dsize)
+ m = VtOWRITE;
+
+ b = vtfileblock(f, offset/e.dsize, m);
+ if(b == nil)
+ return -1;
+
+ memmove(b->data+frag, data, count);
+ if(m == VtOWRITE && frag+count < e.dsize)
+ memset(b->data+frag+count, 0, e.dsize-frag-count);
+
+ if(offset+count > e.size){
+ vtfilegetentry(f, &e);
+ e.size = offset+count;
+ vtfilesetentry(f, &e);
+ }
+
+ vtblockput(b);
+ return count;
+}
+
+long
+vtfilewrite(VtFile *f, void *data, long count, vlong offset)
+{
+ long tot, m;
+
+ assert(ISLOCKED(f));
+
+ tot = 0;
+ m = 0;
+ while(tot < count){
+ m = filewrite1(f, (char*)data+tot, count-tot, offset+tot);
+ if(m <= 0)
+ break;
+ tot += m;
+ }
+ if(tot==0)
+ return m;
+ return tot;
+}
+
+static int
+flushblock(VtCache *c, VtBlock *bb, uchar score[VtScoreSize], int ppb, int epb,
+ int type)
+{
+ u32int addr;
+ VtBlock *b;
+ VtEntry e;
+ int i;
+
+ addr = vtglobaltolocal(score);
+ if(addr == NilBlock)
+ return 0;
+
+ if(bb){
+ b = bb;
+ if(memcmp(b->score, score, VtScoreSize) != 0)
+ abort();
+ }else
+ if((b = vtcachelocal(c, addr, type)) == nil)
+ return -1;
+
+ switch(type){
+ case VtDataType:
+ break;
+
+ case VtDirType:
+ for(i=0; i<epb; i++){
+ if(vtentryunpack(&e, b->data, i) < 0)
+ goto Err;
+ if(!(e.flags&VtEntryActive))
+ continue;
+ if(flushblock(c, nil, e.score, e.psize/VtScoreSize, e.dsize/VtEntrySize,
+ e.type) < 0)
+ goto Err;
+ vtentrypack(&e, b->data, i);
+ }
+ break;
+
+ default: /* VtPointerTypeX */
+ for(i=0; i<ppb; i++){
+ if(flushblock(c, nil, b->data+VtScoreSize*i, ppb, epb, type-1) < 0)
+ goto Err;
+ }
+ break;
+ }
+
+ if(vtblockwrite(b) < 0)
+ goto Err;
+ memmove(score, b->score, VtScoreSize);
+ if(b != bb)
+ vtblockput(b);
+ return 0;
+
+Err:
+ if(b != bb)
+ vtblockput(b);
+ return -1;
+}
+
+int
+vtfileflush(VtFile *f)
+{
+ int ret;
+ VtBlock *b;
+ VtEntry e;
+
+ assert(ISLOCKED(f));
+ b = fileload(f, &e);
+ if(!(e.flags&VtEntryLocal)){
+ vtblockput(b);
+ return 0;
+ }
+
+ ret = flushblock(f->c, nil, e.score, e.psize/VtScoreSize, e.dsize/VtEntrySize,
+ e.type);
+ if(ret < 0){
+ vtblockput(b);
+ return -1;
+ }
+
+ vtentrypack(&e, b->data, f->offset % f->epb);
+ vtblockput(b);
+ return 0;
+}
+
+int
+vtfileflushbefore(VtFile *r, u64int offset)
+{
+ VtBlock *b, *bb;
+ VtEntry e;
+ int i, base, depth, ppb, epb, doflush;
+ int index[VtPointerDepth+1], j, ret;
+ VtBlock *bi[VtPointerDepth+2];
+ uchar *score;
+
+ assert(ISLOCKED(r));
+ if(offset == 0)
+ return 0;
+
+ b = fileload(r, &e);
+ if(b == nil)
+ return -1;
+
+ /*
+ * compute path through tree for the last written byte and the next one.
+ */
+ ret = -1;
+ memset(bi, 0, sizeof bi);
+ depth = DEPTH(e.type);
+ bi[depth+1] = b;
+ i = mkindices(&e, (offset-1)/e.dsize, index);
+ if(i < 0)
+ goto Err;
+ if(i > depth)
+ goto Err;
+ ppb = e.psize / VtScoreSize;
+ epb = e.dsize / VtEntrySize;
+
+ /*
+ * load the blocks along the last written byte
+ */
+ index[depth] = r->offset % r->epb;
+ for(i=depth; i>=0; i--){
+ bb = blockwalk(b, index[i], r->c, VtORDWR, &e);
+ if(bb == nil)
+ goto Err;
+ bi[i] = bb;
+ b = bb;
+ }
+ ret = 0;
+
+ /*
+ * walk up the path from leaf to root, flushing anything that
+ * has been finished.
+ */
+ base = e.type&~VtTypeDepthMask;
+ for(i=0; i<=depth; i++){
+ doflush = 0;
+ if(i == 0){
+ /* leaf: data or dir block */
+ if(offset%e.dsize == 0)
+ doflush = 1;
+ }else{
+ /*
+ * interior node: pointer blocks.
+ * specifically, b = bi[i] is a block whose index[i-1]'th entry
+ * points at bi[i-1].
+ */
+ b = bi[i];
+
+ /*
+ * the index entries up to but not including index[i-1] point at
+ * finished blocks, so flush them for sure.
+ */
+ for(j=0; j<index[i-1]; j++)
+ if(flushblock(r->c, nil, b->data+j*VtScoreSize, ppb, epb, base+i-1) < 0)
+ goto Err;
+
+ /*
+ * if index[i-1] is the last entry in the block and is global
+ * (i.e. the kid is flushed), then we can flush this block.
+ */
+ if(j==ppb-1 && vtglobaltolocal(b->data+j*VtScoreSize)==NilBlock)
+ doflush = 1;
+ }
+ if(doflush){
+ if(i == depth)
+ score = e.score;
+ else
+ score = bi[i+1]->data+index[i]*VtScoreSize;
+ if(flushblock(r->c, bi[i], score, ppb, epb, base+i) < 0)
+ goto Err;
+ }
+ }
+
+Err:
+ /* top: entry. do this always so that the score is up-to-date */
+ vtentrypack(&e, bi[depth+1]->data, index[depth]);
+ for(i=0; i<nelem(bi); i++)
+ if(bi[i])
+ vtblockput(bi[i]);
+ return ret;
+}
diff --git a/sys/src/libventi/hangup.c b/sys/src/libventi/hangup.c
new file mode 100755
index 000000000..365baf1da
--- /dev/null
+++ b/sys/src/libventi/hangup.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#ifdef PLAN9PORT
+#include <sys/socket.h>
+#endif
+#include <libc.h>
+#include <venti.h>
+#include "queue.h"
+
+void
+vthangup(VtConn *z)
+{
+ qlock(&z->lk);
+ z->state = VtStateClosed;
+#ifdef PLAN9PORT
+ /* try to make the read in vtrecvproc fail */
+ shutdown(SHUT_WR, z->infd);
+ shutdown(SHUT_WR, z->outfd);
+#endif
+ if(z->infd >= 0)
+ close(z->infd);
+ if(z->outfd >= 0 && z->outfd != z->infd)
+ close(z->outfd);
+ z->infd = -1;
+ z->outfd = -1;
+ if(z->writeq)
+ _vtqhangup(z->writeq);
+ if(z->readq)
+ _vtqhangup(z->readq);
+ qunlock(&z->lk);
+}
+
diff --git a/sys/src/libventi/log.c b/sys/src/libventi/log.c
new file mode 100755
index 000000000..80a1d4787
--- /dev/null
+++ b/sys/src/libventi/log.c
@@ -0,0 +1,256 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+char *VtServerLog = "libventi/server";
+
+int ventilogging;
+#define log not_the_log_library_call
+
+static char Eremoved[] = "[removed]";
+
+enum
+{ /* defaults */
+ LogChunkSize = 8192,
+ LogSize = 65536
+};
+
+static struct {
+ QLock lk;
+ VtLog *hash[1024];
+} vl;
+
+static uint
+hash(char *s)
+{
+ uint h;
+ uchar *p;
+
+ h = 0;
+ for(p=(uchar*)s; *p; p++)
+ h = h*37 + *p;
+ return h;
+}
+
+char**
+vtlognames(int *pn)
+{
+ int i, nname, size;
+ VtLog *l;
+ char **s, *a, *e;
+
+ qlock(&vl.lk);
+ size = 0;
+ nname = 0;
+ for(i=0; i<nelem(vl.hash); i++)
+ for(l=vl.hash[i]; l; l=l->next){
+ nname++;
+ size += strlen(l->name)+1;
+ }
+
+ s = vtmalloc(nname*sizeof(char*)+size);
+ a = (char*)(s+nname);
+ e = (char*)s+nname*sizeof(char*)+size;
+
+ nname = 0;
+ for(i=0; i<nelem(vl.hash); i++)
+ for(l=vl.hash[i]; l; l=l->next){
+ strcpy(a, l->name);
+ s[nname++] = a;
+ a += strlen(a)+1;
+ }
+ *pn = nname;
+ assert(a == e);
+ qunlock(&vl.lk);
+
+ return s;
+}
+
+VtLog*
+vtlogopen(char *name, uint size)
+{
+ uint h;
+ int i, nc;
+ char *p;
+ VtLog *l, *last;
+
+ if(!ventilogging)
+ return nil;
+
+ h = hash(name)%nelem(vl.hash);
+ qlock(&vl.lk);
+ last = nil;
+ for(l=vl.hash[h]; l; last=l, l=l->next)
+ if(strcmp(l->name, name) == 0){
+ if(last){ /* move to front */
+ last->next = l->next;
+ l->next = vl.hash[h];
+ vl.hash[h] = l;
+ }
+ l->ref++;
+ qunlock(&vl.lk);
+ return l;
+ }
+
+ if(size == 0){
+ qunlock(&vl.lk);
+ return nil;
+ }
+
+ /* allocate */
+ nc = (size+LogChunkSize-1)/LogChunkSize;
+ l = vtmalloc(sizeof *l + nc*(sizeof(*l->chunk)+LogChunkSize) + strlen(name)+1);
+ memset(l, 0, sizeof *l);
+ l->chunk = (VtLogChunk*)(l+1);
+ l->nchunk = nc;
+ l->w = l->chunk;
+ p = (char*)(l->chunk+nc);
+ for(i=0; i<nc; i++){
+ l->chunk[i].p = p;
+ l->chunk[i].wp = p;
+ p += LogChunkSize;
+ l->chunk[i].ep = p;
+ }
+ strcpy(p, name);
+ l->name = p;
+
+ /* insert */
+ l->next = vl.hash[h];
+ vl.hash[h] = l;
+ l->ref++;
+
+ l->ref++;
+ qunlock(&vl.lk);
+ return l;
+}
+
+void
+vtlogclose(VtLog *l)
+{
+ if(l == nil)
+ return;
+
+ qlock(&vl.lk);
+ if(--l->ref == 0){
+ /* must not be in hash table */
+ assert(l->name == Eremoved);
+ free(l);
+ }else
+ assert(l->ref > 0);
+ qunlock(&vl.lk);
+}
+
+void
+vtlogremove(char *name)
+{
+ uint h;
+ VtLog *last, *l;
+
+ h = hash(name)%nelem(vl.hash);
+ qlock(&vl.lk);
+ last = nil;
+ for(l=vl.hash[h]; l; last=l, l=l->next)
+ if(strcmp(l->name, name) == 0){
+ if(last)
+ last->next = l->next;
+ else
+ vl.hash[h] = l->next;
+ l->name = Eremoved;
+ l->next = nil;
+ qunlock(&vl.lk);
+ vtlogclose(l);
+ return;
+ }
+ qunlock(&vl.lk);
+}
+
+static int
+timefmt(Fmt *fmt)
+{
+ static uvlong t0;
+ uvlong t;
+
+ if(t0 == 0)
+ t0 = nsec();
+ t = nsec()-t0;
+ return fmtprint(fmt, "T+%d.%04d", (uint)(t/1000000000), (uint)(t%1000000000)/100000);
+}
+
+void
+vtlogvprint(VtLog *l, char *fmt, va_list arg)
+{
+ int n;
+ char *p;
+ VtLogChunk *c;
+ static int first = 1;
+
+ if(l == nil)
+ return;
+
+ if(first){
+ fmtinstall('T', timefmt);
+ first = 0;
+ }
+
+
+ qlock(&l->lk);
+ c = l->w;
+ n = c->ep - c->wp;
+ if(n < 512){
+ c++;
+ if(c == l->chunk+l->nchunk)
+ c = l->chunk;
+ c->wp = c->p;
+ l->w = c;
+ }
+ p = vseprint(c->wp, c->ep, fmt, arg);
+ if(p)
+ c->wp = p;
+ qunlock(&l->lk);
+}
+
+void
+vtlogprint(VtLog *l, char *fmt, ...)
+{
+ va_list arg;
+
+ if(l == nil)
+ return;
+
+ va_start(arg, fmt);
+ vtlogvprint(l, fmt, arg);
+ va_end(arg);
+}
+
+void
+vtlog(char *name, char *fmt, ...)
+{
+ VtLog *l;
+ va_list arg;
+
+ l = vtlogopen(name, LogSize);
+ if(l == nil)
+ return;
+ va_start(arg, fmt);
+ vtlogvprint(l, fmt, arg);
+ va_end(arg);
+ vtlogclose(l);
+}
+
+void
+vtlogdump(int fd, VtLog *l)
+{
+ int i;
+ VtLogChunk *c;
+
+ if(l == nil)
+ return;
+
+ c = l->w;
+ for(i=0; i<l->nchunk; i++){
+ if(++c == l->chunk+l->nchunk)
+ c = l->chunk;
+ write(fd, c->p, c->wp-c->p);
+ }
+}
+
diff --git a/sys/src/libventi/mem.c b/sys/src/libventi/mem.c
new file mode 100755
index 000000000..dea99a9d1
--- /dev/null
+++ b/sys/src/libventi/mem.c
@@ -0,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+enum {
+ IdealAlignment = 32,
+ ChunkSize = 128*1024
+};
+
+
+void
+vtfree(void *p)
+{
+ if(p == 0)
+ return;
+ free(p);
+}
+
+void *
+vtmalloc(int size)
+{
+ void *p;
+
+ p = malloc(size);
+ if(p == 0)
+ sysfatal("vtmalloc: out of memory");
+ setmalloctag(p, getcallerpc(&size));
+ return p;
+}
+
+void *
+vtmallocz(int size)
+{
+ void *p = vtmalloc(size);
+ memset(p, 0, size);
+ setmalloctag(p, getcallerpc(&size));
+ return p;
+}
+
+void *
+vtrealloc(void *p, int size)
+{
+ if(p == nil)
+ return vtmalloc(size);
+ p = realloc(p, size);
+ if(p == 0)
+ sysfatal("vtMemRealloc: out of memory");
+ setrealloctag(p, getcallerpc(&size));
+ return p;
+}
+
+void *
+vtbrk(int n)
+{
+ static Lock lk;
+ static uchar *buf;
+ static int nbuf, 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 = vtmallocz(ChunkSize);
+ 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;
+}
+
diff --git a/sys/src/libventi/mkfile b/sys/src/libventi/mkfile
new file mode 100755
index 000000000..3862b8756
--- /dev/null
+++ b/sys/src/libventi/mkfile
@@ -0,0 +1,45 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libventi.a
+
+OFILES=\
+ cache.$O\
+ client.$O\
+ conn.$O\
+ dial.$O\
+ debug.$O\
+ dtype.$O\
+ entry.$O\
+ fcall.$O\
+ fcallfmt.$O\
+ file.$O\
+ hangup.$O\
+ log.$O\
+ mem.$O\
+ packet.$O\
+ parsescore.$O\
+ queue.$O\
+ root.$O\
+ rpc.$O\
+ scorefmt.$O\
+ send.$O\
+ server.$O\
+ srvhello.$O\
+ strdup.$O\
+ string.$O\
+ time.$O\
+ version.$O\
+ zero.$O\
+ zeroscore.$O\
+
+HFILES=\
+ /sys/include/venti.h\
+
+</sys/src/cmd/mksyslib
+
+send.$O: queue.h
+server.$O: queue.h
+queue.$O: queue.h
+
+CFLAGS=$CFLAGS -I.
+
diff --git a/sys/src/libventi/packet.acid b/sys/src/libventi/packet.acid
new file mode 100755
index 000000000..70fbe058c
--- /dev/null
+++ b/sys/src/libventi/packet.acid
@@ -0,0 +1,1194 @@
+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");
+};
+
+MaxFragSize = 9216;
+sizeofVtLog = 40;
+aggr VtLog
+{
+ 'A' VtLog 0 next;
+ 'X' 4 name;
+ 'X' 8 chunk;
+ 'U' 12 nchunk;
+ 'X' 16 w;
+ QLock 20 lk;
+ 'D' 36 ref;
+};
+
+defn
+VtLog(addr) {
+ complex VtLog addr;
+ print(" next ", addr.next\X, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" chunk ", addr.chunk\X, "\n");
+ print(" nchunk ", addr.nchunk, "\n");
+ print(" w ", addr.w\X, "\n");
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print(" ref ", addr.ref, "\n");
+};
+
+sizeofVtLogChunk = 12;
+aggr VtLogChunk
+{
+ 'X' 0 p;
+ 'X' 4 ep;
+ 'X' 8 wp;
+};
+
+defn
+VtLogChunk(addr) {
+ complex VtLogChunk addr;
+ print(" p ", addr.p\X, "\n");
+ print(" ep ", addr.ep\X, "\n");
+ print(" wp ", addr.wp\X, "\n");
+};
+
+VtScoreSize = 20;
+VtMaxStringSize = 1024;
+VtMaxLumpSize = 57344;
+VtPointerDepth = 7;
+VtDataType = 0;
+VtDirType = 8;
+VtRootType = 16;
+VtMaxType = 17;
+VtCorruptType = 255;
+VtTypeDepthMask = 7;
+VtTypeBaseMask = -8;
+VtEntryActive = 1;
+_VtEntryDir = 2;
+_VtEntryDepthShift = 2;
+_VtEntryDepthMask = 28;
+VtEntryLocal = 32;
+VtEntrySize = 40;
+sizeofVtEntry = 40;
+aggr VtEntry
+{
+ 'U' 0 gen;
+ 'u' 4 psize;
+ 'u' 6 dsize;
+ 'b' 8 type;
+ '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(" type ", addr.type, "\n");
+ print(" flags ", addr.flags, "\n");
+ print(" size ", addr.size, "\n");
+ print(" score ", addr.score, "\n");
+};
+
+sizeofVtRoot = 300;
+aggr VtRoot
+{
+ 'a' 0 name;
+ 'a' 128 type;
+ 'a' 256 score;
+ 'u' 276 blocksize;
+ 'a' 278 prev;
+};
+
+defn
+VtRoot(addr) {
+ complex VtRoot addr;
+ 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");
+};
+
+VtRootSize = 300;
+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;
+VtRerror = 1;
+VtTping = 2;
+VtRping = 3;
+VtThello = 4;
+VtRhello = 5;
+VtTgoodbye = 6;
+VtRgoodbye = 7;
+VtTauth0 = 8;
+VtRauth0 = 9;
+VtTauth1 = 10;
+VtRauth1 = 11;
+VtTread = 12;
+VtRread = 13;
+VtTwrite = 14;
+VtRwrite = 15;
+VtTsync = 16;
+VtRsync = 17;
+VtTmax = 18;
+sizeofVtFcall = 80;
+aggr VtFcall
+{
+ 'b' 0 msgtype;
+ 'b' 1 tag;
+ 'X' 4 error;
+ 'X' 8 version;
+ 'X' 12 uid;
+ 'b' 16 strength;
+ 'X' 20 crypto;
+ 'U' 24 ncrypto;
+ 'X' 28 codec;
+ 'U' 32 ncodec;
+ 'X' 36 sid;
+ 'b' 40 rcrypto;
+ 'b' 41 rcodec;
+ 'X' 44 auth;
+ 'U' 48 nauth;
+ 'a' 52 score;
+ 'b' 72 blocktype;
+ 'u' 74 count;
+ 'X' 76 data;
+};
+
+defn
+VtFcall(addr) {
+ complex VtFcall addr;
+ print(" msgtype ", addr.msgtype, "\n");
+ print(" tag ", addr.tag, "\n");
+ print(" error ", addr.error\X, "\n");
+ print(" version ", addr.version\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" strength ", addr.strength, "\n");
+ print(" crypto ", addr.crypto\X, "\n");
+ print(" ncrypto ", addr.ncrypto, "\n");
+ print(" codec ", addr.codec\X, "\n");
+ print(" ncodec ", addr.ncodec, "\n");
+ print(" sid ", addr.sid\X, "\n");
+ print(" rcrypto ", addr.rcrypto, "\n");
+ print(" rcodec ", addr.rcodec, "\n");
+ print(" auth ", addr.auth\X, "\n");
+ print(" nauth ", addr.nauth, "\n");
+ print(" score ", addr.score, "\n");
+ print(" blocktype ", addr.blocktype, "\n");
+ print(" count ", addr.count, "\n");
+ print(" data ", addr.data\X, "\n");
+};
+
+VtStateAlloc = 0;
+VtStateConnected = 1;
+VtStateClosed = 2;
+sizeofVtConn = 1404;
+aggr VtConn
+{
+ QLock 0 lk;
+ QLock 16 inlk;
+ QLock 32 outlk;
+ 'D' 48 debug;
+ 'D' 52 infd;
+ 'D' 56 outfd;
+ 'D' 60 muxer;
+ 'X' 64 writeq;
+ 'X' 68 readq;
+ 'D' 72 state;
+ 'a' 76 wait;
+ 'U' 1100 ntag;
+ 'U' 1104 nsleep;
+ 'X' 1108 part;
+ Rendez 1112 tagrend;
+ Rendez 1124 rpcfork;
+ 'X' 1136 version;
+ 'X' 1140 uid;
+ 'X' 1144 sid;
+ 'a' 1148 addr;
+};
+
+defn
+VtConn(addr) {
+ complex VtConn addr;
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print("QLock inlk {\n");
+ QLock(addr.inlk);
+ print("}\n");
+ print("QLock outlk {\n");
+ QLock(addr.outlk);
+ print("}\n");
+ print(" debug ", addr.debug, "\n");
+ print(" infd ", addr.infd, "\n");
+ print(" outfd ", addr.outfd, "\n");
+ print(" muxer ", addr.muxer, "\n");
+ print(" writeq ", addr.writeq\X, "\n");
+ print(" readq ", addr.readq\X, "\n");
+ print(" state ", addr.state, "\n");
+ print(" wait ", addr.wait, "\n");
+ print(" ntag ", addr.ntag, "\n");
+ print(" nsleep ", addr.nsleep, "\n");
+ print(" part ", addr.part\X, "\n");
+ print("Rendez tagrend {\n");
+ Rendez(addr.tagrend);
+ print("}\n");
+ print("Rendez rpcfork {\n");
+ Rendez(addr.rpcfork);
+ print("}\n");
+ print(" version ", addr.version\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" sid ", addr.sid\X, "\n");
+ print(" addr ", addr.addr, "\n");
+};
+
+sizeofVtReq = 168;
+aggr VtReq
+{
+ VtFcall 0 tx;
+ VtFcall 80 rx;
+ 'X' 160 srv;
+ 'X' 164 sc;
+};
+
+defn
+VtReq(addr) {
+ complex VtReq addr;
+ print("VtFcall tx {\n");
+ VtFcall(addr.tx);
+ print("}\n");
+ print("VtFcall rx {\n");
+ VtFcall(addr.rx);
+ print("}\n");
+ print(" srv ", addr.srv\X, "\n");
+ print(" sc ", addr.sc\X, "\n");
+};
+
+NilBlock = -1;
+sizeofVtBlock = 84;
+aggr VtBlock
+{
+ 'X' 0 c;
+ QLock 4 lk;
+ 'X' 20 data;
+ 'a' 24 score;
+ 'b' 44 type;
+ 'D' 48 nlock;
+ 'D' 52 iostate;
+ 'D' 56 ref;
+ 'U' 60 heap;
+ 'A' VtBlock 64 next;
+ 'A' VtBlock 68 prev;
+ 'U' 72 used;
+ 'U' 76 used2;
+ 'U' 80 addr;
+};
+
+defn
+VtBlock(addr) {
+ complex VtBlock addr;
+ print(" c ", addr.c\X, "\n");
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print(" data ", addr.data\X, "\n");
+ print(" score ", addr.score, "\n");
+ print(" type ", addr.type, "\n");
+ print(" nlock ", addr.nlock, "\n");
+ print(" iostate ", addr.iostate, "\n");
+ print(" ref ", addr.ref, "\n");
+ print(" heap ", addr.heap, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" prev ", addr.prev\X, "\n");
+ print(" used ", addr.used, "\n");
+ print(" used2 ", addr.used2, "\n");
+ print(" addr ", addr.addr, "\n");
+};
+
+sizeofVtFile = 84;
+aggr VtFile
+{
+ QLock 0 lk;
+ 'D' 16 ref;
+ 'D' 20 $local;
+ 'A' VtBlock 24 b;
+ 'a' 28 score;
+ 'X' 48 c;
+ 'D' 52 mode;
+ 'U' 56 gen;
+ 'D' 60 dsize;
+ 'D' 64 psize;
+ 'D' 68 dir;
+ 'A' VtFile 72 parent;
+ 'D' 76 epb;
+ 'U' 80 offset;
+};
+
+defn
+VtFile(addr) {
+ complex VtFile addr;
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print(" ref ", addr.ref, "\n");
+ print(" $local ", addr.$local, "\n");
+ print(" b ", addr.b\X, "\n");
+ print(" score ", addr.score, "\n");
+ print(" c ", addr.c\X, "\n");
+ print(" mode ", addr.mode, "\n");
+ print(" gen ", addr.gen, "\n");
+ print(" dsize ", addr.dsize, "\n");
+ print(" psize ", addr.psize, "\n");
+ print(" dir ", addr.dir, "\n");
+ print(" parent ", addr.parent\X, "\n");
+ print(" epb ", addr.epb, "\n");
+ print(" offset ", addr.offset, "\n");
+};
+
+VtOREAD = 0;
+VtOWRITE = 1;
+VtORDWR = 2;
+AESbsize = 16;
+AESmaxkey = 32;
+AESmaxrounds = 14;
+sizeofAESstate = 540;
+aggr AESstate
+{
+ 'U' 0 setup;
+ 'D' 4 rounds;
+ 'D' 8 keybytes;
+ 'a' 12 key;
+ 'a' 44 ekey;
+ 'a' 284 dkey;
+ 'a' 524 ivec;
+};
+
+defn
+AESstate(addr) {
+ complex AESstate addr;
+ print(" setup ", addr.setup, "\n");
+ print(" rounds ", addr.rounds, "\n");
+ print(" keybytes ", addr.keybytes, "\n");
+ print(" key ", addr.key, "\n");
+ print(" ekey ", addr.ekey, "\n");
+ print(" dkey ", addr.dkey, "\n");
+ print(" ivec ", addr.ivec, "\n");
+};
+
+BFbsize = 8;
+BFrounds = 16;
+sizeofBFstate = 4236;
+aggr BFstate
+{
+ 'U' 0 setup;
+ 'a' 4 key;
+ 'a' 60 ivec;
+ 'a' 68 pbox;
+ 'a' 140 sbox;
+};
+
+defn
+BFstate(addr) {
+ complex BFstate addr;
+ print(" setup ", addr.setup, "\n");
+ print(" key ", addr.key, "\n");
+ print(" ivec ", addr.ivec, "\n");
+ print(" pbox ", addr.pbox, "\n");
+ print(" sbox ", addr.sbox, "\n");
+};
+
+DESbsize = 8;
+sizeofDESstate = 148;
+aggr DESstate
+{
+ 'U' 0 setup;
+ 'a' 4 key;
+ 'a' 12 expanded;
+ 'a' 140 ivec;
+};
+
+defn
+DESstate(addr) {
+ complex DESstate addr;
+ print(" setup ", addr.setup, "\n");
+ print(" key ", addr.key, "\n");
+ print(" expanded ", addr.expanded, "\n");
+ print(" ivec ", addr.ivec, "\n");
+};
+
+DES3E = 0;
+DES3D = 1;
+DES3EEE = 0;
+DES3EDE = 2;
+DES3DED = 5;
+DES3DDD = 7;
+sizeofDES3state = 420;
+aggr DES3state
+{
+ 'U' 0 setup;
+ 'a' 4 key;
+ 'a' 28 expanded;
+ 'a' 412 ivec;
+};
+
+defn
+DES3state(addr) {
+ complex DES3state addr;
+ print(" setup ", addr.setup, "\n");
+ print(" key ", addr.key, "\n");
+ print(" expanded ", addr.expanded, "\n");
+ print(" ivec ", addr.ivec, "\n");
+};
+
+SHA1dlen = 20;
+MD4dlen = 16;
+MD5dlen = 16;
+sizeofDigestState = 164;
+aggr DigestState
+{
+ 'W' 0 len;
+ 'a' 8 state;
+ 'a' 28 buf;
+ 'D' 156 blen;
+ 'C' 160 malloced;
+ 'C' 161 seeded;
+};
+
+defn
+DigestState(addr) {
+ complex DigestState addr;
+ print(" len ", addr.len, "\n");
+ print(" state ", addr.state, "\n");
+ print(" buf ", addr.buf, "\n");
+ print(" blen ", addr.blen, "\n");
+ print(" malloced ", addr.malloced, "\n");
+ print(" seeded ", addr.seeded, "\n");
+};
+
+sizeofRC4state = 260;
+aggr RC4state
+{
+ 'a' 0 state;
+ 'b' 256 x;
+ 'b' 257 y;
+};
+
+defn
+RC4state(addr) {
+ complex RC4state addr;
+ print(" state ", addr.state, "\n");
+ print(" x ", addr.x, "\n");
+ print(" y ", addr.y, "\n");
+};
+
+sizeofRSApub = 8;
+aggr RSApub
+{
+ 'X' 0 n;
+ 'X' 4 ek;
+};
+
+defn
+RSApub(addr) {
+ complex RSApub addr;
+ print(" n ", addr.n\X, "\n");
+ print(" ek ", addr.ek\X, "\n");
+};
+
+sizeofRSApriv = 32;
+aggr RSApriv
+{
+ RSApub 0 pub;
+ 'X' 8 dk;
+ 'X' 12 p;
+ 'X' 16 q;
+ 'X' 20 kp;
+ 'X' 24 kq;
+ 'X' 28 c2;
+};
+
+defn
+RSApriv(addr) {
+ complex RSApriv addr;
+ print("RSApub pub {\n");
+ RSApub(addr.pub);
+ print("}\n");
+ print(" dk ", addr.dk\X, "\n");
+ print(" p ", addr.p\X, "\n");
+ print(" q ", addr.q\X, "\n");
+ print(" kp ", addr.kp\X, "\n");
+ print(" kq ", addr.kq\X, "\n");
+ print(" c2 ", addr.c2\X, "\n");
+};
+
+sizeofPEMChain = 12;
+aggr PEMChain
+{
+ 'A' PEMChain 0 next;
+ 'X' 4 pem;
+ 'D' 8 pemlen;
+};
+
+defn
+PEMChain(addr) {
+ complex PEMChain addr;
+ print(" next ", addr.next\X, "\n");
+ print(" pem ", addr.pem\X, "\n");
+ print(" pemlen ", addr.pemlen, "\n");
+};
+
+sizeofEGpub = 12;
+aggr EGpub
+{
+ 'X' 0 p;
+ 'X' 4 alpha;
+ 'X' 8 key;
+};
+
+defn
+EGpub(addr) {
+ complex EGpub addr;
+ print(" p ", addr.p\X, "\n");
+ print(" alpha ", addr.alpha\X, "\n");
+ print(" key ", addr.key\X, "\n");
+};
+
+sizeofEGpriv = 16;
+aggr EGpriv
+{
+ EGpub 0 pub;
+ 'X' 12 secret;
+};
+
+defn
+EGpriv(addr) {
+ complex EGpriv addr;
+ print("EGpub pub {\n");
+ EGpub(addr.pub);
+ print("}\n");
+ print(" secret ", addr.secret\X, "\n");
+};
+
+sizeofEGsig = 8;
+aggr EGsig
+{
+ 'X' 0 r;
+ 'X' 4 s;
+};
+
+defn
+EGsig(addr) {
+ complex EGsig addr;
+ print(" r ", addr.r\X, "\n");
+ print(" s ", addr.s\X, "\n");
+};
+
+sizeofDSApub = 16;
+aggr DSApub
+{
+ 'X' 0 p;
+ 'X' 4 q;
+ 'X' 8 alpha;
+ 'X' 12 key;
+};
+
+defn
+DSApub(addr) {
+ complex DSApub addr;
+ print(" p ", addr.p\X, "\n");
+ print(" q ", addr.q\X, "\n");
+ print(" alpha ", addr.alpha\X, "\n");
+ print(" key ", addr.key\X, "\n");
+};
+
+sizeofDSApriv = 20;
+aggr DSApriv
+{
+ DSApub 0 pub;
+ 'X' 16 secret;
+};
+
+defn
+DSApriv(addr) {
+ complex DSApriv addr;
+ print("DSApub pub {\n");
+ DSApub(addr.pub);
+ print("}\n");
+ print(" secret ", addr.secret\X, "\n");
+};
+
+sizeofDSAsig = 8;
+aggr DSAsig
+{
+ 'X' 0 r;
+ 'X' 4 s;
+};
+
+defn
+DSAsig(addr) {
+ complex DSAsig addr;
+ print(" r ", addr.r\X, "\n");
+ print(" s ", addr.s\X, "\n");
+};
+
+sizeofThumbprint = 24;
+aggr Thumbprint
+{
+ 'A' Thumbprint 0 next;
+ 'a' 4 sha1;
+};
+
+defn
+Thumbprint(addr) {
+ complex Thumbprint addr;
+ print(" next ", addr.next\X, "\n");
+ print(" sha1 ", addr.sha1, "\n");
+};
+
+sizeofTLSconn = 80;
+aggr TLSconn
+{
+ 'a' 0 dir;
+ 'X' 40 cert;
+ 'X' 44 sessionID;
+ 'D' 48 certlen;
+ 'D' 52 sessionIDlen;
+ 'X' 56 trace;
+ 'A' PEMChain 60 chain;
+ 'X' 64 sessionType;
+ 'X' 68 sessionKey;
+ 'D' 72 sessionKeylen;
+ 'X' 76 sessionConst;
+};
+
+defn
+TLSconn(addr) {
+ complex TLSconn addr;
+ print(" dir ", addr.dir, "\n");
+ print(" cert ", addr.cert\X, "\n");
+ print(" sessionID ", addr.sessionID\X, "\n");
+ print(" certlen ", addr.certlen, "\n");
+ print(" sessionIDlen ", addr.sessionIDlen, "\n");
+ print(" trace ", addr.trace\X, "\n");
+ print(" chain ", addr.chain\X, "\n");
+ print(" sessionType ", addr.sessionType\X, "\n");
+ print(" sessionKey ", addr.sessionKey\X, "\n");
+ print(" sessionKeylen ", addr.sessionKeylen, "\n");
+ print(" sessionConst ", addr.sessionConst\X, "\n");
+};
+
+BigMemSize = 9216;
+SmallMemSize = 1152;
+NLocalFrag = 2;
+PFront = 0;
+PMiddle = 1;
+PEnd = 2;
+sizeofMem = 28;
+aggr Mem
+{
+ Lock 0 lk;
+ 'D' 4 ref;
+ 'X' 8 bp;
+ 'X' 12 ep;
+ 'X' 16 rp;
+ 'X' 20 wp;
+ 'A' Mem 24 next;
+};
+
+defn
+Mem(addr) {
+ complex Mem addr;
+ print("Lock lk {\n");
+ Lock(addr.lk);
+ print("}\n");
+ print(" ref ", addr.ref, "\n");
+ print(" bp ", addr.bp\X, "\n");
+ print(" ep ", addr.ep\X, "\n");
+ print(" rp ", addr.rp\X, "\n");
+ print(" wp ", addr.wp\X, "\n");
+ print(" next ", addr.next\X, "\n");
+};
+
+FragLocalFree = 0;
+FragLocalAlloc = 1;
+FragGlobal = 2;
+sizeofFrag = 32;
+aggr Frag
+{
+ 'D' 0 state;
+ 'A' Mem 4 mem;
+ 'X' 8 rp;
+ 'X' 12 wp;
+ 'A' Frag 16 next;
+ 'X' 20 free;
+ 'X' 24 a;
+ 'X' 28 p;
+};
+
+defn
+Frag(addr) {
+ complex Frag addr;
+ print(" state ", addr.state, "\n");
+ print(" mem ", addr.mem\X, "\n");
+ print(" rp ", addr.rp\X, "\n");
+ print(" wp ", addr.wp\X, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" free ", addr.free\X, "\n");
+ print(" a ", addr.a\X, "\n");
+ print(" p ", addr.p\X, "\n");
+};
+
+sizeofPacket = 88;
+aggr Packet
+{
+ 'D' 0 size;
+ 'D' 4 asize;
+ 'U' 8 pc;
+ 'A' Packet 12 next;
+ 'A' Frag 16 first;
+ 'A' Frag 20 last;
+ 'a' 24 $local;
+};
+
+defn
+Packet(addr) {
+ complex Packet addr;
+ print(" size ", addr.size, "\n");
+ print(" asize ", addr.asize, "\n");
+ print(" pc ", addr.pc, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" first ", addr.first\X, "\n");
+ print(" last ", addr.last\X, "\n");
+ print(" $local ", addr.$local, "\n");
+};
+
+sizeof_2_ = 36;
+aggr _2_
+{
+ Lock 0 lk;
+ 'A' Packet 4 packet;
+ 'D' 8 npacket;
+ 'A' Frag 12 frag;
+ 'D' 16 nfrag;
+ 'A' Mem 20 bigmem;
+ 'D' 24 nbigmem;
+ 'A' Mem 28 smallmem;
+ 'D' 32 nsmallmem;
+};
+
+defn
+_2_(addr) {
+ complex _2_ addr;
+ print("Lock lk {\n");
+ Lock(addr.lk);
+ print("}\n");
+ print(" packet ", addr.packet\X, "\n");
+ print(" npacket ", addr.npacket, "\n");
+ print(" frag ", addr.frag\X, "\n");
+ print(" nfrag ", addr.nfrag, "\n");
+ print(" bigmem ", addr.bigmem\X, "\n");
+ print(" nbigmem ", addr.nbigmem, "\n");
+ print(" smallmem ", addr.smallmem\X, "\n");
+ print(" nsmallmem ", addr.nsmallmem, "\n");
+};
+
+complex _2_ freelist;
+complex Packet packetalloc:p;
+complex Packet packetfree:p;
+complex Frag packetfree:f;
+complex Frag packetfree:ff;
+complex Packet packetdup:p;
+complex Frag packetdup:f;
+complex Frag packetdup:ff;
+complex Packet packetdup:pp;
+complex Packet packetsplit:p;
+complex Packet packetsplit:pp;
+complex Frag packetsplit:f;
+complex Frag packetsplit:ff;
+complex Packet packetconsume:p;
+complex Packet packettrim:p;
+complex Frag packettrim:f;
+complex Frag packettrim:ff;
+complex Packet packetheader:p;
+complex Frag packetheader:f;
+complex Mem packetheader:m;
+complex Packet packettrailer:p;
+complex Mem packettrailer:m;
+complex Frag packettrailer:f;
+complex Packet packetprefix:p;
+complex Frag packetprefix:f;
+complex Mem packetprefix:m;
+complex Packet packetappend:p;
+complex Frag packetappend:f;
+complex Mem packetappend:m;
+complex Packet packetconcat:p;
+complex Packet packetconcat:pp;
+complex Frag packetconcat:f;
+complex Packet packetpeek:p;
+complex Frag packetpeek:f;
+complex Packet packetcopy:p;
+complex Packet packetfragments:p;
+complex IOchunk packetfragments:io;
+complex Frag packetfragments:f;
+complex IOchunk packetfragments:eio;
+complex Packet packetstats:p;
+complex Frag packetstats:f;
+complex Mem packetstats:m;
+complex Packet packetsize:p;
+complex Frag packetsize:f;
+complex Packet packetasize:p;
+complex Frag packetasize:f;
+complex Packet packetsha1:p;
+complex DigestState packetsha1:ds;
+complex Frag packetsha1:f;
+complex Packet packetcmp:pkt0;
+complex Packet packetcmp:pkt1;
+complex Frag packetcmp:f0;
+complex Frag packetcmp:f1;
+complex Packet fragalloc:p;
+complex Frag fragalloc:next;
+complex Frag fragalloc:f;
+complex Frag fragalloc:ef;
+complex Mem fragalloc:m;
+complex Packet packetforeign:p;
+complex Frag packetforeign:f;
+complex Packet fragdup:p;
+complex Frag fragdup:f;
+complex Frag fragdup:ff;
+complex Mem fragdup:m;
+complex Frag fragfree:f;
+complex Mem memalloc:m;
+complex Mem memfree:m;
+complex Mem memhead:m;
+complex Mem memtail:m;
diff --git a/sys/src/libventi/packet.c b/sys/src/libventi/packet.c
new file mode 100755
index 000000000..d19d85371
--- /dev/null
+++ b/sys/src/libventi/packet.c
@@ -0,0 +1,1025 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+#include <libsec.h>
+
+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;
+ void (*free)(void*);
+ void *a;
+ Packet *p; /* parent packet, for debugging only */
+};
+
+struct Packet
+{
+ int size;
+ int asize; /* allocated memory - greater than size unless foreign frags */
+ ulong pc;
+
+ Packet *next;
+
+ Frag *first;
+ Frag *last;
+
+ Frag local[NLocalFrag];
+};
+
+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";
+
+#ifdef NOTDEF
+static void checkpacket(Packet*);
+#endif
+
+/*
+ * the free list is primarily for speed, but it is
+ * also necessary for packetsplit that packets
+ * are never freed -- a packet can contain a different
+ * packet's local fragments, thanks to packetsplit!
+ */
+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 ? (f)->mem->ep - (f)->mem->bp : 0)
+
+#define NOTFREE(p) assert((p)->size>=0)/*; checkpacket(p)*/
+
+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 = vtbrk(sizeof(Packet));
+ else
+ assert(p->size == -1);
+ p->size = 0;
+ p->asize = 0;
+ p->first = nil;
+ p->last = nil;
+ p->next = nil;
+ p->pc = getcallerpc((char*)&p+8); /* might not work, but fine */
+
+ NOTFREE(p);
+ return p;
+}
+
+void
+packetfree(Packet *p)
+{
+ Frag *f, *ff;
+
+ if(p == nil)
+ return;
+
+ NOTFREE(p);
+ p->pc = getcallerpc(&p);
+
+ for(f=p->first; f!=nil; f=ff) {
+ ff = f->next;
+ fragfree(f);
+ }
+ p->first = (void*)0xDeadBeef;
+ p->last = (void*)0xDeadBeef;
+ p->size = -1;
+
+ 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) {
+ werrstr(EBadSize);
+ return nil;
+ }
+
+ pp = packetalloc();
+ pp->pc = getcallerpc(&p);
+ if(n == 0){
+ NOTFREE(pp);
+ 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;
+
+ NOTFREE(pp);
+ NOTFREE(p);
+ return pp;
+}
+
+Packet *
+packetsplit(Packet *p, int n)
+{
+ Packet *pp;
+ Frag *f, *ff;
+
+ NOTFREE(p);
+ if(n < 0 || n > p->size) {
+ werrstr(EPacketSize);
+ return nil;
+ }
+
+ pp = packetalloc();
+ pp->pc = getcallerpc(&p);
+ if(n == 0){
+ NOTFREE(pp);
+ 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);
+ f->p = pp;
+ ff = f;
+ }
+
+ /* split shared frag */
+ if(n > 0) {
+ f->p = pp;
+ ff = f;
+ f = fragdup(p, ff);
+ pp->asize += FRAGASIZE(ff);
+ ff->wp = ff->rp + n;
+ f->rp += n;
+ }
+
+ pp->first = p->first;
+ pp->last = ff;
+ ff->next = nil;
+ p->first = f;
+ if(f == nil || f->next == nil)
+ p->last = f;
+ NOTFREE(pp);
+ NOTFREE(p);
+ return pp;
+}
+
+int
+packetconsume(Packet *p, uchar *buf, int n)
+{
+ NOTFREE(p);
+ if(buf && packetcopy(p, buf, 0, n) < 0)
+ return -1;
+ 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) {
+ werrstr(EPacketOffset);
+ return -1;
+ }
+
+ if(n < 0 || offset + n > p->size) {
+ werrstr(EPacketOffset);
+ return -1;
+ }
+
+ 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;
+ NOTFREE(p);
+ return 0;
+ }
+
+ /* 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);
+ }
+ NOTFREE(p);
+ return 0;
+}
+
+uchar *
+packetheader(Packet *p, int n)
+{
+ Frag *f;
+ Mem *m;
+
+ NOTFREE(p);
+ if(n <= 0 || n > MaxFragSize) {
+ werrstr(EPacketSize);
+ return nil;
+ }
+
+ 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) >= 0) {
+ f->rp -= n;
+ NOTFREE(p);
+ 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;
+ NOTFREE(p);
+ return f->rp;
+}
+
+uchar *
+packettrailer(Packet *p, int n)
+{
+ Mem *m;
+ Frag *f;
+
+ NOTFREE(p);
+ if(n <= 0 || n > MaxFragSize) {
+ werrstr(EPacketSize);
+ return nil;
+ }
+
+ 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) >= 0) {
+ f->wp += n;
+ NOTFREE(p);
+ 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;
+ NOTFREE(p);
+ return f->rp;
+}
+
+void
+packetprefix(Packet *p, uchar *buf, int n)
+{
+ Frag *f;
+ int nn;
+ Mem *m;
+
+ NOTFREE(p);
+ if(n <= 0)
+ return;
+
+ 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) >= 0) {
+ 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);
+ }
+ NOTFREE(p);
+}
+
+void
+packetappend(Packet *p, uchar *buf, int n)
+{
+ Frag *f;
+ int nn;
+ Mem *m;
+
+ NOTFREE(p);
+ if(n <= 0)
+ return;
+
+ 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) >= 0) {
+ 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;
+ }
+ NOTFREE(p);
+}
+
+void
+packetconcat(Packet *p, Packet *pp)
+{
+ Frag *f;
+
+ NOTFREE(p);
+ NOTFREE(pp);
+ if(pp->size == 0)
+ return;
+ p->size += pp->size;
+ p->asize += pp->asize;
+ for(f=pp->first; f; f=f->next)
+ f->p = p;
+
+ 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;
+ NOTFREE(p);
+ NOTFREE(pp);
+}
+
+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) {
+ werrstr(EPacketOffset);
+ return nil;
+ }
+
+ if(n < 0 || offset + n > p->size) {
+ werrstr(EPacketSize);
+ return nil;
+ }
+
+ /* skip up to offset */
+ for(f=p->first; offset >= FRAGSIZE(f); f=f->next)
+ offset -= FRAGSIZE(f);
+
+ /* easy case */
+ if(offset + n <= FRAGSIZE(f)){
+ NOTFREE(p);
+ 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;
+ }
+
+ NOTFREE(p);
+ 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 -1;
+ if(b != buf)
+ memmove(buf, b, n);
+ return 0;
+}
+
+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) {
+ werrstr(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++;
+ }
+ for(; io < eio; io++){
+ io->addr = nil;
+ io->len = 0;
+ }
+ 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);
+}
+
+
+uint
+packetsize(Packet *p)
+{
+ NOTFREE(p);
+ if(1) {
+ 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;
+}
+
+uint
+packetasize(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, "packetasize %d %d\n", asize, p->asize);
+ assert(asize == p->asize);
+ }
+ return p->asize;
+}
+
+void
+packetsha1(Packet *p, uchar digest[VtScoreSize])
+{
+ DigestState ds;
+ Frag *f;
+ int size;
+
+ NOTFREE(p);
+ memset(&ds, 0, sizeof ds);
+ size = p->size;
+ for(f=p->first; f; f=f->next) {
+ sha1(f->rp, FRAGSIZE(f), nil, &ds);
+ size -= FRAGSIZE(f);
+ }
+ assert(size == 0);
+ sha1(nil, 0, digest, &ds);
+}
+
+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 = vtbrk(sizeof(Frag));
+ f->state = FragGlobal;
+ }
+
+Found:
+ f->next = next;
+ f->p = p;
+
+ if(n == 0){
+ f->mem = 0;
+ f->rp = 0;
+ f->wp = 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;
+ return f;
+}
+
+Packet*
+packetforeign(uchar *buf, int n, void (*free)(void *a), void *a)
+{
+ Packet *p;
+ Frag *f;
+
+ p = packetalloc();
+ p->pc = getcallerpc(&buf);
+ f = fragalloc(p, 0, 0, nil);
+ f->free = free;
+ f->a = a;
+ f->next = nil;
+ f->rp = buf;
+ f->wp = buf+n;
+
+ p->first = f;
+ p->last = f;
+ p->size = n;
+ NOTFREE(p);
+ return p;
+}
+
+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 && m->ref == 1) {
+ m->rp = f->rp;
+ m->wp = f->wp;
+ }
+
+ ff = fragalloc(p, 0, 0, nil);
+ ff->mem = f->mem;
+ ff->rp = f->rp;
+ ff->wp = f->wp;
+ ff->next = f->next;
+
+ /*
+ * We can't duplicate these -- there's no dup function.
+ */
+ assert(f->free==nil && f->a==nil);
+
+ if(m){
+ lock(&m->lk);
+ m->ref++;
+ unlock(&m->lk);
+ }
+
+
+ return ff;
+}
+
+
+static void
+fragfree(Frag *f)
+{
+ if(f->mem == nil){
+ if(f->free)
+ (*f->free)(f->a);
+ }else{
+ memfree(f->mem);
+ f->mem = 0;
+ }
+
+ 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) {
+ werrstr(EPacketSize);
+ return nil;
+ }
+ 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 = vtbrk(sizeof(Mem));
+ m->bp = vtbrk(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);
+
+/* memset(m->bp, 0xEF, m->ep-m->bp); */
+ 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)
+{
+ fprint(2, "memhead called\n");
+ abort();
+ lock(&m->lk);
+ if(m->rp != rp) {
+ unlock(&m->lk);
+ return -1;
+ }
+ m->rp -= n;
+ unlock(&m->lk);
+ return 0;
+}
+
+static int
+memtail(Mem *m, uchar *wp, int n)
+{
+ fprint(2, "memtail called\n");
+ abort();
+ lock(&m->lk);
+ if(m->wp != wp) {
+ unlock(&m->lk);
+ return -1;
+ }
+ m->wp += n;
+ unlock(&m->lk);
+ return 0;
+}
+
+#ifdef NOTDEF
+static void
+checkpacket(Packet *p)
+{
+ int s, as;
+ Frag *f;
+ Frag *ff;
+
+ s = 0;
+ as = 0;
+ ff=p->first;
+ for(f=p->first; f; ff=f,f=f->next){
+ assert(f->p == p);
+ s += FRAGSIZE(f);
+ as += FRAGASIZE(f);
+ }
+ assert(s == p->size);
+ assert(as == p->asize);
+ if(p->first)
+ assert(ff==p->last);
+}
+#endif
diff --git a/sys/src/libventi/parsescore.c b/sys/src/libventi/parsescore.c
new file mode 100755
index 000000000..bf4a6125b
--- /dev/null
+++ b/sys/src/libventi/parsescore.c
@@ -0,0 +1,43 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+int
+vtparsescore(char *s, char **prefix, uchar score[VtScoreSize])
+{
+ int i, c;
+ char *buf, *colon;
+
+ if((colon = strchr(s, ':')) != nil)
+ buf = colon+1;
+ else
+ buf = s;
+
+ if(strlen(buf) != 2*VtScoreSize)
+ return -1;
+
+ memset(score, 0, VtScoreSize);
+ for(i=0; i<2*VtScoreSize; i++){
+ if(buf[i] >= '0' && buf[i] <= '9')
+ c = buf[i] - '0';
+ else if(buf[i] >= 'a' && buf[i] <= 'z')
+ c = buf[i] - 'a' + 10;
+ else if(buf[i] >= 'A' && buf[i] <= 'Z')
+ c = buf[i] - 'A' + 10;
+ else
+ return -1;
+
+ if((i & 1) == 0)
+ c <<= 4;
+ score[i>>1] |= c;
+ }
+ if(colon){
+ *colon = 0;
+ if(prefix)
+ *prefix = s;
+ }else{
+ if(prefix)
+ *prefix = nil;
+ }
+ return 0;
+}
diff --git a/sys/src/libventi/queue.c b/sys/src/libventi/queue.c
new file mode 100755
index 000000000..bba630324
--- /dev/null
+++ b/sys/src/libventi/queue.c
@@ -0,0 +1,136 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+#include "queue.h"
+
+typedef struct Qel Qel;
+struct Qel
+{
+ Qel *next;
+ void *p;
+};
+
+struct Queue
+{
+ int ref;
+ int hungup;
+ QLock lk;
+ Rendez r;
+ Qel *head;
+ Qel *tail;
+};
+
+Queue*
+_vtqalloc(void)
+{
+ Queue *q;
+
+ q = vtmallocz(sizeof(Queue));
+ q->r.l = &q->lk;
+ q->ref = 1;
+ return q;
+}
+
+Queue*
+_vtqincref(Queue *q)
+{
+ qlock(&q->lk);
+ q->ref++;
+ qunlock(&q->lk);
+ return q;
+}
+
+void
+_vtqdecref(Queue *q)
+{
+ Qel *e;
+
+ qlock(&q->lk);
+ if(--q->ref > 0){
+ qunlock(&q->lk);
+ return;
+ }
+ assert(q->ref == 0);
+ qunlock(&q->lk);
+
+ /* Leaks the pointers e->p! */
+ while(q->head){
+ e = q->head;
+ q->head = e->next;
+ free(e);
+ }
+ free(q);
+}
+
+int
+_vtqsend(Queue *q, void *p)
+{
+ Qel *e;
+
+ e = vtmalloc(sizeof(Qel));
+ qlock(&q->lk);
+ if(q->hungup){
+ werrstr("hungup queue");
+ qunlock(&q->lk);
+ return -1;
+ }
+ e->p = p;
+ e->next = nil;
+ if(q->head == nil)
+ q->head = e;
+ else
+ q->tail->next = e;
+ q->tail = e;
+ rwakeup(&q->r);
+ qunlock(&q->lk);
+ return 0;
+}
+
+void*
+_vtqrecv(Queue *q)
+{
+ void *p;
+ Qel *e;
+
+ qlock(&q->lk);
+ while(q->head == nil && !q->hungup)
+ rsleep(&q->r);
+ if(q->hungup){
+ qunlock(&q->lk);
+ return nil;
+ }
+ e = q->head;
+ q->head = e->next;
+ qunlock(&q->lk);
+ p = e->p;
+ vtfree(e);
+ return p;
+}
+
+void*
+_vtnbqrecv(Queue *q)
+{
+ void *p;
+ Qel *e;
+
+ qlock(&q->lk);
+ if(q->head == nil){
+ qunlock(&q->lk);
+ return nil;
+ }
+ e = q->head;
+ q->head = e->next;
+ qunlock(&q->lk);
+ p = e->p;
+ vtfree(e);
+ return p;
+}
+
+void
+_vtqhangup(Queue *q)
+{
+ qlock(&q->lk);
+ q->hungup = 1;
+ rwakeupall(&q->r);
+ qunlock(&q->lk);
+}
diff --git a/sys/src/libventi/queue.h b/sys/src/libventi/queue.h
new file mode 100755
index 000000000..f62725b1c
--- /dev/null
+++ b/sys/src/libventi/queue.h
@@ -0,0 +1,9 @@
+typedef struct Queue Queue;
+#pragma incomplete Queue
+Queue *_vtqalloc(void);
+int _vtqsend(Queue*, void*);
+void *_vtqrecv(Queue*);
+void _vtqhangup(Queue*);
+void *_vtnbqrecv(Queue*);
+void _vtqdecref(Queue*);
+Queue *_vtqincref(Queue*);
diff --git a/sys/src/libventi/root.c b/sys/src/libventi/root.c
new file mode 100755
index 000000000..5b4ccac6e
--- /dev/null
+++ b/sys/src/libventi/root.c
@@ -0,0 +1,67 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+#include "cvt.h"
+
+static int
+checksize(int n)
+{
+ if(n < 256 || n > VtMaxLumpSize) {
+ werrstr("bad block size");
+ return -1;
+ }
+ return 0;
+}
+
+void
+vtrootpack(VtRoot *r, uchar *p)
+{
+ uchar *op = p;
+
+ U16PUT(p, VtRootVersion);
+ 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;
+ uint vers;
+ memset(r, 0, sizeof(*r));
+
+ vers = U16GET(p);
+ if(vers != VtRootVersion) {
+ werrstr("unknown root version");
+ return -1;
+ }
+ 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) < 0)
+ return -1;
+ p += 2;
+ memmove(r->prev, p, VtScoreSize);
+ p += VtScoreSize;
+
+ assert(p-op == VtRootSize);
+ return 0;
+}
diff --git a/sys/src/libventi/rpc.acid b/sys/src/libventi/rpc.acid
new file mode 100755
index 000000000..68f8f7834
--- /dev/null
+++ b/sys/src/libventi/rpc.acid
@@ -0,0 +1,710 @@
+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");
+};
+
+MaxFragSize = 9216;
+sizeofVtLog = 40;
+aggr VtLog
+{
+ 'A' VtLog 0 next;
+ 'X' 4 name;
+ 'X' 8 chunk;
+ 'U' 12 nchunk;
+ 'X' 16 w;
+ QLock 20 lk;
+ 'D' 36 ref;
+};
+
+defn
+VtLog(addr) {
+ complex VtLog addr;
+ print(" next ", addr.next\X, "\n");
+ print(" name ", addr.name\X, "\n");
+ print(" chunk ", addr.chunk\X, "\n");
+ print(" nchunk ", addr.nchunk, "\n");
+ print(" w ", addr.w\X, "\n");
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print(" ref ", addr.ref, "\n");
+};
+
+sizeofVtLogChunk = 12;
+aggr VtLogChunk
+{
+ 'X' 0 p;
+ 'X' 4 ep;
+ 'X' 8 wp;
+};
+
+defn
+VtLogChunk(addr) {
+ complex VtLogChunk addr;
+ print(" p ", addr.p\X, "\n");
+ print(" ep ", addr.ep\X, "\n");
+ print(" wp ", addr.wp\X, "\n");
+};
+
+VtScoreSize = 20;
+VtMaxStringSize = 1024;
+VtMaxLumpSize = 57344;
+VtPointerDepth = 7;
+VtDataType = 0;
+VtDirType = 8;
+VtRootType = 16;
+VtMaxType = 17;
+VtCorruptType = 255;
+VtTypeDepthMask = 7;
+VtTypeBaseMask = -8;
+VtEntryActive = 1;
+_VtEntryDir = 2;
+_VtEntryDepthShift = 2;
+_VtEntryDepthMask = 28;
+VtEntryLocal = 32;
+VtEntrySize = 40;
+sizeofVtEntry = 40;
+aggr VtEntry
+{
+ 'U' 0 gen;
+ 'u' 4 psize;
+ 'u' 6 dsize;
+ 'b' 8 type;
+ '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(" type ", addr.type, "\n");
+ print(" flags ", addr.flags, "\n");
+ print(" size ", addr.size, "\n");
+ print(" score ", addr.score, "\n");
+};
+
+sizeofVtRoot = 300;
+aggr VtRoot
+{
+ 'a' 0 name;
+ 'a' 128 type;
+ 'a' 256 score;
+ 'u' 276 blocksize;
+ 'a' 278 prev;
+};
+
+defn
+VtRoot(addr) {
+ complex VtRoot addr;
+ 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");
+};
+
+VtRootSize = 300;
+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;
+VtRerror = 1;
+VtTping = 2;
+VtRping = 3;
+VtThello = 4;
+VtRhello = 5;
+VtTgoodbye = 6;
+VtRgoodbye = 7;
+VtTauth0 = 8;
+VtRauth0 = 9;
+VtTauth1 = 10;
+VtRauth1 = 11;
+VtTread = 12;
+VtRread = 13;
+VtTwrite = 14;
+VtRwrite = 15;
+VtTsync = 16;
+VtRsync = 17;
+VtTmax = 18;
+sizeofVtFcall = 80;
+aggr VtFcall
+{
+ 'b' 0 msgtype;
+ 'b' 1 tag;
+ 'X' 4 error;
+ 'X' 8 version;
+ 'X' 12 uid;
+ 'b' 16 strength;
+ 'X' 20 crypto;
+ 'U' 24 ncrypto;
+ 'X' 28 codec;
+ 'U' 32 ncodec;
+ 'X' 36 sid;
+ 'b' 40 rcrypto;
+ 'b' 41 rcodec;
+ 'X' 44 auth;
+ 'U' 48 nauth;
+ 'a' 52 score;
+ 'b' 72 blocktype;
+ 'u' 74 count;
+ 'X' 76 data;
+};
+
+defn
+VtFcall(addr) {
+ complex VtFcall addr;
+ print(" msgtype ", addr.msgtype, "\n");
+ print(" tag ", addr.tag, "\n");
+ print(" error ", addr.error\X, "\n");
+ print(" version ", addr.version\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" strength ", addr.strength, "\n");
+ print(" crypto ", addr.crypto\X, "\n");
+ print(" ncrypto ", addr.ncrypto, "\n");
+ print(" codec ", addr.codec\X, "\n");
+ print(" ncodec ", addr.ncodec, "\n");
+ print(" sid ", addr.sid\X, "\n");
+ print(" rcrypto ", addr.rcrypto, "\n");
+ print(" rcodec ", addr.rcodec, "\n");
+ print(" auth ", addr.auth\X, "\n");
+ print(" nauth ", addr.nauth, "\n");
+ print(" score ", addr.score, "\n");
+ print(" blocktype ", addr.blocktype, "\n");
+ print(" count ", addr.count, "\n");
+ print(" data ", addr.data\X, "\n");
+};
+
+VtStateAlloc = 0;
+VtStateConnected = 1;
+VtStateClosed = 2;
+sizeofVtConn = 1404;
+aggr VtConn
+{
+ QLock 0 lk;
+ QLock 16 inlk;
+ QLock 32 outlk;
+ 'D' 48 debug;
+ 'D' 52 infd;
+ 'D' 56 outfd;
+ 'D' 60 muxer;
+ 'X' 64 writeq;
+ 'X' 68 readq;
+ 'D' 72 state;
+ 'a' 76 wait;
+ 'U' 1100 ntag;
+ 'U' 1104 nsleep;
+ 'X' 1108 part;
+ Rendez 1112 tagrend;
+ Rendez 1124 rpcfork;
+ 'X' 1136 version;
+ 'X' 1140 uid;
+ 'X' 1144 sid;
+ 'a' 1148 addr;
+};
+
+defn
+VtConn(addr) {
+ complex VtConn addr;
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print("QLock inlk {\n");
+ QLock(addr.inlk);
+ print("}\n");
+ print("QLock outlk {\n");
+ QLock(addr.outlk);
+ print("}\n");
+ print(" debug ", addr.debug, "\n");
+ print(" infd ", addr.infd, "\n");
+ print(" outfd ", addr.outfd, "\n");
+ print(" muxer ", addr.muxer, "\n");
+ print(" writeq ", addr.writeq\X, "\n");
+ print(" readq ", addr.readq\X, "\n");
+ print(" state ", addr.state, "\n");
+ print(" wait ", addr.wait, "\n");
+ print(" ntag ", addr.ntag, "\n");
+ print(" nsleep ", addr.nsleep, "\n");
+ print(" part ", addr.part\X, "\n");
+ print("Rendez tagrend {\n");
+ Rendez(addr.tagrend);
+ print("}\n");
+ print("Rendez rpcfork {\n");
+ Rendez(addr.rpcfork);
+ print("}\n");
+ print(" version ", addr.version\X, "\n");
+ print(" uid ", addr.uid\X, "\n");
+ print(" sid ", addr.sid\X, "\n");
+ print(" addr ", addr.addr, "\n");
+};
+
+sizeofVtReq = 168;
+aggr VtReq
+{
+ VtFcall 0 tx;
+ VtFcall 80 rx;
+ 'X' 160 srv;
+ 'X' 164 sc;
+};
+
+defn
+VtReq(addr) {
+ complex VtReq addr;
+ print("VtFcall tx {\n");
+ VtFcall(addr.tx);
+ print("}\n");
+ print("VtFcall rx {\n");
+ VtFcall(addr.rx);
+ print("}\n");
+ print(" srv ", addr.srv\X, "\n");
+ print(" sc ", addr.sc\X, "\n");
+};
+
+NilBlock = -1;
+sizeofVtBlock = 84;
+aggr VtBlock
+{
+ 'X' 0 c;
+ QLock 4 lk;
+ 'X' 20 data;
+ 'a' 24 score;
+ 'b' 44 type;
+ 'D' 48 nlock;
+ 'D' 52 iostate;
+ 'D' 56 ref;
+ 'U' 60 heap;
+ 'A' VtBlock 64 next;
+ 'A' VtBlock 68 prev;
+ 'U' 72 used;
+ 'U' 76 used2;
+ 'U' 80 addr;
+};
+
+defn
+VtBlock(addr) {
+ complex VtBlock addr;
+ print(" c ", addr.c\X, "\n");
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print(" data ", addr.data\X, "\n");
+ print(" score ", addr.score, "\n");
+ print(" type ", addr.type, "\n");
+ print(" nlock ", addr.nlock, "\n");
+ print(" iostate ", addr.iostate, "\n");
+ print(" ref ", addr.ref, "\n");
+ print(" heap ", addr.heap, "\n");
+ print(" next ", addr.next\X, "\n");
+ print(" prev ", addr.prev\X, "\n");
+ print(" used ", addr.used, "\n");
+ print(" used2 ", addr.used2, "\n");
+ print(" addr ", addr.addr, "\n");
+};
+
+sizeofVtFile = 84;
+aggr VtFile
+{
+ QLock 0 lk;
+ 'D' 16 ref;
+ 'D' 20 $local;
+ 'A' VtBlock 24 b;
+ 'a' 28 score;
+ 'X' 48 c;
+ 'D' 52 mode;
+ 'U' 56 gen;
+ 'D' 60 dsize;
+ 'D' 64 psize;
+ 'D' 68 dir;
+ 'A' VtFile 72 parent;
+ 'D' 76 epb;
+ 'U' 80 offset;
+};
+
+defn
+VtFile(addr) {
+ complex VtFile addr;
+ print("QLock lk {\n");
+ QLock(addr.lk);
+ print("}\n");
+ print(" ref ", addr.ref, "\n");
+ print(" $local ", addr.$local, "\n");
+ print(" b ", addr.b\X, "\n");
+ print(" score ", addr.score, "\n");
+ print(" c ", addr.c\X, "\n");
+ print(" mode ", addr.mode, "\n");
+ print(" gen ", addr.gen, "\n");
+ print(" dsize ", addr.dsize, "\n");
+ print(" psize ", addr.psize, "\n");
+ print(" dir ", addr.dir, "\n");
+ print(" parent ", addr.parent\X, "\n");
+ print(" epb ", addr.epb, "\n");
+ print(" offset ", addr.offset, "\n");
+};
+
+VtOREAD = 0;
+VtOWRITE = 1;
+VtORDWR = 2;
+sizeofRwait = 24;
+aggr Rwait
+{
+ Rendez 0 r;
+ 'X' 12 p;
+ 'D' 16 done;
+ 'D' 20 sleeping;
+};
+
+defn
+Rwait(addr) {
+ complex Rwait addr;
+ print("Rendez r {\n");
+ Rendez(addr.r);
+ print("}\n");
+ print(" p ", addr.p\X, "\n");
+ print(" done ", addr.done, "\n");
+ print(" sleeping ", addr.sleeping, "\n");
+};
+
+complex VtConn vtrpc:z;
+complex Rwait vtrpc:r;
+complex Rwait vtrpc:rr;
+complex VtConn gettag:z;
+complex Rwait gettag:r;
+complex VtConn puttag:z;
+complex Rwait puttag:r;
+complex VtConn muxrpc:z;
+complex Rwait muxrpc:r;
diff --git a/sys/src/libventi/rpc.c b/sys/src/libventi/rpc.c
new file mode 100755
index 000000000..5a820a995
--- /dev/null
+++ b/sys/src/libventi/rpc.c
@@ -0,0 +1,173 @@
+/*
+ * Multiplexed Venti client. It would be nice if we
+ * could turn this into a generic library routine rather
+ * than keep it Venti specific. A user-level 9P client
+ * could use something like this too.
+ *
+ * (Actually it does - this should be replaced with libmux,
+ * which should be renamed librpcmux.)
+ *
+ * This is a little more complicated than it might be
+ * because we want it to work well within and without libthread.
+ *
+ * The mux code is inspired by tra's, which is inspired by the Plan 9 kernel.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+typedef struct Rwait Rwait;
+struct Rwait
+{
+ Rendez r;
+ Packet *p;
+ int done;
+ int sleeping;
+};
+
+static int gettag(VtConn*, Rwait*);
+static void puttag(VtConn*, Rwait*, int);
+static void muxrpc(VtConn*, Packet*);
+
+Packet*
+_vtrpc(VtConn *z, Packet *p, VtFcall *tx)
+{
+ int i;
+ uchar tag, buf[2], *top;
+ Rwait *r, *rr;
+
+ /* must malloc because stack could be private */
+ r = vtmallocz(sizeof(Rwait));
+
+ qlock(&z->lk);
+ r->r.l = &z->lk;
+ tag = gettag(z, r);
+ if(tx){
+ /* vtfcallrpc can't print packet because it doesn't have tag */
+ tx->tag = tag;
+ if(chattyventi)
+ fprint(2, "%s -> %F\n", argv0, tx);
+ }
+
+ /* slam tag into packet */
+ top = packetpeek(p, buf, 0, 2);
+ if(top == nil){
+ packetfree(p);
+ return nil;
+ }
+ if(top == buf){
+ werrstr("first two bytes must be in same packet fragment");
+ packetfree(p);
+ vtfree(r);
+ return nil;
+ }
+ top[1] = tag;
+ qunlock(&z->lk);
+ if(vtsend(z, p) < 0){
+ vtfree(r);
+ return nil;
+ }
+
+ qlock(&z->lk);
+ /* wait for the muxer to give us our packet */
+ r->sleeping = 1;
+ z->nsleep++;
+ while(z->muxer && !r->done)
+ rsleep(&r->r);
+ z->nsleep--;
+ r->sleeping = 0;
+
+ /* if not done, there's no muxer: start muxing */
+ if(!r->done){
+ if(z->muxer)
+ abort();
+ z->muxer = 1;
+ while(!r->done){
+ qunlock(&z->lk);
+ if((p = vtrecv(z)) == nil){
+ werrstr("unexpected eof on venti connection");
+ z->muxer = 0;
+ vtfree(r);
+ return nil;
+ }
+ qlock(&z->lk);
+ muxrpc(z, p);
+ }
+ z->muxer = 0;
+ /* if there is anyone else sleeping, wake first unfinished to mux */
+ if(z->nsleep)
+ for(i=0; i<256; i++){
+ rr = z->wait[i];
+ if(rr && rr->sleeping && !rr->done){
+ rwakeup(&rr->r);
+ break;
+ }
+ }
+ }
+
+ p = r->p;
+ puttag(z, r, tag);
+ vtfree(r);
+ qunlock(&z->lk);
+ return p;
+}
+
+Packet*
+vtrpc(VtConn *z, Packet *p)
+{
+ return _vtrpc(z, p, nil);
+}
+
+static int
+gettag(VtConn *z, Rwait *r)
+{
+ int i;
+
+Again:
+ while(z->ntag == 256)
+ rsleep(&z->tagrend);
+ for(i=0; i<256; i++)
+ if(z->wait[i] == 0){
+ z->ntag++;
+ z->wait[i] = r;
+ return i;
+ }
+ fprint(2, "libventi: ntag botch\n");
+ goto Again;
+}
+
+static void
+puttag(VtConn *z, Rwait *r, int tag)
+{
+ assert(z->wait[tag] == r);
+ z->wait[tag] = nil;
+ z->ntag--;
+ rwakeup(&z->tagrend);
+}
+
+static void
+muxrpc(VtConn *z, Packet *p)
+{
+ uchar tag, buf[2], *top;
+ Rwait *r;
+
+ if((top = packetpeek(p, buf, 0, 2)) == nil){
+ fprint(2, "libventi: short packet in vtrpc\n");
+ packetfree(p);
+ return;
+ }
+
+ tag = top[1];
+ if((r = z->wait[tag]) == nil){
+ fprint(2, "libventi: unexpected packet tag %d in vtrpc\n", tag);
+abort();
+ packetfree(p);
+ return;
+ }
+
+ r->p = p;
+ r->done = 1;
+ rwakeup(&r->r);
+}
+
diff --git a/sys/src/libventi/scorefmt.c b/sys/src/libventi/scorefmt.c
new file mode 100755
index 000000000..496f85b7b
--- /dev/null
+++ b/sys/src/libventi/scorefmt.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.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/libventi/send.c b/sys/src/libventi/send.c
new file mode 100755
index 000000000..2b88818da
--- /dev/null
+++ b/sys/src/libventi/send.c
@@ -0,0 +1,251 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+#include "queue.h"
+
+long ventisendbytes, ventisendpackets;
+long ventirecvbytes, ventirecvpackets;
+
+static int
+_vtsend(VtConn *z, Packet *p)
+{
+ IOchunk ioc;
+ int n, tot;
+ uchar buf[2];
+
+ if(z->state != VtStateConnected) {
+ werrstr("session not connected");
+ return -1;
+ }
+
+ /* add framing */
+ n = packetsize(p);
+ if(n >= (1<<16)) {
+ werrstr("packet too large");
+ packetfree(p);
+ return -1;
+ }
+ buf[0] = n>>8;
+ buf[1] = n;
+ packetprefix(p, buf, 2);
+ ventisendbytes += n+2;
+ ventisendpackets++;
+
+ tot = 0;
+ for(;;){
+ n = packetfragments(p, &ioc, 1, 0);
+ if(n == 0)
+ break;
+ if(write(z->outfd, ioc.addr, ioc.len) < ioc.len){
+ vtlog(VtServerLog, "<font size=-1>%T %s:</font> sending packet %p: %r<br>\n", z->addr, p);
+ packetfree(p);
+ return -1;
+ }
+ packetconsume(p, nil, ioc.len);
+ tot += ioc.len;
+ }
+ vtlog(VtServerLog, "<font size=-1>%T %s:</font> sent packet %p (%d bytes)<br>\n", z->addr, p, tot);
+ packetfree(p);
+ return 1;
+}
+
+static int
+interrupted(void)
+{
+ char e[ERRMAX];
+
+ rerrstr(e, sizeof e);
+ return strstr(e, "interrupted") != nil;
+}
+
+
+static Packet*
+_vtrecv(VtConn *z)
+{
+ uchar buf[10], *b;
+ int n;
+ Packet *p;
+ int size, len;
+
+ if(z->state != VtStateConnected) {
+ werrstr("session not connected");
+ return nil;
+ }
+
+ p = z->part;
+ /* get enough for head size */
+ size = packetsize(p);
+ while(size < 2) {
+ b = packettrailer(p, 2);
+ assert(b != nil);
+ if(0) fprint(2, "%d read hdr\n", getpid());
+ n = read(z->infd, b, 2);
+ if(0) fprint(2, "%d got %d (%r)\n", getpid(), n);
+ if(n==0 || (n<0 && !interrupted()))
+ goto Err;
+ size += n;
+ packettrim(p, 0, size);
+ }
+
+ if(packetconsume(p, buf, 2) < 0)
+ 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(0) fprint(2, "%d read body %d\n", getpid(), n);
+ n = read(z->infd, b, n);
+ if(0) fprint(2, "%d got %d (%r)\n", getpid(), n);
+ if(n > 0)
+ size += n;
+ packettrim(p, 0, size);
+ if(n==0 || (n<0 && !interrupted()))
+ goto Err;
+ }
+ ventirecvbytes += len;
+ ventirecvpackets++;
+ p = packetsplit(p, len);
+ vtlog(VtServerLog, "<font size=-1>%T %s:</font> read packet %p len %d<br>\n", z->addr, p, len);
+ return p;
+Err:
+ vtlog(VtServerLog, "<font size=-1>%T %s:</font> error reading packet: %r<br>\n", z->addr);
+ return nil;
+}
+
+/*
+ * If you fork off two procs running vtrecvproc and vtsendproc,
+ * then vtrecv/vtsend (and thus vtrpc) will never block except on
+ * rendevouses, which is nice when it's running in one thread of many.
+ */
+void
+vtrecvproc(void *v)
+{
+ Packet *p;
+ VtConn *z;
+ Queue *q;
+
+ z = v;
+ q = _vtqalloc();
+
+ qlock(&z->lk);
+ z->readq = q;
+ qlock(&z->inlk);
+ rwakeup(&z->rpcfork);
+ qunlock(&z->lk);
+
+ while((p = _vtrecv(z)) != nil)
+ if(_vtqsend(q, p) < 0){
+ packetfree(p);
+ break;
+ }
+ qunlock(&z->inlk);
+ qlock(&z->lk);
+ _vtqhangup(q);
+ while((p = _vtnbqrecv(q)) != nil)
+ packetfree(p);
+ _vtqdecref(q);
+ z->readq = nil;
+ rwakeup(&z->rpcfork);
+ qunlock(&z->lk);
+ vthangup(z);
+}
+
+void
+vtsendproc(void *v)
+{
+ Queue *q;
+ Packet *p;
+ VtConn *z;
+
+ z = v;
+ q = _vtqalloc();
+
+ qlock(&z->lk);
+ z->writeq = q;
+ qlock(&z->outlk);
+ rwakeup(&z->rpcfork);
+ qunlock(&z->lk);
+
+ while((p = _vtqrecv(q)) != nil)
+ if(_vtsend(z, p) < 0)
+ break;
+ qunlock(&z->outlk);
+ qlock(&z->lk);
+ _vtqhangup(q);
+ while((p = _vtnbqrecv(q)) != nil)
+ packetfree(p);
+ _vtqdecref(q);
+ z->writeq = nil;
+ rwakeup(&z->rpcfork);
+ qunlock(&z->lk);
+ return;
+}
+
+Packet*
+vtrecv(VtConn *z)
+{
+ Packet *p;
+ Queue *q;
+
+ qlock(&z->lk);
+ if(z->state != VtStateConnected){
+ werrstr("not connected");
+ qunlock(&z->lk);
+ return nil;
+ }
+ if(z->readq){
+ q = _vtqincref(z->readq);
+ qunlock(&z->lk);
+ p = _vtqrecv(q);
+ _vtqdecref(q);
+ return p;
+ }
+
+ qlock(&z->inlk);
+ qunlock(&z->lk);
+ p = _vtrecv(z);
+ qunlock(&z->inlk);
+ if(!p)
+ vthangup(z);
+ return p;
+}
+
+int
+vtsend(VtConn *z, Packet *p)
+{
+ Queue *q;
+
+ qlock(&z->lk);
+ if(z->state != VtStateConnected){
+ packetfree(p);
+ werrstr("not connected");
+ qunlock(&z->lk);
+ return -1;
+ }
+ if(z->writeq){
+ q = _vtqincref(z->writeq);
+ qunlock(&z->lk);
+ if(_vtqsend(q, p) < 0){
+ _vtqdecref(q);
+ packetfree(p);
+ return -1;
+ }
+ _vtqdecref(q);
+ return 0;
+ }
+
+ qlock(&z->outlk);
+ qunlock(&z->lk);
+ if(_vtsend(z, p) < 0){
+ qunlock(&z->outlk);
+ vthangup(z);
+ return -1;
+ }
+ qunlock(&z->outlk);
+ return 0;
+}
+
diff --git a/sys/src/libventi/server.c b/sys/src/libventi/server.c
new file mode 100755
index 000000000..10b0665fd
--- /dev/null
+++ b/sys/src/libventi/server.c
@@ -0,0 +1,215 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+#include <thread.h>
+#include "queue.h"
+
+enum
+{
+ STACK = 8192
+};
+
+typedef struct VtSconn VtSconn;
+struct VtSconn
+{
+ int ctl;
+ int ref;
+ QLock lk;
+ char dir[NETPATHLEN];
+ VtSrv *srv;
+ VtConn *c;
+};
+
+struct VtSrv
+{
+ int afd;
+ int dead;
+ char adir[NETPATHLEN];
+ Queue *q; /* Queue(VtReq*) */
+};
+
+static void listenproc(void*);
+static void connproc(void*);
+
+static void
+scincref(VtSconn *sc)
+{
+ qlock(&sc->lk);
+ sc->ref++;
+ qunlock(&sc->lk);
+}
+
+static void
+scdecref(VtSconn *sc)
+{
+ qlock(&sc->lk);
+ if(--sc->ref > 0){
+ qunlock(&sc->lk);
+ return;
+ }
+ if(sc->c)
+ vtfreeconn(sc->c);
+ vtfree(sc);
+}
+
+VtSrv*
+vtlisten(char *addr)
+{
+ VtSrv *s;
+
+ s = vtmallocz(sizeof(VtSrv));
+ s->afd = announce(addr, s->adir);
+ if(s->afd < 0){
+ free(s);
+ return nil;
+ }
+ s->q = _vtqalloc();
+ proccreate(listenproc, s, STACK);
+ return s;
+}
+
+static void
+listenproc(void *v)
+{
+ int ctl;
+ char dir[NETPATHLEN];
+ VtSrv *srv;
+ VtSconn *sc;
+
+ srv = v;
+ for(;;){
+ ctl = listen(srv->adir, dir);
+ if(ctl < 0){
+ srv->dead = 1;
+ break;
+ }
+ sc = vtmallocz(sizeof(VtSconn));
+ sc->ref = 1;
+ sc->ctl = ctl;
+ sc->srv = srv;
+ strcpy(sc->dir, dir);
+ proccreate(connproc, sc, STACK);
+ }
+
+ /* hangup */
+}
+
+static void
+connproc(void *v)
+{
+ VtSconn *sc;
+ VtConn *c;
+ Packet *p;
+ VtReq *r;
+ int fd;
+static int first=1;
+
+if(first && chattyventi){
+ first=0;
+ fmtinstall('F', vtfcallfmt);
+}
+ r = nil;
+ sc = v;
+ sc->c = nil;
+ if(0) fprint(2, "new call %s on %d\n", sc->dir, sc->ctl);
+ fd = accept(sc->ctl, sc->dir);
+ close(sc->ctl);
+ if(fd < 0){
+ fprint(2, "accept %s: %r\n", sc->dir);
+ goto out;
+ }
+
+ c = vtconn(fd, fd);
+ sc->c = c;
+ if(vtversion(c) < 0){
+ fprint(2, "vtversion %s: %r\n", sc->dir);
+ goto out;
+ }
+ if(vtsrvhello(c) < 0){
+ fprint(2, "vtsrvhello %s: %r\n", sc->dir);
+ goto out;
+ }
+
+ if(0) fprint(2, "new proc %s\n", sc->dir);
+ proccreate(vtsendproc, c, STACK);
+ qlock(&c->lk);
+ while(!c->writeq)
+ rsleep(&c->rpcfork);
+ qunlock(&c->lk);
+
+ while((p = vtrecv(c)) != nil){
+ r = vtmallocz(sizeof(VtReq));
+ if(vtfcallunpack(&r->tx, p) < 0){
+ vtlog(VtServerLog, "<font size=-1>%T %s:</font> recv bad packet %p: %r<br>\n", c->addr, p);
+ fprint(2, "bad packet on %s: %r\n", sc->dir);
+ packetfree(p);
+ continue;
+ }
+ vtlog(VtServerLog, "<font size=-1>%T %s:</font> recv packet %p (%F)<br>\n", c->addr, p, &r->tx);
+ if(chattyventi)
+ fprint(2, "%s <- %F\n", argv0, &r->tx);
+ packetfree(p);
+ if(r->tx.msgtype == VtTgoodbye)
+ break;
+ r->rx.tag = r->tx.tag;
+ r->sc = sc;
+ scincref(sc);
+ if(_vtqsend(sc->srv->q, r) < 0){
+ scdecref(sc);
+ fprint(2, "hungup queue\n");
+ break;
+ }
+ r = nil;
+ }
+
+ if(0) fprint(2, "eof on %s\n", sc->dir);
+
+out:
+ if(r){
+ vtfcallclear(&r->tx);
+ vtfree(r);
+ }
+ if(0) fprint(2, "freed %s\n", sc->dir);
+ scdecref(sc);
+ return;
+}
+
+VtReq*
+vtgetreq(VtSrv *srv)
+{
+ VtReq *r;
+
+ r = _vtqrecv(srv->q);
+ vtlog(VtServerLog, "<font size=-1>%T %s:</font> vtgetreq %F<br>\n", ((VtSconn*)r->sc)->c->addr, &r->tx);
+ return r;
+}
+
+void
+vtrespond(VtReq *r)
+{
+ Packet *p;
+ VtSconn *sc;
+
+ sc = r->sc;
+ if(r->rx.tag != r->tx.tag)
+ abort();
+ if(r->rx.msgtype != r->tx.msgtype+1 && r->rx.msgtype != VtRerror)
+ abort();
+ if(chattyventi)
+ fprint(2, "%s -> %F\n", argv0, &r->rx);
+ if((p = vtfcallpack(&r->rx)) == nil){
+ vtlog(VtServerLog, "%s: vtfcallpack %F: %r<br>\n", sc->c->addr, &r->rx);
+ fprint(2, "fcallpack on %s: %r\n", sc->dir);
+ packetfree(p);
+ vtfcallclear(&r->rx);
+ return;
+ }
+ vtlog(VtServerLog, "<font size=-1>%T %s:</font> send packet %p (%F)<br>\n", sc->c->addr, p, &r->rx);
+ if(vtsend(sc->c, p) < 0)
+ fprint(2, "vtsend %F: %r\n", &r->rx);
+ scdecref(sc);
+ vtfcallclear(&r->tx);
+ vtfcallclear(&r->rx);
+ vtfree(r);
+}
+
diff --git a/sys/src/libventi/srvhello.c b/sys/src/libventi/srvhello.c
new file mode 100755
index 000000000..ca34221e6
--- /dev/null
+++ b/sys/src/libventi/srvhello.c
@@ -0,0 +1,50 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+int
+vtsrvhello(VtConn *z)
+{
+ VtFcall tx, rx;
+ Packet *p;
+
+ if((p = vtrecv(z)) == nil)
+ return -1;
+
+ if(vtfcallunpack(&tx, p) < 0){
+ packetfree(p);
+ return -1;
+ }
+ packetfree(p);
+
+ if(tx.msgtype != VtThello){
+ vtfcallclear(&tx);
+ werrstr("bad packet type %d; want Thello %d", tx.msgtype, VtThello);
+ return -1;
+ }
+ if(tx.tag != 0){
+ vtfcallclear(&tx);
+ werrstr("bad tag in hello");
+ return -1;
+ }
+ if(strcmp(tx.version, z->version) != 0){
+ vtfcallclear(&tx);
+ werrstr("bad version in hello");
+ return -1;
+ }
+ vtfree(z->uid);
+ z->uid = tx.uid;
+ tx.uid = nil;
+ vtfcallclear(&tx);
+
+ memset(&rx, 0, sizeof rx);
+ rx.msgtype = VtRhello;
+ rx.tag = tx.tag;
+ rx.sid = "anonymous";
+ if((p = vtfcallpack(&rx)) == nil)
+ return -1;
+ if(vtsend(z, p) < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/sys/src/libventi/strdup.c b/sys/src/libventi/strdup.c
new file mode 100755
index 000000000..e191c390c
--- /dev/null
+++ b/sys/src/libventi/strdup.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+char*
+vtstrdup(char *s)
+{
+ int n;
+ char *ss;
+
+ if(s == nil)
+ return nil;
+ n = strlen(s) + 1;
+ ss = vtmalloc(n);
+ memmove(ss, s, n);
+ return ss;
+}
+
diff --git a/sys/src/libventi/string.c b/sys/src/libventi/string.c
new file mode 100755
index 000000000..9763149a1
--- /dev/null
+++ b/sys/src/libventi/string.c
@@ -0,0 +1,50 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+int
+vtputstring(Packet *p, char *s)
+{
+ uchar buf[2];
+ int n;
+
+ if(s == nil){
+ werrstr("null string in packet");
+ return -1;
+ }
+ n = strlen(s);
+ if(n > VtMaxStringSize){
+ werrstr("string too long in packet");
+ return -1;
+ }
+ buf[0] = n>>8;
+ buf[1] = n;
+ packetappend(p, buf, 2);
+ packetappend(p, (uchar*)s, n);
+ return 0;
+}
+
+int
+vtgetstring(Packet *p, char **ps)
+{
+ uchar buf[2];
+ int n;
+ char *s;
+
+ if(packetconsume(p, buf, 2) < 0)
+ return -1;
+ n = (buf[0]<<8) + buf[1];
+ if(n > VtMaxStringSize) {
+ werrstr("string too long in packet");
+ return -1;
+ }
+ s = vtmalloc(n+1);
+ if(packetconsume(p, (uchar*)s, n) < 0){
+ vtfree(s);
+ return -1;
+ }
+ s[n] = 0;
+ *ps = s;
+ return 0;
+}
+
diff --git a/sys/src/libventi/time.c b/sys/src/libventi/time.c
new file mode 100755
index 000000000..2d8058f13
--- /dev/null
+++ b/sys/src/libventi/time.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+int
+vttimefmt(Fmt *fmt)
+{
+ vlong ns;
+ Tm tm;
+
+ if(fmt->flags&FmtLong){
+ ns = nsec();
+ tm = *localtime(ns/1000000000);
+ return fmtprint(fmt, "%04d/%02d%02d %02d:%02d:%02d.%03d",
+ tm.year+1900, tm.mon+1, tm.mday,
+ tm.hour, tm.min, tm.sec,
+ (int)(ns%1000000000)/1000000);
+ }else{
+ tm = *localtime(time(0));
+ return fmtprint(fmt, "%04d/%02d%02d %02d:%02d:%02d",
+ tm.year+1900, tm.mon+1, tm.mday,
+ tm.hour, tm.min, tm.sec);
+ }
+}
+
diff --git a/sys/src/libventi/version.c b/sys/src/libventi/version.c
new file mode 100755
index 000000000..fd1e2389a
--- /dev/null
+++ b/sys/src/libventi/version.c
@@ -0,0 +1,120 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+static char *okvers[] = {
+ "02",
+ nil,
+};
+
+/*
+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 int
+vtreadversion(VtConn *z, char *q, char *v, int nv)
+{
+ int n;
+
+ for(;;){
+ if(nv <= 1){
+ werrstr("version too long");
+ return -1;
+ }
+ n = read(z->infd, v, 1);
+ if(n <= 0){
+ if(n == 0)
+ werrstr("unexpected eof");
+ return -1;
+ }
+ if(*v == '\n'){
+ *v = 0;
+ break;
+ }
+ if((uchar)*v < ' ' || (uchar)*v > 0x7f || (*q && *v != *q)){
+ werrstr(EBadVersion);
+ return -1;
+ }
+ v++;
+ nv--;
+ if(*q)
+ q++;
+ }
+ return 0;
+}
+
+int
+vtversion(VtConn *z)
+{
+ char buf[VtMaxStringSize], *p, *ep, *prefix, *pp;
+ int i;
+
+ qlock(&z->lk);
+ if(z->state != VtStateAlloc){
+ werrstr("bad session state");
+ qunlock(&z->lk);
+ return -1;
+ }
+
+ qlock(&z->inlk);
+ qlock(&z->outlk);
+
+ p = buf;
+ ep = buf + sizeof buf;
+ prefix = "venti-";
+ p = seprint(p, ep, "%s", prefix);
+ p += strlen(p);
+ for(i=0; okvers[i]; i++)
+ p = seprint(p, ep, "%s%s", i ? ":" : "", okvers[i]);
+ p = seprint(p, ep, "-libventi\n");
+ assert(p-buf < sizeof buf);
+
+ if(write(z->outfd, buf, p-buf) != p-buf)
+ goto Err;
+ vtdebug(z, "version string out: %s", buf);
+
+ if(vtreadversion(z, prefix, buf, sizeof buf) < 0)
+ goto Err;
+ vtdebug(z, "version string in: %s", buf);
+
+ p = buf+strlen(prefix);
+ for(; *p; p=pp){
+ if(*p == ':' || *p == '-')
+ p++;
+ pp = strpbrk(p, ":-");
+ if(pp == nil)
+ pp = p+strlen(p);
+ for(i=0; okvers[i]; i++)
+ if(strlen(okvers[i]) == pp-p && memcmp(okvers[i], p, pp-p) == 0){
+ *pp = 0;
+ z->version = vtstrdup(p);
+ goto Okay;
+ }
+ }
+ werrstr("unable to negotiate version");
+ goto Err;
+
+Okay:
+ z->state = VtStateConnected;
+ qunlock(&z->inlk);
+ qunlock(&z->outlk);
+ qunlock(&z->lk);
+ return 0;
+
+Err:
+ werrstr("vtversion: %r");
+ if(z->infd >= 0)
+ close(z->infd);
+ if(z->outfd >= 0 && z->outfd != z->infd)
+ close(z->outfd);
+ z->infd = -1;
+ z->outfd = -1;
+ z->state = VtStateClosed;
+ qunlock(&z->inlk);
+ qunlock(&z->outlk);
+ qunlock(&z->lk);
+ return -1;
+}
diff --git a/sys/src/libventi/zero.c b/sys/src/libventi/zero.c
new file mode 100755
index 000000000..c40aea969
--- /dev/null
+++ b/sys/src/libventi/zero.c
@@ -0,0 +1,55 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+void
+vtzeroextend(int type, uchar *buf, uint n, uint nn)
+{
+ uchar *p, *ep;
+
+ switch(type&7) {
+ case 0:
+ memset(buf+n, 0, nn-n);
+ break;
+ default:
+ 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;
+ }
+}
+
+uint
+vtzerotruncate(int type, uchar *buf, uint n)
+{
+ uchar *p;
+
+ if(type == VtRootType){
+ if(n < VtRootSize)
+ return n;
+ return VtRootSize;
+ }
+
+ switch(type&7){
+ case 0:
+ for(p = buf + n; p > buf; p--) {
+ if(p[-1] != 0)
+ break;
+ }
+ return p - buf;
+ default:
+ /* 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;
+ }
+}
diff --git a/sys/src/libventi/zeroscore.c b/sys/src/libventi/zeroscore.c
new file mode 100755
index 000000000..6f22d72de
--- /dev/null
+++ b/sys/src/libventi/zeroscore.c
@@ -0,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.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
+};
+