xref: /plan9/sys/src/cmd/fossil/source.c (revision 3b86f2f88bade1f00206c7aa750b7add255f5724)
15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier #include "dat.h"
35e96a66cSDavid du Colombier #include "fns.h"
45e96a66cSDavid du Colombier #include "error.h"
5*3b86f2f8SDavid du Colombier #include "9.h"
65e96a66cSDavid du Colombier 
75e96a66cSDavid du Colombier static int	sizeToDepth(uvlong s, int psize, int dsize);
85e96a66cSDavid du Colombier static u32int 	tagGen(void);
95e96a66cSDavid du Colombier static Block 	*sourceLoad(Source *r, Entry *e);
105e96a66cSDavid du Colombier static Block	*sourceLoadUnlocked(Source *r, Entry *e);
115e96a66cSDavid du Colombier static int	sourceShrinkDepth(Source*, Block*, Entry*, int);
125e96a66cSDavid du Colombier static int	sourceShrinkSize(Source*, Entry*, uvlong);
135e96a66cSDavid du Colombier static int	sourceGrowDepth(Source*, Block*, Entry*, int);
145e96a66cSDavid du Colombier 
155e96a66cSDavid du Colombier #define sourceIsLocked(r)	((r)->b != nil)
165e96a66cSDavid du Colombier 
175e96a66cSDavid du Colombier static Source *
1857195852SDavid du Colombier sourceAlloc(Fs *fs, Block *b, Source *p, u32int offset, int mode, int issnapshot)
195e96a66cSDavid du Colombier {
205e96a66cSDavid du Colombier 	int epb;
215e96a66cSDavid du Colombier 	u32int epoch;
22*3b86f2f8SDavid du Colombier 	char *pname = nil;
23*3b86f2f8SDavid du Colombier 	Source *r;
245e96a66cSDavid du Colombier 	Entry e;
255e96a66cSDavid du Colombier 
265e96a66cSDavid du Colombier 	assert(p==nil || sourceIsLocked(p));
275e96a66cSDavid du Colombier 
285e96a66cSDavid du Colombier 	if(p == nil){
295e96a66cSDavid du Colombier 		assert(offset == 0);
305e96a66cSDavid du Colombier 		epb = 1;
315e96a66cSDavid du Colombier 	}else
325e96a66cSDavid du Colombier 		epb = p->dsize / VtEntrySize;
335e96a66cSDavid du Colombier 
345e96a66cSDavid du Colombier 	if(b->l.type != BtDir)
355e96a66cSDavid du Colombier 		goto Bad;
365e96a66cSDavid du Colombier 
375e96a66cSDavid du Colombier 	/*
385e96a66cSDavid du Colombier 	 * a non-active entry is the only thing that
395e96a66cSDavid du Colombier 	 * can legitimately happen here. all the others
405e96a66cSDavid du Colombier 	 * get prints.
415e96a66cSDavid du Colombier 	 */
425e96a66cSDavid du Colombier 	if(!entryUnpack(&e, b->data, offset % epb)){
43*3b86f2f8SDavid du Colombier 		pname = sourceName(p);
44*3b86f2f8SDavid du Colombier 		consPrint("%s: %s %V: sourceAlloc: entryUnpack failed\n",
45*3b86f2f8SDavid du Colombier 			fs->name, pname, b->score);
465e96a66cSDavid du Colombier 		goto Bad;
475e96a66cSDavid du Colombier 	}
485e96a66cSDavid du Colombier 	if(!(e.flags & VtEntryActive)){
49*3b86f2f8SDavid du Colombier 		pname = sourceName(p);
50*3b86f2f8SDavid du Colombier 		if(0) consPrint("%s: %s %V: sourceAlloc: not active\n",
51*3b86f2f8SDavid du Colombier 			fs->name, pname, e.score);
525e96a66cSDavid du Colombier 		goto Bad;
535e96a66cSDavid du Colombier 	}
545e96a66cSDavid du Colombier 	if(e.psize < 256 || e.dsize < 256){
55*3b86f2f8SDavid du Colombier 		pname = sourceName(p);
56*3b86f2f8SDavid du Colombier 		consPrint("%s: %s %V: sourceAlloc: psize %ud or dsize %ud < 256\n",
57*3b86f2f8SDavid du Colombier 			fs->name, pname, e.score, e.psize, e.dsize);
585e96a66cSDavid du Colombier 		goto Bad;
595e96a66cSDavid du Colombier 	}
605e96a66cSDavid du Colombier 
615e96a66cSDavid du Colombier 	if(e.depth < sizeToDepth(e.size, e.psize, e.dsize)){
62*3b86f2f8SDavid du Colombier 		pname = sourceName(p);
63*3b86f2f8SDavid du Colombier 		consPrint("%s: %s %V: sourceAlloc: depth %ud size %llud "
64*3b86f2f8SDavid du Colombier 			"psize %ud dsize %ud\n", fs->name, pname,
65*3b86f2f8SDavid du Colombier 			e.score, e.depth, e.size, e.psize, e.dsize);
665e96a66cSDavid du Colombier 		goto Bad;
675e96a66cSDavid du Colombier 	}
685e96a66cSDavid du Colombier 
695e96a66cSDavid du Colombier 	if((e.flags & VtEntryLocal) && e.tag == 0){
70*3b86f2f8SDavid du Colombier 		pname = sourceName(p);
71*3b86f2f8SDavid du Colombier 		consPrint("%s: %s %V: sourceAlloc: flags %#ux tag %#ux\n",
72*3b86f2f8SDavid du Colombier 			fs->name, pname, e.score, e.flags, e.tag);
735e96a66cSDavid du Colombier 		goto Bad;
745e96a66cSDavid du Colombier 	}
755e96a66cSDavid du Colombier 
765e96a66cSDavid du Colombier 	if(e.dsize > fs->blockSize || e.psize > fs->blockSize){
77*3b86f2f8SDavid du Colombier 		pname = sourceName(p);
78*3b86f2f8SDavid du Colombier 		consPrint("%s: %s %V: sourceAlloc: psize %ud or dsize %ud "
79*3b86f2f8SDavid du Colombier 			"> blocksize %ud\n", fs->name, pname, e.score,
80*3b86f2f8SDavid du Colombier 			e.psize, e.dsize, fs->blockSize);
815e96a66cSDavid du Colombier 		goto Bad;
825e96a66cSDavid du Colombier 	}
835e96a66cSDavid du Colombier 
845e96a66cSDavid du Colombier 	epoch = b->l.epoch;
855e96a66cSDavid du Colombier 	if(mode == OReadWrite){
865e96a66cSDavid du Colombier 		if(e.snap != 0){
875e96a66cSDavid du Colombier 			vtSetError(ESnapRO);
885e96a66cSDavid du Colombier 			return nil;
895e96a66cSDavid du Colombier 		}
905e96a66cSDavid du Colombier 	}else if(e.snap != 0){
915e96a66cSDavid du Colombier 		if(e.snap < fs->elo){
925e96a66cSDavid du Colombier 			vtSetError(ESnapOld);
935e96a66cSDavid du Colombier 			return nil;
945e96a66cSDavid du Colombier 		}
955e96a66cSDavid du Colombier 		if(e.snap >= fs->ehi)
965e96a66cSDavid du Colombier 			goto Bad;
975e96a66cSDavid du Colombier 		epoch = e.snap;
985e96a66cSDavid du Colombier 	}
995e96a66cSDavid du Colombier 
1005e96a66cSDavid du Colombier 	r = vtMemAllocZ(sizeof(Source));
1015e96a66cSDavid du Colombier 	r->fs = fs;
1025e96a66cSDavid du Colombier 	r->mode = mode;
10357195852SDavid du Colombier 	r->issnapshot = issnapshot;
1045e96a66cSDavid du Colombier 	r->dsize = e.dsize;
1055e96a66cSDavid du Colombier 	r->gen = e.gen;
1065e96a66cSDavid du Colombier 	r->dir = (e.flags & VtEntryDir) != 0;
1075e96a66cSDavid du Colombier 	r->lk = vtLockAlloc();
1085e96a66cSDavid du Colombier 	r->ref = 1;
1095e96a66cSDavid du Colombier 	r->parent = p;
1105e96a66cSDavid du Colombier 	if(p){
1115e96a66cSDavid du Colombier 		vtLock(p->lk);
1125e96a66cSDavid du Colombier 		assert(mode == OReadOnly || p->mode == OReadWrite);
1135e96a66cSDavid du Colombier 		p->ref++;
1145e96a66cSDavid du Colombier 		vtUnlock(p->lk);
1155e96a66cSDavid du Colombier 	}
1165e96a66cSDavid du Colombier 	r->epoch = epoch;
117*3b86f2f8SDavid du Colombier //	consPrint("sourceAlloc: have %V be.%d fse.%d %s\n", b->score,
118c168f9f3SDavid du Colombier //		b->l.epoch, r->fs->ehi, mode == OReadWrite? "rw": "ro");
1195e96a66cSDavid du Colombier 	memmove(r->score, b->score, VtScoreSize);
1205e96a66cSDavid du Colombier 	r->scoreEpoch = b->l.epoch;
1215e96a66cSDavid du Colombier 	r->offset = offset;
1225e96a66cSDavid du Colombier 	r->epb = epb;
1235e96a66cSDavid du Colombier 	r->tag = b->l.tag;
1245e96a66cSDavid du Colombier 
125*3b86f2f8SDavid du Colombier //	consPrint("%s: sourceAlloc: %p -> %V %d\n", r, r->score, r->offset);
1265e96a66cSDavid du Colombier 
1275e96a66cSDavid du Colombier 	return r;
1285e96a66cSDavid du Colombier Bad:
129*3b86f2f8SDavid du Colombier 	free(pname);
1305e96a66cSDavid du Colombier 	vtSetError(EBadEntry);
1315e96a66cSDavid du Colombier 	return nil;
1325e96a66cSDavid du Colombier }
1335e96a66cSDavid du Colombier 
1345e96a66cSDavid du Colombier Source *
1355e96a66cSDavid du Colombier sourceRoot(Fs *fs, u32int addr, int mode)
1365e96a66cSDavid du Colombier {
1375e96a66cSDavid du Colombier 	Source *r;
1385e96a66cSDavid du Colombier 	Block *b;
1395e96a66cSDavid du Colombier 
1405e96a66cSDavid du Colombier 	b = cacheLocalData(fs->cache, addr, BtDir, RootTag, mode, 0);
1415e96a66cSDavid du Colombier 	if(b == nil)
1425e96a66cSDavid du Colombier 		return nil;
1435e96a66cSDavid du Colombier 
144e569ccb5SDavid du Colombier 	if(mode == OReadWrite && b->l.epoch != fs->ehi){
145*3b86f2f8SDavid du Colombier 		consPrint("sourceRoot: fs->ehi = %ud, b->l = %L\n",
146*3b86f2f8SDavid du Colombier 			fs->ehi, &b->l);
1475e96a66cSDavid du Colombier 		blockPut(b);
1485e96a66cSDavid du Colombier 		vtSetError(EBadRoot);
1495e96a66cSDavid du Colombier 		return nil;
1505e96a66cSDavid du Colombier 	}
1515e96a66cSDavid du Colombier 
15257195852SDavid du Colombier 	r = sourceAlloc(fs, b, nil, 0, mode, 0);
1535e96a66cSDavid du Colombier 	blockPut(b);
1545e96a66cSDavid du Colombier 	return r;
1555e96a66cSDavid du Colombier }
1565e96a66cSDavid du Colombier 
1575e96a66cSDavid du Colombier Source *
15857195852SDavid du Colombier sourceOpen(Source *r, ulong offset, int mode, int issnapshot)
1595e96a66cSDavid du Colombier {
1605e96a66cSDavid du Colombier 	ulong bn;
1615e96a66cSDavid du Colombier 	Block *b;
1625e96a66cSDavid du Colombier 
1635e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
1645e96a66cSDavid du Colombier 	if(r->mode == OReadWrite)
1655e96a66cSDavid du Colombier 		assert(r->epoch == r->b->l.epoch);
1665e96a66cSDavid du Colombier 	if(!r->dir){
1675e96a66cSDavid du Colombier 		vtSetError(ENotDir);
1685e96a66cSDavid du Colombier 		return nil;
1695e96a66cSDavid du Colombier 	}
1705e96a66cSDavid du Colombier 
1715e96a66cSDavid du Colombier 	bn = offset/(r->dsize/VtEntrySize);
1725e96a66cSDavid du Colombier 
1735e96a66cSDavid du Colombier 	b = sourceBlock(r, bn, mode);
1745e96a66cSDavid du Colombier 	if(b == nil)
1755e96a66cSDavid du Colombier 		return nil;
17657195852SDavid du Colombier 	r = sourceAlloc(r->fs, b, r, offset, mode, issnapshot);
1775e96a66cSDavid du Colombier 	blockPut(b);
1785e96a66cSDavid du Colombier 	return r;
1795e96a66cSDavid du Colombier }
1805e96a66cSDavid du Colombier 
1815e96a66cSDavid du Colombier Source *
1825e96a66cSDavid du Colombier sourceCreate(Source *r, int dsize, int dir, u32int offset)
1835e96a66cSDavid du Colombier {
184*3b86f2f8SDavid du Colombier 	int i, epb, psize;
1855e96a66cSDavid du Colombier 	u32int bn, size;
186*3b86f2f8SDavid du Colombier 	Block *b;
1875e96a66cSDavid du Colombier 	Entry e;
1885e96a66cSDavid du Colombier 	Source *rr;
1895e96a66cSDavid du Colombier 
1905e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
1915e96a66cSDavid du Colombier 
1925e96a66cSDavid du Colombier 	if(!r->dir){
1935e96a66cSDavid du Colombier 		vtSetError(ENotDir);
1945e96a66cSDavid du Colombier 		return nil;
1955e96a66cSDavid du Colombier 	}
1965e96a66cSDavid du Colombier 
1975e96a66cSDavid du Colombier 	epb = r->dsize/VtEntrySize;
1985e96a66cSDavid du Colombier 	psize = (dsize/VtScoreSize)*VtScoreSize;
1995e96a66cSDavid du Colombier 
2005e96a66cSDavid du Colombier 	size = sourceGetDirSize(r);
2015e96a66cSDavid du Colombier 	if(offset == 0){
2025e96a66cSDavid du Colombier 		/*
2035e96a66cSDavid du Colombier 		 * look at a random block to see if we can find an empty entry
2045e96a66cSDavid du Colombier 		 */
2055e96a66cSDavid du Colombier 		offset = lnrand(size+1);
2065e96a66cSDavid du Colombier 		offset -= offset % epb;
2075e96a66cSDavid du Colombier 	}
2085e96a66cSDavid du Colombier 
2095e96a66cSDavid du Colombier 	/* try the given block and then try the last block */
2105e96a66cSDavid du Colombier 	for(;;){
2115e96a66cSDavid du Colombier 		bn = offset/epb;
2125e96a66cSDavid du Colombier 		b = sourceBlock(r, bn, OReadWrite);
2135e96a66cSDavid du Colombier 		if(b == nil)
2145e96a66cSDavid du Colombier 			return nil;
2155e96a66cSDavid du Colombier 		for(i=offset%r->epb; i<epb; i++){
2165e96a66cSDavid du Colombier 			entryUnpack(&e, b->data, i);
2175e96a66cSDavid du Colombier 			if((e.flags&VtEntryActive) == 0 && e.gen != ~0)
2185e96a66cSDavid du Colombier 				goto Found;
2195e96a66cSDavid du Colombier 		}
2205e96a66cSDavid du Colombier 		blockPut(b);
2215e96a66cSDavid du Colombier 		if(offset == size){
2225e96a66cSDavid du Colombier 			fprint(2, "sourceCreate: cannot happen\n");
2235e96a66cSDavid du Colombier 			vtSetError("sourceCreate: cannot happen");
2245e96a66cSDavid du Colombier 			return nil;
2255e96a66cSDavid du Colombier 		}
2265e96a66cSDavid du Colombier 		offset = size;
2275e96a66cSDavid du Colombier 	}
2285e96a66cSDavid du Colombier 
2295e96a66cSDavid du Colombier Found:
2305e96a66cSDavid du Colombier 	/* found an entry - gen already set */
2315e96a66cSDavid du Colombier 	e.psize = psize;
2325e96a66cSDavid du Colombier 	e.dsize = dsize;
233fe853e23SDavid du Colombier 	assert(psize && dsize);
2345e96a66cSDavid du Colombier 	e.flags = VtEntryActive;
2355e96a66cSDavid du Colombier 	if(dir)
2365e96a66cSDavid du Colombier 		e.flags |= VtEntryDir;
2375e96a66cSDavid du Colombier 	e.depth = 0;
2385e96a66cSDavid du Colombier 	e.size = 0;
2395e96a66cSDavid du Colombier 	memmove(e.score, vtZeroScore, VtScoreSize);
2405e96a66cSDavid du Colombier 	e.tag = 0;
2415e96a66cSDavid du Colombier 	e.snap = 0;
2425e96a66cSDavid du Colombier 	e.archive = 0;
2435e96a66cSDavid du Colombier 	entryPack(&e, b->data, i);
2445e96a66cSDavid du Colombier 	blockDirty(b);
2455e96a66cSDavid du Colombier 
2465e96a66cSDavid du Colombier 	offset = bn*epb + i;
2475e96a66cSDavid du Colombier 	if(offset+1 > size){
2485e96a66cSDavid du Colombier 		if(!sourceSetDirSize(r, offset+1)){
2495e96a66cSDavid du Colombier 			blockPut(b);
2505e96a66cSDavid du Colombier 			return nil;
2515e96a66cSDavid du Colombier 		}
2525e96a66cSDavid du Colombier 	}
2535e96a66cSDavid du Colombier 
25457195852SDavid du Colombier 	rr = sourceAlloc(r->fs, b, r, offset, OReadWrite, 0);
2555e96a66cSDavid du Colombier 	blockPut(b);
2565e96a66cSDavid du Colombier 	return rr;
2575e96a66cSDavid du Colombier }
2585e96a66cSDavid du Colombier 
2595e96a66cSDavid du Colombier static int
2605e96a66cSDavid du Colombier sourceKill(Source *r, int doremove)
2615e96a66cSDavid du Colombier {
2625e96a66cSDavid du Colombier 	Entry e;
2635e96a66cSDavid du Colombier 	Block *b;
2645e96a66cSDavid du Colombier 	u32int addr;
2655e96a66cSDavid du Colombier 	u32int tag;
2665e96a66cSDavid du Colombier 	int type;
2675e96a66cSDavid du Colombier 
2685e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
2695e96a66cSDavid du Colombier 	b = sourceLoad(r, &e);
2705e96a66cSDavid du Colombier 	if(b == nil)
2715e96a66cSDavid du Colombier 		return 0;
2725e96a66cSDavid du Colombier 
2735e96a66cSDavid du Colombier 	assert(b->l.epoch == r->fs->ehi);
2745e96a66cSDavid du Colombier 
2755e96a66cSDavid du Colombier 	if(doremove==0 && e.size == 0){
2765e96a66cSDavid du Colombier 		/* already truncated */
2775e96a66cSDavid du Colombier 		blockPut(b);
2785e96a66cSDavid du Colombier 		return 1;
2795e96a66cSDavid du Colombier 	}
2805e96a66cSDavid du Colombier 
2815e96a66cSDavid du Colombier 	/* remember info on link we are removing */
2825e96a66cSDavid du Colombier 	addr = globalToLocal(e.score);
2835e96a66cSDavid du Colombier 	type = entryType(&e);
2845e96a66cSDavid du Colombier 	tag = e.tag;
2855e96a66cSDavid du Colombier 
2865e96a66cSDavid du Colombier 	if(doremove){
2875e96a66cSDavid du Colombier 		if(e.gen != ~0)
2885e96a66cSDavid du Colombier 			e.gen++;
2895e96a66cSDavid du Colombier 		e.dsize = 0;
2905e96a66cSDavid du Colombier 		e.psize = 0;
2915e96a66cSDavid du Colombier 		e.flags = 0;
2925e96a66cSDavid du Colombier 	}else{
2935e96a66cSDavid du Colombier 		e.flags &= ~VtEntryLocal;
2945e96a66cSDavid du Colombier 	}
2955e96a66cSDavid du Colombier 	e.depth = 0;
2965e96a66cSDavid du Colombier 	e.size = 0;
2975e96a66cSDavid du Colombier 	e.tag = 0;
2985e96a66cSDavid du Colombier 	memmove(e.score, vtZeroScore, VtScoreSize);
2995e96a66cSDavid du Colombier 	entryPack(&e, b->data, r->offset % r->epb);
3005e96a66cSDavid du Colombier 	blockDirty(b);
3015e96a66cSDavid du Colombier 	if(addr != NilBlock)
302e569ccb5SDavid du Colombier 		blockRemoveLink(b, addr, type, tag, 1);
3035e96a66cSDavid du Colombier 	blockPut(b);
3045e96a66cSDavid du Colombier 
3055e96a66cSDavid du Colombier 	if(doremove){
3065e96a66cSDavid du Colombier 		sourceUnlock(r);
3075e96a66cSDavid du Colombier 		sourceClose(r);
3085e96a66cSDavid du Colombier 	}
3095e96a66cSDavid du Colombier 
3105e96a66cSDavid du Colombier 	return 1;
3115e96a66cSDavid du Colombier }
3125e96a66cSDavid du Colombier 
3135e96a66cSDavid du Colombier int
3145e96a66cSDavid du Colombier sourceRemove(Source *r)
3155e96a66cSDavid du Colombier {
3165e96a66cSDavid du Colombier 	return sourceKill(r, 1);
3175e96a66cSDavid du Colombier }
3185e96a66cSDavid du Colombier 
3195e96a66cSDavid du Colombier int
3205e96a66cSDavid du Colombier sourceTruncate(Source *r)
3215e96a66cSDavid du Colombier {
3225e96a66cSDavid du Colombier 	return sourceKill(r, 0);
3235e96a66cSDavid du Colombier }
3245e96a66cSDavid du Colombier 
3255e96a66cSDavid du Colombier uvlong
3265e96a66cSDavid du Colombier sourceGetSize(Source *r)
3275e96a66cSDavid du Colombier {
3285e96a66cSDavid du Colombier 	Entry e;
3295e96a66cSDavid du Colombier 	Block *b;
3305e96a66cSDavid du Colombier 
3315e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
3325e96a66cSDavid du Colombier 	b = sourceLoad(r, &e);
3335e96a66cSDavid du Colombier 	if(b == nil)
3345e96a66cSDavid du Colombier 		return 0;
3355e96a66cSDavid du Colombier 	blockPut(b);
3365e96a66cSDavid du Colombier 
3375e96a66cSDavid du Colombier 	return e.size;
3385e96a66cSDavid du Colombier }
3395e96a66cSDavid du Colombier 
3405e96a66cSDavid du Colombier static int
3415e96a66cSDavid du Colombier sourceShrinkSize(Source *r, Entry *e, uvlong size)
3425e96a66cSDavid du Colombier {
3435e96a66cSDavid du Colombier 	int i, type, ppb;
3445e96a66cSDavid du Colombier 	uvlong ptrsz;
3455e96a66cSDavid du Colombier 	u32int addr;
3465e96a66cSDavid du Colombier 	uchar score[VtScoreSize];
3475e96a66cSDavid du Colombier 	Block *b;
3485e96a66cSDavid du Colombier 
3495e96a66cSDavid du Colombier 	type = entryType(e);
3505e96a66cSDavid du Colombier 	b = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite);
3515e96a66cSDavid du Colombier 	if(b == nil)
3525e96a66cSDavid du Colombier 		return 0;
3535e96a66cSDavid du Colombier 
3545e96a66cSDavid du Colombier 	ptrsz = e->dsize;
3555e96a66cSDavid du Colombier 	ppb = e->psize/VtScoreSize;
3565e96a66cSDavid du Colombier 	for(i=0; i+1<e->depth; i++)
3575e96a66cSDavid du Colombier 		ptrsz *= ppb;
3585e96a66cSDavid du Colombier 
3595e96a66cSDavid du Colombier 	while(type&BtLevelMask){
360e569ccb5SDavid du Colombier 		if(b->addr == NilBlock || b->l.epoch != r->fs->ehi){
3615e96a66cSDavid du Colombier 			/* not worth copying the block just so we can zero some of it */
3625e96a66cSDavid du Colombier 			blockPut(b);
3635e96a66cSDavid du Colombier 			return 0;
3645e96a66cSDavid du Colombier 		}
3655e96a66cSDavid du Colombier 
3665e96a66cSDavid du Colombier 		/*
3675e96a66cSDavid du Colombier 		 * invariant: each pointer in the tree rooted at b accounts for ptrsz bytes
3685e96a66cSDavid du Colombier 		 */
3695e96a66cSDavid du Colombier 
3705e96a66cSDavid du Colombier 		/* zero the pointers to unnecessary blocks */
3715e96a66cSDavid du Colombier 		i = (size+ptrsz-1)/ptrsz;
3725e96a66cSDavid du Colombier 		for(; i<ppb; i++){
3735e96a66cSDavid du Colombier 			addr = globalToLocal(b->data+i*VtScoreSize);
3745e96a66cSDavid du Colombier 			memmove(b->data+i*VtScoreSize, vtZeroScore, VtScoreSize);
3755e96a66cSDavid du Colombier 			blockDirty(b);
3765e96a66cSDavid du Colombier 			if(addr != NilBlock)
377e569ccb5SDavid du Colombier 				blockRemoveLink(b, addr, type-1, e->tag, 1);
3785e96a66cSDavid du Colombier 		}
3795e96a66cSDavid du Colombier 
3805e96a66cSDavid du Colombier 		/* recurse (go around again) on the partially necessary block */
3815e96a66cSDavid du Colombier 		i = size/ptrsz;
3825e96a66cSDavid du Colombier 		size = size%ptrsz;
3835e96a66cSDavid du Colombier 		if(size == 0){
3845e96a66cSDavid du Colombier 			blockPut(b);
3855e96a66cSDavid du Colombier 			return 1;
3865e96a66cSDavid du Colombier 		}
3875e96a66cSDavid du Colombier 		ptrsz /= ppb;
3885e96a66cSDavid du Colombier 		type--;
3895e96a66cSDavid du Colombier 		memmove(score, b->data+i*VtScoreSize, VtScoreSize);
3905e96a66cSDavid du Colombier 		blockPut(b);
3915e96a66cSDavid du Colombier 		b = cacheGlobal(r->fs->cache, score, type, e->tag, OReadWrite);
3925e96a66cSDavid du Colombier 		if(b == nil)
3935e96a66cSDavid du Colombier 			return 0;
3945e96a66cSDavid du Colombier 	}
3955e96a66cSDavid du Colombier 
396e569ccb5SDavid du Colombier 	if(b->addr == NilBlock || b->l.epoch != r->fs->ehi){
3975e96a66cSDavid du Colombier 		blockPut(b);
3985e96a66cSDavid du Colombier 		return 0;
3995e96a66cSDavid du Colombier 	}
4005e96a66cSDavid du Colombier 
4015e96a66cSDavid du Colombier 	/*
4025e96a66cSDavid du Colombier 	 * No one ever truncates BtDir blocks.
4035e96a66cSDavid du Colombier 	 */
4045e96a66cSDavid du Colombier 	if(type == BtData && e->dsize > size){
4055e96a66cSDavid du Colombier 		memset(b->data+size, 0, e->dsize-size);
4065e96a66cSDavid du Colombier 		blockDirty(b);
4075e96a66cSDavid du Colombier 	}
4085e96a66cSDavid du Colombier 	blockPut(b);
4095e96a66cSDavid du Colombier 	return 1;
4105e96a66cSDavid du Colombier }
4115e96a66cSDavid du Colombier 
4125e96a66cSDavid du Colombier int
4135e96a66cSDavid du Colombier sourceSetSize(Source *r, uvlong size)
4145e96a66cSDavid du Colombier {
4155e96a66cSDavid du Colombier 	int depth;
4165e96a66cSDavid du Colombier 	Entry e;
4175e96a66cSDavid du Colombier 	Block *b;
4185e96a66cSDavid du Colombier 
4195e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
4205e96a66cSDavid du Colombier 	if(size == 0)
4215e96a66cSDavid du Colombier 		return sourceTruncate(r);
4225e96a66cSDavid du Colombier 
4235e96a66cSDavid du Colombier 	if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){
4245e96a66cSDavid du Colombier 		vtSetError(ETooBig);
4255e96a66cSDavid du Colombier 		return 0;
4265e96a66cSDavid du Colombier 	}
4275e96a66cSDavid du Colombier 
4285e96a66cSDavid du Colombier 	b = sourceLoad(r, &e);
4295e96a66cSDavid du Colombier 	if(b == nil)
4305e96a66cSDavid du Colombier 		return 0;
4315e96a66cSDavid du Colombier 
4325e96a66cSDavid du Colombier 	/* quick out */
4335e96a66cSDavid du Colombier 	if(e.size == size){
4345e96a66cSDavid du Colombier 		blockPut(b);
4355e96a66cSDavid du Colombier 		return 1;
4365e96a66cSDavid du Colombier 	}
4375e96a66cSDavid du Colombier 
4385e96a66cSDavid du Colombier 	depth = sizeToDepth(size, e.psize, e.dsize);
4395e96a66cSDavid du Colombier 
4405e96a66cSDavid du Colombier 	if(depth < e.depth){
4415e96a66cSDavid du Colombier 		if(!sourceShrinkDepth(r, b, &e, depth)){
4425e96a66cSDavid du Colombier 			blockPut(b);
4435e96a66cSDavid du Colombier 			return 0;
4445e96a66cSDavid du Colombier 		}
4455e96a66cSDavid du Colombier 	}else if(depth > e.depth){
4465e96a66cSDavid du Colombier 		if(!sourceGrowDepth(r, b, &e, depth)){
4475e96a66cSDavid du Colombier 			blockPut(b);
4485e96a66cSDavid du Colombier 			return 0;
4495e96a66cSDavid du Colombier 		}
4505e96a66cSDavid du Colombier 	}
4515e96a66cSDavid du Colombier 
4525e96a66cSDavid du Colombier 	if(size < e.size)
4535e96a66cSDavid du Colombier 		sourceShrinkSize(r, &e, size);
4545e96a66cSDavid du Colombier 
4555e96a66cSDavid du Colombier 	e.size = size;
4565e96a66cSDavid du Colombier 	entryPack(&e, b->data, r->offset % r->epb);
4575e96a66cSDavid du Colombier 	blockDirty(b);
4585e96a66cSDavid du Colombier 	blockPut(b);
4595e96a66cSDavid du Colombier 
4605e96a66cSDavid du Colombier 	return 1;
4615e96a66cSDavid du Colombier }
4625e96a66cSDavid du Colombier 
4635e96a66cSDavid du Colombier int
4645e96a66cSDavid du Colombier sourceSetDirSize(Source *r, ulong ds)
4655e96a66cSDavid du Colombier {
4665e96a66cSDavid du Colombier 	uvlong size;
4675e96a66cSDavid du Colombier 	int epb;
4685e96a66cSDavid du Colombier 
4695e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
4705e96a66cSDavid du Colombier 	epb = r->dsize/VtEntrySize;
4715e96a66cSDavid du Colombier 
4725e96a66cSDavid du Colombier 	size = (uvlong)r->dsize*(ds/epb);
4735e96a66cSDavid du Colombier 	size += VtEntrySize*(ds%epb);
4745e96a66cSDavid du Colombier 	return sourceSetSize(r, size);
4755e96a66cSDavid du Colombier }
4765e96a66cSDavid du Colombier 
4775e96a66cSDavid du Colombier ulong
4785e96a66cSDavid du Colombier sourceGetDirSize(Source *r)
4795e96a66cSDavid du Colombier {
4805e96a66cSDavid du Colombier 	ulong ds;
4815e96a66cSDavid du Colombier 	uvlong size;
4825e96a66cSDavid du Colombier 	int epb;
4835e96a66cSDavid du Colombier 
4845e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
4855e96a66cSDavid du Colombier 	epb = r->dsize/VtEntrySize;
4865e96a66cSDavid du Colombier 
4875e96a66cSDavid du Colombier 	size = sourceGetSize(r);
4885e96a66cSDavid du Colombier 	ds = epb*(size/r->dsize);
4895e96a66cSDavid du Colombier 	ds += (size%r->dsize)/VtEntrySize;
4905e96a66cSDavid du Colombier 	return ds;
4915e96a66cSDavid du Colombier }
4925e96a66cSDavid du Colombier 
4935e96a66cSDavid du Colombier int
4945e96a66cSDavid du Colombier sourceGetEntry(Source *r, Entry *e)
4955e96a66cSDavid du Colombier {
4965e96a66cSDavid du Colombier 	Block *b;
4975e96a66cSDavid du Colombier 
4985e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
4995e96a66cSDavid du Colombier 	b = sourceLoad(r, e);
5005e96a66cSDavid du Colombier 	if(b == nil)
5015e96a66cSDavid du Colombier 		return 0;
5025e96a66cSDavid du Colombier 	blockPut(b);
5035e96a66cSDavid du Colombier 
5045e96a66cSDavid du Colombier 	return 1;
5055e96a66cSDavid du Colombier }
5065e96a66cSDavid du Colombier 
507fe853e23SDavid du Colombier /*
508fe853e23SDavid du Colombier  * Must be careful with this.  Doesn't record
509fe853e23SDavid du Colombier  * dependencies, so don't introduce any!
510fe853e23SDavid du Colombier  */
511fe853e23SDavid du Colombier int
512fe853e23SDavid du Colombier sourceSetEntry(Source *r, Entry *e)
513fe853e23SDavid du Colombier {
514fe853e23SDavid du Colombier 	Block *b;
515fe853e23SDavid du Colombier 	Entry oe;
516fe853e23SDavid du Colombier 
517fe853e23SDavid du Colombier 	assert(sourceIsLocked(r));
518fe853e23SDavid du Colombier 	b = sourceLoad(r, &oe);
519fe853e23SDavid du Colombier 	if(b == nil)
520fe853e23SDavid du Colombier 		return 0;
521fe853e23SDavid du Colombier 	entryPack(e, b->data, r->offset%r->epb);
522fe853e23SDavid du Colombier 	blockDirty(b);
523fe853e23SDavid du Colombier 	blockPut(b);
524fe853e23SDavid du Colombier 
525fe853e23SDavid du Colombier 	return 1;
526fe853e23SDavid du Colombier }
527fe853e23SDavid du Colombier 
5285e96a66cSDavid du Colombier static Block *
5295e96a66cSDavid du Colombier blockWalk(Block *p, int index, int mode, Fs *fs, Entry *e)
5305e96a66cSDavid du Colombier {
5315e96a66cSDavid du Colombier 	Block *b;
5325e96a66cSDavid du Colombier 	Cache *c;
5335e96a66cSDavid du Colombier 	u32int addr;
5345e96a66cSDavid du Colombier 	int type;
535fe853e23SDavid du Colombier 	uchar oscore[VtScoreSize], score[VtScoreSize];
53661201b97SDavid du Colombier 	Entry oe;
5375e96a66cSDavid du Colombier 
5385e96a66cSDavid du Colombier 	c = fs->cache;
5395e96a66cSDavid du Colombier 
5405e96a66cSDavid du Colombier 	if((p->l.type & BtLevelMask) == 0){
5415e96a66cSDavid du Colombier 		assert(p->l.type == BtDir);
5425e96a66cSDavid du Colombier 		type = entryType(e);
5435e96a66cSDavid du Colombier 		b = cacheGlobal(c, e->score, type, e->tag, mode);
5445e96a66cSDavid du Colombier 	}else{
5455e96a66cSDavid du Colombier 		type = p->l.type - 1;
5465e96a66cSDavid du Colombier 		b = cacheGlobal(c, p->data + index*VtScoreSize, type, e->tag, mode);
5475e96a66cSDavid du Colombier 	}
5485e96a66cSDavid du Colombier 
549fe853e23SDavid du Colombier 	if(b)
550fe853e23SDavid du Colombier 		b->pc = getcallerpc(&p);
551fe853e23SDavid du Colombier 
5525e96a66cSDavid du Colombier 	if(b == nil || mode == OReadOnly)
5535e96a66cSDavid du Colombier 		return b;
5545e96a66cSDavid du Colombier 
555e569ccb5SDavid du Colombier 	if(p->l.epoch != fs->ehi){
556e569ccb5SDavid du Colombier 		fprint(2, "blockWalk: parent not writable\n");
557e569ccb5SDavid du Colombier 		abort();
558e569ccb5SDavid du Colombier 	}
559e569ccb5SDavid du Colombier 	if(b->l.epoch == fs->ehi)
5605e96a66cSDavid du Colombier 		return b;
5615e96a66cSDavid du Colombier 
56261201b97SDavid du Colombier 	oe = *e;
56361201b97SDavid du Colombier 
5645e96a66cSDavid du Colombier 	/*
5655e96a66cSDavid du Colombier 	 * Copy on write.
5665e96a66cSDavid du Colombier 	 */
5675e96a66cSDavid du Colombier 	if(e->tag == 0){
5685e96a66cSDavid du Colombier 		assert(p->l.type == BtDir);
5695e96a66cSDavid du Colombier 		e->tag = tagGen();
5705e96a66cSDavid du Colombier 		e->flags |= VtEntryLocal;
5715e96a66cSDavid du Colombier 	}
5725e96a66cSDavid du Colombier 
5735e96a66cSDavid du Colombier 	addr = b->addr;
5745e96a66cSDavid du Colombier 	b = blockCopy(b, e->tag, fs->ehi, fs->elo);
5755e96a66cSDavid du Colombier 	if(b == nil)
5765e96a66cSDavid du Colombier 		return nil;
5775e96a66cSDavid du Colombier 
578fe853e23SDavid du Colombier 	b->pc = getcallerpc(&p);
5795e96a66cSDavid du Colombier 	assert(b->l.epoch == fs->ehi);
5805e96a66cSDavid du Colombier 
58161201b97SDavid du Colombier 	blockDirty(b);
582fe853e23SDavid du Colombier 	memmove(score, b->score, VtScoreSize);
5835e96a66cSDavid du Colombier 	if(p->l.type == BtDir){
5845e96a66cSDavid du Colombier 		memmove(e->score, b->score, VtScoreSize);
5855e96a66cSDavid du Colombier 		entryPack(e, p->data, index);
58661201b97SDavid du Colombier 		blockDependency(p, b, index, nil, &oe);
5875e96a66cSDavid du Colombier 	}else{
58861201b97SDavid du Colombier 		memmove(oscore, p->data+index*VtScoreSize, VtScoreSize);
5895e96a66cSDavid du Colombier 		memmove(p->data+index*VtScoreSize, b->score, VtScoreSize);
59061201b97SDavid du Colombier 		blockDependency(p, b, index, oscore, nil);
5915e96a66cSDavid du Colombier 	}
5925e96a66cSDavid du Colombier 	blockDirty(p);
5935e96a66cSDavid du Colombier 
5945e96a66cSDavid du Colombier 	if(addr != NilBlock)
595e569ccb5SDavid du Colombier 		blockRemoveLink(p, addr, type, e->tag, 0);
5965e96a66cSDavid du Colombier 
5975e96a66cSDavid du Colombier 	return b;
5985e96a66cSDavid du Colombier }
5995e96a66cSDavid du Colombier 
6005e96a66cSDavid du Colombier /*
6015e96a66cSDavid du Colombier  * Change the depth of the source r.
6025e96a66cSDavid du Colombier  * The entry e for r is contained in block p.
6035e96a66cSDavid du Colombier  */
6045e96a66cSDavid du Colombier static int
6055e96a66cSDavid du Colombier sourceGrowDepth(Source *r, Block *p, Entry *e, int depth)
6065e96a66cSDavid du Colombier {
6075e96a66cSDavid du Colombier 	Block *b, *bb;
6085e96a66cSDavid du Colombier 	u32int tag;
6095e96a66cSDavid du Colombier 	int type;
61061201b97SDavid du Colombier 	Entry oe;
6115e96a66cSDavid du Colombier 
6125e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
6135e96a66cSDavid du Colombier 	assert(depth <= VtPointerDepth);
6145e96a66cSDavid du Colombier 
6155e96a66cSDavid du Colombier 	type = entryType(e);
6165e96a66cSDavid du Colombier 	b = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite);
6175e96a66cSDavid du Colombier 	if(b == nil)
6185e96a66cSDavid du Colombier 		return 0;
6195e96a66cSDavid du Colombier 
6205e96a66cSDavid du Colombier 	tag = e->tag;
6215e96a66cSDavid du Colombier 	if(tag == 0)
6225e96a66cSDavid du Colombier 		tag = tagGen();
6235e96a66cSDavid du Colombier 
62461201b97SDavid du Colombier 	oe = *e;
62561201b97SDavid du Colombier 
6265e96a66cSDavid du Colombier 	/*
6275e96a66cSDavid du Colombier 	 * Keep adding layers until we get to the right depth
6285e96a66cSDavid du Colombier 	 * or an error occurs.
6295e96a66cSDavid du Colombier 	 */
6305e96a66cSDavid du Colombier 	while(e->depth < depth){
6315e96a66cSDavid du Colombier 		bb = cacheAllocBlock(r->fs->cache, type+1, tag, r->fs->ehi, r->fs->elo);
6325e96a66cSDavid du Colombier 		if(bb == nil)
6335e96a66cSDavid du Colombier 			break;
634dc5a79c1SDavid du Colombier //fprint(2, "alloc %lux grow %V\n", bb->addr, b->score);
6355e96a66cSDavid du Colombier 		memmove(bb->data, b->score, VtScoreSize);
6365e96a66cSDavid du Colombier 		memmove(e->score, bb->score, VtScoreSize);
6375e96a66cSDavid du Colombier 		e->depth++;
6385e96a66cSDavid du Colombier 		type++;
6395e96a66cSDavid du Colombier 		e->tag = tag;
6405e96a66cSDavid du Colombier 		e->flags |= VtEntryLocal;
64161201b97SDavid du Colombier 		blockDependency(bb, b, 0, vtZeroScore, nil);
6425e96a66cSDavid du Colombier 		blockPut(b);
6435e96a66cSDavid du Colombier 		b = bb;
644fe853e23SDavid du Colombier 		blockDirty(b);
6455e96a66cSDavid du Colombier 	}
6465e96a66cSDavid du Colombier 
6475e96a66cSDavid du Colombier 	entryPack(e, p->data, r->offset % r->epb);
64861201b97SDavid du Colombier 	blockDependency(p, b, r->offset % r->epb, nil, &oe);
6495e96a66cSDavid du Colombier 	blockPut(b);
6505e96a66cSDavid du Colombier 	blockDirty(p);
6515e96a66cSDavid du Colombier 
6525e96a66cSDavid du Colombier 	return e->depth == depth;
6535e96a66cSDavid du Colombier }
6545e96a66cSDavid du Colombier 
6555e96a66cSDavid du Colombier static int
6565e96a66cSDavid du Colombier sourceShrinkDepth(Source *r, Block *p, Entry *e, int depth)
6575e96a66cSDavid du Colombier {
6585e96a66cSDavid du Colombier 	Block *b, *nb, *ob, *rb;
6595e96a66cSDavid du Colombier 	u32int tag;
6605e96a66cSDavid du Colombier 	int type, d;
66161201b97SDavid du Colombier 	Entry oe;
6625e96a66cSDavid du Colombier 
6635e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
6645e96a66cSDavid du Colombier 	assert(depth <= VtPointerDepth);
6655e96a66cSDavid du Colombier 
6665e96a66cSDavid du Colombier 	type = entryType(e);
6675e96a66cSDavid du Colombier 	rb = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite);
6685e96a66cSDavid du Colombier 	if(rb == nil)
6695e96a66cSDavid du Colombier 		return 0;
6705e96a66cSDavid du Colombier 
6715e96a66cSDavid du Colombier 	tag = e->tag;
6725e96a66cSDavid du Colombier 	if(tag == 0)
6735e96a66cSDavid du Colombier 		tag = tagGen();
6745e96a66cSDavid du Colombier 
6755e96a66cSDavid du Colombier 	/*
6765e96a66cSDavid du Colombier 	 * Walk down to the new root block.
6775e96a66cSDavid du Colombier 	 * We may stop early, but something is better than nothing.
6785e96a66cSDavid du Colombier 	 */
67961201b97SDavid du Colombier 	oe = *e;
68061201b97SDavid du Colombier 
6815e96a66cSDavid du Colombier 	ob = nil;
6825e96a66cSDavid du Colombier 	b = rb;
683b5e190f4SDavid du Colombier /* BUG: explain type++.  i think it is a real bug */
6845e96a66cSDavid du Colombier 	for(d=e->depth; d > depth; d--, type++){
6855e96a66cSDavid du Colombier 		nb = cacheGlobal(r->fs->cache, b->data, type-1, tag, OReadWrite);
6865e96a66cSDavid du Colombier 		if(nb == nil)
6875e96a66cSDavid du Colombier 			break;
6885e96a66cSDavid du Colombier 		if(ob!=nil && ob!=rb)
6895e96a66cSDavid du Colombier 			blockPut(ob);
6905e96a66cSDavid du Colombier 		ob = b;
6915e96a66cSDavid du Colombier 		b = nb;
6925e96a66cSDavid du Colombier 	}
6935e96a66cSDavid du Colombier 
6945e96a66cSDavid du Colombier 	if(b == rb){
6955e96a66cSDavid du Colombier 		blockPut(rb);
6965e96a66cSDavid du Colombier 		return 0;
6975e96a66cSDavid du Colombier 	}
6985e96a66cSDavid du Colombier 
6995e96a66cSDavid du Colombier 	/*
7005e96a66cSDavid du Colombier 	 * Right now, e points at the root block rb, b is the new root block,
7015e96a66cSDavid du Colombier 	 * and ob points at b.  To update:
7025e96a66cSDavid du Colombier 	 *
7035e96a66cSDavid du Colombier 	 *	(i) change e to point at b
7045e96a66cSDavid du Colombier 	 *	(ii) zero the pointer ob -> b
7055e96a66cSDavid du Colombier 	 *	(iii) free the root block
7065e96a66cSDavid du Colombier 	 *
7075e96a66cSDavid du Colombier 	 * p (the block containing e) must be written before
7085e96a66cSDavid du Colombier 	 * anything else.
7095e96a66cSDavid du Colombier  	 */
7105e96a66cSDavid du Colombier 
7115e96a66cSDavid du Colombier 	/* (i) */
7125e96a66cSDavid du Colombier 	e->depth = d;
7132906cf0cSDavid du Colombier 	/* might have been local and now global; reverse cannot happen */
7142906cf0cSDavid du Colombier 	if(globalToLocal(b->score) == NilBlock)
7152906cf0cSDavid du Colombier 		e->flags &= ~VtEntryLocal;
7165e96a66cSDavid du Colombier 	memmove(e->score, b->score, VtScoreSize);
7175e96a66cSDavid du Colombier 	entryPack(e, p->data, r->offset % r->epb);
71861201b97SDavid du Colombier 	blockDependency(p, b, r->offset % r->epb, nil, &oe);
7195e96a66cSDavid du Colombier 	blockDirty(p);
7205e96a66cSDavid du Colombier 
7215e96a66cSDavid du Colombier 	/* (ii) */
7225e96a66cSDavid du Colombier 	memmove(ob->data, vtZeroScore, VtScoreSize);
72361201b97SDavid du Colombier 	blockDependency(ob, p, 0, b->score, nil);
7245e96a66cSDavid du Colombier 	blockDirty(ob);
7255e96a66cSDavid du Colombier 
7265e96a66cSDavid du Colombier 	/* (iii) */
7275e96a66cSDavid du Colombier 	if(rb->addr != NilBlock)
728e569ccb5SDavid du Colombier 		blockRemoveLink(p, rb->addr, rb->l.type, rb->l.tag, 1);
7295e96a66cSDavid du Colombier 
7305e96a66cSDavid du Colombier 	blockPut(rb);
7315e96a66cSDavid du Colombier 	if(ob!=nil && ob!=rb)
7325e96a66cSDavid du Colombier 		blockPut(ob);
7335e96a66cSDavid du Colombier 	blockPut(b);
7345e96a66cSDavid du Colombier 
7355e96a66cSDavid du Colombier 	return d == depth;
7365e96a66cSDavid du Colombier }
7375e96a66cSDavid du Colombier 
738fe853e23SDavid du Colombier /*
739fe853e23SDavid du Colombier  * Normally we return the block at the given number.
740fe853e23SDavid du Colombier  * If early is set, we stop earlier in the tree.  Setting early
741fe853e23SDavid du Colombier  * to 1 gives us the block that contains the pointer to bn.
742fe853e23SDavid du Colombier  */
7435e96a66cSDavid du Colombier Block *
744fe853e23SDavid du Colombier _sourceBlock(Source *r, ulong bn, int mode, int early, ulong tag)
7455e96a66cSDavid du Colombier {
7465e96a66cSDavid du Colombier 	Block *b, *bb;
7475e96a66cSDavid du Colombier 	int index[VtPointerDepth+1];
7485e96a66cSDavid du Colombier 	Entry e;
7495e96a66cSDavid du Colombier 	int i, np;
7505e96a66cSDavid du Colombier 	int m;
7515e96a66cSDavid du Colombier 
7525e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
7535e96a66cSDavid du Colombier 	assert(bn != NilBlock);
7545e96a66cSDavid du Colombier 
7555e96a66cSDavid du Colombier 	/* mode for intermediate block */
7565e96a66cSDavid du Colombier 	m = mode;
7575e96a66cSDavid du Colombier 	if(m == OOverWrite)
7585e96a66cSDavid du Colombier 		m = OReadWrite;
7595e96a66cSDavid du Colombier 
7605e96a66cSDavid du Colombier 	b = sourceLoad(r, &e);
7615e96a66cSDavid du Colombier 	if(b == nil)
7625e96a66cSDavid du Colombier 		return nil;
76357195852SDavid du Colombier 	if(r->issnapshot && (e.flags & VtEntryNoArchive)){
764fe853e23SDavid du Colombier 		blockPut(b);
765fe853e23SDavid du Colombier 		vtSetError(ENotArchived);
766fe853e23SDavid du Colombier 		return nil;
767fe853e23SDavid du Colombier 	}
768fe853e23SDavid du Colombier 
769fe853e23SDavid du Colombier 	if(tag){
770fe853e23SDavid du Colombier 		if(e.tag == 0)
771fe853e23SDavid du Colombier 			e.tag = tag;
772fe853e23SDavid du Colombier 		else if(e.tag != tag){
773fe853e23SDavid du Colombier 			fprint(2, "tag mismatch\n");
774fe853e23SDavid du Colombier 			vtSetError("tag mismatch");
775fe853e23SDavid du Colombier 			goto Err;
776fe853e23SDavid du Colombier 		}
777fe853e23SDavid du Colombier 	}
7785e96a66cSDavid du Colombier 
7795e96a66cSDavid du Colombier 	np = e.psize/VtScoreSize;
7805e96a66cSDavid du Colombier 	memset(index, 0, sizeof(index));
7815e96a66cSDavid du Colombier 	for(i=0; bn > 0; i++){
7825e96a66cSDavid du Colombier 		if(i >= VtPointerDepth){
7835e96a66cSDavid du Colombier 			vtSetError(EBadAddr);
7845e96a66cSDavid du Colombier 			goto Err;
7855e96a66cSDavid du Colombier 		}
7865e96a66cSDavid du Colombier 		index[i] = bn % np;
7875e96a66cSDavid du Colombier 		bn /= np;
7885e96a66cSDavid du Colombier 	}
7895e96a66cSDavid du Colombier 
7905e96a66cSDavid du Colombier 	if(i > e.depth){
7915e96a66cSDavid du Colombier 		if(mode == OReadOnly){
7925e96a66cSDavid du Colombier 			vtSetError(EBadAddr);
7935e96a66cSDavid du Colombier 			goto Err;
7945e96a66cSDavid du Colombier 		}
7955e96a66cSDavid du Colombier 		if(!sourceGrowDepth(r, b, &e, i))
7965e96a66cSDavid du Colombier 			goto Err;
7975e96a66cSDavid du Colombier 	}
7985e96a66cSDavid du Colombier 
7995e96a66cSDavid du Colombier 	index[e.depth] = r->offset % r->epb;
8005e96a66cSDavid du Colombier 
801fe853e23SDavid du Colombier 	for(i=e.depth; i>=early; i--){
8025e96a66cSDavid du Colombier 		bb = blockWalk(b, index[i], m, r->fs, &e);
8035e96a66cSDavid du Colombier 		if(bb == nil)
8045e96a66cSDavid du Colombier 			goto Err;
8055e96a66cSDavid du Colombier 		blockPut(b);
8065e96a66cSDavid du Colombier 		b = bb;
8075e96a66cSDavid du Colombier 	}
808fe853e23SDavid du Colombier 	b->pc = getcallerpc(&r);
8095e96a66cSDavid du Colombier 	return b;
8105e96a66cSDavid du Colombier Err:
8115e96a66cSDavid du Colombier 	blockPut(b);
8125e96a66cSDavid du Colombier 	return nil;
8135e96a66cSDavid du Colombier }
8145e96a66cSDavid du Colombier 
815fe853e23SDavid du Colombier Block*
816fe853e23SDavid du Colombier sourceBlock(Source *r, ulong bn, int mode)
817fe853e23SDavid du Colombier {
818fe853e23SDavid du Colombier 	Block *b;
819fe853e23SDavid du Colombier 
820fe853e23SDavid du Colombier 	b = _sourceBlock(r, bn, mode, 0, 0);
821fe853e23SDavid du Colombier 	if(b)
822fe853e23SDavid du Colombier 		b->pc = getcallerpc(&r);
823fe853e23SDavid du Colombier 	return b;
824fe853e23SDavid du Colombier }
825fe853e23SDavid du Colombier 
8265e96a66cSDavid du Colombier void
8275e96a66cSDavid du Colombier sourceClose(Source *r)
8285e96a66cSDavid du Colombier {
8295e96a66cSDavid du Colombier 	if(r == nil)
8305e96a66cSDavid du Colombier 		return;
8315e96a66cSDavid du Colombier 	vtLock(r->lk);
8325e96a66cSDavid du Colombier 	r->ref--;
8335e96a66cSDavid du Colombier 	if(r->ref){
8345e96a66cSDavid du Colombier 		vtUnlock(r->lk);
8355e96a66cSDavid du Colombier 		return;
8365e96a66cSDavid du Colombier 	}
8375e96a66cSDavid du Colombier 	assert(r->ref == 0);
8385e96a66cSDavid du Colombier 	vtUnlock(r->lk);
8395e96a66cSDavid du Colombier 	if(r->parent)
8405e96a66cSDavid du Colombier 		sourceClose(r->parent);
8415e96a66cSDavid du Colombier 	vtLockFree(r->lk);
8425e96a66cSDavid du Colombier 	memset(r, ~0, sizeof(*r));
8435e96a66cSDavid du Colombier 	vtMemFree(r);
8445e96a66cSDavid du Colombier }
8455e96a66cSDavid du Colombier 
8465e96a66cSDavid du Colombier /*
8475e96a66cSDavid du Colombier  * Retrieve the block containing the entry for r.
8485e96a66cSDavid du Colombier  * If a snapshot has happened, we might need
8495e96a66cSDavid du Colombier  * to get a new copy of the block.  We avoid this
8505e96a66cSDavid du Colombier  * in the common case by caching the score for
8515e96a66cSDavid du Colombier  * the block and the last epoch in which it was valid.
8525e96a66cSDavid du Colombier  *
8535e96a66cSDavid du Colombier  * We use r->mode to tell the difference between active
8545e96a66cSDavid du Colombier  * file system sources (OReadWrite) and sources for the
8555e96a66cSDavid du Colombier  * snapshot file system (OReadOnly).
8565e96a66cSDavid du Colombier  */
8575e96a66cSDavid du Colombier static Block*
8585e96a66cSDavid du Colombier sourceLoadBlock(Source *r, int mode)
8595e96a66cSDavid du Colombier {
8605e96a66cSDavid du Colombier 	u32int addr;
8615e96a66cSDavid du Colombier 	Block *b;
8625e96a66cSDavid du Colombier 
8635e96a66cSDavid du Colombier 	switch(r->mode){
8645e96a66cSDavid du Colombier 	default:
8655e96a66cSDavid du Colombier 		assert(0);
8665e96a66cSDavid du Colombier 	case OReadWrite:
8675e96a66cSDavid du Colombier 		assert(r->mode == OReadWrite);
86834e04225SDavid du Colombier 		/*
86934e04225SDavid du Colombier 		 * This needn't be true -- we might bump the low epoch
87034e04225SDavid du Colombier 		 * to reclaim some old blocks, but since this score is
87134e04225SDavid du Colombier 		 * OReadWrite, the blocks must all still be open, so none
87234e04225SDavid du Colombier 		 * are reclaimed.  Thus it's okay that the epoch is so low.
87334e04225SDavid du Colombier 		 * Proceed.
8745e96a66cSDavid du Colombier 		assert(r->epoch >= r->fs->elo);
87534e04225SDavid du Colombier 		 */
8765e96a66cSDavid du Colombier 		if(r->epoch == r->fs->ehi){
8775e96a66cSDavid du Colombier 			b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
8785e96a66cSDavid du Colombier 			if(b == nil)
8795e96a66cSDavid du Colombier 				return nil;
8807611b47fSDavid du Colombier 			assert(r->epoch == b->l.epoch);
8815e96a66cSDavid du Colombier 			return b;
8825e96a66cSDavid du Colombier 		}
8835e96a66cSDavid du Colombier 		assert(r->parent != nil);
8845e96a66cSDavid du Colombier 		if(!sourceLock(r->parent, OReadWrite))
8855e96a66cSDavid du Colombier 			return nil;
8865e96a66cSDavid du Colombier 		b = sourceBlock(r->parent, r->offset/r->epb, OReadWrite);
8875e96a66cSDavid du Colombier 		sourceUnlock(r->parent);
8885e96a66cSDavid du Colombier 		if(b == nil)
8895e96a66cSDavid du Colombier 			return nil;
8905e96a66cSDavid du Colombier 		assert(b->l.epoch == r->fs->ehi);
891e569ccb5SDavid du Colombier 	//	fprint(2, "sourceLoadBlock %p %V => %V\n", r, r->score, b->score);
8925e96a66cSDavid du Colombier 		memmove(r->score, b->score, VtScoreSize);
8935e96a66cSDavid du Colombier 		r->scoreEpoch = b->l.epoch;
8945e96a66cSDavid du Colombier 		r->tag = b->l.tag;
8955e96a66cSDavid du Colombier 		r->epoch = r->fs->ehi;
8965e96a66cSDavid du Colombier 		return b;
8975e96a66cSDavid du Colombier 
8985e96a66cSDavid du Colombier 	case OReadOnly:
8995e96a66cSDavid du Colombier 		addr = globalToLocal(r->score);
9005e96a66cSDavid du Colombier 		if(addr == NilBlock)
9015e96a66cSDavid du Colombier 			return cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, mode);
9025e96a66cSDavid du Colombier 
9035e96a66cSDavid du Colombier 		b = cacheLocalData(r->fs->cache, addr, BtDir, r->tag, mode, r->scoreEpoch);
9045e96a66cSDavid du Colombier 		if(b)
9055e96a66cSDavid du Colombier 			return b;
9065e96a66cSDavid du Colombier 
9075e96a66cSDavid du Colombier 		/*
9085e96a66cSDavid du Colombier 		 * If it failed because the epochs don't match, the block has been
9095e96a66cSDavid du Colombier 		 * archived and reclaimed.  Rewalk from the parent and get the
9105e96a66cSDavid du Colombier 		 * new pointer.  This can't happen in the OReadWrite case
9115e96a66cSDavid du Colombier 		 * above because blocks in the current epoch don't get
9125e96a66cSDavid du Colombier 		 * reclaimed.  The fact that we're OReadOnly means we're
9135e96a66cSDavid du Colombier 		 * a snapshot.  (Or else the file system is read-only, but then
9145e96a66cSDavid du Colombier 		 * the archiver isn't going around deleting blocks.)
9155e96a66cSDavid du Colombier 		 */
9165e96a66cSDavid du Colombier 		if(strcmp(vtGetError(), ELabelMismatch) == 0){
9175e96a66cSDavid du Colombier 			if(!sourceLock(r->parent, OReadOnly))
9185e96a66cSDavid du Colombier 				return nil;
9195e96a66cSDavid du Colombier 			b = sourceBlock(r->parent, r->offset/r->epb, OReadOnly);
9205e96a66cSDavid du Colombier 			sourceUnlock(r->parent);
9215e96a66cSDavid du Colombier 			if(b){
9225e96a66cSDavid du Colombier 				fprint(2, "sourceAlloc: lost %V found %V\n",
9235e96a66cSDavid du Colombier 					r->score, b->score);
9245e96a66cSDavid du Colombier 				memmove(r->score, b->score, VtScoreSize);
9255e96a66cSDavid du Colombier 				r->scoreEpoch = b->l.epoch;
9265e96a66cSDavid du Colombier 				return b;
9275e96a66cSDavid du Colombier 			}
9285e96a66cSDavid du Colombier 		}
9295e96a66cSDavid du Colombier 		return nil;
9305e96a66cSDavid du Colombier 	}
9315e96a66cSDavid du Colombier }
9325e96a66cSDavid du Colombier 
9335e96a66cSDavid du Colombier int
9345e96a66cSDavid du Colombier sourceLock(Source *r, int mode)
9355e96a66cSDavid du Colombier {
9365e96a66cSDavid du Colombier 	Block *b;
9375e96a66cSDavid du Colombier 
9385e96a66cSDavid du Colombier 	if(mode == -1)
9395e96a66cSDavid du Colombier 		mode = r->mode;
9405e96a66cSDavid du Colombier 
9415e96a66cSDavid du Colombier 	b = sourceLoadBlock(r, mode);
9425e96a66cSDavid du Colombier 	if(b == nil)
9435e96a66cSDavid du Colombier 		return 0;
9445e96a66cSDavid du Colombier 	/*
9455e96a66cSDavid du Colombier 	 * The fact that we are holding b serves as the
9465e96a66cSDavid du Colombier 	 * lock entitling us to write to r->b.
9475e96a66cSDavid du Colombier 	 */
9485e96a66cSDavid du Colombier 	assert(r->b == nil);
9495e96a66cSDavid du Colombier 	r->b = b;
9505e96a66cSDavid du Colombier 	if(r->mode == OReadWrite)
9515e96a66cSDavid du Colombier 		assert(r->epoch == r->b->l.epoch);
9525e96a66cSDavid du Colombier 	return 1;
9535e96a66cSDavid du Colombier }
9545e96a66cSDavid du Colombier 
9555e96a66cSDavid du Colombier /*
9565e96a66cSDavid du Colombier  * Lock two (usually sibling) sources.  This needs special care
9575e96a66cSDavid du Colombier  * because the Entries for both sources might be in the same block.
9585e96a66cSDavid du Colombier  * We also try to lock blocks in left-to-right order within the tree.
9595e96a66cSDavid du Colombier  */
9605e96a66cSDavid du Colombier int
9615e96a66cSDavid du Colombier sourceLock2(Source *r, Source *rr, int mode)
9625e96a66cSDavid du Colombier {
9635e96a66cSDavid du Colombier 	Block *b, *bb;
9645e96a66cSDavid du Colombier 
9655e96a66cSDavid du Colombier 	if(rr == nil)
9665e96a66cSDavid du Colombier 		return sourceLock(r, mode);
9675e96a66cSDavid du Colombier 
9685e96a66cSDavid du Colombier 	if(mode == -1)
9695e96a66cSDavid du Colombier 		mode = r->mode;
9705e96a66cSDavid du Colombier 
9715e96a66cSDavid du Colombier 	if(r->parent==rr->parent && r->offset/r->epb == rr->offset/rr->epb){
9725e96a66cSDavid du Colombier 		b = sourceLoadBlock(r, mode);
9735e96a66cSDavid du Colombier 		if(b == nil)
9745e96a66cSDavid du Colombier 			return 0;
975e569ccb5SDavid du Colombier 		if(memcmp(r->score, rr->score, VtScoreSize) != 0){
976e569ccb5SDavid du Colombier 			memmove(rr->score, b->score, VtScoreSize);
977e569ccb5SDavid du Colombier 			rr->scoreEpoch = b->l.epoch;
978e569ccb5SDavid du Colombier 			rr->tag = b->l.tag;
979e569ccb5SDavid du Colombier 			rr->epoch = rr->fs->ehi;
980e569ccb5SDavid du Colombier 		}
9815e96a66cSDavid du Colombier 		blockDupLock(b);
9825e96a66cSDavid du Colombier 		bb = b;
9835e96a66cSDavid du Colombier 	}else if(r->parent==rr->parent || r->offset > rr->offset){
9845e96a66cSDavid du Colombier 		bb = sourceLoadBlock(rr, mode);
9855e96a66cSDavid du Colombier 		b = sourceLoadBlock(r, mode);
9865e96a66cSDavid du Colombier 	}else{
9875e96a66cSDavid du Colombier 		b = sourceLoadBlock(r, mode);
9885e96a66cSDavid du Colombier 		bb = sourceLoadBlock(rr, mode);
9895e96a66cSDavid du Colombier 	}
9905e96a66cSDavid du Colombier 	if(b == nil || bb == nil){
9915e96a66cSDavid du Colombier 		if(b)
9925e96a66cSDavid du Colombier 			blockPut(b);
9935e96a66cSDavid du Colombier 		if(bb)
9945e96a66cSDavid du Colombier 			blockPut(bb);
9955e96a66cSDavid du Colombier 		return 0;
9965e96a66cSDavid du Colombier 	}
9975e96a66cSDavid du Colombier 
9985e96a66cSDavid du Colombier 	/*
9995e96a66cSDavid du Colombier 	 * The fact that we are holding b and bb serves
10005e96a66cSDavid du Colombier 	 * as the lock entitling us to write to r->b and rr->b.
10015e96a66cSDavid du Colombier 	 */
10025e96a66cSDavid du Colombier 	r->b = b;
10035e96a66cSDavid du Colombier 	rr->b = bb;
10045e96a66cSDavid du Colombier 	return 1;
10055e96a66cSDavid du Colombier }
10065e96a66cSDavid du Colombier 
10075e96a66cSDavid du Colombier void
10085e96a66cSDavid du Colombier sourceUnlock(Source *r)
10095e96a66cSDavid du Colombier {
10105e96a66cSDavid du Colombier 	Block *b;
10115e96a66cSDavid du Colombier 
10125e96a66cSDavid du Colombier 	if(r->b == nil){
10135e96a66cSDavid du Colombier 		fprint(2, "sourceUnlock: already unlocked\n");
10145e96a66cSDavid du Colombier 		abort();
10155e96a66cSDavid du Colombier 	}
10165e96a66cSDavid du Colombier 	b = r->b;
10175e96a66cSDavid du Colombier 	r->b = nil;
10185e96a66cSDavid du Colombier 	blockPut(b);
10195e96a66cSDavid du Colombier }
10205e96a66cSDavid du Colombier 
10215e96a66cSDavid du Colombier static Block*
10225e96a66cSDavid du Colombier sourceLoad(Source *r, Entry *e)
10235e96a66cSDavid du Colombier {
10245e96a66cSDavid du Colombier 	Block *b;
10255e96a66cSDavid du Colombier 
10265e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
10275e96a66cSDavid du Colombier 	b = r->b;
10285e96a66cSDavid du Colombier 	if(!entryUnpack(e, b->data, r->offset % r->epb))
10295e96a66cSDavid du Colombier 		return nil;
10305e96a66cSDavid du Colombier 	if(e->gen != r->gen){
10315e96a66cSDavid du Colombier 		vtSetError(ERemoved);
10325e96a66cSDavid du Colombier 		return nil;
10335e96a66cSDavid du Colombier 	}
10345e96a66cSDavid du Colombier 	blockDupLock(b);
10355e96a66cSDavid du Colombier 	return b;
10365e96a66cSDavid du Colombier }
10375e96a66cSDavid du Colombier 
10385e96a66cSDavid du Colombier static int
10395e96a66cSDavid du Colombier sizeToDepth(uvlong s, int psize, int dsize)
10405e96a66cSDavid du Colombier {
10415e96a66cSDavid du Colombier 	int np;
10425e96a66cSDavid du Colombier 	int d;
10435e96a66cSDavid du Colombier 
10445e96a66cSDavid du Colombier 	/* determine pointer depth */
10455e96a66cSDavid du Colombier 	np = psize/VtScoreSize;
10465e96a66cSDavid du Colombier 	s = (s + dsize - 1)/dsize;
10475e96a66cSDavid du Colombier 	for(d = 0; s > 1; d++)
10485e96a66cSDavid du Colombier 		s = (s + np - 1)/np;
10495e96a66cSDavid du Colombier 	return d;
10505e96a66cSDavid du Colombier }
10515e96a66cSDavid du Colombier 
10525e96a66cSDavid du Colombier static u32int
10535e96a66cSDavid du Colombier tagGen(void)
10545e96a66cSDavid du Colombier {
10555e96a66cSDavid du Colombier 	u32int tag;
10565e96a66cSDavid du Colombier 
10575e96a66cSDavid du Colombier 	for(;;){
10585e96a66cSDavid du Colombier 		tag = lrand();
10595e96a66cSDavid du Colombier 		if(tag >= UserTag)
10605e96a66cSDavid du Colombier 			break;
10615e96a66cSDavid du Colombier 	}
10625e96a66cSDavid du Colombier 	return tag;
10635e96a66cSDavid du Colombier }
1064*3b86f2f8SDavid du Colombier 
1065*3b86f2f8SDavid du Colombier char *
1066*3b86f2f8SDavid du Colombier sourceName(Source *s)
1067*3b86f2f8SDavid du Colombier {
1068*3b86f2f8SDavid du Colombier 	return fileName(s->file);
1069*3b86f2f8SDavid du Colombier }
1070