start thinking about vac -- doesn't build yet

This commit is contained in:
rsc 2003-11-23 17:55:34 +00:00
parent 7a4ee46d25
commit 7763a61a35
22 changed files with 7959 additions and 0 deletions

876
src/cmd/vac/cache.c Normal file
View file

@ -0,0 +1,876 @@
#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
typedef struct Label Label;
enum {
BadHeap = ~0,
};
/*
* the plan is to store data to the cache in c->size blocks
* with the block zero extended to fill it out. When writing to
* venti, the block will be zero truncated. The walker will also check
* that the block fits within psize or dsize as the case may be.
*/
struct Cache
{
VtLock *lk;
VtSession *z;
u32int now; /* ticks for usage timestamps */
int size; /* max. size of any block; allocated to each block */
Lump **heads; /* hash table for finding address */
int nheap; /* number of available victims */
Lump **heap; /* heap for locating victims */
long nblocks; /* number of blocks allocated */
Lump *blocks; /* array of block descriptors */
u8int *mem; /* memory for all block descriptors */
Lump *free; /* free list of lumps */
long hashSize;
};
/*
* the tag for a block is hash(index, parent tag)
*/
struct Label {
uchar gen[4];
uchar state;
uchar type; /* top bit indicates it is part of a directory */
uchar tag[4]; /* tag of file it is in */
};
static char ENoDir[] = "directory entry is not allocated";
static void fixHeap(int si, Lump *b);
static int upHeap(int i, Lump *b);
static int downHeap(int i, Lump *b);
static char *lumpState(int);
static void lumpSetState(Lump *u, int state);
Cache *
cacheAlloc(VtSession *z, int blockSize, long nblocks)
{
int i;
Cache *c;
Lump *b;
c = vtMemAllocZ(sizeof(Cache));
c->lk = vtLockAlloc();
c->z = z;
c->size = blockSize;
c->nblocks = nblocks;
c->hashSize = nblocks;
c->heads = vtMemAllocZ(c->hashSize*sizeof(Lump*));
c->heap = vtMemAllocZ(nblocks*sizeof(Lump*));
c->blocks = vtMemAllocZ(nblocks*sizeof(Lump));
c->mem = vtMemAllocZ(nblocks * blockSize);
for(i = 0; i < nblocks; i++){
b = &c->blocks[i];
b->lk = vtLockAlloc();
b->c = c;
b->data = &c->mem[i * blockSize];
b->addr = i+1;
b->state = LumpFree;
b->heap = BadHeap;
b->next = c->free;
c->free = b;
}
c->nheap = 0;
return c;
}
long
cacheGetSize(Cache *c)
{
return c->nblocks;
}
int
cacheGetBlockSize(Cache *c)
{
return c->size;
}
int
cacheSetSize(Cache *c, long nblocks)
{
USED(c);
USED(nblocks);
return 0;
}
void
cacheFree(Cache *c)
{
int i;
for(i = 0; i < c->nblocks; i++){
assert(c->blocks[i].ref == 0);
vtLockFree(c->blocks[i].lk);
}
vtMemFree(c->heads);
vtMemFree(c->blocks);
vtMemFree(c->mem);
vtMemFree(c);
}
static u32int
hash(Cache *c, uchar score[VtScoreSize], int type)
{
u32int h;
uchar *p = score + VtScoreSize-4;
h = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
h += type;
return h % c->hashSize;
}
static void
findLump(Cache *c, Lump *bb)
{
Lump *b, *last;
int h;
last = nil;
h = hash(c, bb->score, bb->type);
for(b = c->heads[h]; b != nil; b = b->next){
if(last != b->prev)
vtFatal("bad prev link");
if(b == bb)
return;
last = b;
}
vtFatal("block missing from hash table");
}
void
cacheCheck(Cache *c)
{
u32int size, now;
int i, k, refed, free;
static uchar zero[VtScoreSize];
Lump *p;
size = c->size;
now = c->now;
free = 0;
for(p=c->free; p; p=p->next)
free++;
for(i = 0; i < c->nheap; i++){
if(c->heap[i]->heap != i)
vtFatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
if(i > 0 && c->heap[(i - 1) >> 1]->used2 - now > c->heap[i]->used2 - now)
vtFatal("bad heap ordering");
k = (i << 1) + 1;
if(k < c->nheap && c->heap[i]->used2 - now > c->heap[k]->used2 - now)
vtFatal("bad heap ordering");
k++;
if(k < c->nheap && c->heap[i]->used2 - now > c->heap[k]->used2 - now)
vtFatal("bad heap ordering");
}
refed = 0;
for(i = 0; i < c->nblocks; i++){
if(c->blocks[i].data != &c->mem[i * size])
vtFatal("mis-blocked at %d", i);
if(c->blocks[i].ref && c->blocks[i].heap == BadHeap){
refed++;
}
if(memcmp(zero, c->blocks[i].score, VtScoreSize))
findLump(c, &c->blocks[i]);
}
if(refed > 0)fprint(2, "cacheCheck: nheap %d refed %d free %d\n", c->nheap, refed, free);
assert(c->nheap + refed + free == c->nblocks);
refed = 0;
for(i = 0; i < c->nblocks; i++){
if(c->blocks[i].ref) {
if(1)fprint(2, "%d %V %d %s\n", c->blocks[i].type, c->blocks[i].score, c->blocks[i].ref, lumpState(c->blocks[i].state));
refed++;
}
}
if(refed > 0)fprint(2, "cacheCheck: in used %d\n", refed);
}
/*
* delete an arbitrary block from the heap
*/
static void
delHeap(Lump *db)
{
fixHeap(db->heap, db->c->heap[--db->c->nheap]);
db->heap = BadHeap;
}
static void
fixHeap(int si, Lump *b)
{
int i;
i = upHeap(si, b);
if(i == si)
downHeap(i, b);
}
static int
upHeap(int i, Lump *b)
{
Lump *bb;
u32int now;
int p;
Cache *c;
c = b->c;
now = c->now;
for(; i != 0; i = p){
p = (i - 1) >> 1;
bb = c->heap[p];
if(b->used2 - now >= bb->used2 - now)
break;
c->heap[i] = bb;
bb->heap = i;
}
c->heap[i] = b;
b->heap = i;
return i;
}
static int
downHeap(int i, Lump *b)
{
Lump *bb;
u32int now;
int k;
Cache *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]->used2 - now > c->heap[k + 1]->used2 - now)
k++;
bb = c->heap[k];
if(b->used2 - now <= bb->used2 - now)
break;
c->heap[i] = bb;
bb->heap = i;
}
c->heap[i] = b;
b->heap = i;
return i;
}
/* called with c->lk held */
Lump *
cacheBumpLump(Cache *c)
{
Lump *b;
/*
* missed: locate the block with the oldest second to last use.
* remove it from the heap, and fix up the heap.
*/
if(c->free) {
b = c->free;
c->free = b->next;
} else {
for(;;){
if(c->nheap == 0) {
cacheCheck(c);
assert(0);
return nil;
}
b = c->heap[0];
delHeap(b);
if(b->ref == 0)
break;
}
/*
* unchain the block from hash chain
*/
if(b->prev == nil)
c->heads[hash(c, b->score, b->type)] = b->next;
else
b->prev->next = b->next;
if(b->next != nil)
b->next->prev = b->prev;
}
/*
* the new block has no last use, so assume it happens sometime in the middle
*/
b->used = (b->used2 + c->now) / 2;
b->asize = 0;
return b;
}
Lump *
cacheAllocLump(Cache *c, int type, int size, int dir)
{
Lump *b;
ulong h;
assert(size <= c->size);
again:
vtLock(c->lk);
b = cacheBumpLump(c);
if(b == nil) {
vtUnlock(c->lk);
fprint(2, "cache is full\n");
/* XXX should be better */
sleep(100);
goto again;
}
vtLock(b->lk);
assert(b->ref == 0);
b->ref++;
b->used2 = b->used;
b->used = c->now++;
/* convert addr into score */
memset(b->score, 0, VtScoreSize-4);
b->score[VtScoreSize-4] = b->addr>>24;
b->score[VtScoreSize-3] = b->addr>>16;
b->score[VtScoreSize-2] = b->addr>>8;
b->score[VtScoreSize-1] = b->addr;
b->dir = dir;
b->type = type;
b->gen = 0;
b->asize = size;
b->state = LumpFree;
h = hash(c, b->score, b->type);
/* chain onto correct hash */
b->next = c->heads[h];
c->heads[h] = b;
if(b->next != nil)
b->next->prev = b;
b->prev = nil;
vtUnlock(c->lk);
vtZeroExtend(type, b->data, 0, size);
lumpSetState(b, LumpActive);
return b;
}
int
scoreIsLocal(uchar score[VtScoreSize])
{
static uchar zero[VtScoreSize];
return memcmp(score, zero, VtScoreSize-4) == 0;
}
Lump *
cacheGetLump(Cache *c, uchar score[VtScoreSize], int type, int size)
{
Lump *b;
ulong h;
int n;
static uchar zero[VtScoreSize];
assert(size <= c->size);
h = hash(c, score, type);
again:
/*
* look for the block in the cache
*/
vtLock(c->lk);
for(b = c->heads[h]; b != nil; b = b->next){
if(memcmp(b->score, score, VtScoreSize) == 0 && b->type == type)
goto found;
}
/* should not be looking for a temp block */
if(scoreIsLocal(score)) {
if(memcmp(score, zero, VtScoreSize) == 0)
vtSetError("looking for zero score");
else
vtSetError("missing local block");
vtUnlock(c->lk);
return nil;
}
b = cacheBumpLump(c);
if(b == nil) {
vtUnlock(c->lk);
sleep(100);
goto again;
}
/* chain onto correct hash */
b->next = c->heads[h];
c->heads[h] = b;
if(b->next != nil)
b->next->prev = b;
b->prev = nil;
memmove(b->score, score, VtScoreSize);
b->type = type;
b->state = LumpFree;
found:
b->ref++;
b->used2 = b->used;
b->used = c->now++;
if(b->heap != BadHeap)
fixHeap(b->heap, b);
vtUnlock(c->lk);
vtLock(b->lk);
if(b->state != LumpFree)
return b;
n = vtRead(c->z, score, type, b->data, size);
if(n < 0) {
lumpDecRef(b, 1);
return nil;
}
if(!vtSha1Check(score, b->data, n)) {
vtSetError("vtSha1Check failed");
lumpDecRef(b, 1);
return nil;
}
vtZeroExtend(type, b->data, n, size);
b->asize = size;
lumpSetState(b, LumpVenti);
return b;
}
static char *
lumpState(int state)
{
switch(state) {
default:
return "Unknown!!";
case LumpFree:
return "Free";
case LumpActive:
return "Active";
case LumpSnap:
return "Snap";
case LumpZombie:
return "Zombie";
case LumpVenti:
return "Venti";
}
}
static void
lumpSetState(Lump *u, int state)
{
// if(u->state != LumpFree)
// fprint(2, "%V: %s -> %s\n", u->score, lumpState(u->state), lumpState(state));
u->state = state;
}
int
lumpGetScore(Lump *u, int offset, uchar score[VtScoreSize])
{
uchar *sp;
VtRoot root;
VtEntry dir;
vtLock(u->lk);
switch(u->type) {
default:
vtSetError("bad type");
goto Err;
case VtPointerType0:
case VtPointerType1:
case VtPointerType2:
case VtPointerType3:
case VtPointerType4:
case VtPointerType5:
case VtPointerType6:
if((offset+1)*VtScoreSize > u->asize)
sp = nil;
else
sp = u->data + offset*VtScoreSize;
break;
case VtRootType:
if(u->asize < VtRootSize) {
vtSetError("runt root block");
goto Err;
}
if(!vtRootUnpack(&root, u->data))
goto Err;
sp = root.score;
break;
case VtDirType:
if((offset+1)*VtEntrySize > u->asize) {
vtSetError(ENoDir);
goto Err;
}
if(!vtEntryUnpack(&dir, u->data, offset))
goto Err;
if(!dir.flags & VtEntryActive) {
vtSetError(ENoDir);
goto Err;
}
sp = dir.score;
break;
}
if(sp == nil)
memmove(score, vtZeroScore, VtScoreSize);
else
memmove(score, sp, VtScoreSize);
vtUnlock(u->lk);
return !scoreIsLocal(score);
Err:
vtUnlock(u->lk);
return 0;
}
Lump *
lumpWalk(Lump *u, int offset, int type, int size, int readOnly, int lock)
{
Lump *v, *vv;
Cache *c;
uchar score[VtScoreSize], *sp;
VtRoot root;
VtEntry dir;
int split, isdir;
c = u->c;
vtLock(u->lk);
Again:
v = nil;
vv = nil;
isdir = u->dir;
switch(u->type) {
default:
vtSetError("bad type");
goto Err;
case VtPointerType0:
case VtPointerType1:
case VtPointerType2:
case VtPointerType3:
case VtPointerType4:
case VtPointerType5:
case VtPointerType6:
if((offset+1)*VtScoreSize > u->asize)
sp = nil;
else
sp = u->data + offset*VtScoreSize;
break;
case VtRootType:
if(u->asize < VtRootSize) {
vtSetError("runt root block");
goto Err;
}
if(!vtRootUnpack(&root, u->data))
goto Err;
sp = root.score;
break;
case VtDirType:
if((offset+1)*VtEntrySize > u->asize) {
vtSetError(ENoDir);
goto Err;
}
if(!vtEntryUnpack(&dir, u->data, offset))
goto Err;
if(!(dir.flags & VtEntryActive)) {
vtSetError(ENoDir);
goto Err;
}
isdir = (dir.flags & VtEntryDir) != 0;
// sp = dir.score;
sp = u->data + offset*VtEntrySize + 20;
break;
}
if(sp == nil)
memmove(score, vtZeroScore, VtScoreSize);
else
memmove(score, sp, VtScoreSize);
vtUnlock(u->lk);
if(0)fprint(2, "lumpWalk: %V:%s %d:%d-> %V:%d\n", u->score, lumpState(u->state), u->type, offset, score, type);
v = cacheGetLump(c, score, type, size);
if(v == nil)
return nil;
split = 1;
if(readOnly)
split = 0;
switch(v->state) {
default:
assert(0);
case LumpFree:
fprint(2, "block is free %V!\n", v->score);
vtSetError("phase error");
goto Err2;
case LumpActive:
if(v->gen < u->gen) {
print("LumpActive gen\n");
lumpSetState(v, LumpSnap);
v->gen = u->gen;
} else
split = 0;
break;
case LumpSnap:
case LumpVenti:
break;
}
/* easy case */
if(!split) {
if(!lock)
vtUnlock(v->lk);
return v;
}
if(sp == nil) {
vtSetError("bad offset");
goto Err2;
}
vv = cacheAllocLump(c, v->type, size, isdir);
/* vv is locked */
vv->gen = u->gen;
memmove(vv->data, v->data, v->asize);
if(0)fprint(2, "split %V into %V\n", v->score, vv->score);
lumpDecRef(v, 1);
v = nil;
vtLock(u->lk);
if(u->state != LumpActive) {
vtSetError("bad parent state: can not happen");
goto Err;
}
/* check that nothing changed underfoot */
if(memcmp(sp, score, VtScoreSize) != 0) {
lumpDecRef(vv, 1);
fprint(2, "lumpWalk: parent changed under foot\n");
goto Again;
}
/* XXX - hold Active blocks up - will go eventually */
lumpIncRef(vv);
/* change the parent */
memmove(sp, vv->score, VtScoreSize);
vtUnlock(u->lk);
if(!lock)
vtUnlock(vv->lk);
return vv;
Err:
vtUnlock(u->lk);
lumpDecRef(v, 0);
lumpDecRef(vv, 1);
return nil;
Err2:
lumpDecRef(v, 1);
return nil;
}
void
lumpFreeEntry(Lump *u, int entry)
{
uchar score[VtScoreSize];
int type;
ulong gen;
VtEntry dir;
Cache *c;
c = u->c;
vtLock(u->lk);
if(u->state == LumpVenti)
goto Exit;
switch(u->type) {
default:
fprint(2, "freeing bad lump type: %d\n", u->type);
return;
case VtPointerType0:
if((entry+1)*VtScoreSize > u->asize)
goto Exit;
memmove(score, u->data + entry*VtScoreSize, VtScoreSize);
memmove(u->data + entry*VtScoreSize, vtZeroScore, VtScoreSize);
type = u->dir?VtDirType:VtDataType;
break;
case VtPointerType1:
case VtPointerType2:
case VtPointerType3:
case VtPointerType4:
case VtPointerType5:
case VtPointerType6:
if((entry+1)*VtScoreSize > u->asize)
goto Exit;
memmove(score, u->data + entry*VtScoreSize, VtScoreSize);
memmove(u->data + entry*VtScoreSize, vtZeroScore, VtScoreSize);
type = u->type-1;
break;
case VtDirType:
if((entry+1)*VtEntrySize > u->asize)
goto Exit;
if(!vtEntryUnpack(&dir, u->data, entry))
goto Exit;
if(!dir.flags & VtEntryActive)
goto Exit;
gen = dir.gen;
if(gen != ~0)
gen++;
if(dir.depth == 0)
type = (dir.flags&VtEntryDir)?VtDirType:VtDataType;
else
type = VtPointerType0 + dir.depth - 1;
memmove(score, dir.score, VtScoreSize);
memset(&dir, 0, sizeof(dir));
dir.gen = gen;
vtEntryPack(&dir, u->data, entry);
break;
case VtDataType:
type = VtErrType;
break;
}
vtUnlock(u->lk);
if(type == VtErrType || !scoreIsLocal(score))
return;
u = cacheGetLump(c, score, type, c->size);
if(u == nil)
return;
lumpDecRef(u, 1);
/* XXX remove extra reference */
lumpDecRef(u, 0);
return;
Exit:
vtUnlock(u->lk);
return;
}
void
lumpCleanup(Lump *u)
{
int i, n;
switch(u->type) {
default:
return;
case VtPointerType0:
case VtPointerType1:
case VtPointerType2:
case VtPointerType3:
case VtPointerType4:
case VtPointerType5:
case VtPointerType6:
n = u->asize/VtScoreSize;
break;
case VtDirType:
n = u->asize/VtEntrySize;
break;
}
for(i=0; i<n; i++)
lumpFreeEntry(u, i);
}
void
lumpDecRef(Lump *b, int unlock)
{
int i;
Cache *c;
if(b == nil)
return;
if(unlock)
vtUnlock(b->lk);
c = b->c;
vtLock(c->lk);
if(--b->ref > 0) {
vtUnlock(c->lk);
return;
}
assert(b->ref == 0);
switch(b->state) {
default:
fprint(2, "bad state: %s\n", lumpState(b->state));
assert(0);
case LumpActive:
/* hack - but will do for now */
b->ref++;
vtUnlock(c->lk);
lumpCleanup(b);
vtLock(c->lk);
b->ref--;
lumpSetState(b, LumpFree);
break;
case LumpZombie:
lumpSetState(b, LumpFree);
break;
case LumpFree:
case LumpVenti:
break;
}
/*
* reinsert in the free heap
*/
if(b->heap == BadHeap) {
i = upHeap(c->nheap++, b);
c->heap[i] = b;
b->heap = i;
}
vtUnlock(c->lk);
}
Lump *
lumpIncRef(Lump *b)
{
Cache *c;
c = b->c;
vtLock(c->lk);
assert(b->ref > 0);
b->ref++;
vtUnlock(c->lk);
return b;
}

