tnew venti library. - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 056fe1ba7fa0b70f871dfb9005b24eb8e4cc230b | |
parent 9df487d720a59bf8cb0dc4ccffc30ad8eb48256a | |
Author: rsc <devnull@localhost> | |
Date: Sun, 23 Nov 2003 18:19:58 +0000 | |
new venti library. | |
Diffstat: | |
A src/libventi/cache.c | 560 +++++++++++++++++++++++++++++… | |
A src/libventi/client.c | 151 +++++++++++++++++++++++++++++… | |
A src/libventi/conn.c | 36 +++++++++++++++++++++++++++++… | |
A src/libventi/cvt.h | 15 +++++++++++++++ | |
A src/libventi/debug.c | 17 +++++++++++++++++ | |
A src/libventi/dial.c | 21 +++++++++++++++++++++ | |
A src/libventi/dtype.c | 78 +++++++++++++++++++++++++++++… | |
A src/libventi/entry.c | 85 +++++++++++++++++++++++++++++… | |
A src/libventi/fcall.c | 230 +++++++++++++++++++++++++++++… | |
A src/libventi/fcallfmt.c | 55 +++++++++++++++++++++++++++++… | |
A src/libventi/file.c | 1264 +++++++++++++++++++++++++++++… | |
A src/libventi/hangup.c | 22 ++++++++++++++++++++++ | |
A src/libventi/mem.c | 87 +++++++++++++++++++++++++++++… | |
A src/libventi/mkfile | 42 +++++++++++++++++++++++++++++… | |
A src/libventi/packet.c | 941 +++++++++++++++++++++++++++++… | |
A src/libventi/queue.c | 103 +++++++++++++++++++++++++++++… | |
A src/libventi/queue.h | 6 ++++++ | |
A src/libventi/root.c | 67 +++++++++++++++++++++++++++++… | |
A src/libventi/rpc.c | 155 +++++++++++++++++++++++++++++… | |
A src/libventi/scorefmt.c | 18 ++++++++++++++++++ | |
A src/libventi/send.c | 212 ++++++++++++++++++++++++++++++ | |
A src/libventi/server.c | 172 ++++++++++++++++++++++++++++++ | |
A src/libventi/srvhello.c | 50 +++++++++++++++++++++++++++++… | |
A src/libventi/strdup.c | 18 ++++++++++++++++++ | |
A src/libventi/string.c | 50 +++++++++++++++++++++++++++++… | |
A src/libventi/version.c | 115 +++++++++++++++++++++++++++++… | |
A src/libventi/zero.c | 55 +++++++++++++++++++++++++++++… | |
A src/libventi/zeroscore.c | 10 ++++++++++ | |
28 files changed, 4635 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/src/libventi/cache.c b/src/libventi/cache.c | |
t@@ -0,0 +1,560 @@ | |
+/* | |
+ * 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 nread, ncopy, nwrite; | |
+ | |
+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 mode; | |
+}; | |
+ | |
+static void cachecheck(VtCache*); | |
+ | |
+VtCache* | |
+vtcachealloc(VtConn *z, int blocksize, ulong nblock, int mode) | |
+{ | |
+ 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->mode = mode; | |
+ | |
+ 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; | |
+} | |
+ | |
+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… | |
+ i, b->type, b->score, b->iostate, b->addr, b->ref, b->… | |
+ } | |
+} | |
+ | |
+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]->us… | |
+ sysfatal("bad heap ordering"); | |
+ k = (i << 1) + 1; | |
+ if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used -… | |
+ sysfatal("bad heap ordering"); | |
+ k++; | |
+ if(k < c->nheap && c->heap[i]->used - now > c->heap[k]->used -… | |
+ 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++; | |
+ } | |
+if(c->nheap + refed != c->nblock){ | |
+fprint(2, "cachecheck: nheap %d refed %d nblocks %d\n", c->nheap, refed, c->nb… | |
+//vtcachedump(c); | |
+} | |
+ assert(c->nheap + refed == c->nblock); | |
+ refed = 0; | |
+ for(i = 0; i < c->nblock; i++){ | |
+ b = &c->block[i]; | |
+ if(b->ref){ | |
+if(1)fprint(2, "a=%ud %V ref=%d\n", b->addr, b->score, b->ref); | |
+ refed++; | |
+ } | |
+ } | |
+if(refed > 0)fprint(2, "cachecheck: in used %d\n", 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]… | |
+ 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); | |
+abort(); | |
+ sysfatal("vtcachebumpblock: no free blocks in vtCache"); | |
+ } | |
+ 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 >= c->nblock) | |
+ sysfatal("vtcachelocal: asked for block #%ud; only %d blocks\n… | |
+ addr, c->nblock); | |
+ | |
+ b = &c->block[addr]; | |
+ if(b->addr == NilBlock || b->iostate != BioLocal) | |
+{ | |
+abort(); | |
+ sysfatal("vtcachelocal: block is not local"); | |
+} | |
+ | |
+ if(b->type != type) | |
+{ | |
+print("%d != %d\n", b->type, type); | |
+abort(); | |
+ sysfatal("vtcachelocal: block has wrong type %d != %d", b->typ… | |
+} | |
+ | |
+ qlock(&c->lk); | |
+ b->ref++; | |
+ qunlock(&c->lk); | |
+ | |
+ qlock(&b->lk); | |
+ b->nlock = 1; | |
+ return b; | |
+} | |
+ | |
+VtBlock* | |
+vtcacheallocblock(VtCache *c, int type) | |
+{ | |
+ VtBlock *b; | |
+ | |
+if(type >= VtMaxType) | |
+ abort(); | |
+ | |
+ qlock(&c->lk); | |
+ b = vtcachebumpblock(c); | |
+ b->iostate = BioLocal; | |
+ b->type = type; | |
+ b->addr = b - c->block; | |
+ vtzeroextend(type, b->data, 0, c->blocksize); | |
+ vtlocaltoglobal(b->addr, b->score); | |
+ qunlock(&c->lk); | |
+ | |
+ qlock(&b->lk); | |
+ b->nlock = 1; | |
+ | |
+ 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; | |
+ | |
+ addr = vtglobaltolocal(score); | |
+ if(addr != NilBlock) | |
+ return vtcachelocal(c, addr, type); | |
+ | |
+ h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c… | |
+ | |
+ /* | |
+ * 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)… | |
+ continue; | |
+ heapdel(b); | |
+ b->ref++; | |
+ qunlock(&c->lk); | |
+ qlock(&b->lk); | |
+ b->nlock = 1; | |
+ 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 qunloc… | |
+ * 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 s… | |
+ * We're certainly living on the edge. | |
+ */ | |
+ qlock(&b->lk); | |
+ b->nlock = 1; | |
+ qunlock(&c->lk); | |
+ | |
+ n = vtread(c->z, score, type, b->data, c->blocksize); | |
+ if(n < 0){ | |
+fprint(2, "vtread: %r\n"); | |
+ b->iostate = BioVentiError; | |
+ vtblockput(b); | |
+ return nil; | |
+ } | |
+ vtzeroextend(type, b->data, n, c->blocksize); | |
+ b->iostate = BioVenti; | |
+ b->nlock = 1; | |
+ b->decrypted = 0; | |
+ 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->i… | |
+ | |
+ 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++; | |
+ 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){ | |
+ abort(); | |
+ sysfatal("vtBlockWrite: not a local block"); | |
+ } | |
+ | |
+ c = b->c; | |
+ n = vtzerotruncate(b->type, b->data, c->blocksize); | |
+ if(vtwrite(c->z, score, b->type, b->data, n) < 0) | |
+ return -1; | |
+ | |
+ memmove(b->score, score, VtScoreSize); | |
+ | |
+ qlock(&c->lk); | |
+ b->iostate = BioVenti; | |
+ h = (u32int)(score[0]|(score[1]<<8)|(score[2]<<16)|(score[3]<<24)) % c… | |
+ 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; | |
+ | |
+ncopy++; | |
+ bb = vtcacheallocblock(b->c, b->type); | |
+ if(bb == nil){ | |
+ vtblockput(b); | |
+ return nil; | |
+ } | |
+ memmove(bb->data, b->data, b->c->blocksize); | |
+ vtblockput(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/src/libventi/client.c b/src/libventi/client.c | |
t@@ -0,0 +1,151 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <venti.h> | |
+ | |
+static int | |
+vtfcallrpc(VtConn *z, VtFcall *ou, VtFcall *in) | |
+{ | |
+ Packet *p; | |
+ | |
+ p = vtfcallpack(ou); | |
+ if(p == nil) | |
+ return -1; | |
+ if((p = vtrpc(z, p)) == nil) | |
+ return -1; | |
+ if(vtfcallunpack(in, p) < 0){ | |
+ packetfree(p); | |
+ return -1; | |
+ } | |
+ if(in->type == VtRerror){ | |
+ werrstr(in->error); | |
+ vtfcallclear(in); | |
+ packetfree(p); | |
+ return -1; | |
+ } | |
+ if(in->type != ou->type+1){ | |
+ werrstr("type mismatch: sent %c%d got %c%d", | |
+ "TR"[ou->type&1], ou->type>>1, | |
+ "TR"[in->type&1], in->type>>1); | |
+ vtfcallclear(in); | |
+ packetfree(p); | |
+ return -1; | |
+ } | |
+ packetfree(p); | |
+ return 0; | |
+} | |
+ | |
+int | |
+vthello(VtConn *z) | |
+{ | |
+ VtFcall tx, rx; | |
+ | |
+ memset(&tx, 0, sizeof tx); | |
+ tx.type = 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; | |
+ | |
+ memset(&tx, 0, sizeof tx); | |
+ tx.type = VtTread; | |
+ tx.dtype = 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; | |
+ } | |
+ 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(); | |
+ return nn; | |
+} | |
+ | |
+int | |
+vtwritepacket(VtConn *z, uchar score[VtScoreSize], uint type, Packet *p) | |
+{ | |
+ VtFcall tx, rx; | |
+ | |
+ tx.type = VtTwrite; | |
+ tx.dtype = type; | |
+ tx.data = p; | |
+ packetsha1(p, score); | |
+ if(vtfcallrpc(z, &tx, &rx) < 0) | |
+ return -1; | |
+ if(memcmp(score, rx.score, VtScoreSize) != 0){ | |
+ werrstr("sha1 hash mismatch: want %V got %V", score, rx.score); | |
+ return -1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int | |
+vtwrite(VtConn *z, uchar score[VtScoreSize], uint type, uchar *buf, int n) | |
+{ | |
+ Packet *p; | |
+ | |
+ p = packetforeign(buf, n, nil, nil); | |
+ return vtwritepacket(z, score, type, p); | |
+} | |
+ | |
+int | |
+vtsync(VtConn *z) | |
+{ | |
+ VtFcall tx, rx; | |
+ | |
+ tx.type = VtTsync; | |
+ return vtfcallrpc(z, &tx, &rx); | |
+} | |
+ | |
+int | |
+vtping(VtConn *z) | |
+{ | |
+ VtFcall tx, rx; | |
+ | |
+ tx.type = VtTping; | |
+ return vtfcallrpc(z, &tx, &rx); | |
+} | |
+ | |
+int | |
+vtconnect(VtConn *z) | |
+{ | |
+ if(vtversion(z) < 0) | |
+ return -1; | |
+ if(vthello(z) < 0) | |
+ return -1; | |
+ return 0; | |
+} | |
+ | |
diff --git a/src/libventi/conn.c b/src/libventi/conn.c | |
t@@ -0,0 +1,36 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <venti.h> | |
+#include "queue.h" | |
+ | |
+VtConn* | |
+vtconn(int infd, int outfd) | |
+{ | |
+ VtConn *z; | |
+ | |
+ z = vtmallocz(sizeof(VtConn)); | |
+ z->tagrend.l = &z->lk; | |
+ z->rpcfork.l = &z->lk; | |
+ z->infd = infd; | |
+ z->outfd = outfd; | |
+ z->part = packetalloc(); | |
+ return z; | |
+} | |
+ | |
+void | |
+vtfreeconn(VtConn *z) | |
+{ | |
+ vthangup(z); | |
+ qlock(&z->lk); | |
+ for(;;){ | |
+ if(z->readq) | |
+ _vtqhangup(z->readq); | |
+ else if(z->writeq) | |
+ _vtqhangup(z->writeq); | |
+ else | |
+ break; | |
+ rsleep(&z->rpcfork); | |
+ } | |
+ packetfree(z->part); | |
+ vtfree(z); | |
+} | |
diff --git a/src/libventi/cvt.h b/src/libventi/cvt.h | |
t@@ -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… | |
+#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;… | |
+#define U48PUT(p,v,t32) t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT… | |
+#define U64PUT(p,v,t32) t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT… | |
+ | |
diff --git a/src/libventi/debug.c b/src/libventi/debug.c | |
t@@ -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/src/libventi/dial.c b/src/libventi/dial.c | |
t@@ -0,0 +1,21 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <venti.h> | |
+ | |
+VtConn* | |
+vtdial(char *addr) | |
+{ | |
+ char *na; | |
+ int fd; | |
+ | |
+ if(addr == nil) | |
+ addr = getenv("venti"); | |
+ if(addr == nil) | |
+ addr = "$venti"; | |
+ | |
+ na = netmkaddr(addr, "net", "venti"); | |
+ if((fd = dial(na, nil, nil, nil)) < 0) | |
+ return nil; | |
+ | |
+ return vtconn(fd, fd); | |
+} | |
diff --git a/src/libventi/dtype.c b/src/libventi/dtype.c | |
t@@ -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[] = { | |
+ ~0, | |
+ VtRootType, | |
+ VtDirType, | |
+ VtDirType+1, | |
+ VtDirType+2, | |
+ VtDirType+3, | |
+ VtDirType+4, | |
+ VtDirType+5, | |
+ VtDirType+6, | |
+ VtDirType+7, | |
+ ~0, | |
+ ~0, | |
+ ~0, | |
+ VtDataType, | |
+}; | |
+ | |
+uint | |
+vttodisktype(uint n) | |
+{ | |
+ if(n >= nelem(todisk)) | |
+ return ~0; | |
+ return todisk[n]; | |
+} | |
+ | |
+uint | |
+vtfromdisktype(uint n) | |
+{ | |
+ if(n >= nelem(fromdisk)) | |
+ return ~0; | |
+ return fromdisk[n]; | |
+} | |
+ | |
diff --git a/src/libventi/entry.c b/src/libventi/entry.c | |
t@@ -0,0 +1,85 @@ | |
+#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 | |
+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|VtEntryDepthShift)); | |
+ flags |= depth << VtEntryDepthShift; | |
+ if(e->type - depth == VtEntryDir) | |
+ 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; | |
+ | |
+ if(checksize(e->psize) < 0 || checksize(e->dsize) < 0) | |
+ return -1; | |
+ | |
+ return 0; | |
+} | |
+ | |
diff --git a/src/libventi/fcall.c b/src/libventi/fcall.c | |
t@@ -0,0 +1,230 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <venti.h> | |
+ | |
+Packet* | |
+vtfcallpack(VtFcall *f) | |
+{ | |
+ uchar buf[4]; | |
+ Packet *p; | |
+ | |
+ p = packetalloc(); | |
+ | |
+ buf[0] = f->type; | |
+ buf[1] = f->tag; | |
+ packetappend(p, buf, 2); | |
+ | |
+ switch(f->type){ | |
+ default: | |
+ werrstr("vtfcallpack: unknown packet type %d", f->type); | |
+ 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->dtype); | |
+ 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->dtype); | |
+ 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->type = buf[0]; | |
+ f->tag = buf[1]; | |
+ | |
+ switch(f->type){ | |
+ default: | |
+ werrstr("vtfcallunpack: unknown bad packet type %d", f->type); | |
+ 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->dtype = vtfromdisktype(buf[0]); | |
+ if(~f->dtype == 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->dtype = vtfromdisktype(buf[0]); | |
+ if(~f->dtype == 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"); | |
+ 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->auth = nil; | |
+} | |
diff --git a/src/libventi/fcallfmt.c b/src/libventi/fcallfmt.c | |
t@@ -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->type){ | |
+ default: | |
+ return fmtprint(f, "%c%d tag %ud", "TR"[t->type&1], t->type>>1… | |
+ 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 … | |
+ t->version, t->uid, t->strength, t->ncrypto, t->ncrypt… | |
+ t->ncodec, t->ncodec, t->codec); | |
+ case VtRhello: | |
+ return fmtprint(f, "Rhello tag %ud sid %s rcrypto %d rcodec %d… | |
+ 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->naut… | |
+ case VtRauth0: | |
+ return fmtprint(f, "Rauth0 tag %ud auth %.*H", t->tag, t->naut… | |
+ case VtTauth1: | |
+ return fmtprint(f, "Tauth1 tag %ud auth %.*H", t->tag, t->naut… | |
+ case VtRauth1: | |
+ return fmtprint(f, "Rauth1 tag %ud auth %.*H", t->tag, t->naut… | |
+ case VtTread: | |
+ return fmtprint(f, "Tread tag %ud score %V dtype %d count %d",… | |
+ case VtRread: | |
+ return fmtprint(f, "Rread tag %ud count %d", t->tag, packetsiz… | |
+ case VtTwrite: | |
+ return fmtprint(f, "Twrite tag %ud dtype %d count %d", t->tag,… | |
+ 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/src/libventi/file.c b/src/libventi/file.c | |
t@@ -0,0 +1,1264 @@ | |
+/* | |
+ * 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> | |
+ | |
+enum | |
+{ | |
+ MaxBlock = (1UL<<31), | |
+}; | |
+ | |
+struct VtFile | |
+{ | |
+ QLock lk; | |
+ int ref; | |
+ int local; | |
+ VtBlock *b; /* block containing this file */ | |
+ uchar score[VtScoreSize]; /* score of block containing this fil… | |
+ | |
+/* immutable */ | |
+ VtCache *c; | |
+ int mode; | |
+ u32int gen; | |
+ int dsize; | |
+ int dir; | |
+ VtFile *parent; | |
+ int epb; /* entries per block in parent */ | |
+ u32int offset; /* entry offset in parent */ | |
+}; | |
+ | |
+static char EBadEntry[] = "bad VtEntry"; | |
+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) | |
+ goto Bad; | |
+ | |
+ /* | |
+ * 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\n"); | |
+ goto Bad; | |
+ } | |
+ if(!(e.flags & VtEntryActive)){ | |
+ if(0)fprint(2, "not active\n"); | |
+ goto Bad; | |
+ } | |
+ if(e.psize < 256 || e.dsize < 256){ | |
+ fprint(2, "psize %ud dsize %ud\n", e.psize, e.dsize); | |
+ goto Bad; | |
+ } | |
+ | |
+ 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); | |
+ goto Bad; | |
+ } | |
+ | |
+ size = vtcacheblocksize(c); | |
+ if(e.dsize > size || e.psize > size){ | |
+ fprint(2, "psize %ud dsize %ud blocksize %ud\n", e.psize, e.ds… | |
+ goto Bad; | |
+ } | |
+ | |
+ r = vtmallocz(sizeof(VtFile)); | |
+ r->c = c; | |
+ r->mode = mode; | |
+ r->dsize = e.dsize; | |
+ r->gen = e.gen; | |
+ r->dir = (e.flags & VtEntryDir) != 0; | |
+ r->ref = 1; | |
+ r->parent = p; | |
+ if(p){ | |
+ qlock(&p->lk); | |
+ assert(mode == VtOREAD || p->mode == VtORDWR); | |
+ p->ref++; | |
+ qunlock(&p->lk); | |
+ } | |
+ memmove(r->score, b->score, VtScoreSize); | |
+ r->offset = offset; | |
+ r->epb = epb; | |
+ | |
+ return r; | |
+Bad: | |
+ werrstr(EBadEntry); | |
+ return nil; | |
+ | |
+} | |
+ | |
+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; | |
+ if(type == VtDirType) | |
+ e.flags |= VtEntryDir; | |
+ 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 dir) | |
+{ | |
+ int i; | |
+ VtBlock *b; | |
+ u32int bn, size; | |
+ VtEntry e; | |
+ int epb; | |
+ VtFile *rr; | |
+ u32int offset; | |
+ | |
+ assert(ISLOCKED(r)); | |
+ | |
+ 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 | |
+ */ | |
+ offset = lnrand(size+1); | |
+ offset -= offset % epb; | |
+ | |
+ /* 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 = dir ? VtDirType : VtDataType; | |
+ 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 som… | |
+ vtblockput(b); | |
+ return -1; | |
+ } | |
+ | |
+ /* | |
+ * invariant: each pointer in the tree rooted at b accounts fo… | |
+ */ | |
+ | |
+ /* zero the pointers to unnecessary blocks */ | |
+ i = (size+ptrsz-1)/ptrsz; | |
+ for(; i<ppb; i++) | |
+ memmove(b->data+i*VtScoreSize, vtzeroscore, VtScoreSiz… | |
+ | |
+ /* 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, uvlong 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… | |
+ | |
+ 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 0; | |
+ | |
+ /* | |
+ * 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*sizeof(int)); | |
+ | |
+ np = e->psize/VtScoreSize; | |
+ for(i=0; bn > 0; i++){ | |
+ if(i >= VtPointerDepth){ | |
+ werrstr(EBadAddr); | |
+ 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) | |
+ return nil; | |
+ if(i > DEPTH(e.type)){ | |
+ if(mode == VtOREAD){ | |
+ werrstr(EBadAddr); | |
+ 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; | |
+ } | |
+ return b; | |
+Err: | |
+ vtblockput(b); | |
+ return nil; | |
+} | |
+ | |
+int | |
+vtfileblockhash(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; | |
+ | |
+ 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: | |
+fprint(2, "vtfileblockhash: %r\n"); | |
+ 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; | |
+ 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 … | |
+ * 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; | |
+ 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; | |
+ 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(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(flushblock(c, nil, e.score, e.psize/VtScoreSize, e.… | |
+ e.type) < 0) | |
+ goto Err; | |
+ } | |
+ break; | |
+ | |
+ default: /* VtPointerTypeX */ | |
+ for(i=0; i<ppb; i++){ | |
+ if(flushblock(c, nil, b->data+VtScoreSize*i, ppb, epb,… | |
+ 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/VtEn… | |
+ e.type); | |
+ if(!ret){ | |
+ 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, ok; | |
+ int index[VtPointerDepth+1], index1[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; | |
+ | |
+ 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; | |
+ mkindices(&e, offset/e.dsize, index1); | |
+ ppb = e.psize / VtScoreSize; | |
+ epb = e.dsize / VtEntrySize; | |
+ | |
+ 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; | |
+ | |
+ base = e.type&~VtTypeDepthMask; | |
+ for(i=0; i<depth; i++){ | |
+ if(i == 0){ | |
+ /* bottom: data or dir block */ | |
+ ok = offset%e.dsize == 0; | |
+ }else{ | |
+ /* middle: pointer blocks */ | |
+ b = bi[i]; | |
+ /* | |
+ * flush everything up to the break | |
+ */ | |
+ for(j=0; j<index[i-1]; j++) | |
+ if(flushblock(r->c, nil, b->data+j*VtScoreSize… | |
+ goto Err; | |
+ /* | |
+ * if the rest of the block is already flushed, | |
+ * we can flush the whole block. | |
+ */ | |
+ ok = 1; | |
+ for(; j<ppb; j++) | |
+ if(vtglobaltolocal(b->data+j*VtScoreSize) != N… | |
+ ok = 0; | |
+ } | |
+ if(ok){ | |
+ 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) < … | |
+ 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/src/libventi/hangup.c b/src/libventi/hangup.c | |
t@@ -0,0 +1,22 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <venti.h> | |
+#include "queue.h" | |
+ | |
+void | |
+vthangup(VtConn *z) | |
+{ | |
+ qlock(&z->lk); | |
+ z->state = VtStateClosed; | |
+ 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/src/libventi/mem.c b/src/libventi/mem.c | |
t@@ -0,0 +1,87 @@ | |
+#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; | |
+ static int nchunk; | |
+ int align, pad; | |
+ void *p; | |
+ | |
+ if(n >= IdealAlignment) | |
+ align = IdealAlignment; | |
+ else if(n > 8) | |
+ align = 8; | |
+ else | |
+ align = 4; | |
+ | |
+ lock(&lk); | |
+ pad = (align - (ulong)buf) & (align-1); | |
+ if(n + pad > nbuf) { | |
+ buf = vtmallocz(ChunkSize); | |
+ nbuf = ChunkSize; | |
+ pad = (align - (ulong)buf) & (align-1); | |
+ nchunk++; | |
+ } | |
+ | |
+ assert(n + pad <= nbuf); | |
+ | |
+ p = buf + pad; | |
+ buf += pad + n; | |
+ nbuf -= pad + n; | |
+ unlock(&lk); | |
+ | |
+ return p; | |
+} | |
+ | |
diff --git a/src/libventi/mkfile b/src/libventi/mkfile | |
t@@ -0,0 +1,42 @@ | |
+PLAN9=../.. | |
+<$PLAN9/src/mkhdr | |
+ | |
+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\ | |
+ mem.$O\ | |
+ packet.$O\ | |
+ queue.$O\ | |
+ root.$O\ | |
+ rpc.$O\ | |
+ scorefmt.$O\ | |
+ send.$O\ | |
+ server.$O\ | |
+ srvhello.$O\ | |
+ strdup.$O\ | |
+ string.$O\ | |
+ version.$O\ | |
+ zero.$O\ | |
+ zeroscore.$O\ | |
+ | |
+HFILES=\ | |
+ $PLAN9/include/venti.h\ | |
+ | |
+<$PLAN9/src/mksyslib | |
+ | |
+send.$O: queue.h | |
+server.$O: queue.h | |
+queue.$O: queue.h | |
+ | |
+ | |
diff --git a/src/libventi/packet.c b/src/libventi/packet.c | |
t@@ -0,0 +1,941 @@ | |
+#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; | |
+}; | |
+ | |
+struct Packet | |
+{ | |
+ int size; | |
+ int asize; /* allocated memory - greater than size unless foreign fra… | |
+ | |
+ 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"; | |
+ | |
+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) | |
+ | |
+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; | |
+ | |
+if(1)fprint(2, "packetalloc %p from %08lux %08lux %08lux\n", p, *((uint*)&p+2)… | |
+ | |
+ return p; | |
+} | |
+ | |
+void | |
+packetfree(Packet *p) | |
+{ | |
+ Frag *f, *ff; | |
+ | |
+if(1)fprint(2, "packetfree %p from %08lux\n", p, getcallerpc(&p)); | |
+ | |
+ if(p == nil) | |
+ return; | |
+ | |
+ NOTFREE(p); | |
+ p->size = -1; | |
+ | |
+ for(f=p->first; f!=nil; f=ff) { | |
+ ff = f->next; | |
+ fragfree(f); | |
+ } | |
+ p->first = nil; | |
+ p->last = nil; | |
+ | |
+ lock(&freelist.lk); | |
+ p->next = freelist.packet; | |
+ freelist.packet = p; | |
+ unlock(&freelist.lk); | |
+} | |
+ | |
+Packet * | |
+packetdup(Packet *p, int offset, int n) | |
+{ | |
+ Frag *f, *ff; | |
+ Packet *pp; | |
+ | |
+ NOTFREE(p); | |
+ if(offset < 0 || n < 0 || offset+n > p->size) { | |
+ werrstr(EBadSize); | |
+ return nil; | |
+ } | |
+ | |
+ pp = packetalloc(); | |
+ if(n == 0) | |
+ return pp; | |
+ | |
+ pp->size = n; | |
+ | |
+ /* skip offset */ | |
+ for(f=p->first; offset >= FRAGSIZE(f); f=f->next) | |
+ offset -= FRAGSIZE(f); | |
+ | |
+ /* first frag */ | |
+ ff = fragdup(pp, f); | |
+ ff->rp += offset; | |
+ pp->first = ff; | |
+ n -= FRAGSIZE(ff); | |
+ pp->asize += FRAGASIZE(ff); | |
+ | |
+ /* the remaining */ | |
+ while(n > 0) { | |
+ f = f->next; | |
+ ff->next = fragdup(pp, f); | |
+ ff = ff->next; | |
+ n -= FRAGSIZE(ff); | |
+ pp->asize += FRAGASIZE(ff); | |
+ } | |
+ | |
+ /* fix up last frag: note n <= 0 */ | |
+ ff->wp += n; | |
+ ff->next = nil; | |
+ pp->last = ff; | |
+ | |
+ return pp; | |
+} | |
+ | |
+Packet * | |
+packetsplit(Packet *p, int n) | |
+{ | |
+ Packet *pp; | |
+ Frag *f, *ff; | |
+ | |
+ NOTFREE(p); | |
+ if(n < 0 || n > p->size) { | |
+ werrstr(EPacketSize); | |
+ return nil; | |
+ } | |
+ | |
+ pp = packetalloc(); | |
+ if(n == 0) | |
+ return pp; | |
+ | |
+ pp->size = n; | |
+ p->size -= n; | |
+ ff = nil; | |
+ for(f=p->first; n > 0 && n >= FRAGSIZE(f); f=f->next) { | |
+ n -= FRAGSIZE(f); | |
+ p->asize -= FRAGASIZE(f); | |
+ pp->asize += FRAGASIZE(f); | |
+ ff = f; | |
+ } | |
+ | |
+ /* split shared frag */ | |
+ if(n > 0) { | |
+ ff = f; | |
+ f = fragdup(pp, ff); | |
+ pp->asize += FRAGASIZE(ff); | |
+ ff->next = nil; | |
+ ff->wp = ff->rp + n; | |
+ f->rp += n; | |
+ } | |
+ | |
+ pp->first = p->first; | |
+ pp->last = ff; | |
+ p->first = f; | |
+ return pp; | |
+} | |
+ | |
+int | |
+packetconsume(Packet *p, uchar *buf, int n) | |
+{ | |
+ NOTFREE(p); | |
+ if(buf && packetcopy(p, buf, 0, n) < 0) | |
+ return 0; | |
+ return packettrim(p, n, p->size-n); | |
+} | |
+ | |
+int | |
+packettrim(Packet *p, int offset, int n) | |
+{ | |
+ Frag *f, *ff; | |
+ | |
+ NOTFREE(p); | |
+ if(offset < 0 || offset > p->size) { | |
+ 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; | |
+ 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); | |
+ } | |
+ 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; | |
+ return f->rp; | |
+ } | |
+ } | |
+ | |
+ /* add frag to front */ | |
+ f = fragalloc(p, n, PEnd, p->first); | |
+ p->asize += FRAGASIZE(f); | |
+ if(p->first == nil) | |
+ p->last = f; | |
+ p->first = f; | |
+ return f->rp; | |
+} | |
+ | |
+uchar * | |
+packettrailer(Packet *p, int n) | |
+{ | |
+ Mem *m; | |
+ Frag *f; | |
+ | |
+ NOTFREE(p); | |
+ if(n <= 0 || n > MaxFragSize) { | |
+ 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; | |
+ return f->wp - n; | |
+ } | |
+ } | |
+ | |
+ /* add frag to end */ | |
+ f = fragalloc(p, n, (p->first == nil)?PMiddle:PFront, nil); | |
+ p->asize += FRAGASIZE(f); | |
+ if(p->first == nil) | |
+ p->first = f; | |
+ else | |
+ p->last->next = f; | |
+ p->last = f; | |
+ return f->rp; | |
+} | |
+ | |
+int | |
+packetprefix(Packet *p, uchar *buf, int n) | |
+{ | |
+ Frag *f; | |
+ int nn; | |
+ Mem *m; | |
+ | |
+ NOTFREE(p); | |
+ if(n <= 0) | |
+ return 0; | |
+ | |
+ 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); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int | |
+packetappend(Packet *p, uchar *buf, int n) | |
+{ | |
+ Frag *f; | |
+ int nn; | |
+ Mem *m; | |
+ | |
+ NOTFREE(p); | |
+ if(n <= 0) | |
+ return 0; | |
+ | |
+ 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; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int | |
+packetconcat(Packet *p, Packet *pp) | |
+{ | |
+ NOTFREE(p); | |
+ NOTFREE(pp); | |
+ if(pp->size == 0) | |
+ return 0; | |
+ p->size += pp->size; | |
+ p->asize += pp->asize; | |
+ | |
+ if(p->first != nil) | |
+ p->last->next = pp->first; | |
+ else | |
+ p->first = pp->first; | |
+ p->last = pp->last; | |
+ pp->size = 0; | |
+ pp->asize = 0; | |
+ pp->first = nil; | |
+ pp->last = nil; | |
+ return 0; | |
+} | |
+ | |
+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)) | |
+ return f->rp + offset; | |
+ | |
+ for(b=buf; n>0; n -= nn) { | |
+ nn = FRAGSIZE(f) - offset; | |
+ if(nn > n) | |
+ nn = n; | |
+ memmove(b, f->rp+offset, nn); | |
+ offset = 0; | |
+ f = f->next; | |
+ b += nn; | |
+ } | |
+ | |
+ return buf; | |
+} | |
+ | |
+int | |
+packetcopy(Packet *p, uchar *buf, int offset, int n) | |
+{ | |
+ uchar *b; | |
+ | |
+ NOTFREE(p); | |
+ b = packetpeek(p, buf, offset, n); | |
+ if(b == nil) | |
+ return -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++; | |
+ } | |
+ | |
+ 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(0) { | |
+ Frag *f; | |
+ int size = 0; | |
+ | |
+ for(f=p->first; f; f=f->next) | |
+ size += FRAGSIZE(f); | |
+ if(size != p->size) | |
+ fprint(2, "packetsize %d %d\n", size, p->size); | |
+ assert(size == p->size); | |
+ } | |
+ return p->size; | |
+} | |
+ | |
+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); | |
+ } | |
+ } | |
+ return 0; /* for ken */ | |
+} | |
+ | |
+ | |
+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; | |
+ | |
+ 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(); | |
+ 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->size = n; | |
+ 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 = *f; | |
+ 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 0; | |
+ } | |
+ if(n <= SmallMemSize) { | |
+ lock(&freelist.lk); | |
+ m = freelist.smallmem; | |
+ if(m != nil) | |
+ freelist.smallmem = m->next; | |
+ else | |
+ freelist.nsmallmem++; | |
+ unlock(&freelist.lk); | |
+ nn = SmallMemSize; | |
+ } else { | |
+ lock(&freelist.lk); | |
+ m = freelist.bigmem; | |
+ if(m != nil) | |
+ freelist.bigmem = m->next; | |
+ else | |
+ freelist.nbigmem++; | |
+ unlock(&freelist.lk); | |
+ nn = BigMemSize; | |
+ } | |
+ | |
+ if(m == nil) { | |
+ m = 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); | |
+ | |
+ switch(m->ep - m->bp) { | |
+ default: | |
+ assert(0); | |
+ case SmallMemSize: | |
+ lock(&freelist.lk); | |
+ m->next = freelist.smallmem; | |
+ freelist.smallmem = m; | |
+ unlock(&freelist.lk); | |
+ break; | |
+ case BigMemSize: | |
+ lock(&freelist.lk); | |
+ m->next = freelist.bigmem; | |
+ freelist.bigmem = m; | |
+ unlock(&freelist.lk); | |
+ break; | |
+ } | |
+} | |
+ | |
+static int | |
+memhead(Mem *m, uchar *rp, int n) | |
+{ | |
+ lock(&m->lk); | |
+ if(m->rp != rp) { | |
+ unlock(&m->lk); | |
+ return -1; | |
+ } | |
+ m->rp -= n; | |
+ unlock(&m->lk); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+memtail(Mem *m, uchar *wp, int n) | |
+{ | |
+ lock(&m->lk); | |
+ if(m->wp != wp) { | |
+ unlock(&m->lk); | |
+ return -1; | |
+ } | |
+ m->wp += n; | |
+ unlock(&m->lk); | |
+ return 0; | |
+} | |
diff --git a/src/libventi/queue.c b/src/libventi/queue.c | |
t@@ -0,0 +1,103 @@ | |
+#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 hungup; | |
+ QLock lk; | |
+ Rendez r; | |
+ Qel *head; | |
+ Qel *tail; | |
+}; | |
+ | |
+Queue* | |
+_vtqalloc(void) | |
+{ | |
+ Queue *q; | |
+ | |
+ q = vtmallocz(sizeof(Queue)); | |
+ q->r.l = &q->lk; | |
+ return 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/src/libventi/queue.h b/src/libventi/queue.h | |
t@@ -0,0 +1,6 @@ | |
+typedef struct Queue Queue; | |
+Queue *_vtqalloc(void); | |
+int _vtqsend(Queue*, void*); | |
+void *_vtqrecv(Queue*); | |
+void _vtqhangup(Queue*); | |
+void *_vtnbqrecv(Queue*); | |
diff --git a/src/libventi/root.c b/src/libventi/root.c | |
t@@ -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 0; | |
+ } | |
+ p += 2; | |
+ memmove(r->name, p, sizeof(r->name)); | |
+ r->name[sizeof(r->name)-1] = 0; | |
+ p += sizeof(r->name); | |
+ memmove(r->type, p, sizeof(r->type)); | |
+ r->type[sizeof(r->type)-1] = 0; | |
+ p += sizeof(r->type); | |
+ memmove(r->score, p, VtScoreSize); | |
+ p += VtScoreSize; | |
+ r->blocksize = U16GET(p); | |
+ if(checksize(r->blocksize) < 0) | |
+ return -1; | |
+ p += 2; | |
+ memmove(r->prev, p, VtScoreSize); | |
+ p += VtScoreSize; | |
+ | |
+ assert(p-op == VtRootSize); | |
+ return 0; | |
+} | |
diff --git a/src/libventi/rpc.c b/src/libventi/rpc.c | |
t@@ -0,0 +1,155 @@ | |
+/* | |
+ * 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. | |
+ * | |
+ * 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*, Packet*); | |
+ | |
+Packet* | |
+vtrpc(VtConn *z, Packet *p) | |
+{ | |
+ int i; | |
+ uchar tag, buf[2], *top; | |
+ Rwait *r; | |
+ | |
+ /* must malloc because stack could be private */ | |
+ r = vtmallocz(sizeof(Rwait)); | |
+ | |
+ qlock(&z->lk); | |
+ r->r.l = &z->lk; | |
+ tag = gettag(z, r); | |
+ | |
+ /* 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); | |
+ return nil; | |
+ } | |
+ top[1] = tag; | |
+ qunlock(&z->lk); | |
+ if(vtsend(z, p) < 0) | |
+ 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){ | |
+ z->muxer = 0; | |
+ return nil; | |
+ } | |
+ qlock(&z->lk); | |
+ muxrpc(z, p); | |
+ } | |
+ z->muxer = 0; | |
+ /* if there is anyone else sleeping, wake them to mux */ | |
+ if(z->nsleep){ | |
+ for(i=0; i<256; i++) | |
+ if(z->wait[i] != nil && ((Rwait*)z->wait[i])->… | |
+ break; | |
+ if(i==256) | |
+ fprint(2, "libventi: nsleep botch\n"); | |
+ else | |
+ rwakeup(&((Rwait*)z->wait[i])->r); | |
+ } | |
+ } | |
+ | |
+ p = r->p; | |
+ puttag(z, r, tag); | |
+ vtfree(r); | |
+ qunlock(&z->lk); | |
+ return p; | |
+} | |
+ | |
+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/src/libventi/scorefmt.c b/src/libventi/scorefmt.c | |
t@@ -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/src/libventi/send.c b/src/libventi/send.c | |
t@@ -0,0 +1,212 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <venti.h> | |
+#include "queue.h" | |
+ | |
+static int | |
+_vtsend(VtConn *z, Packet *p) | |
+{ | |
+ IOchunk ioc; | |
+ int n; | |
+ 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); | |
+ | |
+ for(;;){ | |
+ n = packetfragments(p, &ioc, 1, 0); | |
+ if(n == 0) | |
+ break; | |
+ if(write(z->outfd, ioc.addr, ioc.len) < ioc.len){ | |
+ packetfree(p); | |
+ return 0; | |
+ } | |
+ packetconsume(p, nil, ioc.len); | |
+ } | |
+ packetfree(p); | |
+ return 1; | |
+} | |
+ | |
+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, MaxFragSize); | |
+ assert(b != nil); | |
+ n = read(z->infd, b, MaxFragSize); | |
+ if(n <= 0) | |
+ 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(readn(z->infd, b, n) != n) | |
+ goto Err; | |
+ size += n; | |
+ } | |
+ p = packetsplit(p, len); | |
+ return p; | |
+Err: | |
+ 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); | |
+ vtfree(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); | |
+ vtfree(q); | |
+ z->writeq = nil; | |
+ rwakeup(&z->rpcfork); | |
+ qunlock(&z->lk); | |
+ return; | |
+} | |
+ | |
+Packet* | |
+vtrecv(VtConn *z) | |
+{ | |
+ Packet *p; | |
+ | |
+ qlock(&z->lk); | |
+ if(z->state != VtStateConnected){ | |
+ werrstr("not connected"); | |
+ qunlock(&z->lk); | |
+ return nil; | |
+ } | |
+ if(z->readq){ | |
+ qunlock(&z->lk); | |
+ return _vtqrecv(z->readq); | |
+ } | |
+ | |
+ qlock(&z->inlk); | |
+ qunlock(&z->lk); | |
+ p = _vtrecv(z); | |
+ qunlock(&z->inlk); | |
+ if(!p) | |
+ vthangup(z); | |
+ return p; | |
+} | |
+ | |
+int | |
+vtsend(VtConn *z, Packet *p) | |
+{ | |
+ qlock(&z->lk); | |
+ if(z->state != VtStateConnected){ | |
+ packetfree(p); | |
+ werrstr("not connected"); | |
+ qunlock(&z->lk); | |
+ return -1; | |
+ } | |
+ if(z->writeq){ | |
+ qunlock(&z->lk); | |
+ if(_vtqsend(z->writeq, p) < 0){ | |
+ packetfree(p); | |
+ return -1; | |
+ } | |
+ 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/src/libventi/server.c b/src/libventi/server.c | |
t@@ -0,0 +1,172 @@ | |
+#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; | |
+ 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*); | |
+ | |
+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->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; | |
+ | |
+ r = nil; | |
+ c = nil; | |
+ sc = v; | |
+ 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; | |
+ } | |
+ | |
+ 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){ | |
+ packetfree(p); | |
+ fprint(2, "bad packet on %s: %r\n", sc->dir); | |
+ continue; | |
+ } | |
+ packetfree(p); | |
+ if(r->tx.type == VtTgoodbye) | |
+ break; | |
+ r->rx.tag = r->tx.tag; | |
+ r->sc = sc; | |
+ if(_vtqsend(sc->srv->q, r) < 0){ | |
+ fprint(2, "hungup queue\n"); | |
+ break; | |
+ } | |
+ r = nil; | |
+ } | |
+ | |
+ fprint(2, "eof on %s\n", sc->dir); | |
+ | |
+out: | |
+ if(r){ | |
+ vtfcallclear(&r->tx); | |
+ vtfree(r); | |
+ } | |
+ if(c) | |
+ vtfreeconn(c); | |
+ fprint(2, "freed %s\n", sc->dir); | |
+ vtfree(sc); | |
+ return; | |
+} | |
+ | |
+VtReq* | |
+vtgetreq(VtSrv *srv) | |
+{ | |
+ return _vtqrecv(srv->q); | |
+} | |
+ | |
+void | |
+vtrespond(VtReq *r) | |
+{ | |
+ Packet *p; | |
+ VtSconn *sc; | |
+ | |
+ sc = r->sc; | |
+ if(r->rx.tag != r->tx.tag) | |
+ abort(); | |
+ if(r->rx.type != r->tx.type+1 && r->rx.type != VtRerror) | |
+ abort(); | |
+ if((p = vtfcallpack(&r->rx)) == nil){ | |
+ fprint(2, "fcallpack on %s: %r\n", sc->dir); | |
+ packetfree(p); | |
+ vtfcallclear(&r->rx); | |
+ return; | |
+ } | |
+ vtsend(sc->c, p); | |
+ vtfcallclear(&r->tx); | |
+ vtfcallclear(&r->rx); | |
+ vtfree(r); | |
+} | |
+ | |
diff --git a/src/libventi/srvhello.c b/src/libventi/srvhello.c | |
t@@ -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 0; | |
+ | |
+ if(vtfcallunpack(&tx, p) < 0){ | |
+ packetfree(p); | |
+ return 0; | |
+ } | |
+ packetfree(p); | |
+ | |
+ if(tx.type != VtThello){ | |
+ vtfcallclear(&tx); | |
+ werrstr("bad packet type %d; want Thello %d", tx.type, VtThell… | |
+ return 0; | |
+ } | |
+ if(tx.tag != 0){ | |
+ vtfcallclear(&tx); | |
+ werrstr("bad tag in hello"); | |
+ return 0; | |
+ } | |
+ if(strcmp(tx.version, z->version) != 0){ | |
+ vtfcallclear(&tx); | |
+ werrstr("bad version in hello"); | |
+ return 0; | |
+ } | |
+ vtfree(z->uid); | |
+ z->uid = tx.uid; | |
+ tx.uid = nil; | |
+ vtfcallclear(&tx); | |
+ | |
+ memset(&rx, 0, sizeof rx); | |
+ rx.type = VtRhello; | |
+ rx.tag = tx.tag; | |
+ rx.sid = "anonymous"; | |
+ if((p = vtfcallpack(&rx)) == nil) | |
+ return 0; | |
+ if(vtsend(z, p) < 0) | |
+ return 0; | |
+ | |
+ return 1; | |
+} | |
diff --git a/src/libventi/strdup.c b/src/libventi/strdup.c | |
t@@ -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/src/libventi/string.c b/src/libventi/string.c | |
t@@ -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/src/libventi/version.c b/src/libventi/version.c | |
t@@ -0,0 +1,115 @@ | |
+#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(;;){ | |
+ pp = strpbrk(p, ":-"); | |
+ for(i=0; okvers[i]; i++) | |
+ if(strlen(okvers[i]) == pp-p && memcmp(okvers[i], p, p… | |
+ *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: | |
+ 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/src/libventi/zero.c b/src/libventi/zero.c | |
t@@ -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) !… | |
+ break; | |
+ p -= VtScoreSize; | |
+ } | |
+ return p - buf; | |
+ } | |
+} | |
diff --git a/src/libventi/zeroscore.c b/src/libventi/zeroscore.c | |
t@@ -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 | |
+}; | |
+ |