Blob


1 #include "stdinc.h"
2 #include "vac.h"
3 #include "dat.h"
4 #include "fns.h"
5 #include "error.h"
7 static int sizeToDepth(uvlong s, int psize, int dsize);
9 static int
10 sizeToDepth(uvlong s, int psize, int dsize)
11 {
12 int np;
13 int d;
15 /* determine pointer depth */
16 np = psize/VtScoreSize;
17 s = (s + dsize - 1)/dsize;
18 for(d = 0; s > 1; d++)
19 s = (s + np - 1)/np;
20 return d;
21 }
23 /* assumes u is lock? */
24 Source *
25 sourceAlloc(Cache *c, Lump *u, ulong block, int entry, int readOnly)
26 {
27 Source *r;
28 VtEntry d;
30 if(u->asize < (entry+1)*VtEntrySize) {
31 vtSetError(ENoDir);
32 return nil;
33 }
35 if(!vtEntryUnpack(&d, u->data, entry))
36 return nil;
38 if(!(d.flags & VtEntryActive)) {
39 fprint(2, "bad flags %#ux %V\n", d.flags, d.score);
40 vtSetError(ENoDir);
41 return nil;
42 }
44 /* HACK for backwards compatiblity - should go away at some point */
45 if(d.depth == 0) {
46 if(d.size > d.dsize) fprint(2, "depth == 0! size = %ulld\n", d.size);
47 d.depth = sizeToDepth(d.size, d.psize, d.dsize);
48 }
50 if(d.depth < sizeToDepth(d.size, d.psize, d.dsize)) {
51 vtSetError(EBadDir);
52 return nil;
53 }
55 r = vtMemAllocZ(sizeof(Source));
56 r->lk = vtLockAlloc();
57 r->cache = c;
58 r->readOnly = readOnly;
59 r->lump = lumpIncRef(u);
60 r->block = block;
61 r->entry = entry;
62 r->gen = d.gen;
63 r->dir = (d.flags & VtEntryDir) != 0;
64 r->depth = d.depth;
65 r->psize = d.psize;
66 r->dsize = d.dsize;
67 r->size = d.size;
69 r->epb = r->dsize/VtEntrySize;
71 return r;
72 }
74 Source *
75 sourceOpen(Source *r, ulong entry, int readOnly)
76 {
77 ulong bn;
78 Lump *u;
80 if(0)fprint(2, "sourceOpen: %V:%d: %lud\n", r->lump->score, r->entry, entry);
81 if(r->readOnly && !readOnly) {
82 vtSetError(EReadOnly);
83 return nil;
84 }
86 bn = entry/r->epb;
88 u = sourceGetLump(r, bn, readOnly, 1);
89 if(u == nil)
90 return nil;
92 r = sourceAlloc(r->cache, u, bn, entry%r->epb, readOnly);
93 lumpDecRef(u, 1);
94 return r;
95 }
97 Source *
98 sourceCreate(Source *r, int psize, int dsize, int isdir, ulong entry)
99 {
100 Source *rr;
101 int i;
102 Lump *u;
103 ulong bn;
104 VtEntry dir;
106 if(r->readOnly) {
107 vtSetError(EReadOnly);
108 return nil;
111 if(entry == 0) {
112 /*
113 * look at a random block to see if we can find an empty entry
114 */
115 entry = sourceGetDirSize(r);
116 entry = r->epb*lnrand(entry/r->epb+1);
119 /*
120 * need to loop since multiple threads could be trying to allocate
121 */
122 for(;;) {
123 bn = entry/r->epb;
124 sourceSetDepth(r, (uvlong)(bn+1)*r->dsize);
125 u = sourceGetLump(r, bn, 0, 1);
126 if(u == nil)
127 return nil;
128 for(i=entry%r->epb; i<r->epb; i++) {
129 vtEntryUnpack(&dir, u->data, i);
130 if((dir.flags&VtEntryActive) == 0 && dir.gen != ~0)
131 goto Found;
133 lumpDecRef(u, 1);
134 entry = sourceGetDirSize(r);
136 Found:
137 /* found an entry */
138 dir.psize = psize;
139 dir.dsize = dsize;
140 dir.flags = VtEntryActive;
141 if(isdir)
142 dir.flags |= VtEntryDir;
143 dir.depth = 0;
144 dir.size = 0;
145 memmove(dir.score, vtZeroScore, VtScoreSize);
146 vtEntryPack(&dir, u->data, i);
148 sourceSetDirSize(r, bn*r->epb + i + 1);
149 rr = sourceAlloc(r->cache, u, bn, i, 0);
151 lumpDecRef(u, 1);
152 return rr;
155 void
156 sourceRemove(Source *r)
158 lumpFreeEntry(r->lump, r->entry);
159 sourceFree(r);
162 int
163 sourceSetDepth(Source *r, uvlong size)
165 Lump *u, *v;
166 VtEntry dir;
167 int depth;
169 if(r->readOnly){
170 vtSetError(EReadOnly);
171 return 0;
174 depth = sizeToDepth(size, r->psize, r->dsize);
176 assert(depth >= 0);
178 if(depth > VtPointerDepth) {
179 vtSetError(ETooBig);
180 return 0;
183 vtLock(r->lk);
185 if(r->depth >= depth) {
186 vtUnlock(r->lk);
187 return 1;
190 u = r->lump;
191 vtLock(u->lk);
192 if(!vtEntryUnpack(&dir, u->data, r->entry)) {
193 vtUnlock(u->lk);
194 vtUnlock(r->lk);
195 return 0;
197 while(dir.depth < depth) {
198 v = cacheAllocLump(r->cache, VtPointerType0+r->depth, r->psize, r->dir);
199 if(v == nil)
200 break;
201 memmove(v->data, dir.score, VtScoreSize);
202 memmove(dir.score, v->score, VtScoreSize);
203 dir.depth++;
204 vtUnlock(v->lk);
206 vtEntryPack(&dir, u->data, r->entry);
207 vtUnlock(u->lk);
209 r->depth = dir.depth;
210 vtUnlock(r->lk);
212 return dir.depth == depth;
215 int
216 sourceGetVtEntry(Source *r, VtEntry *dir)
218 Lump *u;
220 u = r->lump;
221 vtLock(u->lk);
222 if(!vtEntryUnpack(dir, u->data, r->entry)) {
223 vtUnlock(u->lk);
224 return 0;
226 vtUnlock(u->lk);
227 return 1;
230 uvlong
231 sourceGetSize(Source *r)
233 uvlong size;
235 vtLock(r->lk);
236 size = r->size;
237 vtUnlock(r->lk);
239 return size;
243 int
244 sourceSetSize(Source *r, uvlong size)
246 Lump *u;
247 VtEntry dir;
248 int depth;
250 if(r->readOnly) {
251 vtSetError(EReadOnly);
252 return 0;
255 if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize) {
256 vtSetError(ETooBig);
257 return 0;
260 vtLock(r->lk);
261 depth = sizeToDepth(size, r->psize, r->dsize);
262 if(size < r->size) {
263 vtUnlock(r->lk);
264 return 1;
266 if(depth > r->depth) {
267 vtSetError(EBadDir);
268 vtUnlock(r->lk);
269 return 0;
272 u = r->lump;
273 vtLock(u->lk);
274 vtEntryUnpack(&dir, u->data, r->entry);
275 dir.size = size;
276 vtEntryPack(&dir, u->data, r->entry);
277 vtUnlock(u->lk);
278 r->size = size;
279 vtUnlock(r->lk);
280 return 1;
283 int
284 sourceSetDirSize(Source *r, ulong ds)
286 uvlong size;
288 size = (uvlong)r->dsize*(ds/r->epb);
289 size += VtEntrySize*(ds%r->epb);
290 return sourceSetSize(r, size);
293 ulong
294 sourceGetDirSize(Source *r)
296 ulong ds;
297 uvlong size;
299 size = sourceGetSize(r);
300 ds = r->epb*(size/r->dsize);
301 ds += (size%r->dsize)/VtEntrySize;
302 return ds;
305 ulong
306 sourceGetNumBlocks(Source *r)
308 return (sourceGetSize(r)+r->dsize-1)/r->dsize;
311 Lump *
312 sourceWalk(Source *r, ulong block, int readOnly, int *off)
314 int depth;
315 int i, np;
316 Lump *u, *v;
317 int elem[VtPointerDepth+1];
318 ulong b;
320 if(r->readOnly && !readOnly) {
321 vtSetError(EReadOnly);
322 return nil;
325 vtLock(r->lk);
326 np = r->psize/VtScoreSize;
327 b = block;
328 for(i=0; i<r->depth; i++) {
329 elem[i] = b % np;
330 b /= np;
332 if(b != 0) {
333 vtUnlock(r->lk);
334 vtSetError(EBadOffset);
335 return nil;
337 elem[i] = r->entry;
338 u = lumpIncRef(r->lump);
339 depth = r->depth;
340 *off = elem[0];
341 vtUnlock(r->lk);
343 for(i=depth; i>0; i--) {
344 v = lumpWalk(u, elem[i], VtPointerType0+i-1, r->psize, readOnly, 0);
345 lumpDecRef(u, 0);
346 if(v == nil)
347 return nil;
348 u = v;
351 return u;
354 Lump *
355 sourceGetLump(Source *r, ulong block, int readOnly, int lock)
357 int type, off;
358 Lump *u, *v;
360 if(r->readOnly && !readOnly) {
361 vtSetError(EReadOnly);
362 return nil;
364 if(block == NilBlock) {
365 vtSetError(ENilBlock);
366 return nil;
368 if(0)fprint(2, "sourceGetLump: %V:%d %lud\n", r->lump->score, r->entry, block);
369 u = sourceWalk(r, block, readOnly, &off);
370 if(u == nil)
371 return nil;
372 if(r->dir)
373 type = VtDirType;
374 else
375 type = VtDataType;
376 v = lumpWalk(u, off, type, r->dsize, readOnly, lock);
377 lumpDecRef(u, 0);
378 return v;
381 void
382 sourceFree(Source *k)
384 if(k == nil)
385 return;
386 lumpDecRef(k->lump, 0);
387 vtLockFree(k->lk);
388 memset(k, ~0, sizeof(*k));
389 vtMemFree(k);