156
src/cmd/vac/dat.h Normal file
View file

@ -0,0 +1,156 @@
typedef struct Source Source;
typedef struct VacFile VacFile;
typedef struct MetaBlock MetaBlock;
typedef struct MetaEntry MetaEntry;
typedef struct Lump Lump;
typedef struct Cache Cache;
typedef struct Super Super;
enum {
NilBlock = (~0UL),
MaxBlock = (1UL<<31),
};
struct VacFS {
int ref;
/* need a read write lock? */
uchar score[VtScoreSize];
VacFile *root;
VtSession *z;
int readOnly;
int bsize; /* maximum block size */
uvlong qid; /* next qid */
Cache *cache;
};
struct Source {
VtLock *lk;
Cache *cache; /* immutable */
int readOnly; /* immutable */
Lump *lump; /* lump containing venti dir entry */
ulong block; /* block number within parent: immutable */
int entry; /* which entry in the block: immutable */
/* most of a VtEntry, except the score */
ulong gen; /* generation: immutable */
int dir; /* dir flags: immutable */
int depth; /* number of levels of pointer blocks */
int psize; /* pointer block size: immutable */
int dsize; /* data block size: immutable */
uvlong size; /* size in bytes of file */
int epb; /* dir entries per block = dize/VtEntrySize: immutable */
};
struct MetaEntry {
uchar *p;
ushort size;
};
struct MetaBlock {
int maxsize; /* size of block */
int size; /* size used */
int free; /* free space within used size */
int maxindex; /* entries allocated for table */
int nindex; /* amount of table used */
int unbotch;
uchar *buf;
};
/*
* contains a one block buffer
* to avoid problems of the block changing underfoot
* and to enable an interface that supports unget.
*/
struct VacDirEnum {
VacFile *file;
ulong block; /* current block */
MetaBlock mb; /* parsed version of block */
int index; /* index in block */
};
/* Lump states */
enum {
LumpFree,
LumpVenti, /* on venti server: score > 2^32: just a cached copy */
LumpActive, /* active */
LumpActiveRO, /* active: read only block */
LumpActiveA, /* active: achrived */
LumpSnap, /* snapshot: */
LumpSnapRO, /* snapshot: read only */
LumpSnapA, /* snapshot: achived */
LumpZombie, /* block with no pointer to it: waiting to be freed */
LumpMax
};
/*
* Each lump has a state and generation
* The following invariants are maintained
* Each lump has no more than than one parent per generation
* For Active*, no child has a parent of a greater generation
* For Snap*, there is a snap parent of given generation and there are
* no parents of greater gen - implies no children of a greater gen
* For *RO, the lump is fixed - no change ca be made - all pointers
* are valid venti addresses
* For *A, the lump is on the venti server
* There are no pointers to Zombie lumps
*
* Transitions
* Archiver at generation g
* Mutator at generation h
*
* Want to modify a lump
* Venti: create new Active(h)
* Active(x): x == h: do nothing
* Acitve(x): x < h: change to Snap(h-1) + add Active(h)
* ActiveRO(x): change to SnapRO(h-1) + add Active(h)
* ActiveA(x): add Active(h)
* Snap*(x): should not occur
* Zombie(x): should not occur
* Want to archive
* Active(x): x != g: should never happen
* Active(x): x == g fix children and free them: move to ActoveRO(g);
* ActiveRO(x): x != g: should never happen
* ActiveRO(x): x == g: wait until it hits ActiveA or SnapA
* ActiveA(x): done
* Active(x): x < g: should never happen
* Snap(x): x >= g: fix children, freeing all SnapA(y) x == y;
* SnapRO(x): wait until it hits SnapA
*
*/
struct Lump {
int ref;
Cache *c;
VtLock *lk;
int state;
ulong gen;
uchar *data;
uchar score[VtScoreSize]; /* score of packet */
uchar vscore[VtScoreSize]; /* venti score - when archived */
u8int type; /* type of packet */
int dir; /* part of a directory - extension of type */
u16int asize; /* allocated size of block */
Lump *next; /* doubly linked hash chains */
Lump *prev;
u32int heap; /* index in heap table */
u32int used; /* last reference times */
u32int used2;
u32int addr; /* mutable block address */
};

20
src/cmd/vac/error.c Normal file
View file

@ -0,0 +1,20 @@
#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
char ENoDir[] = "directory entry is not allocated";
char EBadDir[] = "corrupted directory entry";
char EBadMeta[] = "corrupted meta data";
char ENotDir[] = "not a directory";
char ENotFile[] = "not a file";
char EIO[] = "i/o error";
char EBadOffset[] = "illegal offset";
char ETooBig[] = "file too big";
char EReadOnly[] = "read only";
char ERemoved[] = "file has been removed";
char ENilBlock[] = "illegal block address";
char ENotEmpty[] = "directory not empty";
char EExists[] = "file already exists";
char ERoot[] = "cannot remove root";

14
src/cmd/vac/error.h Normal file
View file

@ -0,0 +1,14 @@
extern char ENoDir[];
extern char EBadDir[];
extern char EBadMeta[];
extern char ENilBlock[];
extern char ENotDir[];
extern char ENotFile[];
extern char EIO[];
extern char EBadOffset[];
extern char ETooBig[];
extern char EReadOnly[];
extern char ERemoved[];
extern char ENotEmpty[];
extern char EExists[];
extern char ERoot[];

1214
src/cmd/vac/file.c Normal file

File diff suppressed because it is too large Load diff

46
src/cmd/vac/fns.h Normal file
View file

