xref: /plan9/sys/src/cmd/fossil/source.c (revision 5e96a66c77eb9140492ca53f857cbbf108e128ed)
1*5e96a66cSDavid du Colombier #include "stdinc.h"
2*5e96a66cSDavid du Colombier #include "dat.h"
3*5e96a66cSDavid du Colombier #include "fns.h"
4*5e96a66cSDavid du Colombier #include "error.h"
5*5e96a66cSDavid du Colombier 
6*5e96a66cSDavid du Colombier static int	sizeToDepth(uvlong s, int psize, int dsize);
7*5e96a66cSDavid du Colombier static u32int 	tagGen(void);
8*5e96a66cSDavid du Colombier static Block 	*sourceLoad(Source *r, Entry *e);
9*5e96a66cSDavid du Colombier static Block	*sourceLoadUnlocked(Source *r, Entry *e);
10*5e96a66cSDavid du Colombier static int	sourceShrinkDepth(Source*, Block*, Entry*, int);
11*5e96a66cSDavid du Colombier static int	sourceShrinkSize(Source*, Entry*, uvlong);
12*5e96a66cSDavid du Colombier static int	sourceGrowDepth(Source*, Block*, Entry*, int);
13*5e96a66cSDavid du Colombier 
14*5e96a66cSDavid du Colombier #define sourceIsLocked(r)	((r)->b != nil)
15*5e96a66cSDavid du Colombier 
16*5e96a66cSDavid du Colombier static Source *
17*5e96a66cSDavid du Colombier sourceAlloc(Fs *fs, Block *b, Source *p, u32int offset, int mode)
18*5e96a66cSDavid du Colombier {
19*5e96a66cSDavid du Colombier 	Source *r;
20*5e96a66cSDavid du Colombier 	int epb;
21*5e96a66cSDavid du Colombier 	u32int epoch;
22*5e96a66cSDavid du Colombier 	Entry e;
23*5e96a66cSDavid du Colombier 
24*5e96a66cSDavid du Colombier 	assert(p==nil || sourceIsLocked(p));
25*5e96a66cSDavid du Colombier 
26*5e96a66cSDavid du Colombier 	if(p == nil){
27*5e96a66cSDavid du Colombier 		assert(offset == 0);
28*5e96a66cSDavid du Colombier 		epb = 1;
29*5e96a66cSDavid du Colombier 	}else
30*5e96a66cSDavid du Colombier 		epb = p->dsize / VtEntrySize;
31*5e96a66cSDavid du Colombier 
32*5e96a66cSDavid du Colombier 	if(b->l.type != BtDir)
33*5e96a66cSDavid du Colombier 		goto Bad;
34*5e96a66cSDavid du Colombier 
35*5e96a66cSDavid du Colombier 	/*
36*5e96a66cSDavid du Colombier 	 * a non-active entry is the only thing that
37*5e96a66cSDavid du Colombier 	 * can legitimately happen here. all the others
38*5e96a66cSDavid du Colombier 	 * get prints.
39*5e96a66cSDavid du Colombier 	 */
40*5e96a66cSDavid du Colombier 	if(!entryUnpack(&e, b->data, offset % epb)){
41*5e96a66cSDavid du Colombier 		fprint(2, "entryUnpack failed\n");
42*5e96a66cSDavid du Colombier 		goto Bad;
43*5e96a66cSDavid du Colombier 	}
44*5e96a66cSDavid du Colombier 	if(!(e.flags & VtEntryActive)){
45*5e96a66cSDavid du Colombier 		if(0)fprint(2, "not active\n");
46*5e96a66cSDavid du Colombier 		goto Bad;
47*5e96a66cSDavid du Colombier 	}
48*5e96a66cSDavid du Colombier 	if(e.psize < 256 || e.dsize < 256){
49*5e96a66cSDavid du Colombier 		fprint(2, "psize %ud dsize %ud\n", e.psize, e.dsize);
50*5e96a66cSDavid du Colombier 		goto Bad;
51*5e96a66cSDavid du Colombier 	}
52*5e96a66cSDavid du Colombier 
53*5e96a66cSDavid du Colombier 	if(e.depth < sizeToDepth(e.size, e.psize, e.dsize)){
54*5e96a66cSDavid du Colombier 		fprint(2, "depth %ud size %llud psize %ud dsize %ud\n", e.depth, e.size, e.psize, e.dsize);
55*5e96a66cSDavid du Colombier 		goto Bad;
56*5e96a66cSDavid du Colombier 	}
57*5e96a66cSDavid du Colombier 
58*5e96a66cSDavid du Colombier 	if((e.flags & VtEntryLocal) && e.tag == 0){
59*5e96a66cSDavid du Colombier 		fprint(2, "flags %#ux tag %#ux\n", e.flags, e.tag);
60*5e96a66cSDavid du Colombier 		goto Bad;
61*5e96a66cSDavid du Colombier 	}
62*5e96a66cSDavid du Colombier 
63*5e96a66cSDavid du Colombier 	if(e.dsize > fs->blockSize || e.psize > fs->blockSize){
64*5e96a66cSDavid du Colombier 		fprint(2, "psize %ud dsize %ud blocksize %ud\n", e.psize, e.dsize, fs->blockSize);
65*5e96a66cSDavid du Colombier 		goto Bad;
66*5e96a66cSDavid du Colombier 	}
67*5e96a66cSDavid du Colombier 
68*5e96a66cSDavid du Colombier 	epoch = b->l.epoch;
69*5e96a66cSDavid du Colombier 	if(mode == OReadWrite){
70*5e96a66cSDavid du Colombier 		if(e.snap != 0){
71*5e96a66cSDavid du Colombier 			vtSetError(ESnapRO);
72*5e96a66cSDavid du Colombier 			return nil;
73*5e96a66cSDavid du Colombier 		}
74*5e96a66cSDavid du Colombier 	}else if(e.snap != 0){
75*5e96a66cSDavid du Colombier 		if(e.snap < fs->elo){
76*5e96a66cSDavid du Colombier 			vtSetError(ESnapOld);
77*5e96a66cSDavid du Colombier 			return nil;
78*5e96a66cSDavid du Colombier 		}
79*5e96a66cSDavid du Colombier 		if(e.snap >= fs->ehi)
80*5e96a66cSDavid du Colombier 			goto Bad;
81*5e96a66cSDavid du Colombier 		epoch = e.snap;
82*5e96a66cSDavid du Colombier 	}
83*5e96a66cSDavid du Colombier 
84*5e96a66cSDavid du Colombier 	r = vtMemAllocZ(sizeof(Source));
85*5e96a66cSDavid du Colombier 	r->fs = fs;
86*5e96a66cSDavid du Colombier 	r->mode = mode;
87*5e96a66cSDavid du Colombier 	r->dsize = e.dsize;
88*5e96a66cSDavid du Colombier 	r->gen = e.gen;
89*5e96a66cSDavid du Colombier 	r->dir = (e.flags & VtEntryDir) != 0;
90*5e96a66cSDavid du Colombier 	r->lk = vtLockAlloc();
91*5e96a66cSDavid du Colombier 	r->ref = 1;
92*5e96a66cSDavid du Colombier 	r->parent = p;
93*5e96a66cSDavid du Colombier 	if(p){
94*5e96a66cSDavid du Colombier 		vtLock(p->lk);
95*5e96a66cSDavid du Colombier 		assert(mode == OReadOnly || p->mode == OReadWrite);
96*5e96a66cSDavid du Colombier 		p->ref++;
97*5e96a66cSDavid du Colombier 		vtUnlock(p->lk);
98*5e96a66cSDavid du Colombier 	}
99*5e96a66cSDavid du Colombier 	r->epoch = epoch;
100*5e96a66cSDavid du Colombier //fprint(2, "sourceAlloc have %V be.%d fse.%d %s\n", b->score, b->l.epoch, r->fs->ehi, mode==OReadWrite ? "rw" : "ro");
101*5e96a66cSDavid du Colombier 	memmove(r->score, b->score, VtScoreSize);
102*5e96a66cSDavid du Colombier 	r->scoreEpoch = b->l.epoch;
103*5e96a66cSDavid du Colombier 	r->offset = offset;
104*5e96a66cSDavid du Colombier 	r->epb = epb;
105*5e96a66cSDavid du Colombier 	r->tag = b->l.tag;
106*5e96a66cSDavid du Colombier 
107*5e96a66cSDavid du Colombier //fprint(2, "sourceAlloc: %p -> %V %d\n", r, r->score, r->offset);
108*5e96a66cSDavid du Colombier 
109*5e96a66cSDavid du Colombier 	return r;
110*5e96a66cSDavid du Colombier Bad:
111*5e96a66cSDavid du Colombier 	vtSetError(EBadEntry);
112*5e96a66cSDavid du Colombier 	return nil;
113*5e96a66cSDavid du Colombier 
114*5e96a66cSDavid du Colombier }
115*5e96a66cSDavid du Colombier 
116*5e96a66cSDavid du Colombier Source *
117*5e96a66cSDavid du Colombier sourceRoot(Fs *fs, u32int addr, int mode)
118*5e96a66cSDavid du Colombier {
119*5e96a66cSDavid du Colombier 	Source *r;
120*5e96a66cSDavid du Colombier 	Block *b;
121*5e96a66cSDavid du Colombier 
122*5e96a66cSDavid du Colombier 	b = cacheLocalData(fs->cache, addr, BtDir, RootTag, mode, 0);
123*5e96a66cSDavid du Colombier 	if(b == nil)
124*5e96a66cSDavid du Colombier 		return nil;
125*5e96a66cSDavid du Colombier 
126*5e96a66cSDavid du Colombier 	if(mode == OReadWrite)
127*5e96a66cSDavid du Colombier 	if((b->l.state&BsClosed) || b->l.epoch != fs->ehi){
128*5e96a66cSDavid du Colombier 		fprint(2, "sourceRoot: fs->ehi = %ud, b->l = %L\n", fs->ehi, &b->l);
129*5e96a66cSDavid du Colombier 		blockPut(b);
130*5e96a66cSDavid du Colombier 		vtSetError(EBadRoot);
131*5e96a66cSDavid du Colombier 		return nil;
132*5e96a66cSDavid du Colombier 	}
133*5e96a66cSDavid du Colombier 
134*5e96a66cSDavid du Colombier 	r = sourceAlloc(fs, b, nil, 0, mode);
135*5e96a66cSDavid du Colombier 	blockPut(b);
136*5e96a66cSDavid du Colombier 	return r;
137*5e96a66cSDavid du Colombier }
138*5e96a66cSDavid du Colombier 
139*5e96a66cSDavid du Colombier Source *
140*5e96a66cSDavid du Colombier sourceOpen(Source *r, ulong offset, int mode)
141*5e96a66cSDavid du Colombier {
142*5e96a66cSDavid du Colombier 	ulong bn;
143*5e96a66cSDavid du Colombier 	Block *b;
144*5e96a66cSDavid du Colombier 
145*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
146*5e96a66cSDavid du Colombier 	if(r->mode == OReadWrite)
147*5e96a66cSDavid du Colombier 		assert(r->epoch == r->b->l.epoch);
148*5e96a66cSDavid du Colombier 	if(!r->dir){
149*5e96a66cSDavid du Colombier 		vtSetError(ENotDir);
150*5e96a66cSDavid du Colombier 		return nil;
151*5e96a66cSDavid du Colombier 	}
152*5e96a66cSDavid du Colombier 
153*5e96a66cSDavid du Colombier 	bn = offset/(r->dsize/VtEntrySize);
154*5e96a66cSDavid du Colombier 
155*5e96a66cSDavid du Colombier 	b = sourceBlock(r, bn, mode);
156*5e96a66cSDavid du Colombier 	if(b == nil)
157*5e96a66cSDavid du Colombier 		return nil;
158*5e96a66cSDavid du Colombier 	r = sourceAlloc(r->fs, b, r, offset, mode);
159*5e96a66cSDavid du Colombier 	blockPut(b);
160*5e96a66cSDavid du Colombier 	return r;
161*5e96a66cSDavid du Colombier }
162*5e96a66cSDavid du Colombier 
163*5e96a66cSDavid du Colombier Source *
164*5e96a66cSDavid du Colombier sourceCreate(Source *r, int dsize, int dir, u32int offset)
165*5e96a66cSDavid du Colombier {
166*5e96a66cSDavid du Colombier 	int i;
167*5e96a66cSDavid du Colombier 	Block *b;
168*5e96a66cSDavid du Colombier 	u32int bn, size;
169*5e96a66cSDavid du Colombier 	Entry e;
170*5e96a66cSDavid du Colombier 	int epb;
171*5e96a66cSDavid du Colombier 	int psize;
172*5e96a66cSDavid du Colombier 	Source *rr;
173*5e96a66cSDavid du Colombier 
174*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
175*5e96a66cSDavid du Colombier 
176*5e96a66cSDavid du Colombier 	if(!r->dir){
177*5e96a66cSDavid du Colombier 		vtSetError(ENotDir);
178*5e96a66cSDavid du Colombier 		return nil;
179*5e96a66cSDavid du Colombier 	}
180*5e96a66cSDavid du Colombier 
181*5e96a66cSDavid du Colombier 	epb = r->dsize/VtEntrySize;
182*5e96a66cSDavid du Colombier 	psize = (dsize/VtScoreSize)*VtScoreSize;
183*5e96a66cSDavid du Colombier 
184*5e96a66cSDavid du Colombier 	size = sourceGetDirSize(r);
185*5e96a66cSDavid du Colombier 	if(offset == 0){
186*5e96a66cSDavid du Colombier 		/*
187*5e96a66cSDavid du Colombier 		 * look at a random block to see if we can find an empty entry
188*5e96a66cSDavid du Colombier 		 */
189*5e96a66cSDavid du Colombier 		offset = lnrand(size+1);
190*5e96a66cSDavid du Colombier 		offset -= offset % epb;
191*5e96a66cSDavid du Colombier 	}
192*5e96a66cSDavid du Colombier 
193*5e96a66cSDavid du Colombier 	/* try the given block and then try the last block */
194*5e96a66cSDavid du Colombier 	for(;;){
195*5e96a66cSDavid du Colombier 		bn = offset/epb;
196*5e96a66cSDavid du Colombier 		b = sourceBlock(r, bn, OReadWrite);
197*5e96a66cSDavid du Colombier 		if(b == nil)
198*5e96a66cSDavid du Colombier 			return nil;
199*5e96a66cSDavid du Colombier 		for(i=offset%r->epb; i<epb; i++){
200*5e96a66cSDavid du Colombier 			entryUnpack(&e, b->data, i);
201*5e96a66cSDavid du Colombier 			if((e.flags&VtEntryActive) == 0 && e.gen != ~0)
202*5e96a66cSDavid du Colombier 				goto Found;
203*5e96a66cSDavid du Colombier 		}
204*5e96a66cSDavid du Colombier 		blockPut(b);
205*5e96a66cSDavid du Colombier 		if(offset == size){
206*5e96a66cSDavid du Colombier 			fprint(2, "sourceCreate: cannot happen\n");
207*5e96a66cSDavid du Colombier 			vtSetError("sourceCreate: cannot happen");
208*5e96a66cSDavid du Colombier 			return nil;
209*5e96a66cSDavid du Colombier 		}
210*5e96a66cSDavid du Colombier 		offset = size;
211*5e96a66cSDavid du Colombier 	}
212*5e96a66cSDavid du Colombier 
213*5e96a66cSDavid du Colombier Found:
214*5e96a66cSDavid du Colombier 	/* found an entry - gen already set */
215*5e96a66cSDavid du Colombier 	e.psize = psize;
216*5e96a66cSDavid du Colombier 	e.dsize = dsize;
217*5e96a66cSDavid du Colombier 	e.flags = VtEntryActive;
218*5e96a66cSDavid du Colombier 	if(dir)
219*5e96a66cSDavid du Colombier 		e.flags |= VtEntryDir;
220*5e96a66cSDavid du Colombier 	e.depth = 0;
221*5e96a66cSDavid du Colombier 	e.size = 0;
222*5e96a66cSDavid du Colombier 	memmove(e.score, vtZeroScore, VtScoreSize);
223*5e96a66cSDavid du Colombier 	e.tag = 0;
224*5e96a66cSDavid du Colombier 	e.snap = 0;
225*5e96a66cSDavid du Colombier 	e.archive = 0;
226*5e96a66cSDavid du Colombier 	entryPack(&e, b->data, i);
227*5e96a66cSDavid du Colombier 	blockDirty(b);
228*5e96a66cSDavid du Colombier 
229*5e96a66cSDavid du Colombier 	offset = bn*epb + i;
230*5e96a66cSDavid du Colombier 	if(offset+1 > size){
231*5e96a66cSDavid du Colombier 		if(!sourceSetDirSize(r, offset+1)){
232*5e96a66cSDavid du Colombier 			blockPut(b);
233*5e96a66cSDavid du Colombier 			return nil;
234*5e96a66cSDavid du Colombier 		}
235*5e96a66cSDavid du Colombier 	}
236*5e96a66cSDavid du Colombier 
237*5e96a66cSDavid du Colombier 	rr = sourceAlloc(r->fs, b, r, offset, OReadWrite);
238*5e96a66cSDavid du Colombier 	blockPut(b);
239*5e96a66cSDavid du Colombier 	return rr;
240*5e96a66cSDavid du Colombier }
241*5e96a66cSDavid du Colombier 
242*5e96a66cSDavid du Colombier static int
243*5e96a66cSDavid du Colombier sourceKill(Source *r, int doremove)
244*5e96a66cSDavid du Colombier {
245*5e96a66cSDavid du Colombier 	Entry e;
246*5e96a66cSDavid du Colombier 	Block *b;
247*5e96a66cSDavid du Colombier 	u32int addr;
248*5e96a66cSDavid du Colombier 	u32int tag;
249*5e96a66cSDavid du Colombier 	int type;
250*5e96a66cSDavid du Colombier 
251*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
252*5e96a66cSDavid du Colombier 	b = sourceLoad(r, &e);
253*5e96a66cSDavid du Colombier 	if(b == nil)
254*5e96a66cSDavid du Colombier 		return 0;
255*5e96a66cSDavid du Colombier 
256*5e96a66cSDavid du Colombier 	assert(b->l.epoch == r->fs->ehi);
257*5e96a66cSDavid du Colombier 
258*5e96a66cSDavid du Colombier 	if(doremove==0 && e.size == 0){
259*5e96a66cSDavid du Colombier 		/* already truncated */
260*5e96a66cSDavid du Colombier 		blockPut(b);
261*5e96a66cSDavid du Colombier 		return 1;
262*5e96a66cSDavid du Colombier 	}
263*5e96a66cSDavid du Colombier 
264*5e96a66cSDavid du Colombier 	/* remember info on link we are removing */
265*5e96a66cSDavid du Colombier 	addr = globalToLocal(e.score);
266*5e96a66cSDavid du Colombier 	type = entryType(&e);
267*5e96a66cSDavid du Colombier 	tag = e.tag;
268*5e96a66cSDavid du Colombier 
269*5e96a66cSDavid du Colombier 	if(doremove){
270*5e96a66cSDavid du Colombier 		if(e.gen != ~0)
271*5e96a66cSDavid du Colombier 			e.gen++;
272*5e96a66cSDavid du Colombier 		e.dsize = 0;
273*5e96a66cSDavid du Colombier 		e.psize = 0;
274*5e96a66cSDavid du Colombier 		e.flags = 0;
275*5e96a66cSDavid du Colombier 	}else{
276*5e96a66cSDavid du Colombier 		e.flags &= ~VtEntryLocal;
277*5e96a66cSDavid du Colombier 	}
278*5e96a66cSDavid du Colombier 	e.depth = 0;
279*5e96a66cSDavid du Colombier 	e.size = 0;
280*5e96a66cSDavid du Colombier 	e.tag = 0;
281*5e96a66cSDavid du Colombier 	memmove(e.score, vtZeroScore, VtScoreSize);
282*5e96a66cSDavid du Colombier 	entryPack(&e, b->data, r->offset % r->epb);
283*5e96a66cSDavid du Colombier 	blockDirty(b);
284*5e96a66cSDavid du Colombier 	if(addr != NilBlock)
285*5e96a66cSDavid du Colombier 		blockRemoveLink(b, addr, type, tag);
286*5e96a66cSDavid du Colombier 	blockPut(b);
287*5e96a66cSDavid du Colombier 
288*5e96a66cSDavid du Colombier 	if(doremove){
289*5e96a66cSDavid du Colombier 		sourceUnlock(r);
290*5e96a66cSDavid du Colombier 		sourceClose(r);
291*5e96a66cSDavid du Colombier 	}
292*5e96a66cSDavid du Colombier 
293*5e96a66cSDavid du Colombier 	return 1;
294*5e96a66cSDavid du Colombier }
295*5e96a66cSDavid du Colombier 
296*5e96a66cSDavid du Colombier int
297*5e96a66cSDavid du Colombier sourceRemove(Source *r)
298*5e96a66cSDavid du Colombier {
299*5e96a66cSDavid du Colombier 	return sourceKill(r, 1);
300*5e96a66cSDavid du Colombier }
301*5e96a66cSDavid du Colombier 
302*5e96a66cSDavid du Colombier int
303*5e96a66cSDavid du Colombier sourceTruncate(Source *r)
304*5e96a66cSDavid du Colombier {
305*5e96a66cSDavid du Colombier 	return sourceKill(r, 0);
306*5e96a66cSDavid du Colombier }
307*5e96a66cSDavid du Colombier 
308*5e96a66cSDavid du Colombier uvlong
309*5e96a66cSDavid du Colombier sourceGetSize(Source *r)
310*5e96a66cSDavid du Colombier {
311*5e96a66cSDavid du Colombier 	Entry e;
312*5e96a66cSDavid du Colombier 	Block *b;
313*5e96a66cSDavid du Colombier 
314*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
315*5e96a66cSDavid du Colombier 	b = sourceLoad(r, &e);
316*5e96a66cSDavid du Colombier 	if(b == nil)
317*5e96a66cSDavid du Colombier 		return 0;
318*5e96a66cSDavid du Colombier 	blockPut(b);
319*5e96a66cSDavid du Colombier 
320*5e96a66cSDavid du Colombier 	return e.size;
321*5e96a66cSDavid du Colombier }
322*5e96a66cSDavid du Colombier 
323*5e96a66cSDavid du Colombier static int
324*5e96a66cSDavid du Colombier sourceShrinkSize(Source *r, Entry *e, uvlong size)
325*5e96a66cSDavid du Colombier {
326*5e96a66cSDavid du Colombier 	int i, type, ppb;
327*5e96a66cSDavid du Colombier 	uvlong ptrsz;
328*5e96a66cSDavid du Colombier 	u32int addr;
329*5e96a66cSDavid du Colombier 	uchar score[VtScoreSize];
330*5e96a66cSDavid du Colombier 	Block *b;
331*5e96a66cSDavid du Colombier 
332*5e96a66cSDavid du Colombier 	type = entryType(e);
333*5e96a66cSDavid du Colombier 	b = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite);
334*5e96a66cSDavid du Colombier 	if(b == nil)
335*5e96a66cSDavid du Colombier 		return 0;
336*5e96a66cSDavid du Colombier 
337*5e96a66cSDavid du Colombier 	ptrsz = e->dsize;
338*5e96a66cSDavid du Colombier 	ppb = e->psize/VtScoreSize;
339*5e96a66cSDavid du Colombier 	for(i=0; i+1<e->depth; i++)
340*5e96a66cSDavid du Colombier 		ptrsz *= ppb;
341*5e96a66cSDavid du Colombier 
342*5e96a66cSDavid du Colombier 	while(type&BtLevelMask){
343*5e96a66cSDavid du Colombier 		if(b->addr == NilBlock
344*5e96a66cSDavid du Colombier 		|| (b->l.state&BsClosed)
345*5e96a66cSDavid du Colombier 		|| b->l.epoch != r->fs->ehi){
346*5e96a66cSDavid du Colombier 			/* not worth copying the block just so we can zero some of it */
347*5e96a66cSDavid du Colombier 			blockPut(b);
348*5e96a66cSDavid du Colombier 			return 0;
349*5e96a66cSDavid du Colombier 		}
350*5e96a66cSDavid du Colombier 
351*5e96a66cSDavid du Colombier 		/*
352*5e96a66cSDavid du Colombier 		 * invariant: each pointer in the tree rooted at b accounts for ptrsz bytes
353*5e96a66cSDavid du Colombier 		 */
354*5e96a66cSDavid du Colombier 
355*5e96a66cSDavid du Colombier 		/* zero the pointers to unnecessary blocks */
356*5e96a66cSDavid du Colombier 		i = (size+ptrsz-1)/ptrsz;
357*5e96a66cSDavid du Colombier 		for(; i<ppb; i++){
358*5e96a66cSDavid du Colombier 			addr = globalToLocal(b->data+i*VtScoreSize);
359*5e96a66cSDavid du Colombier 			memmove(b->data+i*VtScoreSize, vtZeroScore, VtScoreSize);
360*5e96a66cSDavid du Colombier 			blockDirty(b);
361*5e96a66cSDavid du Colombier 			if(addr != NilBlock)
362*5e96a66cSDavid du Colombier 				blockRemoveLink(b, addr, type-1, e->tag);
363*5e96a66cSDavid du Colombier 		}
364*5e96a66cSDavid du Colombier 
365*5e96a66cSDavid du Colombier 		/* recurse (go around again) on the partially necessary block */
366*5e96a66cSDavid du Colombier 		i = size/ptrsz;
367*5e96a66cSDavid du Colombier 		size = size%ptrsz;
368*5e96a66cSDavid du Colombier 		if(size == 0){
369*5e96a66cSDavid du Colombier 			blockPut(b);
370*5e96a66cSDavid du Colombier 			return 1;
371*5e96a66cSDavid du Colombier 		}
372*5e96a66cSDavid du Colombier 		ptrsz /= ppb;
373*5e96a66cSDavid du Colombier 		type--;
374*5e96a66cSDavid du Colombier 		memmove(score, b->data+i*VtScoreSize, VtScoreSize);
375*5e96a66cSDavid du Colombier 		blockPut(b);
376*5e96a66cSDavid du Colombier 		b = cacheGlobal(r->fs->cache, score, type, e->tag, OReadWrite);
377*5e96a66cSDavid du Colombier 		if(b == nil)
378*5e96a66cSDavid du Colombier 			return 0;
379*5e96a66cSDavid du Colombier 	}
380*5e96a66cSDavid du Colombier 
381*5e96a66cSDavid du Colombier 	if(b->addr == NilBlock
382*5e96a66cSDavid du Colombier 	|| (b->l.state&BsClosed)
383*5e96a66cSDavid du Colombier 	|| b->l.epoch != r->fs->ehi){
384*5e96a66cSDavid du Colombier 		blockPut(b);
385*5e96a66cSDavid du Colombier 		return 0;
386*5e96a66cSDavid du Colombier 	}
387*5e96a66cSDavid du Colombier 
388*5e96a66cSDavid du Colombier 	/*
389*5e96a66cSDavid du Colombier 	 * No one ever truncates BtDir blocks.
390*5e96a66cSDavid du Colombier 	 */
391*5e96a66cSDavid du Colombier 	if(type == BtData && e->dsize > size){
392*5e96a66cSDavid du Colombier 		memset(b->data+size, 0, e->dsize-size);
393*5e96a66cSDavid du Colombier 		blockDirty(b);
394*5e96a66cSDavid du Colombier 	}
395*5e96a66cSDavid du Colombier 	blockPut(b);
396*5e96a66cSDavid du Colombier 	return 1;
397*5e96a66cSDavid du Colombier }
398*5e96a66cSDavid du Colombier 
399*5e96a66cSDavid du Colombier int
400*5e96a66cSDavid du Colombier sourceSetSize(Source *r, uvlong size)
401*5e96a66cSDavid du Colombier {
402*5e96a66cSDavid du Colombier 	int depth;
403*5e96a66cSDavid du Colombier 	Entry e;
404*5e96a66cSDavid du Colombier 	Block *b;
405*5e96a66cSDavid du Colombier 
406*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
407*5e96a66cSDavid du Colombier 	if(size == 0)
408*5e96a66cSDavid du Colombier 		return sourceTruncate(r);
409*5e96a66cSDavid du Colombier 
410*5e96a66cSDavid du Colombier 	if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){
411*5e96a66cSDavid du Colombier 		vtSetError(ETooBig);
412*5e96a66cSDavid du Colombier 		return 0;
413*5e96a66cSDavid du Colombier 	}
414*5e96a66cSDavid du Colombier 
415*5e96a66cSDavid du Colombier 	b = sourceLoad(r, &e);
416*5e96a66cSDavid du Colombier 	if(b == nil)
417*5e96a66cSDavid du Colombier 		return 0;
418*5e96a66cSDavid du Colombier 
419*5e96a66cSDavid du Colombier 	/* quick out */
420*5e96a66cSDavid du Colombier 	if(e.size == size){
421*5e96a66cSDavid du Colombier 		blockPut(b);
422*5e96a66cSDavid du Colombier 		return 1;
423*5e96a66cSDavid du Colombier 	}
424*5e96a66cSDavid du Colombier 
425*5e96a66cSDavid du Colombier 	depth = sizeToDepth(size, e.psize, e.dsize);
426*5e96a66cSDavid du Colombier 
427*5e96a66cSDavid du Colombier 	if(depth < e.depth){
428*5e96a66cSDavid du Colombier 		if(!sourceShrinkDepth(r, b, &e, depth)){
429*5e96a66cSDavid du Colombier 			blockPut(b);
430*5e96a66cSDavid du Colombier 			return 0;
431*5e96a66cSDavid du Colombier 		}
432*5e96a66cSDavid du Colombier 	}else if(depth > e.depth){
433*5e96a66cSDavid du Colombier 		if(!sourceGrowDepth(r, b, &e, depth)){
434*5e96a66cSDavid du Colombier 			blockPut(b);
435*5e96a66cSDavid du Colombier 			return 0;
436*5e96a66cSDavid du Colombier 		}
437*5e96a66cSDavid du Colombier 	}
438*5e96a66cSDavid du Colombier 
439*5e96a66cSDavid du Colombier 	if(size < e.size)
440*5e96a66cSDavid du Colombier 		sourceShrinkSize(r, &e, size);
441*5e96a66cSDavid du Colombier 
442*5e96a66cSDavid du Colombier 	e.size = size;
443*5e96a66cSDavid du Colombier 	entryPack(&e, b->data, r->offset % r->epb);
444*5e96a66cSDavid du Colombier 	blockDirty(b);
445*5e96a66cSDavid du Colombier 	blockPut(b);
446*5e96a66cSDavid du Colombier 
447*5e96a66cSDavid du Colombier 	return 1;
448*5e96a66cSDavid du Colombier }
449*5e96a66cSDavid du Colombier 
450*5e96a66cSDavid du Colombier int
451*5e96a66cSDavid du Colombier sourceSetDirSize(Source *r, ulong ds)
452*5e96a66cSDavid du Colombier {
453*5e96a66cSDavid du Colombier 	uvlong size;
454*5e96a66cSDavid du Colombier 	int epb;
455*5e96a66cSDavid du Colombier 
456*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
457*5e96a66cSDavid du Colombier 	epb = r->dsize/VtEntrySize;
458*5e96a66cSDavid du Colombier 
459*5e96a66cSDavid du Colombier 	size = (uvlong)r->dsize*(ds/epb);
460*5e96a66cSDavid du Colombier 	size += VtEntrySize*(ds%epb);
461*5e96a66cSDavid du Colombier 	return sourceSetSize(r, size);
462*5e96a66cSDavid du Colombier }
463*5e96a66cSDavid du Colombier 
464*5e96a66cSDavid du Colombier ulong
465*5e96a66cSDavid du Colombier sourceGetDirSize(Source *r)
466*5e96a66cSDavid du Colombier {
467*5e96a66cSDavid du Colombier 	ulong ds;
468*5e96a66cSDavid du Colombier 	uvlong size;
469*5e96a66cSDavid du Colombier 	int epb;
470*5e96a66cSDavid du Colombier 
471*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
472*5e96a66cSDavid du Colombier 	epb = r->dsize/VtEntrySize;
473*5e96a66cSDavid du Colombier 
474*5e96a66cSDavid du Colombier 	size = sourceGetSize(r);
475*5e96a66cSDavid du Colombier 	ds = epb*(size/r->dsize);
476*5e96a66cSDavid du Colombier 	ds += (size%r->dsize)/VtEntrySize;
477*5e96a66cSDavid du Colombier 	return ds;
478*5e96a66cSDavid du Colombier }
479*5e96a66cSDavid du Colombier 
480*5e96a66cSDavid du Colombier int
481*5e96a66cSDavid du Colombier sourceGetEntry(Source *r, Entry *e)
482*5e96a66cSDavid du Colombier {
483*5e96a66cSDavid du Colombier 	Block *b;
484*5e96a66cSDavid du Colombier 
485*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
486*5e96a66cSDavid du Colombier 	b = sourceLoad(r, e);
487*5e96a66cSDavid du Colombier 	if(b == nil)
488*5e96a66cSDavid du Colombier 		return 0;
489*5e96a66cSDavid du Colombier 	blockPut(b);
490*5e96a66cSDavid du Colombier 
491*5e96a66cSDavid du Colombier 	return 1;
492*5e96a66cSDavid du Colombier }
493*5e96a66cSDavid du Colombier 
494*5e96a66cSDavid du Colombier static Block *
495*5e96a66cSDavid du Colombier blockWalk(Block *p, int index, int mode, Fs *fs, Entry *e)
496*5e96a66cSDavid du Colombier {
497*5e96a66cSDavid du Colombier 	Block *b;
498*5e96a66cSDavid du Colombier 	Cache *c;
499*5e96a66cSDavid du Colombier 	u32int addr;
500*5e96a66cSDavid du Colombier 	int type;
501*5e96a66cSDavid du Colombier 
502*5e96a66cSDavid du Colombier 	c = fs->cache;
503*5e96a66cSDavid du Colombier 
504*5e96a66cSDavid du Colombier 	if((p->l.type & BtLevelMask) == 0){
505*5e96a66cSDavid du Colombier 		assert(p->l.type == BtDir);
506*5e96a66cSDavid du Colombier 		type = entryType(e);
507*5e96a66cSDavid du Colombier 		b = cacheGlobal(c, e->score, type, e->tag, mode);
508*5e96a66cSDavid du Colombier 	}else{
509*5e96a66cSDavid du Colombier 		type = p->l.type - 1;
510*5e96a66cSDavid du Colombier 		b = cacheGlobal(c, p->data + index*VtScoreSize, type, e->tag, mode);
511*5e96a66cSDavid du Colombier 	}
512*5e96a66cSDavid du Colombier 
513*5e96a66cSDavid du Colombier 	if(b == nil || mode == OReadOnly)
514*5e96a66cSDavid du Colombier 		return b;
515*5e96a66cSDavid du Colombier 
516*5e96a66cSDavid du Colombier 	assert(!(p->l.state&BsClosed) && p->l.epoch == fs->ehi);
517*5e96a66cSDavid du Colombier 	if(!(b->l.state&BsClosed) && b->l.epoch == fs->ehi)
518*5e96a66cSDavid du Colombier 		return b;
519*5e96a66cSDavid du Colombier 
520*5e96a66cSDavid du Colombier 	/*
521*5e96a66cSDavid du Colombier 	 * Copy on write.
522*5e96a66cSDavid du Colombier 	 */
523*5e96a66cSDavid du Colombier 	if(e->tag == 0){
524*5e96a66cSDavid du Colombier 		assert(p->l.type == BtDir);
525*5e96a66cSDavid du Colombier 		e->tag = tagGen();
526*5e96a66cSDavid du Colombier 		e->flags |= VtEntryLocal;
527*5e96a66cSDavid du Colombier 	}
528*5e96a66cSDavid du Colombier 
529*5e96a66cSDavid du Colombier 	addr = b->addr;
530*5e96a66cSDavid du Colombier 	b = blockCopy(b, e->tag, fs->ehi, fs->elo);
531*5e96a66cSDavid du Colombier 	if(b == nil)
532*5e96a66cSDavid du Colombier 		return nil;
533*5e96a66cSDavid du Colombier 
534*5e96a66cSDavid du Colombier 	assert(b->l.epoch == fs->ehi);
535*5e96a66cSDavid du Colombier 
536*5e96a66cSDavid du Colombier 	if(p->l.type == BtDir){
537*5e96a66cSDavid du Colombier 		memmove(e->score, b->score, VtScoreSize);
538*5e96a66cSDavid du Colombier 		entryPack(e, p->data, index);
539*5e96a66cSDavid du Colombier 	}else{
540*5e96a66cSDavid du Colombier 		memmove(p->data+index*VtScoreSize, b->score, VtScoreSize);
541*5e96a66cSDavid du Colombier 	}
542*5e96a66cSDavid du Colombier 	blockDirty(b);
543*5e96a66cSDavid du Colombier 	blockDependency(p, b, index, b->score);
544*5e96a66cSDavid du Colombier 	blockDirty(p);
545*5e96a66cSDavid du Colombier 
546*5e96a66cSDavid du Colombier 	if(addr != NilBlock)
547*5e96a66cSDavid du Colombier 		blockRemoveLink(p, addr, type, e->tag);
548*5e96a66cSDavid du Colombier 
549*5e96a66cSDavid du Colombier 	return b;
550*5e96a66cSDavid du Colombier }
551*5e96a66cSDavid du Colombier 
552*5e96a66cSDavid du Colombier /*
553*5e96a66cSDavid du Colombier  * Change the depth of the source r.
554*5e96a66cSDavid du Colombier  * The entry e for r is contained in block p.
555*5e96a66cSDavid du Colombier  */
556*5e96a66cSDavid du Colombier static int
557*5e96a66cSDavid du Colombier sourceGrowDepth(Source *r, Block *p, Entry *e, int depth)
558*5e96a66cSDavid du Colombier {
559*5e96a66cSDavid du Colombier 	Block *b, *bb;
560*5e96a66cSDavid du Colombier 	u32int tag;
561*5e96a66cSDavid du Colombier 	int type;
562*5e96a66cSDavid du Colombier 
563*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
564*5e96a66cSDavid du Colombier 	assert(depth <= VtPointerDepth);
565*5e96a66cSDavid du Colombier 
566*5e96a66cSDavid du Colombier 	type = entryType(e);
567*5e96a66cSDavid du Colombier 	b = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite);
568*5e96a66cSDavid du Colombier 	if(b == nil)
569*5e96a66cSDavid du Colombier 		return 0;
570*5e96a66cSDavid du Colombier 
571*5e96a66cSDavid du Colombier 	tag = e->tag;
572*5e96a66cSDavid du Colombier 	if(tag == 0)
573*5e96a66cSDavid du Colombier 		tag = tagGen();
574*5e96a66cSDavid du Colombier 
575*5e96a66cSDavid du Colombier 	/*
576*5e96a66cSDavid du Colombier 	 * Keep adding layers until we get to the right depth
577*5e96a66cSDavid du Colombier 	 * or an error occurs.
578*5e96a66cSDavid du Colombier 	 */
579*5e96a66cSDavid du Colombier 	while(e->depth < depth){
580*5e96a66cSDavid du Colombier 		bb = cacheAllocBlock(r->fs->cache, type+1, tag, r->fs->ehi, r->fs->elo);
581*5e96a66cSDavid du Colombier 		if(bb == nil)
582*5e96a66cSDavid du Colombier 			break;
583*5e96a66cSDavid du Colombier 		memmove(bb->data, b->score, VtScoreSize);
584*5e96a66cSDavid du Colombier 		memmove(e->score, bb->score, VtScoreSize);
585*5e96a66cSDavid du Colombier 		e->depth++;
586*5e96a66cSDavid du Colombier 		type++;
587*5e96a66cSDavid du Colombier 		e->tag = tag;
588*5e96a66cSDavid du Colombier 		e->flags |= VtEntryLocal;
589*5e96a66cSDavid du Colombier 		blockDependency(bb, b, -1, nil);
590*5e96a66cSDavid du Colombier 		blockPut(b);
591*5e96a66cSDavid du Colombier 		blockDirty(bb);
592*5e96a66cSDavid du Colombier 		b = bb;
593*5e96a66cSDavid du Colombier 	}
594*5e96a66cSDavid du Colombier 
595*5e96a66cSDavid du Colombier 	entryPack(e, p->data, r->offset % r->epb);
596*5e96a66cSDavid du Colombier 	blockDependency(p, b, -1, nil);
597*5e96a66cSDavid du Colombier 	blockPut(b);
598*5e96a66cSDavid du Colombier 	blockDirty(p);
599*5e96a66cSDavid du Colombier 
600*5e96a66cSDavid du Colombier 	return e->depth == depth;
601*5e96a66cSDavid du Colombier }
602*5e96a66cSDavid du Colombier 
603*5e96a66cSDavid du Colombier static int
604*5e96a66cSDavid du Colombier sourceShrinkDepth(Source *r, Block *p, Entry *e, int depth)
605*5e96a66cSDavid du Colombier {
606*5e96a66cSDavid du Colombier 	Block *b, *nb, *ob, *rb;
607*5e96a66cSDavid du Colombier 	u32int tag;
608*5e96a66cSDavid du Colombier 	int type, d;
609*5e96a66cSDavid du Colombier 
610*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
611*5e96a66cSDavid du Colombier 	assert(depth <= VtPointerDepth);
612*5e96a66cSDavid du Colombier 
613*5e96a66cSDavid du Colombier 	type = entryType(e);
614*5e96a66cSDavid du Colombier 	rb = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite);
615*5e96a66cSDavid du Colombier 	if(rb == nil)
616*5e96a66cSDavid du Colombier 		return 0;
617*5e96a66cSDavid du Colombier 
618*5e96a66cSDavid du Colombier 	tag = e->tag;
619*5e96a66cSDavid du Colombier 	if(tag == 0)
620*5e96a66cSDavid du Colombier 		tag = tagGen();
621*5e96a66cSDavid du Colombier 
622*5e96a66cSDavid du Colombier 	/*
623*5e96a66cSDavid du Colombier 	 * Walk down to the new root block.
624*5e96a66cSDavid du Colombier 	 * We may stop early, but something is better than nothing.
625*5e96a66cSDavid du Colombier 	 */
626*5e96a66cSDavid du Colombier 	ob = nil;
627*5e96a66cSDavid du Colombier 	b = rb;
628*5e96a66cSDavid du Colombier 	for(d=e->depth; d > depth; d--, type++){
629*5e96a66cSDavid du Colombier 		nb = cacheGlobal(r->fs->cache, b->data, type-1, tag, OReadWrite);
630*5e96a66cSDavid du Colombier 		if(nb == nil)
631*5e96a66cSDavid du Colombier 			break;
632*5e96a66cSDavid du Colombier 		if(ob!=nil && ob!=rb)
633*5e96a66cSDavid du Colombier 			blockPut(ob);
634*5e96a66cSDavid du Colombier 		ob = b;
635*5e96a66cSDavid du Colombier 		b = nb;
636*5e96a66cSDavid du Colombier 	}
637*5e96a66cSDavid du Colombier 
638*5e96a66cSDavid du Colombier 	if(b == rb){
639*5e96a66cSDavid du Colombier 		blockPut(rb);
640*5e96a66cSDavid du Colombier 		return 0;
641*5e96a66cSDavid du Colombier 	}
642*5e96a66cSDavid du Colombier 
643*5e96a66cSDavid du Colombier 	/*
644*5e96a66cSDavid du Colombier 	 * Right now, e points at the root block rb, b is the new root block,
645*5e96a66cSDavid du Colombier 	 * and ob points at b.  To update:
646*5e96a66cSDavid du Colombier 	 *
647*5e96a66cSDavid du Colombier 	 *	(i) change e to point at b
648*5e96a66cSDavid du Colombier 	 *	(ii) zero the pointer ob -> b
649*5e96a66cSDavid du Colombier 	 *	(iii) free the root block
650*5e96a66cSDavid du Colombier 	 *
651*5e96a66cSDavid du Colombier 	 * p (the block containing e) must be written before
652*5e96a66cSDavid du Colombier 	 * anything else.
653*5e96a66cSDavid du Colombier  	 */
654*5e96a66cSDavid du Colombier 
655*5e96a66cSDavid du Colombier 	/* (i) */
656*5e96a66cSDavid du Colombier 	e->depth = d;
657*5e96a66cSDavid du Colombier 	memmove(e->score, b->score, VtScoreSize);
658*5e96a66cSDavid du Colombier 	entryPack(e, p->data, r->offset % r->epb);
659*5e96a66cSDavid du Colombier 	blockDirty(p);
660*5e96a66cSDavid du Colombier 
661*5e96a66cSDavid du Colombier 	/* (ii) */
662*5e96a66cSDavid du Colombier 	memmove(ob->data, vtZeroScore, VtScoreSize);
663*5e96a66cSDavid du Colombier 	blockDependency(ob, p, -1, nil);
664*5e96a66cSDavid du Colombier 	blockDirty(ob);
665*5e96a66cSDavid du Colombier 
666*5e96a66cSDavid du Colombier 	/* (iii) */
667*5e96a66cSDavid du Colombier 	if(rb->addr != NilBlock)
668*5e96a66cSDavid du Colombier 		blockRemoveLink(p, rb->addr, rb->l.type, rb->l.tag);
669*5e96a66cSDavid du Colombier 
670*5e96a66cSDavid du Colombier 	blockPut(rb);
671*5e96a66cSDavid du Colombier 	if(ob!=nil && ob!=rb)
672*5e96a66cSDavid du Colombier 		blockPut(ob);
673*5e96a66cSDavid du Colombier 	if(b!=rb)
674*5e96a66cSDavid du Colombier 		blockPut(b);
675*5e96a66cSDavid du Colombier 
676*5e96a66cSDavid du Colombier 	return d == depth;
677*5e96a66cSDavid du Colombier }
678*5e96a66cSDavid du Colombier 
679*5e96a66cSDavid du Colombier Block *
680*5e96a66cSDavid du Colombier sourceBlock(Source *r, ulong bn, int mode)
681*5e96a66cSDavid du Colombier {
682*5e96a66cSDavid du Colombier 	Block *b, *bb;
683*5e96a66cSDavid du Colombier 	int index[VtPointerDepth+1];
684*5e96a66cSDavid du Colombier 	Entry e;
685*5e96a66cSDavid du Colombier 	int i, np;
686*5e96a66cSDavid du Colombier 	int m;
687*5e96a66cSDavid du Colombier 
688*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
689*5e96a66cSDavid du Colombier 	assert(bn != NilBlock);
690*5e96a66cSDavid du Colombier 
691*5e96a66cSDavid du Colombier 	/* mode for intermediate block */
692*5e96a66cSDavid du Colombier 	m = mode;
693*5e96a66cSDavid du Colombier 	if(m == OOverWrite)
694*5e96a66cSDavid du Colombier 		m = OReadWrite;
695*5e96a66cSDavid du Colombier 
696*5e96a66cSDavid du Colombier 	b = sourceLoad(r, &e);
697*5e96a66cSDavid du Colombier 	if(b == nil)
698*5e96a66cSDavid du Colombier 		return nil;
699*5e96a66cSDavid du Colombier 
700*5e96a66cSDavid du Colombier 	np = e.psize/VtScoreSize;
701*5e96a66cSDavid du Colombier 	memset(index, 0, sizeof(index));
702*5e96a66cSDavid du Colombier 	for(i=0; bn > 0; i++){
703*5e96a66cSDavid du Colombier 		if(i >= VtPointerDepth){
704*5e96a66cSDavid du Colombier 			vtSetError(EBadAddr);
705*5e96a66cSDavid du Colombier 			goto Err;
706*5e96a66cSDavid du Colombier 		}
707*5e96a66cSDavid du Colombier 		index[i] = bn % np;
708*5e96a66cSDavid du Colombier 		bn /= np;
709*5e96a66cSDavid du Colombier 	}
710*5e96a66cSDavid du Colombier 
711*5e96a66cSDavid du Colombier 	if(i > e.depth){
712*5e96a66cSDavid du Colombier 		if(mode == OReadOnly){
713*5e96a66cSDavid du Colombier 			vtSetError(EBadAddr);
714*5e96a66cSDavid du Colombier 			goto Err;
715*5e96a66cSDavid du Colombier 		}
716*5e96a66cSDavid du Colombier 		if(!sourceGrowDepth(r, b, &e, i))
717*5e96a66cSDavid du Colombier 			goto Err;
718*5e96a66cSDavid du Colombier 	}
719*5e96a66cSDavid du Colombier 
720*5e96a66cSDavid du Colombier 	index[e.depth] = r->offset % r->epb;
721*5e96a66cSDavid du Colombier 
722*5e96a66cSDavid du Colombier 	for(i=e.depth; i>=0; i--){
723*5e96a66cSDavid du Colombier 		bb = blockWalk(b, index[i], m, r->fs, &e);
724*5e96a66cSDavid du Colombier 		if(bb == nil)
725*5e96a66cSDavid du Colombier 			goto Err;
726*5e96a66cSDavid du Colombier 		blockPut(b);
727*5e96a66cSDavid du Colombier 		b = bb;
728*5e96a66cSDavid du Colombier 	}
729*5e96a66cSDavid du Colombier 	return b;
730*5e96a66cSDavid du Colombier Err:
731*5e96a66cSDavid du Colombier 	blockPut(b);
732*5e96a66cSDavid du Colombier 	return nil;
733*5e96a66cSDavid du Colombier }
734*5e96a66cSDavid du Colombier 
735*5e96a66cSDavid du Colombier void
736*5e96a66cSDavid du Colombier sourceClose(Source *r)
737*5e96a66cSDavid du Colombier {
738*5e96a66cSDavid du Colombier 	if(r == nil)
739*5e96a66cSDavid du Colombier 		return;
740*5e96a66cSDavid du Colombier 	vtLock(r->lk);
741*5e96a66cSDavid du Colombier 	r->ref--;
742*5e96a66cSDavid du Colombier 	if(r->ref){
743*5e96a66cSDavid du Colombier 		vtUnlock(r->lk);
744*5e96a66cSDavid du Colombier 		return;
745*5e96a66cSDavid du Colombier 	}
746*5e96a66cSDavid du Colombier 	assert(r->ref == 0);
747*5e96a66cSDavid du Colombier 	vtUnlock(r->lk);
748*5e96a66cSDavid du Colombier 	if(r->parent)
749*5e96a66cSDavid du Colombier 		sourceClose(r->parent);
750*5e96a66cSDavid du Colombier 	vtLockFree(r->lk);
751*5e96a66cSDavid du Colombier 	memset(r, ~0, sizeof(*r));
752*5e96a66cSDavid du Colombier 	vtMemFree(r);
753*5e96a66cSDavid du Colombier }
754*5e96a66cSDavid du Colombier 
755*5e96a66cSDavid du Colombier /*
756*5e96a66cSDavid du Colombier  * Retrieve the block containing the entry for r.
757*5e96a66cSDavid du Colombier  * If a snapshot has happened, we might need
758*5e96a66cSDavid du Colombier  * to get a new copy of the block.  We avoid this
759*5e96a66cSDavid du Colombier  * in the common case by caching the score for
760*5e96a66cSDavid du Colombier  * the block and the last epoch in which it was valid.
761*5e96a66cSDavid du Colombier  *
762*5e96a66cSDavid du Colombier  * We use r->mode to tell the difference between active
763*5e96a66cSDavid du Colombier  * file system sources (OReadWrite) and sources for the
764*5e96a66cSDavid du Colombier  * snapshot file system (OReadOnly).
765*5e96a66cSDavid du Colombier  */
766*5e96a66cSDavid du Colombier static Block*
767*5e96a66cSDavid du Colombier sourceLoadBlock(Source *r, int mode)
768*5e96a66cSDavid du Colombier {
769*5e96a66cSDavid du Colombier 	u32int addr;
770*5e96a66cSDavid du Colombier 	Block *b;
771*5e96a66cSDavid du Colombier 
772*5e96a66cSDavid du Colombier 	switch(r->mode){
773*5e96a66cSDavid du Colombier 	default:
774*5e96a66cSDavid du Colombier 		assert(0);
775*5e96a66cSDavid du Colombier 	case OReadWrite:
776*5e96a66cSDavid du Colombier 		assert(r->mode == OReadWrite);
777*5e96a66cSDavid du Colombier 		assert(r->epoch >= r->fs->elo);
778*5e96a66cSDavid du Colombier 		if(r->epoch == r->fs->ehi){
779*5e96a66cSDavid du Colombier 			b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
780*5e96a66cSDavid du Colombier 			assert(r->epoch == b->l.epoch);
781*5e96a66cSDavid du Colombier 			if(b == nil)
782*5e96a66cSDavid du Colombier 				return nil;
783*5e96a66cSDavid du Colombier 			return b;
784*5e96a66cSDavid du Colombier 		}
785*5e96a66cSDavid du Colombier 		assert(r->parent != nil);
786*5e96a66cSDavid du Colombier 		if(!sourceLock(r->parent, OReadWrite))
787*5e96a66cSDavid du Colombier 			return nil;
788*5e96a66cSDavid du Colombier 		b = sourceBlock(r->parent, r->offset/r->epb, OReadWrite);
789*5e96a66cSDavid du Colombier 		sourceUnlock(r->parent);
790*5e96a66cSDavid du Colombier 		if(b == nil)
791*5e96a66cSDavid du Colombier 			return nil;
792*5e96a66cSDavid du Colombier 		assert(b->l.epoch == r->fs->ehi);
793*5e96a66cSDavid du Colombier 		memmove(r->score, b->score, VtScoreSize);
794*5e96a66cSDavid du Colombier 		r->scoreEpoch = b->l.epoch;
795*5e96a66cSDavid du Colombier 		r->tag = b->l.tag;
796*5e96a66cSDavid du Colombier 		r->epoch = r->fs->ehi;
797*5e96a66cSDavid du Colombier 		return b;
798*5e96a66cSDavid du Colombier 
799*5e96a66cSDavid du Colombier 	case OReadOnly:
800*5e96a66cSDavid du Colombier 		addr = globalToLocal(r->score);
801*5e96a66cSDavid du Colombier 		if(addr == NilBlock)
802*5e96a66cSDavid du Colombier 			return cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, mode);
803*5e96a66cSDavid du Colombier 
804*5e96a66cSDavid du Colombier 		b = cacheLocalData(r->fs->cache, addr, BtDir, r->tag, mode, r->scoreEpoch);
805*5e96a66cSDavid du Colombier 		if(b)
806*5e96a66cSDavid du Colombier 			return b;
807*5e96a66cSDavid du Colombier 
808*5e96a66cSDavid du Colombier 		/*
809*5e96a66cSDavid du Colombier 		 * If it failed because the epochs don't match, the block has been
810*5e96a66cSDavid du Colombier 		 * archived and reclaimed.  Rewalk from the parent and get the
811*5e96a66cSDavid du Colombier 		 * new pointer.  This can't happen in the OReadWrite case
812*5e96a66cSDavid du Colombier 		 * above because blocks in the current epoch don't get
813*5e96a66cSDavid du Colombier 		 * reclaimed.  The fact that we're OReadOnly means we're
814*5e96a66cSDavid du Colombier 		 * a snapshot.  (Or else the file system is read-only, but then
815*5e96a66cSDavid du Colombier 		 * the archiver isn't going around deleting blocks.)
816*5e96a66cSDavid du Colombier 		 */
817*5e96a66cSDavid du Colombier 		if(strcmp(vtGetError(), ELabelMismatch) == 0){
818*5e96a66cSDavid du Colombier 			if(!sourceLock(r->parent, OReadOnly))
819*5e96a66cSDavid du Colombier 				return nil;
820*5e96a66cSDavid du Colombier 			b = sourceBlock(r->parent, r->offset/r->epb, OReadOnly);
821*5e96a66cSDavid du Colombier 			sourceUnlock(r->parent);
822*5e96a66cSDavid du Colombier 			if(b){
823*5e96a66cSDavid du Colombier 				fprint(2, "sourceAlloc: lost %V found %V\n",
824*5e96a66cSDavid du Colombier 					r->score, b->score);
825*5e96a66cSDavid du Colombier 				memmove(r->score, b->score, VtScoreSize);
826*5e96a66cSDavid du Colombier 				r->scoreEpoch = b->l.epoch;
827*5e96a66cSDavid du Colombier 				return b;
828*5e96a66cSDavid du Colombier 			}
829*5e96a66cSDavid du Colombier 		}
830*5e96a66cSDavid du Colombier 		return nil;
831*5e96a66cSDavid du Colombier 	}
832*5e96a66cSDavid du Colombier }
833*5e96a66cSDavid du Colombier 
834*5e96a66cSDavid du Colombier int
835*5e96a66cSDavid du Colombier sourceLock(Source *r, int mode)
836*5e96a66cSDavid du Colombier {
837*5e96a66cSDavid du Colombier 	Block *b;
838*5e96a66cSDavid du Colombier 
839*5e96a66cSDavid du Colombier 	if(mode == -1)
840*5e96a66cSDavid du Colombier 		mode = r->mode;
841*5e96a66cSDavid du Colombier 
842*5e96a66cSDavid du Colombier 	b = sourceLoadBlock(r, mode);
843*5e96a66cSDavid du Colombier 	if(b == nil)
844*5e96a66cSDavid du Colombier 		return 0;
845*5e96a66cSDavid du Colombier 	/*
846*5e96a66cSDavid du Colombier 	 * The fact that we are holding b serves as the
847*5e96a66cSDavid du Colombier 	 * lock entitling us to write to r->b.
848*5e96a66cSDavid du Colombier 	 */
849*5e96a66cSDavid du Colombier 	assert(r->b == nil);
850*5e96a66cSDavid du Colombier 	r->b = b;
851*5e96a66cSDavid du Colombier 	if(r->mode == OReadWrite)
852*5e96a66cSDavid du Colombier 		assert(r->epoch == r->b->l.epoch);
853*5e96a66cSDavid du Colombier 	return 1;
854*5e96a66cSDavid du Colombier }
855*5e96a66cSDavid du Colombier 
856*5e96a66cSDavid du Colombier /*
857*5e96a66cSDavid du Colombier  * Lock two (usually sibling) sources.  This needs special care
858*5e96a66cSDavid du Colombier  * because the Entries for both sources might be in the same block.
859*5e96a66cSDavid du Colombier  * We also try to lock blocks in left-to-right order within the tree.
860*5e96a66cSDavid du Colombier  */
861*5e96a66cSDavid du Colombier int
862*5e96a66cSDavid du Colombier sourceLock2(Source *r, Source *rr, int mode)
863*5e96a66cSDavid du Colombier {
864*5e96a66cSDavid du Colombier 	Block *b, *bb;
865*5e96a66cSDavid du Colombier 
866*5e96a66cSDavid du Colombier 	if(rr == nil)
867*5e96a66cSDavid du Colombier 		return sourceLock(r, mode);
868*5e96a66cSDavid du Colombier 
869*5e96a66cSDavid du Colombier 	if(mode == -1)
870*5e96a66cSDavid du Colombier 		mode = r->mode;
871*5e96a66cSDavid du Colombier 
872*5e96a66cSDavid du Colombier 	if(r->parent==rr->parent && r->offset/r->epb == rr->offset/rr->epb){
873*5e96a66cSDavid du Colombier 		b = sourceLoadBlock(r, mode);
874*5e96a66cSDavid du Colombier 		if(b == nil)
875*5e96a66cSDavid du Colombier 			return 0;
876*5e96a66cSDavid du Colombier 		blockDupLock(b);
877*5e96a66cSDavid du Colombier 		bb = b;
878*5e96a66cSDavid du Colombier 	}else if(r->parent==rr->parent || r->offset > rr->offset){
879*5e96a66cSDavid du Colombier 		bb = sourceLoadBlock(rr, mode);
880*5e96a66cSDavid du Colombier 		b = sourceLoadBlock(r, mode);
881*5e96a66cSDavid du Colombier 	}else{
882*5e96a66cSDavid du Colombier 		b = sourceLoadBlock(r, mode);
883*5e96a66cSDavid du Colombier 		bb = sourceLoadBlock(rr, mode);
884*5e96a66cSDavid du Colombier 	}
885*5e96a66cSDavid du Colombier 	if(b == nil || bb == nil){
886*5e96a66cSDavid du Colombier 		if(b)
887*5e96a66cSDavid du Colombier 			blockPut(b);
888*5e96a66cSDavid du Colombier 		if(bb)
889*5e96a66cSDavid du Colombier 			blockPut(bb);
890*5e96a66cSDavid du Colombier 		return 0;
891*5e96a66cSDavid du Colombier 	}
892*5e96a66cSDavid du Colombier 
893*5e96a66cSDavid du Colombier 	/*
894*5e96a66cSDavid du Colombier 	 * The fact that we are holding b and bb serves
895*5e96a66cSDavid du Colombier 	 * as the lock entitling us to write to r->b and rr->b.
896*5e96a66cSDavid du Colombier 	 */
897*5e96a66cSDavid du Colombier 	r->b = b;
898*5e96a66cSDavid du Colombier 	rr->b = bb;
899*5e96a66cSDavid du Colombier 	return 1;
900*5e96a66cSDavid du Colombier }
901*5e96a66cSDavid du Colombier 
902*5e96a66cSDavid du Colombier void
903*5e96a66cSDavid du Colombier sourceUnlock(Source *r)
904*5e96a66cSDavid du Colombier {
905*5e96a66cSDavid du Colombier 	Block *b;
906*5e96a66cSDavid du Colombier 
907*5e96a66cSDavid du Colombier 	if(r->b == nil){
908*5e96a66cSDavid du Colombier 		fprint(2, "sourceUnlock: already unlocked\n");
909*5e96a66cSDavid du Colombier 		abort();
910*5e96a66cSDavid du Colombier 	}
911*5e96a66cSDavid du Colombier 	b = r->b;
912*5e96a66cSDavid du Colombier 	r->b = nil;
913*5e96a66cSDavid du Colombier 	blockPut(b);
914*5e96a66cSDavid du Colombier }
915*5e96a66cSDavid du Colombier 
916*5e96a66cSDavid du Colombier static Block*
917*5e96a66cSDavid du Colombier sourceLoad(Source *r, Entry *e)
918*5e96a66cSDavid du Colombier {
919*5e96a66cSDavid du Colombier 	Block *b;
920*5e96a66cSDavid du Colombier 
921*5e96a66cSDavid du Colombier 	assert(sourceIsLocked(r));
922*5e96a66cSDavid du Colombier 	b = r->b;
923*5e96a66cSDavid du Colombier 	if(!entryUnpack(e, b->data, r->offset % r->epb))
924*5e96a66cSDavid du Colombier 		return nil;
925*5e96a66cSDavid du Colombier 	if(e->gen != r->gen){
926*5e96a66cSDavid du Colombier 		vtSetError(ERemoved);
927*5e96a66cSDavid du Colombier 		return nil;
928*5e96a66cSDavid du Colombier 	}
929*5e96a66cSDavid du Colombier 	blockDupLock(b);
930*5e96a66cSDavid du Colombier 	return b;
931*5e96a66cSDavid du Colombier }
932*5e96a66cSDavid du Colombier 
933*5e96a66cSDavid du Colombier static int
934*5e96a66cSDavid du Colombier sizeToDepth(uvlong s, int psize, int dsize)
935*5e96a66cSDavid du Colombier {
936*5e96a66cSDavid du Colombier 	int np;
937*5e96a66cSDavid du Colombier 	int d;
938*5e96a66cSDavid du Colombier 
939*5e96a66cSDavid du Colombier 	/* determine pointer depth */
940*5e96a66cSDavid du Colombier 	np = psize/VtScoreSize;
941*5e96a66cSDavid du Colombier 	s = (s + dsize - 1)/dsize;
942*5e96a66cSDavid du Colombier 	for(d = 0; s > 1; d++)
943*5e96a66cSDavid du Colombier 		s = (s + np - 1)/np;
944*5e96a66cSDavid du Colombier 	return d;
945*5e96a66cSDavid du Colombier }
946*5e96a66cSDavid du Colombier 
947*5e96a66cSDavid du Colombier static u32int
948*5e96a66cSDavid du Colombier tagGen(void)
949*5e96a66cSDavid du Colombier {
950*5e96a66cSDavid du Colombier 	u32int tag;
951*5e96a66cSDavid du Colombier 
952*5e96a66cSDavid du Colombier 	for(;;){
953*5e96a66cSDavid du Colombier 		tag = lrand();
954*5e96a66cSDavid du Colombier 		if(tag >= UserTag)
955*5e96a66cSDavid du Colombier 			break;
956*5e96a66cSDavid du Colombier 	}
957*5e96a66cSDavid du Colombier 	return tag;
958*5e96a66cSDavid du Colombier }
959