mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-24 11:41:58 +00:00
start thinking about vac -- doesn't build yet
This commit is contained in:
parent
7a4ee46d25
commit
7763a61a35
22 changed files with 7959 additions and 0 deletions
876
src/cmd/vac/cache.c
Normal file
876
src/cmd/vac/cache.c
Normal 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
156
src/cmd/vac/dat.h
Normal 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
20
src/cmd/vac/error.c
Normal 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
14
src/cmd/vac/error.h
Normal 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
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
46
src/cmd/vac/fns.h
Normal 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
188
src/cmd/vac/fs.c
Normal 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
36
src/cmd/vac/mkfile
Normal 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
609
src/cmd/vac/pack.c
Normal 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
71
src/cmd/vac/rtest.c
Normal 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
390
src/cmd/vac/source.c
Normal 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
302
src/cmd/vac/srcload.c
Normal 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
8
src/cmd/vac/stdinc.h
Normal 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
71
src/cmd/vac/util.c
Normal 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
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
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
126
src/cmd/vac/vac.h
Normal 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
849
src/cmd/vac/vacfs.c
Normal 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
182
src/cmd/vac/vactest.c
Normal 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
391
src/cmd/vac/vtdump.c
Normal 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
126
src/cmd/vac/vtread.c
Normal 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
47
src/cmd/vac/wtest.c
Normal 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();
|
||||
}
|
Loading…
Reference in a new issue