@ -0,0 +1,46 @@
Source *sourceAlloc(Cache*, Lump *u, ulong block, int elem, int readonly);
Source *sourceOpen(Source*, ulong entry, int readOnly);
Source *sourceCreate(Source*, int psize, int dsize, int isdir, ulong entry);
Lump *sourceGetLump(Source*, ulong block, int readOnly, int lock);
Lump *sourceWalk(Source *r, ulong block, int readOnly, int *);
int sourceSetDepth(Source *r, uvlong size);
int sourceSetSize(Source *r, uvlong size);
uvlong sourceGetSize(Source *r);
int sourceSetDirSize(Source *r, ulong size);
ulong sourceGetDirSize(Source *r);
void sourceRemove(Source*);
void sourceFree(Source*);
int sourceGetVtEntry(Source *r, VtEntry *dir);
ulong sourceGetNumBlocks(Source *r);
Lump *lumpWalk(Lump *u, int offset, int type, int size, int readOnly, int lock);
int lumpGetScore(Lump *u, int offset, uchar score[VtScoreSize]);
void lumpDecRef(Lump*, int unlock);
Lump *lumpIncRef(Lump*);
void lumpFreeEntry(Lump *u, int entry);
Cache *cacheAlloc(VtSession *z, int blockSize, long nblocks);
Lump *cacheAllocLump(Cache *c, int type, int size, int dir);
void cacheFree(Cache *c);
long cacheGetSize(Cache*);
int cacheSetSize(Cache*, long);
int cacheGetBlockSize(Cache *c);
Lump *cacheGetLump(Cache *c, uchar score[VtScoreSize], int type, int size);
void cacheCheck(Cache*);
int mbUnpack(MetaBlock *mb, uchar *p, int n);
void mbInsert(MetaBlock *mb, int i, MetaEntry*);
void mbDelete(MetaBlock *mb, int i, MetaEntry*);
void mbPack(MetaBlock *mb);
uchar *mbAlloc(MetaBlock *mb, int n);
int meUnpack(MetaEntry*, MetaBlock *mb, int i);
int meCmp(MetaEntry*, char *s);
int meCmpNew(MetaEntry*, char *s);
int vdSize(VacDir *dir);
int vdUnpack(VacDir *dir, MetaEntry*);
void vdPack(VacDir *dir, MetaEntry*);
VacFile *vfRoot(VacFS *fs, uchar *score);

188
src/cmd/vac/fs.c Normal file
View file

@ -0,0 +1,188 @@
#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
static char EBadVacFormat[] = "bad format for vac file";
static VacFS *
vfsAlloc(VtSession *z, int bsize, long ncache)
{
VacFS *fs;
fs = vtMemAllocZ(sizeof(VacFS));
fs->ref = 1;
fs->z = z;
fs->bsize = bsize;
fs->cache = cacheAlloc(z, bsize, ncache);
return fs;
}
static int
readScore(int fd, uchar score[VtScoreSize])
{
char buf[44];
int i, n, c;
n = readn(fd, buf, sizeof(buf));
if(n < sizeof(buf)) {
vtSetError("short read");
return 0;
}
if(strncmp(buf, "vac:", 4) != 0) {
vtSetError("not a vac file");
return 0;
}
memset(score, 0, VtScoreSize);
for(i=4; i<sizeof(buf); i++) {
if(buf[i] >= '0' && buf[i] <= '9')
c = buf[i] - '0';
else if(buf[i] >= 'a' && buf[i] <= 'f')
c = buf[i] - 'a' + 10;
else if(buf[i] >= 'A' && buf[i] <= 'F')
c = buf[i] - 'A' + 10;
else {
vtSetError("bad format for venti score");
return 0;
}
if((i & 1) == 0)
c <<= 4;
score[(i>>1)-2] |= c;
}
return 1;
}
VacFS *
vfsOpen(VtSession *z, char *file, int readOnly, long ncache)
{
VacFS *fs;
int n, fd;
VtRoot rt;
uchar score[VtScoreSize], buf[VtRootSize];
VacFile *root;
fd = open(file, OREAD);
if(fd < 0) {
vtOSError();
return nil;
}
if(!readScore(fd, score)) {
close(fd);
return nil;
}
close(fd);
n = vtRead(z, score, VtRootType, buf, VtRootSize);
if(n < 0)
return nil;
if(n != VtRootSize) {
vtSetError("vtRead on root too short");
return nil;
}
if(!vtSha1Check(score, buf, VtRootSize)) {
vtSetError("vtSha1Check failed on root block");
return nil;
}
if(!vtRootUnpack(&rt, buf))
return nil;
if(strcmp(rt.type, "vac") != 0) {
vtSetError("not a vac root");
return nil;
}
fs = vfsAlloc(z, rt.blockSize, ncache);
memmove(fs->score, score, VtScoreSize);
fs->readOnly = readOnly;
root = vfRoot(fs, rt.score);
if(root == nil)
goto Err;
fs->root = root;
return fs;
Err:
if(root)
vfDecRef(root);
vfsClose(fs);
return nil;
}
VacFS *
vacFsCreate(VtSession *z, int bsize, long ncache)
{
VacFS *fs;
fs = vfsAlloc(z, bsize, ncache);
return fs;
}
int
vfsIsReadOnly(VacFS *fs)
{
return fs->readOnly != 0;
}
VacFile *
vfsGetRoot(VacFS *fs)
{
return vfIncRef(fs->root);
}
int
vfsGetBlockSize(VacFS *fs)
{
return fs->bsize;
}
int
vfsGetScore(VacFS *fs, uchar score[VtScoreSize])
{
memmove(fs, score, VtScoreSize);
return 1;
}
long
vfsGetCacheSize(VacFS *fs)
{
return cacheGetSize(fs->cache);
}
int
vfsSetCacheSize(VacFS *fs, long size)
{
return cacheSetSize(fs->cache, size);
}
int
vfsSnapshot(VacFS *fs, char *src, char *dst)
{
USED(fs);
USED(src);
USED(dst);
return 1;
}
int
vfsSync(VacFS*)
{
return 1;
}
int
vfsClose(VacFS *fs)
{
if(fs->root)
vfDecRef(fs->root);
fs->root = nil;
cacheCheck(fs->cache);
cacheFree(fs->cache);
memset(fs, 0, sizeof(VacFS));
vtMemFree(fs);
return 1;
}

36
src/cmd/vac/mkfile Normal file
View file

@ -0,0 +1,36 @@
PLAN9=../../..
<$PLAN9/src/mkhdr
LIBFILES=\
cache\
error\
file\
fs\
source\
pack\
LIB=${LIBFILES:%=%.$O}
HFILES=\
$PLAN9/include/venti.h\
stdinc.h\
error.h\
vac.h\
dat.h\
fns.h\
TARG=vac vtdump
CFILES=${TARG:%=%.c} ${LIBFILES:%=%.c} srcload.c vactest.c
UPDATE=\
mkfile\
$CFILES\
$HFILES\
${TARG:%=/386/bin/%}
default:V: all
test:V: $O.srcload $O.wtest $O.rtest $O.vtdump $O.vtread
<$PLAN9/src/mkmany

609
src/cmd/vac/pack.c Normal file
View file

