tfile.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tfile.c (23784B) | |
--- | |
1 /* | |
2 * Manage tree of VtFiles stored in the block cache. | |
3 * | |
4 * The single point of truth for the info about the VtFiles themselves | |
5 * is the block data. Because of this, there is no explicit locking of | |
6 * VtFile structures, and indeed there may be more than one VtFile | |
7 * structure for a given Venti file. They synchronize through the | |
8 * block cache. | |
9 * | |
10 * This is a bit simpler than fossil because there are no epochs | |
11 * or tags or anything else. Just mutable local blocks and immutable | |
12 * Venti blocks. | |
13 */ | |
14 | |
15 #include <u.h> | |
16 #include <libc.h> | |
17 #include <venti.h> | |
18 | |
19 #define MaxBlock (1UL<<31) | |
20 | |
21 static char ENotDir[] = "walk in non-directory"; | |
22 static char ETooBig[] = "file too big"; | |
23 /* static char EBadAddr[] = "bad address"; */ | |
24 static char ELabelMismatch[] = "label mismatch"; | |
25 | |
26 static int sizetodepth(uvlong s, int psize, int dsize); | |
27 static VtBlock *fileload(VtFile *r, VtEntry *e); | |
28 static int shrinkdepth(VtFile*, VtBlock*, VtEntry*, int); | |
29 static int shrinksize(VtFile*, VtEntry*, uvlong); | |
30 static int growdepth(VtFile*, VtBlock*, VtEntry*, int); | |
31 | |
32 #define ISLOCKED(r) ((r)->b != nil) | |
33 #define DEPTH(t) ((t)&VtTypeDepthMask) | |
34 | |
35 static VtFile * | |
36 vtfilealloc(VtCache *c, VtBlock *b, VtFile *p, u32int offset, int mode) | |
37 { | |
38 int epb; | |
39 VtEntry e; | |
40 VtFile *r; | |
41 | |
42 assert(p==nil || ISLOCKED(p)); | |
43 | |
44 if(p == nil){ | |
45 assert(offset == 0); | |
46 epb = 1; | |
47 }else | |
48 epb = p->dsize / VtEntrySize; | |
49 | |
50 if(b->type != VtDirType){ | |
51 werrstr("bad block type %#uo", b->type); | |
52 return nil; | |
53 } | |
54 | |
55 /* | |
56 * a non-active entry is the only thing that | |
57 * can legitimately happen here. all the others | |
58 * get prints. | |
59 */ | |
60 if(vtentryunpack(&e, b->data, offset % epb) < 0){ | |
61 fprint(2, "vtentryunpack failed: %r (%.*H)\n", VtEntrySi… | |
62 return nil; | |
63 } | |
64 if(!(e.flags & VtEntryActive)){ | |
65 werrstr("entry not active"); | |
66 return nil; | |
67 } | |
68 | |
69 if(DEPTH(e.type) < sizetodepth(e.size, e.psize, e.dsize)){ | |
70 fprint(2, "depth %ud size %llud psize %lud dsize %lud\n", | |
71 DEPTH(e.type), e.size, e.psize, e.dsize); | |
72 werrstr("bad depth"); | |
73 return nil; | |
74 } | |
75 | |
76 r = vtmallocz(sizeof(VtFile)); | |
77 r->c = c; | |
78 r->bsize = b->size; | |
79 r->mode = mode; | |
80 r->dsize = e.dsize; | |
81 r->psize = e.psize; | |
82 r->gen = e.gen; | |
83 r->dir = (e.type & VtTypeBaseMask) == VtDirType; | |
84 r->ref = 1; | |
85 r->parent = p; | |
86 if(p){ | |
87 qlock(&p->lk); | |
88 assert(mode == VtOREAD || p->mode == VtORDWR); | |
89 p->ref++; | |
90 qunlock(&p->lk); | |
91 }else{ | |
92 assert(b->addr != NilBlock); | |
93 r->local = 1; | |
94 } | |
95 memmove(r->score, b->score, VtScoreSize); | |
96 r->offset = offset; | |
97 r->epb = epb; | |
98 | |
99 return r; | |
100 } | |
101 | |
102 VtFile * | |
103 vtfileroot(VtCache *c, u32int addr, int mode) | |
104 { | |
105 VtFile *r; | |
106 VtBlock *b; | |
107 | |
108 b = vtcachelocal(c, addr, VtDirType); | |
109 if(b == nil) | |
110 return nil; | |
111 r = vtfilealloc(c, b, nil, 0, mode); | |
112 vtblockput(b); | |
113 return r; | |
114 } | |
115 | |
116 VtFile* | |
117 vtfileopenroot(VtCache *c, VtEntry *e) | |
118 { | |
119 VtBlock *b; | |
120 VtFile *f; | |
121 | |
122 b = vtcacheallocblock(c, VtDirType, VtEntrySize); | |
123 if(b == nil) | |
124 return nil; | |
125 | |
126 vtentrypack(e, b->data, 0); | |
127 f = vtfilealloc(c, b, nil, 0, VtORDWR); | |
128 vtblockput(b); | |
129 return f; | |
130 } | |
131 | |
132 VtFile * | |
133 vtfilecreateroot(VtCache *c, int psize, int dsize, int type) | |
134 { | |
135 VtEntry e; | |
136 | |
137 memset(&e, 0, sizeof e); | |
138 e.flags = VtEntryActive; | |
139 e.psize = psize; | |
140 e.dsize = dsize; | |
141 e.type = type; | |
142 memmove(e.score, vtzeroscore, VtScoreSize); | |
143 | |
144 return vtfileopenroot(c, &e); | |
145 } | |
146 | |
147 VtFile * | |
148 vtfileopen(VtFile *r, u32int offset, int mode) | |
149 { | |
150 ulong bn; | |
151 VtBlock *b; | |
152 | |
153 assert(ISLOCKED(r)); | |
154 if(!r->dir){ | |
155 werrstr(ENotDir); | |
156 return nil; | |
157 } | |
158 | |
159 bn = offset/(r->dsize/VtEntrySize); | |
160 | |
161 b = vtfileblock(r, bn, mode); | |
162 if(b == nil) | |
163 return nil; | |
164 r = vtfilealloc(r->c, b, r, offset, mode); | |
165 vtblockput(b); | |
166 return r; | |
167 } | |
168 | |
169 VtFile* | |
170 vtfilecreate(VtFile *r, int psize, int dsize, int type) | |
171 { | |
172 return _vtfilecreate(r, -1, psize, dsize, type); | |
173 } | |
174 | |
175 VtFile* | |
176 _vtfilecreate(VtFile *r, int o, int psize, int dsize, int type) | |
177 { | |
178 int i; | |
179 VtBlock *b; | |
180 u32int bn, size; | |
181 VtEntry e; | |
182 int epb; | |
183 VtFile *rr; | |
184 u32int offset; | |
185 | |
186 assert(ISLOCKED(r)); | |
187 assert(type == VtDirType || type == VtDataType); | |
188 | |
189 if(!r->dir){ | |
190 werrstr(ENotDir); | |
191 return nil; | |
192 } | |
193 | |
194 epb = r->dsize/VtEntrySize; | |
195 | |
196 size = vtfilegetdirsize(r); | |
197 /* | |
198 * look at a random block to see if we can find an empty entry | |
199 */ | |
200 if(o == -1){ | |
201 offset = lnrand(size+1); | |
202 offset -= offset % epb; | |
203 }else | |
204 offset = o; | |
205 | |
206 /* try the given block and then try the last block */ | |
207 for(;;){ | |
208 bn = offset/epb; | |
209 b = vtfileblock(r, bn, VtORDWR); | |
210 if(b == nil) | |
211 return nil; | |
212 for(i=offset%r->epb; i<epb; i++){ | |
213 if(vtentryunpack(&e, b->data, i) < 0) | |
214 continue; | |
215 if((e.flags&VtEntryActive) == 0 && e.gen != ~0) | |
216 goto Found; | |
217 } | |
218 vtblockput(b); | |
219 if(offset == size){ | |
220 fprint(2, "vtfilecreate: cannot happen\n"); | |
221 werrstr("vtfilecreate: cannot happen"); | |
222 return nil; | |
223 } | |
224 offset = size; | |
225 } | |
226 | |
227 Found: | |
228 /* found an entry - gen already set */ | |
229 e.psize = psize; | |
230 e.dsize = dsize; | |
231 e.flags = VtEntryActive; | |
232 e.type = type; | |
233 e.size = 0; | |
234 memmove(e.score, vtzeroscore, VtScoreSize); | |
235 vtentrypack(&e, b->data, i); | |
236 | |
237 offset = bn*epb + i; | |
238 if(offset+1 > size){ | |
239 if(vtfilesetdirsize(r, offset+1) < 0){ | |
240 vtblockput(b); | |
241 return nil; | |
242 } | |
243 } | |
244 | |
245 rr = vtfilealloc(r->c, b, r, offset, VtORDWR); | |
246 vtblockput(b); | |
247 return rr; | |
248 } | |
249 | |
250 static int | |
251 vtfilekill(VtFile *r, int doremove) | |
252 { | |
253 VtEntry e; | |
254 VtBlock *b; | |
255 | |
256 assert(ISLOCKED(r)); | |
257 b = fileload(r, &e); | |
258 if(b == nil) | |
259 return -1; | |
260 | |
261 if(doremove==0 && e.size == 0){ | |
262 /* already truncated */ | |
263 vtblockput(b); | |
264 return 0; | |
265 } | |
266 | |
267 if(doremove){ | |
268 if(e.gen != ~0) | |
269 e.gen++; | |
270 e.dsize = 0; | |
271 e.psize = 0; | |
272 e.flags = 0; | |
273 }else | |
274 e.flags &= ~VtEntryLocal; | |
275 e.type = 0; | |
276 e.size = 0; | |
277 memmove(e.score, vtzeroscore, VtScoreSize); | |
278 vtentrypack(&e, b->data, r->offset % r->epb); | |
279 vtblockput(b); | |
280 | |
281 if(doremove){ | |
282 vtfileunlock(r); | |
283 vtfileclose(r); | |
284 } | |
285 | |
286 return 0; | |
287 } | |
288 | |
289 int | |
290 vtfileremove(VtFile *r) | |
291 { | |
292 return vtfilekill(r, 1); | |
293 } | |
294 | |
295 int | |
296 vtfiletruncate(VtFile *r) | |
297 { | |
298 return vtfilekill(r, 0); | |
299 } | |
300 | |
301 uvlong | |
302 vtfilegetsize(VtFile *r) | |
303 { | |
304 VtEntry e; | |
305 VtBlock *b; | |
306 | |
307 assert(ISLOCKED(r)); | |
308 b = fileload(r, &e); | |
309 if(b == nil) | |
310 return ~(uvlong)0; | |
311 vtblockput(b); | |
312 | |
313 return e.size; | |
314 } | |
315 | |
316 static int | |
317 shrinksize(VtFile *r, VtEntry *e, uvlong size) | |
318 { | |
319 int i, depth, bsiz, type, isdir, ppb; | |
320 uvlong ptrsz; | |
321 uchar score[VtScoreSize]; | |
322 VtBlock *b; | |
323 | |
324 b = vtcacheglobal(r->c, e->score, e->type, r->dsize); | |
325 if(b == nil) | |
326 return -1; | |
327 | |
328 ptrsz = e->dsize; | |
329 ppb = e->psize/VtScoreSize; | |
330 type = e->type; | |
331 depth = DEPTH(type); | |
332 for(i=0; i+1<depth; i++) | |
333 ptrsz *= ppb; | |
334 | |
335 isdir = r->dir; | |
336 while(DEPTH(type) > 0){ | |
337 if(b->addr == NilBlock){ | |
338 /* not worth copying the block just so we can ze… | |
339 vtblockput(b); | |
340 return -1; | |
341 } | |
342 | |
343 /* | |
344 * invariant: each pointer in the tree rooted at b accou… | |
345 */ | |
346 | |
347 /* zero the pointers to unnecessary blocks */ | |
348 i = (size+ptrsz-1)/ptrsz; | |
349 for(; i<ppb; i++) | |
350 memmove(b->data+i*VtScoreSize, vtzeroscore, VtSc… | |
351 | |
352 /* recurse (go around again) on the partially necessary … | |
353 i = size/ptrsz; | |
354 size = size%ptrsz; | |
355 if(size == 0){ | |
356 vtblockput(b); | |
357 return 0; | |
358 } | |
359 ptrsz /= ppb; | |
360 type--; | |
361 memmove(score, b->data+i*VtScoreSize, VtScoreSize); | |
362 vtblockput(b); | |
363 if(type == VtDataType || type == VtDirType) | |
364 bsiz = r->dsize; | |
365 else | |
366 bsiz = r->psize; | |
367 b = vtcacheglobal(r->c, score, type, bsiz); | |
368 if(b == nil) | |
369 return -1; | |
370 } | |
371 | |
372 if(b->addr == NilBlock){ | |
373 vtblockput(b); | |
374 return -1; | |
375 } | |
376 | |
377 /* | |
378 * No one ever truncates BtDir blocks. | |
379 */ | |
380 if(depth==0 && !isdir && e->dsize > size) | |
381 memset(b->data+size, 0, e->dsize-size); | |
382 vtblockput(b); | |
383 return 0; | |
384 } | |
385 | |
386 int | |
387 vtfilesetsize(VtFile *r, u64int size) | |
388 { | |
389 int depth, edepth; | |
390 VtEntry e; | |
391 VtBlock *b; | |
392 | |
393 assert(ISLOCKED(r)); | |
394 if(size == 0) | |
395 return vtfiletruncate(r); | |
396 | |
397 if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){ | |
398 werrstr(ETooBig); | |
399 return -1; | |
400 } | |
401 | |
402 b = fileload(r, &e); | |
403 if(b == nil) | |
404 return -1; | |
405 | |
406 /* quick out */ | |
407 if(e.size == size){ | |
408 vtblockput(b); | |
409 return 0; | |
410 } | |
411 | |
412 depth = sizetodepth(size, e.psize, e.dsize); | |
413 edepth = DEPTH(e.type); | |
414 if(depth < edepth){ | |
415 if(shrinkdepth(r, b, &e, depth) < 0){ | |
416 vtblockput(b); | |
417 return -1; | |
418 } | |
419 }else if(depth > edepth){ | |
420 if(growdepth(r, b, &e, depth) < 0){ | |
421 vtblockput(b); | |
422 return -1; | |
423 } | |
424 } | |
425 | |
426 if(size < e.size) | |
427 shrinksize(r, &e, size); | |
428 | |
429 e.size = size; | |
430 vtentrypack(&e, b->data, r->offset % r->epb); | |
431 vtblockput(b); | |
432 | |
433 return 0; | |
434 } | |
435 | |
436 int | |
437 vtfilesetdirsize(VtFile *r, u32int ds) | |
438 { | |
439 uvlong size; | |
440 int epb; | |
441 | |
442 assert(ISLOCKED(r)); | |
443 epb = r->dsize/VtEntrySize; | |
444 | |
445 size = (uvlong)r->dsize*(ds/epb); | |
446 size += VtEntrySize*(ds%epb); | |
447 return vtfilesetsize(r, size); | |
448 } | |
449 | |
450 u32int | |
451 vtfilegetdirsize(VtFile *r) | |
452 { | |
453 ulong ds; | |
454 uvlong size; | |
455 int epb; | |
456 | |
457 assert(ISLOCKED(r)); | |
458 epb = r->dsize/VtEntrySize; | |
459 | |
460 size = vtfilegetsize(r); | |
461 ds = epb*(size/r->dsize); | |
462 ds += (size%r->dsize)/VtEntrySize; | |
463 return ds; | |
464 } | |
465 | |
466 int | |
467 vtfilegetentry(VtFile *r, VtEntry *e) | |
468 { | |
469 VtBlock *b; | |
470 | |
471 assert(ISLOCKED(r)); | |
472 b = fileload(r, e); | |
473 if(b == nil) | |
474 return -1; | |
475 vtblockput(b); | |
476 | |
477 return 0; | |
478 } | |
479 | |
480 int | |
481 vtfilesetentry(VtFile *r, VtEntry *e) | |
482 { | |
483 VtBlock *b; | |
484 VtEntry ee; | |
485 | |
486 assert(ISLOCKED(r)); | |
487 b = fileload(r, &ee); | |
488 if(b == nil) | |
489 return -1; | |
490 vtentrypack(e, b->data, r->offset % r->epb); | |
491 vtblockput(b); | |
492 return 0; | |
493 } | |
494 | |
495 static VtBlock * | |
496 blockwalk(VtFile *r, VtBlock *p, int index, VtCache *c, int mode, VtEntr… | |
497 { | |
498 VtBlock *b; | |
499 int type, size; | |
500 uchar *score; | |
501 | |
502 switch(p->type){ | |
503 case VtDataType: | |
504 assert(0); | |
505 case VtDirType: | |
506 type = e->type; | |
507 score = e->score; | |
508 break; | |
509 default: | |
510 type = p->type - 1; | |
511 score = p->data+index*VtScoreSize; | |
512 break; | |
513 } | |
514 /*print("walk from %V/%d ty %d to %V ty %d\n", p->score, index, p->type,… | |
515 | |
516 if(type == VtDirType || type == VtDataType) | |
517 size = r->dsize; | |
518 else | |
519 size = r->psize; | |
520 if(mode == VtOWRITE && vtglobaltolocal(score) == NilBlock){ | |
521 b = vtcacheallocblock(c, type, size); | |
522 if(b) | |
523 goto HaveCopy; | |
524 }else | |
525 b = vtcacheglobal(c, score, type, size); | |
526 | |
527 if(b == nil || mode == VtOREAD) | |
528 return b; | |
529 | |
530 if(vtglobaltolocal(b->score) != NilBlock) | |
531 return b; | |
532 | |
533 /* | |
534 * Copy on write. | |
535 */ | |
536 e->flags |= VtEntryLocal; | |
537 | |
538 b = vtblockcopy(b/*, e->tag, fs->ehi, fs->elo*/); | |
539 if(b == nil) | |
540 return nil; | |
541 | |
542 HaveCopy: | |
543 if(p->type == VtDirType){ | |
544 memmove(e->score, b->score, VtScoreSize); | |
545 vtentrypack(e, p->data, index); | |
546 }else{ | |
547 memmove(p->data+index*VtScoreSize, b->score, VtScoreSize… | |
548 } | |
549 return b; | |
550 } | |
551 | |
552 /* | |
553 * Change the depth of the VtFile r. | |
554 * The entry e for r is contained in block p. | |
555 */ | |
556 static int | |
557 growdepth(VtFile *r, VtBlock *p, VtEntry *e, int depth) | |
558 { | |
559 VtBlock *b, *bb; | |
560 | |
561 assert(ISLOCKED(r)); | |
562 assert(depth <= VtPointerDepth); | |
563 | |
564 b = vtcacheglobal(r->c, e->score, e->type, r->dsize); | |
565 if(b == nil) | |
566 return -1; | |
567 | |
568 /* | |
569 * Keep adding layers until we get to the right depth | |
570 * or an error occurs. | |
571 */ | |
572 while(DEPTH(e->type) < depth){ | |
573 bb = vtcacheallocblock(r->c, e->type+1, r->psize); | |
574 if(bb == nil) | |
575 break; | |
576 memmove(bb->data, b->score, VtScoreSize); | |
577 memmove(e->score, bb->score, VtScoreSize); | |
578 e->type++; | |
579 e->flags |= VtEntryLocal; | |
580 vtblockput(b); | |
581 b = bb; | |
582 } | |
583 | |
584 vtentrypack(e, p->data, r->offset % r->epb); | |
585 vtblockput(b); | |
586 | |
587 if(DEPTH(e->type) == depth) | |
588 return 0; | |
589 return -1; | |
590 } | |
591 | |
592 static int | |
593 shrinkdepth(VtFile *r, VtBlock *p, VtEntry *e, int depth) | |
594 { | |
595 VtBlock *b, *nb, *ob, *rb; | |
596 | |
597 assert(ISLOCKED(r)); | |
598 assert(depth <= VtPointerDepth); | |
599 | |
600 rb = vtcacheglobal(r->c, e->score, e->type, r->psize); | |
601 if(rb == nil) | |
602 return -1; | |
603 | |
604 /* | |
605 * Walk down to the new root block. | |
606 * We may stop early, but something is better than nothing. | |
607 */ | |
608 | |
609 ob = nil; | |
610 b = rb; | |
611 for(; DEPTH(e->type) > depth; e->type--){ | |
612 nb = vtcacheglobal(r->c, b->data, e->type-1, r->psize); | |
613 if(nb == nil) | |
614 break; | |
615 if(ob!=nil && ob!=rb) | |
616 vtblockput(ob); | |
617 ob = b; | |
618 b = nb; | |
619 } | |
620 | |
621 if(b == rb){ | |
622 vtblockput(rb); | |
623 return 0; | |
624 } | |
625 | |
626 /* | |
627 * Right now, e points at the root block rb, b is the new root b… | |
628 * and ob points at b. To update: | |
629 * | |
630 * (i) change e to point at b | |
631 * (ii) zero the pointer ob -> b | |
632 * (iii) free the root block | |
633 * | |
634 * p (the block containing e) must be written before | |
635 * anything else. | |
636 */ | |
637 | |
638 /* (i) */ | |
639 memmove(e->score, b->score, VtScoreSize); | |
640 vtentrypack(e, p->data, r->offset % r->epb); | |
641 | |
642 /* (ii) */ | |
643 memmove(ob->data, vtzeroscore, VtScoreSize); | |
644 | |
645 /* (iii) */ | |
646 vtblockput(rb); | |
647 if(ob!=nil && ob!=rb) | |
648 vtblockput(ob); | |
649 vtblockput(b); | |
650 | |
651 if(DEPTH(e->type) == depth) | |
652 return 0; | |
653 return -1; | |
654 } | |
655 | |
656 static int | |
657 mkindices(VtEntry *e, u32int bn, int *index) | |
658 { | |
659 int i, np; | |
660 | |
661 memset(index, 0, (VtPointerDepth+1)*sizeof(int)); | |
662 | |
663 np = e->psize/VtScoreSize; | |
664 for(i=0; bn > 0; i++){ | |
665 if(i >= VtPointerDepth){ | |
666 werrstr("bad address 0x%lux", (ulong)bn); | |
667 return -1; | |
668 } | |
669 index[i] = bn % np; | |
670 bn /= np; | |
671 } | |
672 return i; | |
673 } | |
674 | |
675 VtBlock * | |
676 vtfileblock(VtFile *r, u32int bn, int mode) | |
677 { | |
678 VtBlock *b, *bb; | |
679 int index[VtPointerDepth+1]; | |
680 VtEntry e; | |
681 int i; | |
682 int m; | |
683 | |
684 assert(ISLOCKED(r)); | |
685 assert(bn != NilBlock); | |
686 | |
687 b = fileload(r, &e); | |
688 if(b == nil) | |
689 return nil; | |
690 | |
691 i = mkindices(&e, bn, index); | |
692 if(i < 0) | |
693 goto Err; | |
694 if(i > DEPTH(e.type)){ | |
695 if(mode == VtOREAD){ | |
696 werrstr("bad address 0x%lux", (ulong)bn); | |
697 goto Err; | |
698 } | |
699 index[i] = 0; | |
700 if(growdepth(r, b, &e, i) < 0) | |
701 goto Err; | |
702 } | |
703 | |
704 assert(b->type == VtDirType); | |
705 | |
706 index[DEPTH(e.type)] = r->offset % r->epb; | |
707 | |
708 /* mode for intermediate block */ | |
709 m = mode; | |
710 if(m == VtOWRITE) | |
711 m = VtORDWR; | |
712 | |
713 for(i=DEPTH(e.type); i>=0; i--){ | |
714 bb = blockwalk(r, b, index[i], r->c, i==0 ? mode : m, &e… | |
715 if(bb == nil) | |
716 goto Err; | |
717 vtblockput(b); | |
718 b = bb; | |
719 } | |
720 b->pc = getcallerpc(&r); | |
721 return b; | |
722 Err: | |
723 vtblockput(b); | |
724 return nil; | |
725 } | |
726 | |
727 int | |
728 vtfileblockscore(VtFile *r, u32int bn, uchar score[VtScoreSize]) | |
729 { | |
730 VtBlock *b, *bb; | |
731 int index[VtPointerDepth+1]; | |
732 VtEntry e; | |
733 int i; | |
734 | |
735 assert(ISLOCKED(r)); | |
736 assert(bn != NilBlock); | |
737 | |
738 b = fileload(r, &e); | |
739 if(b == nil) | |
740 return -1; | |
741 | |
742 if(DEPTH(e.type) == 0){ | |
743 memmove(score, e.score, VtScoreSize); | |
744 vtblockput(b); | |
745 return 0; | |
746 } | |
747 | |
748 i = mkindices(&e, bn, index); | |
749 if(i < 0){ | |
750 vtblockput(b); | |
751 return -1; | |
752 } | |
753 if(i > DEPTH(e.type)){ | |
754 memmove(score, vtzeroscore, VtScoreSize); | |
755 vtblockput(b); | |
756 return 0; | |
757 } | |
758 | |
759 index[DEPTH(e.type)] = r->offset % r->epb; | |
760 | |
761 for(i=DEPTH(e.type); i>=1; i--){ | |
762 bb = blockwalk(r, b, index[i], r->c, VtOREAD, &e); | |
763 if(bb == nil) | |
764 goto Err; | |
765 vtblockput(b); | |
766 b = bb; | |
767 if(memcmp(b->score, vtzeroscore, VtScoreSize) == 0) | |
768 break; | |
769 } | |
770 | |
771 memmove(score, b->data+index[0]*VtScoreSize, VtScoreSize); | |
772 vtblockput(b); | |
773 return 0; | |
774 | |
775 Err: | |
776 vtblockput(b); | |
777 return -1; | |
778 } | |
779 | |
780 void | |
781 vtfileincref(VtFile *r) | |
782 { | |
783 qlock(&r->lk); | |
784 r->ref++; | |
785 qunlock(&r->lk); | |
786 } | |
787 | |
788 void | |
789 vtfileclose(VtFile *r) | |
790 { | |
791 if(r == nil) | |
792 return; | |
793 qlock(&r->lk); | |
794 r->ref--; | |
795 if(r->ref){ | |
796 qunlock(&r->lk); | |
797 return; | |
798 } | |
799 assert(r->ref == 0); | |
800 qunlock(&r->lk); | |
801 if(r->parent) | |
802 vtfileclose(r->parent); | |
803 memset(r, ~0, sizeof(*r)); | |
804 vtfree(r); | |
805 } | |
806 | |
807 /* | |
808 * Retrieve the block containing the entry for r. | |
809 * If a snapshot has happened, we might need | |
810 * to get a new copy of the block. We avoid this | |
811 * in the common case by caching the score for | |
812 * the block and the last epoch in which it was valid. | |
813 * | |
814 * We use r->mode to tell the difference between active | |
815 * file system VtFiles (VtORDWR) and VtFiles for the | |
816 * snapshot file system (VtOREAD). | |
817 */ | |
818 static VtBlock* | |
819 fileloadblock(VtFile *r, int mode) | |
820 { | |
821 char e[ERRMAX]; | |
822 u32int addr; | |
823 VtBlock *b; | |
824 | |
825 switch(r->mode){ | |
826 default: | |
827 assert(0); | |
828 case VtORDWR: | |
829 assert(r->mode == VtORDWR); | |
830 if(r->local == 1){ | |
831 b = vtcacheglobal(r->c, r->score, VtDirType, r->… | |
832 if(b == nil) | |
833 return nil; | |
834 b->pc = getcallerpc(&r); | |
835 return b; | |
836 } | |
837 assert(r->parent != nil); | |
838 if(vtfilelock(r->parent, VtORDWR) < 0) | |
839 return nil; | |
840 b = vtfileblock(r->parent, r->offset/r->epb, VtORDWR); | |
841 vtfileunlock(r->parent); | |
842 if(b == nil) | |
843 return nil; | |
844 memmove(r->score, b->score, VtScoreSize); | |
845 r->local = 1; | |
846 return b; | |
847 | |
848 case VtOREAD: | |
849 if(mode == VtORDWR){ | |
850 werrstr("read/write lock of read-only file"); | |
851 return nil; | |
852 } | |
853 addr = vtglobaltolocal(r->score); | |
854 if(addr == NilBlock) | |
855 return vtcacheglobal(r->c, r->score, VtDirType, … | |
856 | |
857 b = vtcachelocal(r->c, addr, VtDirType); | |
858 if(b) | |
859 return b; | |
860 | |
861 /* | |
862 * If it failed because the epochs don't match, the bloc… | |
863 * archived and reclaimed. Rewalk from the parent and g… | |
864 * new pointer. This can't happen in the VtORDWR case | |
865 * above because blocks in the current epoch don't get | |
866 * reclaimed. The fact that we're VtOREAD means we're | |
867 * a snapshot. (Or else the file system is read-only, b… | |
868 * the archiver isn't going around deleting blocks.) | |
869 */ | |
870 rerrstr(e, sizeof e); | |
871 if(strcmp(e, ELabelMismatch) == 0){ | |
872 if(vtfilelock(r->parent, VtOREAD) < 0) | |
873 return nil; | |
874 b = vtfileblock(r->parent, r->offset/r->epb, VtO… | |
875 vtfileunlock(r->parent); | |
876 if(b){ | |
877 fprint(2, "vtfilealloc: lost %V found %V… | |
878 r->score, b->score); | |
879 memmove(r->score, b->score, VtScoreSize); | |
880 return b; | |
881 } | |
882 } | |
883 return nil; | |
884 } | |
885 } | |
886 | |
887 int | |
888 vtfilelock(VtFile *r, int mode) | |
889 { | |
890 VtBlock *b; | |
891 | |
892 if(mode == -1) | |
893 mode = r->mode; | |
894 | |
895 b = fileloadblock(r, mode); | |
896 if(b == nil) | |
897 return -1; | |
898 /* | |
899 * The fact that we are holding b serves as the | |
900 * lock entitling us to write to r->b. | |
901 */ | |
902 assert(r->b == nil); | |
903 r->b = b; | |
904 b->pc = getcallerpc(&r); | |
905 return 0; | |
906 } | |
907 | |
908 /* | |
909 * Lock two (usually sibling) VtFiles. This needs special care | |
910 * because the Entries for both vtFiles might be in the same block. | |
911 * We also try to lock blocks in left-to-right order within the tree. | |
912 */ | |
913 int | |
914 vtfilelock2(VtFile *r, VtFile *rr, int mode) | |
915 { | |
916 VtBlock *b, *bb; | |
917 | |
918 if(rr == nil) | |
919 return vtfilelock(r, mode); | |
920 | |
921 if(mode == -1) | |
922 mode = r->mode; | |
923 | |
924 if(r->parent==rr->parent && r->offset/r->epb == rr->offset/rr->e… | |
925 b = fileloadblock(r, mode); | |
926 if(b == nil) | |
927 return -1; | |
928 vtblockduplock(b); | |
929 bb = b; | |
930 }else if(r->parent==rr->parent || r->offset > rr->offset){ | |
931 bb = fileloadblock(rr, mode); | |
932 b = fileloadblock(r, mode); | |
933 }else{ | |
934 b = fileloadblock(r, mode); | |
935 bb = fileloadblock(rr, mode); | |
936 } | |
937 if(b == nil || bb == nil){ | |
938 if(b) | |
939 vtblockput(b); | |
940 if(bb) | |
941 vtblockput(bb); | |
942 return -1; | |
943 } | |
944 | |
945 /* | |
946 * The fact that we are holding b and bb serves | |
947 * as the lock entitling us to write to r->b and rr->b. | |
948 */ | |
949 r->b = b; | |
950 rr->b = bb; | |
951 b->pc = getcallerpc(&r); | |
952 bb->pc = getcallerpc(&r); | |
953 return 0; | |
954 } | |
955 | |
956 void | |
957 vtfileunlock(VtFile *r) | |
958 { | |
959 VtBlock *b; | |
960 | |
961 if(r->b == nil){ | |
962 fprint(2, "vtfileunlock: already unlocked\n"); | |
963 abort(); | |
964 } | |
965 b = r->b; | |
966 r->b = nil; | |
967 vtblockput(b); | |
968 } | |
969 | |
970 static VtBlock* | |
971 fileload(VtFile *r, VtEntry *e) | |
972 { | |
973 VtBlock *b; | |
974 | |
975 assert(ISLOCKED(r)); | |
976 b = r->b; | |
977 if(vtentryunpack(e, b->data, r->offset % r->epb) < 0) | |
978 return nil; | |
979 vtblockduplock(b); | |
980 return b; | |
981 } | |
982 | |
983 static int | |
984 sizetodepth(uvlong s, int psize, int dsize) | |
985 { | |
986 int np; | |
987 int d; | |
988 | |
989 /* determine pointer depth */ | |
990 np = psize/VtScoreSize; | |
991 s = (s + dsize - 1)/dsize; | |
992 for(d = 0; s > 1; d++) | |
993 s = (s + np - 1)/np; | |
994 return d; | |
995 } | |
996 | |
997 long | |
998 vtfileread(VtFile *f, void *data, long count, vlong offset) | |
999 { | |
1000 int frag; | |
1001 VtBlock *b; | |
1002 VtEntry e; | |
1003 | |
1004 assert(ISLOCKED(f)); | |
1005 | |
1006 vtfilegetentry(f, &e); | |
1007 if(count == 0) | |
1008 return 0; | |
1009 if(count < 0 || offset < 0){ | |
1010 werrstr("vtfileread: bad offset or count"); | |
1011 return -1; | |
1012 } | |
1013 if(offset >= e.size) | |
1014 return 0; | |
1015 | |
1016 if(offset+count > e.size) | |
1017 count = e.size - offset; | |
1018 | |
1019 frag = offset % e.dsize; | |
1020 if(frag+count > e.dsize) | |
1021 count = e.dsize - frag; | |
1022 | |
1023 b = vtfileblock(f, offset/e.dsize, VtOREAD); | |
1024 if(b == nil) | |
1025 return -1; | |
1026 | |
1027 memmove(data, b->data+frag, count); | |
1028 vtblockput(b); | |
1029 return count; | |
1030 } | |
1031 | |
1032 static long | |
1033 filewrite1(VtFile *f, void *data, long count, vlong offset) | |
1034 { | |
1035 int frag, m; | |
1036 VtBlock *b; | |
1037 VtEntry e; | |
1038 | |
1039 vtfilegetentry(f, &e); | |
1040 if(count < 0 || offset < 0){ | |
1041 werrstr("vtfilewrite: bad offset or count"); | |
1042 return -1; | |
1043 } | |
1044 | |
1045 frag = offset % e.dsize; | |
1046 if(frag+count > e.dsize) | |
1047 count = e.dsize - frag; | |
1048 | |
1049 m = VtORDWR; | |
1050 if(frag == 0 && count == e.dsize) | |
1051 m = VtOWRITE; | |
1052 | |
1053 b = vtfileblock(f, offset/e.dsize, m); | |
1054 if(b == nil) | |
1055 return -1; | |
1056 | |
1057 memmove(b->data+frag, data, count); | |
1058 if(m == VtOWRITE && frag+count < e.dsize) | |
1059 memset(b->data+frag+count, 0, e.dsize-frag-count); | |
1060 | |
1061 if(offset+count > e.size){ | |
1062 vtfilegetentry(f, &e); | |
1063 e.size = offset+count; | |
1064 vtfilesetentry(f, &e); | |
1065 } | |
1066 | |
1067 vtblockput(b); | |
1068 return count; | |
1069 } | |
1070 | |
1071 long | |
1072 vtfilewrite(VtFile *f, void *data, long count, vlong offset) | |
1073 { | |
1074 long tot, m; | |
1075 | |
1076 assert(ISLOCKED(f)); | |
1077 | |
1078 tot = 0; | |
1079 m = 0; | |
1080 while(tot < count){ | |
1081 m = filewrite1(f, (char*)data+tot, count-tot, offset+tot… | |
1082 if(m <= 0) | |
1083 break; | |
1084 tot += m; | |
1085 } | |
1086 if(tot==0) | |
1087 return m; | |
1088 return tot; | |
1089 } | |
1090 | |
1091 static int | |
1092 flushblock(VtCache *c, VtBlock *bb, uchar score[VtScoreSize], int ppb, i… | |
1093 int type) | |
1094 { | |
1095 u32int addr; | |
1096 VtBlock *b; | |
1097 VtEntry e; | |
1098 int i; | |
1099 | |
1100 addr = vtglobaltolocal(score); | |
1101 if(addr == NilBlock) | |
1102 return 0; | |
1103 | |
1104 if(bb){ | |
1105 b = bb; | |
1106 if(memcmp(b->score, score, VtScoreSize) != 0) | |
1107 abort(); | |
1108 }else | |
1109 if((b = vtcachelocal(c, addr, type)) == nil) | |
1110 return -1; | |
1111 | |
1112 switch(type){ | |
1113 case VtDataType: | |
1114 break; | |
1115 | |
1116 case VtDirType: | |
1117 for(i=0; i<epb; i++){ | |
1118 if(vtentryunpack(&e, b->data, i) < 0) | |
1119 goto Err; | |
1120 if(!(e.flags&VtEntryActive)) | |
1121 continue; | |
1122 if(flushblock(c, nil, e.score, e.psize/VtScoreSi… | |
1123 e.type) < 0) | |
1124 goto Err; | |
1125 vtentrypack(&e, b->data, i); | |
1126 } | |
1127 break; | |
1128 | |
1129 default: /* VtPointerTypeX */ | |
1130 for(i=0; i<ppb; i++){ | |
1131 if(flushblock(c, nil, b->data+VtScoreSize*i, ppb… | |
1132 goto Err; | |
1133 } | |
1134 break; | |
1135 } | |
1136 | |
1137 if(vtblockwrite(b) < 0) | |
1138 goto Err; | |
1139 memmove(score, b->score, VtScoreSize); | |
1140 if(b != bb) | |
1141 vtblockput(b); | |
1142 return 0; | |
1143 | |
1144 Err: | |
1145 if(b != bb) | |
1146 vtblockput(b); | |
1147 return -1; | |
1148 } | |
1149 | |
1150 int | |
1151 vtfileflush(VtFile *f) | |
1152 { | |
1153 int ret; | |
1154 VtBlock *b; | |
1155 VtEntry e; | |
1156 | |
1157 assert(ISLOCKED(f)); | |
1158 b = fileload(f, &e); | |
1159 if(!(e.flags&VtEntryLocal)){ | |
1160 vtblockput(b); | |
1161 return 0; | |
1162 } | |
1163 | |
1164 ret = flushblock(f->c, nil, e.score, e.psize/VtScoreSize, e.dsiz… | |
1165 e.type); | |
1166 if(ret < 0){ | |
1167 vtblockput(b); | |
1168 return -1; | |
1169 } | |
1170 | |
1171 vtentrypack(&e, b->data, f->offset % f->epb); | |
1172 vtblockput(b); | |
1173 return 0; | |
1174 } | |
1175 | |
1176 int | |
1177 vtfileflushbefore(VtFile *r, u64int offset) | |
1178 { | |
1179 VtBlock *b, *bb; | |
1180 VtEntry e; | |
1181 int i, base, depth, ppb, epb, doflush; | |
1182 int index[VtPointerDepth+1], j, ret; | |
1183 VtBlock *bi[VtPointerDepth+2]; | |
1184 uchar *score; | |
1185 | |
1186 assert(ISLOCKED(r)); | |
1187 if(offset == 0) | |
1188 return 0; | |
1189 | |
1190 b = fileload(r, &e); | |
1191 if(b == nil) | |
1192 return -1; | |
1193 | |
1194 /* | |
1195 * compute path through tree for the last written byte and the n… | |
1196 */ | |
1197 ret = -1; | |
1198 memset(bi, 0, sizeof bi); | |
1199 depth = DEPTH(e.type); | |
1200 bi[depth+1] = b; | |
1201 i = mkindices(&e, (offset-1)/e.dsize, index); | |
1202 if(i < 0) | |
1203 goto Err; | |
1204 if(i > depth) | |
1205 goto Err; | |
1206 ppb = e.psize / VtScoreSize; | |
1207 epb = e.dsize / VtEntrySize; | |
1208 | |
1209 /* | |
1210 * load the blocks along the last written byte | |
1211 */ | |
1212 index[depth] = r->offset % r->epb; | |
1213 for(i=depth; i>=0; i--){ | |
1214 bb = blockwalk(r, b, index[i], r->c, VtORDWR, &e); | |
1215 if(bb == nil) | |
1216 goto Err; | |
1217 bi[i] = bb; | |
1218 b = bb; | |
1219 } | |
1220 ret = 0; | |
1221 | |
1222 /* | |
1223 * walk up the path from leaf to root, flushing anything that | |
1224 * has been finished. | |
1225 */ | |
1226 base = e.type&~VtTypeDepthMask; | |
1227 for(i=0; i<=depth; i++){ | |
1228 doflush = 0; | |
1229 if(i == 0){ | |
1230 /* leaf: data or dir block */ | |
1231 if(offset%e.dsize == 0) | |
1232 doflush = 1; | |
1233 }else{ | |
1234 /* | |
1235 * interior node: pointer blocks. | |
1236 * specifically, b = bi[i] is a block whose inde… | |
1237 * points at bi[i-1]. | |
1238 */ | |
1239 b = bi[i]; | |
1240 | |
1241 /* | |
1242 * the index entries up to but not including ind… | |
1243 * finished blocks, so flush them for sure. | |
1244 */ | |
1245 for(j=0; j<index[i-1]; j++) | |
1246 if(flushblock(r->c, nil, b->data+j*VtSco… | |
1247 goto Err; | |
1248 | |
1249 /* | |
1250 * if index[i-1] is the last entry in the block … | |
1251 * (i.e. the kid is flushed), then we can flush … | |
1252 */ | |
1253 if(j==ppb-1 && vtglobaltolocal(b->data+j*VtScore… | |
1254 doflush = 1; | |
1255 } | |
1256 if(doflush){ | |
1257 if(i == depth) | |
1258 score = e.score; | |
1259 else | |
1260 score = bi[i+1]->data+index[i]*VtScoreSi… | |
1261 if(flushblock(r->c, bi[i], score, ppb, epb, base… | |
1262 goto Err; | |
1263 } | |
1264 } | |
1265 | |
1266 Err: | |
1267 /* top: entry. do this always so that the score is up-to-date */ | |
1268 vtentrypack(&e, bi[depth+1]->data, index[depth]); | |
1269 for(i=0; i<nelem(bi); i++) | |
1270 if(bi[i]) | |
1271 vtblockput(bi[i]); | |
1272 return ret; | |
1273 } |