start thinking about vac -- doesn't build yet
This commit is contained in:
parent
7a4ee46d25
commit
7763a61a35
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 New Issue
Block a user