@ -0,0 +1,609 @@
#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
typedef struct MetaChunk MetaChunk;
struct MetaChunk {
ushort offset;
ushort size;
ushort index;
};
static int stringUnpack(char **s, uchar **p, int *n);
/*
* integer conversion routines
*/
#define U8GET(p) ((p)[0])
#define U16GET(p) (((p)[0]<<8)|(p)[1])
#define U32GET(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3])
#define U48GET(p) (((uvlong)U16GET(p)<<32)|(uvlong)U32GET((p)+2))
#define U64GET(p) (((uvlong)U32GET(p)<<32)|(uvlong)U32GET((p)+4))
#define U8PUT(p,v) (p)[0]=(v)
#define U16PUT(p,v) (p)[0]=(v)>>8;(p)[1]=(v)
#define U32PUT(p,v) (p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v)
#define U48PUT(p,v,t32) t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT((p)+2,t32)
#define U64PUT(p,v,t32) t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT((p)+4,t32)
static int
stringUnpack(char **s, uchar **p, int *n)
{
int nn;
if(*n < 2)
return 0;
nn = U16GET(*p);
*p += 2;
*n -= 2;
if(nn > *n)
return 0;
*s = vtMemAlloc(nn+1);
memmove(*s, *p, nn);
(*s)[nn] = 0;
*p += nn;
*n -= nn;
return 1;
}
static int
stringPack(char *s, uchar *p)
{
int n;
n = strlen(s);
U16PUT(p, n);
memmove(p+2, s, n);
return n+2;
}
int
mbUnpack(MetaBlock *mb, uchar *p, int n)
{
u32int magic;
mb->maxsize = n;
mb->buf = p;
if(n == 0) {
memset(mb, 0, sizeof(MetaBlock));
return 1;
}
magic = U32GET(p);
if(magic != MetaMagic && magic != MetaMagic+1) {
vtSetError("bad meta block magic");
return 0;
}
mb->size = U16GET(p+4);
mb->free = U16GET(p+6);
mb->maxindex = U16GET(p+8);
mb->nindex = U16GET(p+10);
mb->unbotch = (magic == MetaMagic+1);
if(mb->size > n) {
vtSetError("bad meta block size");
return 0;
}
p += MetaHeaderSize;
n -= MetaHeaderSize;
USED(p);
if(n < mb->maxindex*MetaIndexSize) {
vtSetError("truncated meta block 2");
return 0;
}
return 1;
}
void
mbPack(MetaBlock *mb)
{
uchar *p;
p = mb->buf;
U32PUT(p, MetaMagic);
U16PUT(p+4, mb->size);
U16PUT(p+6, mb->free);
U16PUT(p+8, mb->maxindex);
U16PUT(p+10, mb->nindex);
}
void
mbDelete(MetaBlock *mb, int i, MetaEntry *me)
{
uchar *p;
int n;
assert(i < mb->nindex);
if(me->p - mb->buf + me->size == mb->size)
mb->size -= me->size;
else
mb->free += me->size;
p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
n = (mb->nindex-i-1)*MetaIndexSize;
memmove(p, p+MetaIndexSize, n);
memset(p+n, 0, MetaIndexSize);
mb->nindex--;
}
void
mbInsert(MetaBlock *mb, int i, MetaEntry *me)
{
uchar *p;
int o, n;
assert(mb->nindex < mb->maxindex);
o = me->p - mb->buf;
n = me->size;
if(o+n > mb->size) {
mb->free -= mb->size - o;
mb->size = o + n;
} else
mb->free -= n;
p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
n = (mb->nindex-i)*MetaIndexSize;
memmove(p+MetaIndexSize, p, n);
U16PUT(p, me->p - mb->buf);
U16PUT(p+2, me->size);
mb->nindex++;
}
int
meUnpack(MetaEntry *me, MetaBlock *mb, int i)
{
uchar *p;
int eo, en;
if(i < 0 || i >= mb->nindex) {
vtSetError("bad meta entry index");
return 0;
}
p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
eo = U16GET(p);
en = U16GET(p+2);
if(0)print("eo = %d en = %d\n", eo, en);
if(eo < MetaHeaderSize + mb->maxindex*MetaIndexSize) {
vtSetError("corrupted entry in meta block");
return 0;
}
if(eo+en > mb->size) {
vtSetError("truncated meta block");
return 0;
}
p = mb->buf + eo;
/* make sure entry looks ok and includes an elem name */
if(en < 8 || U32GET(p) != DirMagic || en < 8 + U16GET(p+6)) {
vtSetError("corrupted meta block entry");
return 0;
}
me->p = p;
me->size = en;
return 1;
}
/* assumes a small amount of checking has been done in mbEntry */
int
meCmp(MetaEntry *me, char *s)
{
int n;
uchar *p;
p = me->p;
p += 6;
n = U16GET(p);
p += 2;
assert(n + 8 < me->size);
while(n > 0) {
if(*s == 0)
return -1;
if(*p < (uchar)*s)
return -1;
if(*p > (uchar)*s)
return 1;
p++;
s++;
n--;
}
return *s != 0;
}
int
meCmpNew(MetaEntry *me, char *s)
{
int n;
uchar *p;
p = me->p;
p += 6;
n = U16GET(p);
p += 2;
assert(n + 8 < me->size);
while(n > 0) {
if(*s == 0)
return 1;
if(*p < (uchar)*s)
return -1;
if(*p > (uchar)*s)
return 1;
p++;
s++;
n--;
}
return -(*s != 0);
}
static int
offsetCmp(void *s0, void *s1)
{
MetaChunk *mc0, *mc1;
mc0 = s0;
mc1 = s1;
if(mc0->offset < mc1->offset)
return -1;
if(mc0->offset > mc1->offset)
return 1;
return 0;
}
static MetaChunk *
metaChunks(MetaBlock *mb)
{
MetaChunk *mc;
int oo, o, n, i;
uchar *p;
mc = vtMemAlloc(mb->nindex*sizeof(MetaChunk));
p = mb->buf + MetaHeaderSize;
for(i = 0; i<mb->nindex; i++) {
mc[i].offset = U16GET(p);
mc[i].size = U16GET(p+2);
mc[i].index = i;
p += MetaIndexSize;
}
qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp);
/* check block looks ok */
oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
o = oo;
n = 0;
for(i=0; i<mb->nindex; i++) {
o = mc[i].offset;
n = mc[i].size;
if(o < oo)
goto Err;
oo += n;
}
if(o+n <= mb->size)
goto Err;
if(mb->size - oo != mb->free)
goto Err;
return mc;
Err:
vtMemFree(mc);
return nil;
}
static void
mbCompact(MetaBlock *mb, MetaChunk *mc)
{
int oo, o, n, i;
oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
for(i=0; i<mb->nindex; i++) {
o = mc[i].offset;
n = mc[i].size;
if(o != oo) {
memmove(mb->buf + oo, mb->buf + o, n);
U16PUT(mb->buf + MetaHeaderSize + mc[i].index*MetaIndexSize, oo);
}
oo += n;
}
mb->size = oo;
mb->free = 0;
}
uchar *
mbAlloc(MetaBlock *mb, int n)
{
int i, o;
MetaChunk *mc;
/* off the end */
if(mb->maxsize - mb->size >= n)
return mb->buf + mb->size;
/* check if possible */
if(mb->maxsize - mb->size + mb->free < n)
return nil;
mc = metaChunks(mb);
/* look for hole */
o = MetaHeaderSize + mb->maxindex*MetaIndexSize;
for(i=0; i<mb->nindex; i++) {
if(mc[i].offset - o >= n) {
vtMemFree(mc);
return mb->buf + o;
}
o = mc[i].offset + mc[i].size;
}
if(mb->maxsize - o >= n) {
vtMemFree(mc);
return mb->buf + o;
}
/* compact and return off the end */
mbCompact(mb, mc);
vtMemFree(mc);
assert(mb->maxsize - mb->size >= n);
return mb->buf + mb->size;
}
int
vdSize(VacDir *dir)
{
int n;
/* constant part */
n = 4 + /* magic */
2 + /* version */
4 + /* entry */
4 + /* guid */
4 + /* mentry */
4 + /* mgen */
8 + /* qid */
4 + /* mtime */
4 + /* mcount */
4 + /* ctime */
4 + /* atime */
4 + /* mode */
0;
/* strings */
n += 2 + strlen(dir->elem);
n += 2 + strlen(dir->uid);
n += 2 + strlen(dir->gid);
n += 2 + strlen(dir->mid);
/* optional sections */
if(dir->qidSpace) {
n += 3 + /* option header */
8 + /* qidOffset */
8; /* qid Max */
}
return n;
}
void
vdPack(VacDir *dir, MetaEntry *me)
{
uchar *p;
ulong t32;
p = me->p;
U32PUT(p, DirMagic);
U16PUT(p+4, 9); /* version */
p += 6;
p += stringPack(dir->elem, p);
U32PUT(p, dir->entry);
U32PUT(p+4, dir->gen);
U32PUT(p+8, dir->mentry);
U32PUT(p+12, dir->mgen);
U64PUT(p+16, dir->qid, t32);
p += 24;
p += stringPack(dir->uid, p);
p += stringPack(dir->gid, p);
p += stringPack(dir->mid, p);
U32PUT(p, dir->mtime);
U32PUT(p+4, dir->mcount);
U32PUT(p+8, dir->ctime);
U32PUT(p+12, dir->atime);
U32PUT(p+16, dir->mode);
p += 5*4;
if(dir->qidSpace) {
U8PUT(p, DirQidSpaceEntry);
U16PUT(p+1, 2*8);
p += 3;
U64PUT(p, dir->qidOffset, t32);
U64PUT(p+8, dir->qidMax, t32);
}
assert(p == me->p + me->size);
}
int
vdUnpack(VacDir *dir, MetaEntry *me)
{
int t, nn, n, version;
uchar *p;
p = me->p;
n = me->size;
memset(dir, 0, sizeof(VacDir));
if(0)print("vdUnpack\n");
/* magic */
if(n < 4 || U32GET(p) != DirMagic)
goto Err;
p += 4;
n -= 4;
if(0)print("vdUnpack: got magic\n");
/* version */
if(n < 2)
goto Err;
version = U16GET(p);
if(version < 7 || version > 9)
goto Err;
p += 2;
n -= 2;
if(0)print("vdUnpack: got version\n");
/* elem */
if(!stringUnpack(&dir->elem, &p, &n))
goto Err;
if(0)print("vdUnpack: got elem\n");
/* entry */
if(n < 4)
goto Err;
dir->entry = U32GET(p);
p += 4;
n -= 4;
if(0)print("vdUnpack: got entry\n");
if(version < 9) {
dir->gen = 0;
dir->mentry = dir->entry+1;
dir->mgen = 0;
} else {
if(n < 3*4)
goto Err;
dir->gen = U32GET(p);
dir->mentry = U32GET(p+4);
dir->mgen = U32GET(p+8);
p += 3*4;
n -= 3*4;
}
if(0)print("vdUnpack: got gen etc\n");
/* size is gotten from DirEntry */
/* qid */
if(n < 8)
goto Err;
dir->qid = U64GET(p);
p += 8;
n -= 8;
if(0)print("vdUnpack: got qid\n");
/* skip replacement */
if(version == 7) {
if(n < VtScoreSize)
goto Err;
p += VtScoreSize;
n -= VtScoreSize;
}
/* uid */
if(!stringUnpack(&dir->uid, &p, &n))
goto Err;
/* gid */
if(!stringUnpack(&dir->gid, &p, &n))
goto Err;
/* mid */
if(!stringUnpack(&dir->mid, &p, &n))
goto Err;
if(0)print("vdUnpack: got ids\n");
if(n < 5*4)
goto Err;
dir->mtime = U32GET(p);
dir->mcount = U32GET(p+4);
dir->ctime = U32GET(p+8);
dir->atime = U32GET(p+12);
dir->mode = U32GET(p+16);
p += 5*4;
n -= 5*4;
if(0)print("vdUnpack: got times\n");
/* optional meta data */
while(n > 0) {
if(n < 3)
goto Err;
t = p[0];
nn = U16GET(p+1);
p += 3;
n -= 3;
if(n < nn)
goto Err;
switch(t) {
case DirPlan9Entry:
/* not valid in version >= 9 */
if(version >= 9)
break;
if(dir->plan9 || nn != 12)
goto Err;
dir->plan9 = 1;
dir->p9path = U64GET(p);
dir->p9version = U32GET(p+8);
if(dir->mcount == 0)
dir->mcount = dir->p9version;
break;
case DirGenEntry:
/* not valid in version >= 9 */
if(version >= 9)
break;
break;
case DirQidSpaceEntry:
if(dir->qidSpace || nn != 16)
goto Err;
dir->qidSpace = 1;
dir->qidOffset = U64GET(p);
dir->qidMax = U64GET(p+8);
break;
}
p += nn;
n -= nn;
}
if(0)print("vdUnpack: got options\n");
if(p != me->p + me->size)
goto Err;
if(0)print("vdUnpack: correct size\n");
return 1;
Err:
if(0)print("vdUnpack: XXXXXXXXXXXX EbadMeta\n");
vtSetError(EBadMeta);
vdCleanup(dir);
return 0;
}

71
src/cmd/vac/rtest.c Normal file
View file

@ -0,0 +1,71 @@
#include "stdinc.h"
enum {
Nblock = 300000,
BlockSize = 8*1024,
};
uchar data[Nblock*VtScoreSize];
int rflag;
int nblock = 10000;
int perm[Nblock];
void
main(int argc, char *argv[])
{
VtSession *z;
int i, j, t;
int start;
uchar buf[BlockSize];
srand(time(0));
ARGBEGIN{
case 'r':
rflag++;
break;
case 'n':
nblock = atoi(ARGF());
break;
}ARGEND
for(i=0; i<nblock; i++)
perm[i] = i;
if(rflag) {
for(i=0; i<nblock; i++) {
j = nrand(nblock);
t = perm[j];
perm[j] = perm[i];
perm[i] = t;
}
}
if(readn(0, data, VtScoreSize*nblock) < VtScoreSize*nblock)
sysfatal("read failed: %r");
vtAttach();
z = vtDial("iolaire2");
if(z == nil)
sysfatal("cound not connect to venti");
if(!vtConnect(z, 0))
vtFatal("vtConnect: %s", vtGetError());
print("starting\n");
start = times(0);
if(rflag && nblock > 10000)
nblock = 10000;
for(i=0; i<nblock; i++) {
if(vtRead(z, data+perm[i]*VtScoreSize, VtDataType, buf, BlockSize) < 0)
vtFatal("vtRead failed: %d: %s", i, vtGetError());
}
print("time = %f\n", (times(0) - start)*0.001);
vtClose(z);
vtDetach();
}

390
src/cmd/vac/source.c Normal file
View file

@ -0,0 +1,390 @@
#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
static int sizeToDepth(uvlong s, int psize, int dsize);
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;
}
/* assumes u is lock? */
Source *
sourceAlloc(Cache *c, Lump *u, ulong block, int entry, int readOnly)
{
Source *r;
VtEntry d;
if(u->asize < (entry+1)*VtEntrySize) {
vtSetError(ENoDir);
return nil;
}
if(!vtEntryUnpack(&d, u->data, entry))
return nil;
if(!(d.flags & VtEntryActive)) {
fprint(2, "bad flags %#ux %V\n", d.flags, d.score);
vtSetError(ENoDir);
return nil;
}
/* HACK for backwards compatiblity - should go away at some point */
if(d.depth == 0) {
if(d.size > d.dsize) fprint(2, "depth == 0! size = %ulld\n", d.size);
d.depth = sizeToDepth(d.size, d.psize, d.dsize);
}
if(d.depth < sizeToDepth(d.size, d.psize, d.dsize)) {
vtSetError(EBadDir);
return nil;
}
r = vtMemAllocZ(sizeof(Source));
r->lk = vtLockAlloc();
r->cache = c;
r->readOnly = readOnly;
r->lump = lumpIncRef(u);
r->block = block;
r->entry = entry;
r->gen = d.gen;
r->dir = (d.flags & VtEntryDir) != 0;
r->depth = d.depth;
r->psize = d.psize;
r->dsize = d.dsize;
r->size = d.size;
r->epb = r->dsize/VtEntrySize;
return r;
}
Source *
sourceOpen(Source *r, ulong entry, int readOnly)
{
ulong bn;
Lump *u;
if(0)fprint(2, "sourceOpen: %V:%d: %lud\n", r->lump->score, r->entry, entry);
if(r->readOnly && !readOnly) {
vtSetError(EReadOnly);
return nil;
}
bn = entry/r->epb;
u = sourceGetLump(r, bn, readOnly, 1);
if(u == nil)
return nil;
r = sourceAlloc(r->cache, u, bn, entry%r->epb, readOnly);
lumpDecRef(u, 1);
return r;
}
Source *
sourceCreate(Source *r, int psize, int dsize, int isdir, ulong entry)
{
Source *rr;
int i;
Lump *u;
ulong bn;
VtEntry dir;
if(r->readOnly) {
vtSetError(EReadOnly);
return nil;
}
if(entry == 0) {
/*
* look at a random block to see if we can find an empty entry
*/
entry = sourceGetDirSize(r);
entry = r->epb*lnrand(entry/r->epb+1);
}
/*
* need to loop since multiple threads could be trying to allocate
*/
for(;;) {
bn = entry/r->epb;
sourceSetDepth(r, (uvlong)(bn+1)*r->dsize);
u = sourceGetLump(r, bn, 0, 1);
if(u == nil)
return nil;
for(i=entry%r->epb; i<r->epb; i++) {
vtEntryUnpack(&dir, u->data, i);
if((dir.flags&VtEntryActive) == 0 && dir.gen != ~0)
goto Found;
}
lumpDecRef(u, 1);
entry = sourceGetDirSize(r);
}
Found:
/* found an entry */
dir.psize = psize;
dir.dsize = dsize;
dir.flags = VtEntryActive;
if(isdir)
dir.flags |= VtEntryDir;
dir.depth = 0;
dir.size = 0;
memmove(dir.score, vtZeroScore, VtScoreSize);
vtEntryPack(&dir, u->data, i);
sourceSetDirSize(r, bn*r->epb + i + 1);
rr = sourceAlloc(r->cache, u, bn, i, 0);
lumpDecRef(u, 1);
return rr;
}
void
sourceRemove(Source *r)
{
lumpFreeEntry(r->lump, r->entry);
sourceFree(r);
}
int
sourceSetDepth(Source *r, uvlong size)
{
Lump *u, *v;
VtEntry dir;
int depth;
if(r->readOnly){
vtSetError(EReadOnly);
return 0;
}
depth = sizeToDepth(size, r->psize, r->dsize);
assert(depth >= 0);
if(depth > VtPointerDepth) {
vtSetError(ETooBig);
return 0;
}
vtLock(r->lk);
if(r->depth >= depth) {
vtUnlock(r->lk);
return 1;
}
u = r->lump;
vtLock(u->lk);
if(!vtEntryUnpack(&dir, u->data, r->entry)) {
vtUnlock(u->lk);
vtUnlock(r->lk);
return 0;
}
while(dir.depth < depth) {
v = cacheAllocLump(r->cache, VtPointerType0+r->depth, r->psize, r->dir);
if(v == nil)
break;
memmove(v->data, dir.score, VtScoreSize);
memmove(dir.score, v->score, VtScoreSize);
dir.depth++;
vtUnlock(v->lk);
}
vtEntryPack(&dir, u->data, r->entry);
vtUnlock(u->lk);
r->depth = dir.depth;
vtUnlock(r->lk);
return dir.depth == depth;
}
int
sourceGetVtEntry(Source *r, VtEntry *dir)
{
Lump *u;
u = r->lump;
vtLock(u->lk);
if(!vtEntryUnpack(dir, u->data, r->entry)) {
vtUnlock(u->lk);
return 0;
}
vtUnlock(u->lk);
return 1;
}
uvlong
sourceGetSize(Source *r)
{
uvlong size;
vtLock(r->lk);
size = r->size;
vtUnlock(r->lk);
return size;
}
int
sourceSetSize(Source *r, uvlong size)
{
Lump *u;
VtEntry dir;
int depth;
if(r->readOnly) {
vtSetError(EReadOnly);
return 0;
}
if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize) {
vtSetError(ETooBig);
return 0;
}
vtLock(r->lk);
depth = sizeToDepth(size, r->psize, r->dsize);
if(size < r->size) {
vtUnlock(r->lk);
return 1;
}
if(depth > r->depth) {
vtSetError(EBadDir);
vtUnlock(r->lk);
return 0;
}
u = r->lump;
vtLock(u->lk);
vtEntryUnpack(&dir, u->data, r->entry);
dir.size = size;
vtEntryPack(&dir, u->data, r->entry);
vtUnlock(u->lk);
r->size = size;
vtUnlock(r->lk);
return 1;
}
int
sourceSetDirSize(Source *r, ulong ds)
{
uvlong size;
size = (uvlong)r->dsize*(ds/r->epb);
size += VtEntrySize*(ds%r->epb);
return sourceSetSize(r, size);
}
ulong
sourceGetDirSize(Source *r)
{
ulong ds;
uvlong size;
size = sourceGetSize(r);
ds = r->epb*(size/r->dsize);
ds += (size%r->dsize)/VtEntrySize;
return ds;
}
ulong
sourceGetNumBlocks(Source *r)
{
return (sourceGetSize(r)+r->dsize-1)/r->dsize;
}
Lump *
sourceWalk(Source *r, ulong block, int readOnly, int *off)
{
int depth;
int i, np;
Lump *u, *v;
int elem[VtPointerDepth+1];
ulong b;
if(r->readOnly && !readOnly) {
vtSetError(EReadOnly);
return nil;
}
vtLock(r->lk);
np = r->psize/VtScoreSize;
b = block;
for(i=0; i<r->depth; i++) {
elem[i] = b % np;
b /= np;
}
if(b != 0) {
vtUnlock(r->lk);
vtSetError(EBadOffset);
return nil;
}
elem[i] = r->entry;
u = lumpIncRef(r->lump);
depth = r->depth;
*off = elem[0];
vtUnlock(r->lk);
for(i=depth; i>0; i--) {
v = lumpWalk(u, elem[i], VtPointerType0+i-1, r->psize, readOnly, 0);
lumpDecRef(u, 0);
if(v == nil)
return nil;
u = v;
}
return u;
}
Lump *
sourceGetLump(Source *r, ulong block, int readOnly, int lock)
{
int type, off;
Lump *u, *v;
if(r->readOnly && !readOnly) {
vtSetError(EReadOnly);
return nil;
}
if(block == NilBlock) {
vtSetError(ENilBlock);
return nil;
}
if(0)fprint(2, "sourceGetLump: %V:%d %lud\n", r->lump->score, r->entry, block);
u = sourceWalk(r, block, readOnly, &off);
if(u == nil)
return nil;
if(r->dir)
type = VtDirType;
else
type = VtDataType;
v = lumpWalk(u, off, type, r->dsize, readOnly, lock);
lumpDecRef(u, 0);
return v;
}
void
sourceFree(Source *k)
{
if(k == nil)
return;
lumpDecRef(k->lump, 0);
vtLockFree(k->lk);
memset(k, ~0, sizeof(*k));
vtMemFree(k);
}

302
src/cmd/vac/srcload.c Normal file
View file

@ -0,0 +1,302 @@
#include "stdinc.h"
#include <bio.h>
#include "vac.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
int num = 1000;
int length = 20*1024;
int block= 1024;
int bush = 4;
int iter = 10000;
Biobuf *bout;
int maxdepth;
Source *mkroot(Cache*);
void new(Source*, int trace, int);
int delete(Source*);
void dump(Source*, int indent, ulong nentry);
void dumpone(Source *s);
int count(Source *s, int);
void stats(Source *s);
void
main(int argc, char *argv[])
{
int i;
Cache *c;
char *host = nil;
VtSession *z;
int csize = 10000;
Source *r;
ulong t;
t = time(0);
fprint(1, "time = %lud\n", t);
srand(t);
ARGBEGIN{
case 'i':
iter = atoi(ARGF());
break;
case 'n':
num = atoi(ARGF());
break;
case 'l':
length = atoi(ARGF());
break;
case 'b':
block = atoi(ARGF());
break;
case 'h':
host = ARGF();
break;
case 'u':
bush = atoi(ARGF());
break;
case 'c':
csize = atoi(ARGF());
break;
}ARGEND;
vtAttach();
bout = vtMemAllocZ(sizeof(Biobuf));
Binit(bout, 1, OWRITE);
fmtinstall('V', vtScoreFmt);
fmtinstall('R', vtErrFmt);
z = vtDial(host);
if(z == nil)
vtFatal("could not connect to server: %s", vtGetError());
if(!vtConnect(z, 0))
sysfatal("vtConnect: %r");
c = cacheAlloc(z, block, csize);
r = mkroot(c);
for(i=0; i<num; i++)
new(r, 0, 0);
for(i=0; i<iter; i++) {
if(i % 10000 == 0)
stats(r);
new(r, 0, 0);
delete(r);
}
fprint(2, "count = %d top = %lud\n", count(r, 0), sourceGetDirSize(r));
// cacheCheck(c);
fprint(2, "deleting\n");
for(i=0; i<num; i++)
delete(r);
// dump(r, 0, 0);
lumpDecRef(r->lump, 0);
sourceRemove(r);
cacheCheck(c);
vtClose(z);
vtDetach();
exits(0);
}
Source *
mkroot(Cache *c)
{
Lump *u;
VtEntry *dir;
Source *r;
u = cacheAllocLump(c, VtDirType, cacheGetBlockSize(c), 1);
dir = (VtEntry*)u->data;
vtPutUint16(dir->psize, cacheGetBlockSize(c));
vtPutUint16(dir->dsize, cacheGetBlockSize(c));
dir->flag = VtEntryActive|VtEntryDir;
memmove(dir->score, vtZeroScore, VtScoreSize);
r = sourceAlloc(c, u, 0, 0);
vtUnlock(u->lk);
if(r == nil)
sysfatal("could not create root source: %R");
return r;
}
void
new(Source *s, int trace, int depth)
{
int i, n;
Source *ss;
if(depth > maxdepth)
maxdepth = depth;
n = sourceGetDirSize(s);
for(i=0; i<n; i++) {
ss = sourceOpen(s, nrand(n), 0);
if(ss == nil)
continue;
if(ss->dir && frand() < 1./bush) {
if(trace) {
int j;
for(j=0; j<trace; j++)
Bprint(bout, " ");
Bprint(bout, "decend %d\n", i);
}
new(ss, trace?trace+1:0, depth+1);
sourceFree(ss);
return;
}
sourceFree(ss);
}
ss = sourceCreate(s, s->psize, s->dsize, 1+frand()>.5, 0);
if(ss == nil)
fprint(2, "could not create directory: %R\n");
if(trace) {
int j;
for(j=1; j<trace; j++)
Bprint(bout, " ");
Bprint(bout, "create %d %V\n", ss->entry, ss->lump->score);
}
sourceFree(ss);
}
int
delete(Source *s)
{
int i, n;
Source *ss;
assert(s->dir);
n = sourceGetDirSize(s);
/* check if empty */
for(i=0; i<n; i++) {
ss = sourceOpen(s, i, 1);
if(ss != nil) {
sourceFree(ss);
break;
}
}
if(i == n)
return 0;
for(;;) {
ss = sourceOpen(s, nrand(n), 0);
if(ss == nil)
continue;
if(ss->dir && delete(ss)) {
sourceFree(ss);
return 1;
}
if(1)
break;
sourceFree(ss);
}
sourceRemove(ss);
return 1;
}
void
dumpone(Source *s)
{
ulong i, n;
Source *ss;
Bprint(bout, "gen %4lud depth %d %V", s->gen, s->depth, s->lump->score);
if(!s->dir) {
Bprint(bout, " data size: %llud\n", s->size);
return;
}
n = sourceGetDirSize(s);
Bprint(bout, " dir size: %lud\n", n);
for(i=0; i<n; i++) {
ss = sourceOpen(s, i, 1);
if(ss == nil) {
fprint(2, "%lud: %R\n", i);
continue;
}
Bprint(bout, "\t%lud %d %llud %V\n", i, ss->dir, ss->size, ss->lump->score);
sourceFree(ss);
}
return;
}
void
dump(Source *s, int ident, ulong entry)
{
ulong i, n;
Source *ss;
for(i=0; i<ident; i++)
Bprint(bout, " ");
Bprint(bout, "%4lud: gen %4lud depth %d", entry, s->gen, s->depth);
if(!s->dir) {
Bprint(bout, " data size: %llud\n", s->size);
return;
}
n = sourceGetDirSize(s);
Bprint(bout, " dir size: %lud\n", n);
for(i=0; i<n; i++) {
ss = sourceOpen(s, i, 1);
if(ss == nil)
continue;
dump(ss, ident+1, i);
sourceFree(ss);
}
return;
}
int
count(Source *s, int rec)
{
ulong i, n;
int c;
Source *ss;
if(!s->dir)
return 0;
n = sourceGetDirSize(s);
c = 0;
for(i=0; i<n; i++) {
ss = sourceOpen(s, i, 1);
if(ss == nil)
continue;
if(rec)
c += count(ss, rec);
c++;
sourceFree(ss);
}
return c;
}
void
stats(Source *s)
{
int n, i, c, cc, max;
Source *ss;
cc = 0;
max = 0;
n = sourceGetDirSize(s);
for(i=0; i<n; i++) {
ss = sourceOpen(s, i, 1);
if(ss == nil)
continue;
cc++;
c = count(ss, 1);
if(c > max)
max = c;
sourceFree(ss);
}
fprint(2, "count = %d top = %d depth=%d maxcount %d\n", cc, n, maxdepth, max);
}

8
src/cmd/vac/stdinc.h Normal file
View file

@ -0,0 +1,8 @@
#include <u.h>
#include <libc.h>
#include "venti.h"
typedef uvlong u64int;
typedef uchar u8int;
typedef ushort u16int;

71
src/cmd/vac/util.c Normal file
View file

@ -0,0 +1,71 @@
#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
int
vtGetUint16(uchar *p)
{
return (p[0]<<8)|p[1];
}
ulong
vtGetUint32(uchar *p)
{
return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
}
uvlong
vtGetUint48(uchar *p)
{
return ((uvlong)p[0]<<40)|((uvlong)p[1]<<32)|
(p[2]<<24)|(p[3]<<16)|(p[4]<<8)|p[5];
}
uvlong
vtGetUint64(uchar *p)
{
return ((uvlong)p[0]<<56)|((uvlong)p[1]<<48)|((uvlong)p[2]<<40)|
((uvlong)p[3]<<32)|(p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
}
void
vtPutUint16(uchar *p, int x)
{
p[0] = x>>8;
p[1] = x;
}
void
vtPutUint32(uchar *p, ulong x)
{
p[0] = x>>24;
p[1] = x>>16;
p[2] = x>>8;
p[3] = x;
}
void
vtPutUint48(uchar *p, uvlong x)
{
p[0] = x>>40;
p[1] = x>>32;
p[2] = x>>24;
p[3] = x>>16;
p[4] = x>>8;
p[5] = x;
}
void
vtPutUint64(uchar *p, uvlong x)
{
p[0] = x>>56;
p[1] = x>>48;
p[2] = x>>40;
p[3] = x>>32;
p[4] = x>>24;
p[5] = x>>16;
p[6] = x>>8;
p[7] = x;
}

1213
src/cmd/vac/vac-orig.c Normal file

File diff suppressed because it is too large Load diff

1024
src/cmd/vac/vac.c Normal file

File diff suppressed because it is too large Load diff

126
src/cmd/vac/vac.h Normal file
View file

@ -0,0 +1,126 @@
typedef struct VacFS VacFS;
typedef struct VacDir VacDir;
typedef struct VacFile VacFile;
typedef struct VacDirEnum VacDirEnum;
/*
* Mode bits
*/
enum {
ModeOtherExec = (1<<0),
ModeOtherWrite = (1<<1),
ModeOtherRead = (1<<2),
ModeGroupExec = (1<<3),
ModeGroupWrite = (1<<4),
ModeGroupRead = (1<<5),
ModeOwnerExec = (1<<6),
ModeOwnerWrite = (1<<7),
ModeOwnerRead = (1<<8),
ModeSticky = (1<<9),
ModeSetUid = (1<<10),
ModeSetGid = (1<<11),
ModeAppend = (1<<12), /* append only file */
ModeExclusive = (1<<13), /* lock file - plan 9 */
ModeLink = (1<<14), /* sym link */
ModeDir = (1<<15), /* duplicate of DirEntry */
ModeHidden = (1<<16), /* MS-DOS */
ModeSystem = (1<<17), /* MS-DOS */
ModeArchive = (1<<18), /* MS-DOS */
ModeTemporary = (1<<19), /* MS-DOS */
ModeSnapshot = (1<<20), /* read only snapshot */
};
enum {
MetaMagic = 0x5656fc79,
MetaHeaderSize = 12,
MetaIndexSize = 4,
IndexEntrySize = 8,
DirMagic = 0x1c4d9072,
};
enum {
DirPlan9Entry = 1, /* not valid in version >= 9 */
DirNTEntry, /* not valid in version >= 9 */
DirQidSpaceEntry,
DirGenEntry, /* not valid in version >= 9 */
};
struct VacDir {
char *elem; /* path element */
ulong entry; /* entry in directory for data */
ulong gen; /* generation of data entry */
ulong mentry; /* entry in directory for meta */
ulong mgen; /* generation of meta entry */
uvlong size; /* size of file */
uvlong qid; /* unique file id */
char *uid; /* owner id */
char *gid; /* group id */
char *mid; /* last modified by */
ulong mtime; /* last modified time */
ulong mcount; /* number of modifications: can wrap! */
ulong ctime; /* directory entry last changed */
ulong atime; /* last time accessed */
ulong mode; /* various mode bits */
/* plan 9 */
int plan9;
uvlong p9path;
ulong p9version;
/* sub space of qid */
int qidSpace;
uvlong qidOffset; /* qid offset */
uvlong qidMax; /* qid maximum */
};
VacFS *vfsOpen(VtSession *z, char *file, int readOnly, long ncache);
VacFS *vfsCreate(VtSession *z, int bsize, long ncache);
int vfsGetBlockSize(VacFS*);
int vfsIsReadOnly(VacFS*);
VacFile *vfsGetRoot(VacFS*);
long vfsGetCacheSize(VacFS*);
int vfsSetCacheSize(VacFS*, long);
int vfsSnapshot(VacFS*, char *src, char *dst);
int vfsSync(VacFS*);
int vfsClose(VacFS*);
int vfsGetScore(VacFS*, uchar score[VtScoreSize]);
/*
* other ideas
*
* VacFS *vfsSnapshot(VacFS*, char *src);
* int vfsGraft(VacFS*, char *name, VacFS*);
*/
VacFile *vfOpen(VacFS*, char *path);
VacFile *vfCreate(VacFile*, char *elem, ulong perm, char *user);
VacFile *vfWalk(VacFile*, char *elem);
int vfRemove(VacFile*, char*);
int vfRead(VacFile*, void *, int n, vlong offset);
int vfWrite(VacFile*, void *, int n, vlong offset, char *user);
int vfReadPacket(VacFile*, Packet**, vlong offset);
int vfWritePacket(VacFile*, Packet*, vlong offset, char *user);
uvlong vfGetId(VacFile*);
ulong vfGetMcount(VacFile*);
int vfIsDir(VacFile*);
int vfGetBlockScore(VacFile*, ulong bn, uchar score[VtScoreSize]);
int vfGetSize(VacFile*, uvlong *size);
int vfGetDir(VacFile*, VacDir*);
int vfSetDir(VacFile*, VacDir*);
int vfGetVtEntry(VacFile*, VtEntry*);
VacFile *vfGetParent(VacFile*);
int vfSync(VacFile*);
VacFile *vfIncRef(VacFile*);
void vfDecRef(VacFile*);
VacDirEnum *vfDirEnum(VacFile*);
int vfIsRoot(VacFile *vf);
void vdCleanup(VacDir *dir);
void vdCopy(VacDir *dst, VacDir *src);
VacDirEnum *vdeOpen(VacFS*, char *path);
int vdeRead(VacDirEnum*, VacDir *, int n);
void vdeFree(VacDirEnum*);

849
src/cmd/vac/vacfs.c Normal file
View file

@ -0,0 +1,849 @@
#include "stdinc.h"
#include <auth.h>
#include <fcall.h>
#include "vac.h"
typedef struct Fid Fid;
typedef struct DirBuf DirBuf;
enum
{
OPERM = 0x3, /* mask of all permission types in open mode */
};
enum
{
DirBufSize = 20,
};
struct Fid
{
short busy;
short open;
int fid;
char *user;
Qid qid;
VacFile *file;
DirBuf *db;
Fid *next;
};
struct DirBuf
{
VacDirEnum *vde;
VacDir buf[DirBufSize];
int i, n;
int eof;
};
enum
{
Pexec = 1,
Pwrite = 2,
Pread = 4,
Pother = 1,
Pgroup = 8,
Powner = 64,
};
Fid *fids;
uchar *data;
int mfd[2];
char *user;
uchar mdata[8192+IOHDRSZ];
int messagesize = sizeof mdata;
Fcall rhdr;
Fcall thdr;
VacFS *fs;
VtSession *session;
int noperm;
Fid * newfid(int);
void error(char*);
void io(void);
void shutdown(void);
void usage(void);
int perm(Fid*, int);
int permf(VacFile*, char*, int);
ulong getl(void *p);
void init(char*, char*, long, int);
DirBuf *dirBufAlloc(VacFile*);
VacDir *dirBufGet(DirBuf*);
int dirBufUnget(DirBuf*);
void dirBufFree(DirBuf*);
int vacdirread(Fid *f, char *p, long off, long cnt);
int vdStat(VacDir *vd, uchar *p, int np);
char *rflush(Fid*), *rversion(Fid*),
*rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
*ropen(Fid*), *rcreate(Fid*),
*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
char *(*fcalls[])(Fid*) = {
[Tflush] rflush,
[Tversion] rversion,
[Tattach] rattach,
[Tauth] rauth,
[Twalk] rwalk,
[Topen] ropen,
[Tcreate] rcreate,
[Tread] rread,
[Twrite] rwrite,
[Tclunk] rclunk,
[Tremove] rremove,
[Tstat] rstat,
[Twstat] rwstat,
};
char Eperm[] = "permission denied";
char Enotdir[] = "not a directory";
char Enotexist[] = "file does not exist";
char Einuse[] = "file in use";
char Eexist[] = "file exists";
char Enotowner[] = "not owner";
char Eisopen[] = "file already open for I/O";
char Excl[] = "exclusive use file already open";
char Ename[] = "illegal name";
char Erdonly[] = "read only file system";
char Eio[] = "i/o error";
char Eempty[] = "directory is not empty";
char Emode[] = "illegal mode";
int dflag;
void
notifyf(void *a, char *s)
{
USED(a);
if(strncmp(s, "interrupt", 9) == 0)
noted(NCONT);
noted(NDFLT);
}
void
main(int argc, char *argv[])
{
char *defmnt;
int p[2];
char buf[12];
int fd;
int stdio = 0;
char *host = nil;
long ncache = 1000;
int readOnly = 1;
defmnt = "/n/vac";
ARGBEGIN{
case 'd':
fmtinstall('F', fcallfmt);
dflag = 1;
break;
case 'c':
ncache = atoi(ARGF());
break;
case 'i':
defmnt = 0;
stdio = 1;
mfd[0] = 0;
mfd[1] = 1;
break;
case 'h':
host = ARGF();
break;
case 's':
defmnt = 0;
break;
case 'p':
noperm = 1;
break;
case 'm':
defmnt = ARGF();
break;
default:
usage();
}ARGEND
if(argc != 1)
usage();
vtAttach();
init(argv[0], host, ncache, readOnly);
if(pipe(p) < 0)
sysfatal("pipe failed: %r");
if(!stdio){
mfd[0] = p[0];
mfd[1] = p[0];
if(defmnt == 0){
fd = create("#s/vacfs", OWRITE, 0666);
if(fd < 0)
sysfatal("create of /srv/vacfs failed: %r");
sprint(buf, "%d", p[1]);
if(write(fd, buf, strlen(buf)) < 0)
sysfatal("writing /srv/vacfs: %r");
}
}
switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
case -1:
sysfatal("fork: %r");
case 0:
vtAttach();
close(p[1]);
io();
shutdown();
break;
default:
close(p[0]); /* don't deadlock if child fails */
if(defmnt && mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)
sysfatal("mount failed: %r");
}
vtDetach();
exits(0);
}
void
usage(void)
{
fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0);
exits("usage");
}
char*
rversion(Fid *unused)
{
Fid *f;
USED(unused);
for(f = fids; f; f = f->next)
if(f->busy)
rclunk(f);
if(rhdr.msize < 256)
return "version: message size too small";
messagesize = rhdr.msize;
if(messagesize > sizeof mdata)
messagesize = sizeof mdata;
thdr.msize = messagesize;
if(strncmp(rhdr.version, "9P2000", 6) != 0)
return "unrecognized 9P version";
thdr.version = "9P2000";
return nil;
}
char*
rflush(Fid *f)
{
USED(f);
return 0;
}
char*
rauth(Fid *f)
{
USED(f);
return "vacfs: authentication not required";
}
char*
rattach(Fid *f)
{
/* no authentication for the momment */
VacFile *file;
file = vfsGetRoot(fs);
if(file == nil)
return vtGetError();
f->busy = 1;
f->file = file;
f->qid = (Qid){vfGetId(f->file), 0, QTDIR};
thdr.qid = f->qid;
if(rhdr.uname[0])
f->user = vtStrDup(rhdr.uname);
else
f->user = "none";
return 0;
}
VacFile*
_vfWalk(VacFile *file, char *name)
{
VacFile *n;
n = vfWalk(file, name);
if(n)
return n;
if(strcmp(name, "SLASH") == 0)
return vfWalk(file, "/");
return nil;
}
char*
rwalk(Fid *f)
{
VacFile *file, *nfile;
Fid *nf;
int nqid, nwname;
Qid qid;
if(f->busy == 0)
return Enotexist;
nf = nil;
if(rhdr.fid != rhdr.newfid){
if(f->open)
return Eisopen;
if(f->busy == 0)
return Enotexist;
nf = newfid(rhdr.newfid);
if(nf->busy)
return Eisopen;
nf->busy = 1;
nf->open = 0;
nf->qid = f->qid;
nf->file = vfIncRef(f->file);
nf->user = vtStrDup(f->user);
f = nf;
}
nwname = rhdr.nwname;
/* easy case */
if(nwname == 0) {
thdr.nwqid = 0;
return 0;
}
file = f->file;
vfIncRef(file);
qid = f->qid;
for(nqid = 0; nqid < nwname; nqid++){
if((qid.type & QTDIR) == 0){
vtSetError(Enotdir);
break;
}
if(!permf(file, f->user, Pexec)) {
vtSetError(Eperm);
break;
}
nfile = _vfWalk(file, rhdr.wname[nqid]);
if(nfile == nil)
break;
vfDecRef(file);
file = nfile;
qid.type = QTFILE;
if(vfIsDir(file))
qid.type = QTDIR;
qid.vers = vfGetMcount(file);
qid.path = vfGetId(file);
thdr.wqid[nqid] = qid;
}
thdr.nwqid = nqid;
if(nqid == nwname){
/* success */
f->qid = thdr.wqid[nqid-1];
vfDecRef(f->file);
f->file = file;
return 0;
}
vfDecRef(file);
if(nf != nil)
rclunk(nf);
/* only error on the first element */
if(nqid == 0)
return vtGetError();
return 0;
}
char *
ropen(Fid *f)
{
int mode, trunc;
if(f->open)
return Eisopen;
if(!f->busy)
return Enotexist;
mode = rhdr.mode;
thdr.iounit = messagesize - IOHDRSZ;
if(f->qid.type & QTDIR){
if(mode != OREAD)
return Eperm;
if(!perm(f, Pread))
return Eperm;
thdr.qid = f->qid;
f->db = nil;
f->open = 1;
return 0;
}
if(mode & ORCLOSE)
return Erdonly;
trunc = mode & OTRUNC;
mode &= OPERM;
if(mode==OWRITE || mode==ORDWR || trunc)
if(!perm(f, Pwrite))
return Eperm;
if(mode==OREAD || mode==ORDWR)
if(!perm(f, Pread))
return Eperm;
if(mode==OEXEC)
if(!perm(f, Pexec))
return Eperm;
thdr.qid = f->qid;
thdr.iounit = messagesize - IOHDRSZ;
f->open = 1;
return 0;
}
char*
rcreate(Fid* fid)
{
VacFile *vf;
ulong mode;
if(fid->open)
return Eisopen;
if(!fid->busy)
return Enotexist;
if(vfsIsReadOnly(fs))
return Erdonly;
vf = fid->file;
if(!vfIsDir(vf))
return Enotdir;
if(!permf(vf, fid->user, Pwrite))
return Eperm;
mode = rhdr.perm & 0777;
if(rhdr.perm & DMDIR){
if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
return Emode;
switch(rhdr.mode & OPERM){
default:
return Emode;
case OEXEC:
case OREAD:
break;
case OWRITE:
case ORDWR:
return Eperm;
}
mode |= ModeDir;
}
vf = vfCreate(vf, rhdr.name, mode, "none");
if(vf == nil)
return vtGetError();
vfDecRef(fid->file);
fid->file = vf;
fid->qid.type = QTFILE;
if(vfIsDir(vf))
fid->qid.type = QTDIR;
fid->qid.vers = vfGetMcount(vf);
fid->qid.path = vfGetId(vf);
thdr.qid = fid->qid;
thdr.iounit = messagesize - IOHDRSZ;
return 0;
}
char*
rread(Fid *f)
{
char *buf;
vlong off;
int cnt;
VacFile *vf;
char *err;
int n;
if(!f->busy)
return Enotexist;
vf = f->file;
thdr.count = 0;
off = rhdr.offset;
buf = thdr.data;
cnt = rhdr.count;
if(f->qid.type & QTDIR)
n = vacdirread(f, buf, off, cnt);
else
n = vfRead(vf, buf, cnt, off);
if(n < 0) {
err = vtGetError();
if(err == nil)
err = "unknown error!";
return err;
}
thdr.count = n;
return 0;
}
char*
rwrite(Fid *f)
{
char *buf;
vlong off;
int cnt;
VacFile *vf;
if(!f->busy)
return Enotexist;
vf = f->file;
thdr.count = 0;
off = rhdr.offset;
buf = rhdr.data;
cnt = rhdr.count;
if(f->qid.type & QTDIR)
return "file is a directory";
thdr.count = vfWrite(vf, buf, cnt, off, "none");
if(thdr.count < 0) {
fprint(2, "write failed: %s\n", vtGetError());
return vtGetError();
}
return 0;
}
char *
rclunk(Fid *f)
{
f->busy = 0;
f->open = 0;
vtMemFree(f->user);
f->user = nil;
vfDecRef(f->file);
f->file = nil;
dirBufFree(f->db);
f->db = nil;
return 0;
}
char *
rremove(Fid *f)
{
VacFile *vf, *vfp;
char *err = nil;
if(!f->busy)
return Enotexist;
vf = f->file;
vfp = vfGetParent(vf);
if(!permf(vfp, f->user, Pwrite)) {
err = Eperm;
goto Exit;
}
if(!vfRemove(vf, "none")) {
print("vfRemove failed\n");
err = vtGetError();
}
Exit:
vfDecRef(vfp);
rclunk(f);
return err;
}
char *
rstat(Fid *f)
{
VacDir dir;
static uchar statbuf[1024];
if(!f->busy)
return Enotexist;
vfGetDir(f->file, &dir);
thdr.stat = statbuf;
thdr.nstat = vdStat(&dir, thdr.stat, sizeof statbuf);
vdCleanup(&dir);
return 0;
}
char *
rwstat(Fid *f)
{
if(!f->busy)
return Enotexist;
return Erdonly;
}
int
vdStat(VacDir *vd, uchar *p, int np)
{
Dir dir;
memset(&dir, 0, sizeof(dir));
/*
* Where do path and version come from
*/
dir.qid.path = vd->qid;
dir.qid.vers = vd->mcount;
dir.mode = vd->mode & 0777;
if(vd->mode & ModeAppend){
dir.qid.type |= QTAPPEND;
dir.mode |= DMAPPEND;
}
if(vd->mode & ModeExclusive){
dir.qid.type |= QTEXCL;
dir.mode |= DMEXCL;
}
if(vd->mode & ModeDir){
dir.qid.type |= QTDIR;
dir.mode |= DMDIR;
}
dir.atime = vd->atime;
dir.mtime = vd->mtime;
dir.length = vd->size;
dir.name = vd->elem;
dir.uid = vd->uid;
dir.gid = vd->gid;
dir.muid = vd->mid;
return convD2M(&dir, p, np);
}
DirBuf*
dirBufAlloc(VacFile *vf)
{
DirBuf *db;
db = vtMemAllocZ(sizeof(DirBuf));
db->vde = vfDirEnum(vf);
return db;
}
VacDir *
dirBufGet(DirBuf *db)
{
VacDir *vd;
int n;
if(db->eof)
return nil;
if(db->i >= db->n) {
n = vdeRead(db->vde, db->buf, DirBufSize);
if(n < 0)
return nil;
db->i = 0;
db->n = n;
if(n == 0) {
db->eof = 1;
return nil;
}
}
vd = db->buf + db->i;
db->i++;
return vd;
}
int
dirBufUnget(DirBuf *db)
{
assert(db->i > 0);
db->i--;
return 1;
}
void
dirBufFree(DirBuf *db)
{
int i;
if(db == nil)
return;
for(i=db->i; i<db->n; i++)
vdCleanup(db->buf + i);
vdeFree(db->vde);
vtMemFree(db);
}
int
vacdirread(Fid *f, char *p, long off, long cnt)
{
int n, nb;
VacDir *vd;
/*
* special case of rewinding a directory
* otherwise ignore the offset
*/
if(off == 0 && f->db) {
dirBufFree(f->db);
f->db = nil;
}
if(f->db == nil)
f->db = dirBufAlloc(f->file);
for(nb = 0; nb < cnt; nb += n) {
vd = dirBufGet(f->db);
if(vd == nil) {
if(!f->db->eof)
return -1;
break;
}
n = vdStat(vd, (uchar*)p, cnt-nb);
if(n <= BIT16SZ) {
dirBufUnget(f->db);
break;
}
vdCleanup(vd);
p += n;
}
return nb;
}
Fid *
newfid(int fid)
{
Fid *f, *ff;
ff = 0;
for(f = fids; f; f = f->next)
if(f->fid == fid)
return f;
else if(!ff && !f->busy)
ff = f;
if(ff){
ff->fid = fid;
return ff;
}
f = vtMemAllocZ(sizeof *f);
f->fid = fid;
f->next = fids;
fids = f;
return f;
}
void
io(void)
{
char *err;
int n;
for(;;){
/*
* reading from a pipe or a network device
* will give an error after a few eof reads
* however, we cannot tell the difference
* between a zero-length read and an interrupt
* on the processes writing to us,
* so we wait for the error
*/
n = read9pmsg(mfd[0], mdata, sizeof mdata);
if(n == 0)
continue;
if(n < 0)
break;
if(convM2S(mdata, n, &rhdr) != n)
sysfatal("convM2S conversion error");
if(dflag)
fprint(2, "vacfs:<-%F\n", &rhdr);
thdr.data = (char*)mdata + IOHDRSZ;
if(!fcalls[rhdr.type])
err = "bad fcall type";
else
err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
if(err){
thdr.type = Rerror;
thdr.ename = err;
}else{
thdr.type = rhdr.type + 1;
thdr.fid = rhdr.fid;
}
thdr.tag = rhdr.tag;
if(dflag)
fprint(2, "vacfs:->%F\n", &thdr);
n = convS2M(&thdr, mdata, messagesize);
if(write(mfd[1], mdata, n) != n)
sysfatal("mount write: %r");
}
}
int
permf(VacFile *vf, char *user, int p)
{
VacDir dir;
ulong perm;
if(!vfGetDir(vf, &dir))
return 0;
perm = dir.mode & 0777;
if(noperm)
goto Good;
if((p*Pother) & perm)
goto Good;
if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
goto Good;
if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
goto Good;
vdCleanup(&dir);
return 0;
Good:
vdCleanup(&dir);
return 1;
}
int
perm(Fid *f, int p)
{
return permf(f->file, f->user, p);
}
void
init(char *file, char *host, long ncache, int readOnly)
{
notify(notifyf);
user = getuser();
fmtinstall('V', vtScoreFmt);
fmtinstall('R', vtErrFmt);
session = vtDial(host, 0);
if(session == nil)
vtFatal("could not connect to server: %s", vtGetError());
if(!vtConnect(session, 0))
vtFatal("vtConnect: %s", vtGetError());
fs = vfsOpen(session, file, readOnly, ncache);
if(fs == nil)
vtFatal("vfsOpen: %s", vtGetError());
}
void
shutdown(void)
{
Fid *f;
for(f = fids; f; f = f->next) {
if(!f->busy)
continue;
fprint(2, "open fid: %d\n", f->fid);
rclunk(f);
}
vfsClose(fs);
vtClose(session);
}

182
src/cmd/vac/vactest.c Normal file
View file

@ -0,0 +1,182 @@
#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
void usage(void);
int unvac(VacFS *fs);
int readScore(int fd, uchar score[VtScoreSize]);
static void warn(char *fmt, ...);
void dirlist(VacFS *fs, char *path);
static int nwant;
static char **want;
static int dflag = 1;
static int cflag;
static int lower;
static int verbose;
static int settimes;
void
main(int argc, char *argv[])
{
char *zfile;
int ok, table;
VtSession *z;
char *vsrv = nil;
char *host = nil;
char *p;
int ncache = 1000;
VacFS *fs;
table = 0;
zfile = nil;
ARGBEGIN{
case 'D':
dflag++;
break;
case 'c':
cflag++;
break;
case 'C':
p = ARGF();
if(p == nil)
usage();
ncache = atoi(p);
if(ncache < 10)
ncache = 10;
if(ncache > 1000000)
ncache = 1000000;
break;
case 'i':
lower++;
break;
case 'f':
zfile = ARGF();
if(zfile == nil)
usage();
break;
case 'h':
host = ARGF();
break;
case 't':
table++;
break;
case 'T':
settimes++;
break;
case 's':
vsrv = ARGF();
break;
case 'v':
verbose++;
break;
default:
usage();
break;
}ARGEND
nwant = argc;
want = argv;
vtAttach();
if(zfile == nil)
usage();
if(vsrv != nil)
z = vtStdioServer(vsrv);
else
z = vtDial(host);
if(z == nil)
vtFatal("could not connect to server: %s", vtGetError());
vtSetDebug(z, 0);
if(!vtConnect(z, 0))
vtFatal("vtConnect: %s", vtGetError());
fs = vfsOpen(z, zfile, 1, ncache);
if(fs == nil)
vtFatal("vfsOpen: %s", vtGetError());
ok = unvac(fs);
vtClose(z);
vtDetach();
exits(ok? 0 : "error");
}
void
usage(void)
{
fprint(2, "usage: %s [-tTcDv] -f zipfile [-s ventid] [-h host] [file ...]\n", argv0);
exits("usage");
}
void
suck(VacFile *f)
{
USED(f);
}
void
vacfile(VacFS *fs, char *path, VacDir *vd)
{
char *path2;
path2 = vtMemAlloc(strlen(path) + 1 + strlen(vd->elem) + 1);
if(path[1] == 0)
sprintf(path2, "/%s", vd->elem);
else
sprintf(path2, "%s/%s", path, vd->elem);
fprint(2, "vac file: %s\n", path2);
if(vd->mode & ModeDir)
dirlist(fs, path2);
vtMemFree(path2);
}
void
dirlist(VacFS *fs, char *path)
{
VacDir vd[50];
VacDirEnum *ds;
int i, n;
ds = vdeOpen(fs, path);
if(ds == nil) {
fprint(2, "could not open: %s: %s\n", path, vtGetError());
return;
}
for(;;) {
n = vdeRead(ds, vd, sizeof(vd)/sizeof(VacDir));
if(n < 0) {
warn("vdRead failed: %s: %s", path, vtGetError());
return;
}
if(n == 0)
break;
for(i=0; i<n; i++) {
vacfile(fs, path, &vd[i]);
vdCleanup(&vd[i]);
}
}
vdeFree(ds);
}
int
unvac(VacFS *fs)
{
dirlist(fs, "/");
return 1;
}
static void
warn(char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
fprint(2, "%s: ", argv0);
vfprint(2, fmt, arg);
fprint(2, "\n");
va_end(arg);
}

391
src/cmd/vac/vtdump.c Normal file
View file

@ -0,0 +1,391 @@
#include "stdinc.h"
#include <bio.h>
typedef struct Source Source;
struct Source
{
ulong gen;
int psize;
int dsize;
int dir;
int active;
int depth;
uvlong size;
uchar score[VtScoreSize];
int reserved;
};
int bsize;
Biobuf *bout;
VtRoot root;
int ver;
int cmp;
int all;
int find;
uchar fscore[VtScoreSize];
VtSession *z;
int vtGetUint16(uchar *p);
ulong vtGetUint32(uchar *p);
uvlong vtGetUint48(uchar *p);
void usage(void);
int parseScore(uchar *score, char *buf, int n);
void readRoot(VtRoot*, uchar *score, char *file);
int dumpDir(Source*, int indent);
void
main(int argc, char *argv[])
{
char *host = nil;
uchar score[VtScoreSize];
Source source;
uchar buf[VtMaxLumpSize];
char *p;
int n;
ARGBEGIN{
case 'h':
host = ARGF();
break;
case 'c':
cmp++;
break;
case 'f':
find++;
p = ARGF();
if(p == nil || !parseScore(fscore, p, strlen(p)))
usage();
break;
case 'a':
all = 1;
break;
}ARGEND
vtAttach();
bout = vtMemAllocZ(sizeof(Biobuf));
Binit(bout, 1, OWRITE);
if(argc > 1)
usage();
vtAttach();
fmtinstall('V', vtScoreFmt);
fmtinstall('R', vtErrFmt);
z = vtDial(host, 0);
if(z == nil)
vtFatal("could not connect to server: %s", vtGetError());
if(!vtConnect(z, 0))
sysfatal("vtConnect: %r");
readRoot(&root, score, argv[0]);
ver = root.version;
bsize = root.blockSize;
if(!find) {
Bprint(bout, "score: %V\n", score);
Bprint(bout, "version: %d\n", ver);
Bprint(bout, "name: %s\n", root.name);
Bprint(bout, "type: %s\n", root.type);
Bprint(bout, "bsize: %d\n", bsize);
Bprint(bout, "prev: %V\n", root.prev);
}
switch(ver) {
default:
sysfatal("unknown version");
case VtRootVersion:
break;
}
n = vtRead(z, root.score, VtDirType, buf, bsize);
if(n < 0)
sysfatal("could not read root dir");
/* fake up top level source */
memset(&source, 0, sizeof(source));
memmove(source.score, root.score, VtScoreSize);
source.psize = bsize;
source.dsize = bsize;
source.dir = 1;
source.active = 1;
source.depth = 0;
source.size = n;
dumpDir(&source, 0);
Bterm(bout);
vtClose(z);
vtDetach();
exits(0);
}
void
sourcePrint(Source *s, int indent, int entry)
{
int i;
uvlong size;
int ne;
for(i=0; i<indent; i++)
Bprint(bout, " ");
Bprint(bout, "%4d", entry);
if(s->active) {
/* dir size in directory entries */
if(s->dir) {
ne = s->dsize/VtEntrySize;
size = ne*(s->size/s->dsize) + (s->size%s->dsize)/VtEntrySize;
} else
size = s->size;
if(cmp) {
Bprint(bout, ": gen: %lud size: %llud",
s->gen, size);
if(!s->dir)
Bprint(bout, ": %V", s->score);
} else {
Bprint(bout, ": gen: %lud psize: %d dsize: %d",
s->gen, s->psize, s->dsize);
Bprint(bout, " depth: %d size: %llud: %V",
s->depth, size, s->score);
}
if(s->reserved)
Bprint(bout, ": reserved not emtpy");
}
Bprint(bout, "\n");
}
int
parse(Source *s, uchar *p)
{
VtEntry dir;
memset(s, 0, sizeof(*s));
if(!vtEntryUnpack(&dir, p, 0))
return 0;
if(!(dir.flags & VtEntryActive))
return 1;
s->active = 1;
s->gen = dir.gen;
s->psize = dir.psize;
s->dsize = dir.size;
s->size = dir.size;
memmove(s->score, dir.score, VtScoreSize);
if(dir.flags & VtEntryDir)
s->dir = 1;
s->depth = dir.depth;
return 1;
}
int
sourceRead(Source *s, ulong block, uchar *p, int n)
{
uchar buf[VtMaxLumpSize];
uchar score[VtScoreSize];
int i, nn, np, type;
int elem[VtPointerDepth];
memmove(score, s->score, VtScoreSize);
np = s->psize/VtScoreSize;
for(i=0; i<s->depth; i++) {
elem[i] = block % np;
block /= np;
}
assert(block == 0);
for(i=s->depth-1; i>=0; i--) {
nn = vtRead(z, score, VtPointerType0+i, buf, s->psize);
if(nn < 0)
return -1;
if(!vtSha1Check(score, buf, nn)) {
vtSetError("vtSha1Check failed on root block");
return -1;
}
if((elem[i]+1)*VtScoreSize > nn)
return 0;
memmove(score, buf + elem[i]*VtScoreSize, VtScoreSize);
}
if(s->dir)
type = VtDirType;
else
type = VtDataType;
nn = vtRead(z, score, type, p, n);
if(nn < 0)
return -1;
if(!vtSha1Check(score, p, nn)) {
vtSetError("vtSha1Check failed on root block");
return -1;
}
return nn;
}
void
dumpFileContents(Source *s)
{
int nb, lb, i, n;
uchar buf[VtMaxLumpSize];
nb = (s->size + s->dsize - 1)/s->dsize;
lb = s->size%s->dsize;
for(i=0; i<nb; i++) {
memset(buf, 0, s->dsize);
n = sourceRead(s, i, buf, s->dsize);
if(n < 0) {
fprint(2, "could not read block: %d: %s\n", i, vtGetError());
continue;
}
if(i < nb-1)
Bwrite(bout, buf, s->dsize);
else
Bwrite(bout, buf, lb);
}
}
void
dumpFile(Source *s, int indent)
{
int nb, i, j, n;
uchar buf[VtMaxLumpSize];
uchar score[VtScoreSize];
nb = (s->size + s->dsize - 1)/s->dsize;
for(i=0; i<nb; i++) {
memset(buf, 0, s->dsize);
n = sourceRead(s, i, buf, s->dsize);
if(n < 0) {
fprint(2, "could not read block: %d: %s\n", i, vtGetError());
continue;
}
for(j=0; j<indent; j++)
Bprint(bout, " ");
vtSha1(score, buf, n);
Bprint(bout, "%4d: size: %ud: %V\n", i, n, score);
}
}
int
dumpDir(Source *s, int indent)
{
int pb, ne, nb, i, j, n, entry;
uchar buf[VtMaxLumpSize];
Source ss;
pb = s->dsize/VtEntrySize;
ne = pb*(s->size/s->dsize) + (s->size%s->dsize)/VtEntrySize;
nb = (s->size + s->dsize - 1)/s->dsize;
for(i=0; i<nb; i++) {
memset(buf, 0, s->dsize);
n = sourceRead(s, i, buf, s->dsize);
if(n < 0) {
fprint(2, "could not read block: %d: %s\n", i, vtGetError());
continue;
}
for(j=0; j<pb; j++) {
entry = i*pb + j;
if(entry >= ne)
break;
parse(&ss, buf + j * VtEntrySize);
if(!find)
sourcePrint(&ss, indent, entry);
else if(memcmp(ss.score, fscore, VtScoreSize) == 0) {
dumpFileContents(&ss);
return 0;
}
if(ss.dir) {
if(!dumpDir(&ss, indent+1))
return 0;
} else if(all)
dumpFile(&ss, indent+1);
}
}
return 1;
}
void
usage(void)
{
fprint(2, "%s: [file]\n", argv0);
exits("usage");
}
int
parseScore(uchar *score, char *buf, int n)
{
int i, c;
memset(score, 0, VtScoreSize);
if(n < VtScoreSize*2)
return 0;
for(i=0; i<VtScoreSize*2; i++) {
if(buf[i] >= '0' && buf[i] <= '9')
c = buf[i] - '0';
else if(buf[i] >= 'a' && buf[i] <= 'f')
c = buf[i] - 'a' + 10;
else if(buf[i] >= 'A' && buf[i] <= 'F')
c = buf[i] - 'A' + 10;
else {
return 0;
}
if((i & 1) == 0)
c <<= 4;
score[i>>1] |= c;
}
return 1;
}
void
readRoot(VtRoot *root, uchar *score, char *file)
{
int fd;
uchar buf[VtRootSize];
int i, n, nn;
if(file == 0)
fd = 0;
else {
fd = open(file, OREAD);
if(fd < 0)
sysfatal("could not open file: %s: %r\n", file);
}
n = readn(fd, buf, sizeof(buf)-1);
if(n < 0)
sysfatal("read failed: %r\n");
buf[n] = 0;
close(fd);
for(i=0; i<n; i++) {
if(!parseScore(score, (char*)(buf+i), n-i))
continue;
nn = vtRead(z, score, VtRootType, buf, VtRootSize);
if(nn >= 0) {
if(nn != VtRootSize)
sysfatal("vtRead on root too short");
if(!vtSha1Check(score, buf, VtRootSize))
sysfatal("vtSha1Check failed on root block");
if(!vtRootUnpack(root, buf))
sysfatal("could not parse root: %r");
return;
}
}
sysfatal("could not find root");
}

126
src/cmd/vac/vtread.c Normal file
View file

@ -0,0 +1,126 @@
#include "stdinc.h"
#include <bio.h>
typedef struct Source Source;
struct Source
{
ulong gen;
int psize;
int dsize;
int dir;
int active;
int depth;
uvlong size;
uchar score[VtScoreSize];
int reserved;
};
int bsize;
Biobuf *bout;
VtRootLump root;
int ver;
int cmp;
int all;
int find;
uchar fscore[VtScoreSize];
int dirSize;
void (*parse)(Source*, uchar*);
VtSession *z;
int vtGetUint16(uchar *p);
ulong vtGetUint32(uchar *p);
uvlong vtGetUint48(uchar *p);
void usage(void);
int parseScore(uchar *score, char *buf, int n);
void readRoot(VtRootLump*, uchar *score, char *file);
void parse1(Source*, uchar*);
void parse2(Source*, uchar*);
int dumpDir(Source*, int indent);
void
main(int argc, char *argv[])
{
char *host = nil;
uchar score[VtScoreSize];
uchar buf[VtMaxLumpSize];
int type;
int n;
type = VtDataType;
ARGBEGIN{
case 't':
type = atoi(ARGF());
break;
}ARGEND
vtAttach();
bout = vtMemAllocZ(sizeof(Biobuf));
Binit(bout, 1, OWRITE);
if(argc != 1)
usage();
vtAttach();
fmtinstall('V', vtScoreFmt);
fmtinstall('R', vtErrFmt);
z = vtDial(host);
if(z == nil)
vtFatal("could not connect to server: %s", vtGetError());
if(!vtConnect(z, 0))
sysfatal("vtConnect: %r");
if(!parseScore(score, argv[0], strlen(argv[0])))
vtFatal("could not parse score: %s", vtGetError());
n = vtRead(z, score, type, buf, VtMaxLumpSize);
if(n < 0)
vtFatal("could not read block: %s", vtGetError());
Bwrite(bout, buf, n);
Bterm(bout);
vtClose(z);
vtDetach();
exits(0);
}
void
usage(void)
{
fprint(2, "%s: -t type score\n", argv0);
exits("usage");
}
int
parseScore(uchar *score, char *buf, int n)
{
int i, c;
memset(score, 0, VtScoreSize);
if(n < VtScoreSize*2)
return 0;
for(i=0; i<VtScoreSize*2; i++) {
if(buf[i] >= '0' && buf[i] <= '9')
c = buf[i] - '0';
else if(buf[i] >= 'a' && buf[i] <= 'f')
c = buf[i] - 'a' + 10;
else if(buf[i] >= 'A' && buf[i] <= 'F')
c = buf[i] - 'A' + 10;
else {
return 0;
}
if((i & 1) == 0)
c <<= 4;
score[i>>1] |= c;
}
return 1;
}

47
src/cmd/vac/wtest.c Normal file
View file

@ -0,0 +1,47 @@
#include "stdinc.h"
enum {
Nblock = 10000,
BlockSize = 8*1024,
};
uchar data[Nblock*BlockSize];
void
main(int argc, char *argv[])
{
VtSession *z;
int i;
uchar score[VtScoreSize];
int start;
ARGBEGIN{
}ARGEND
for(i=0; i<Nblock; i++) {
if(readn(0, data+i*BlockSize, BlockSize) < BlockSize)
sysfatal("read failed: %r");
}
vtAttach();
z = vtDial("iolaire2");
if(z == nil)
sysfatal("cound not connect to venti");
if(!vtConnect(z, 0))
vtFatal("vtConnect: %s", vtGetError());
print("starting\n");
start = times(0);
for(i=0; i<Nblock; i++) {
if(!vtWrite(z, score, VtDataType, data+i*BlockSize, BlockSize))
vtFatal("vtWrite failed: %s", vtGetError());
}
print("time = %f\n", (times(0) - start)*0.001);
vtClose(z);
vtDetach();
}