1 #include "stdinc.h"
2 #include "vac.h"
3 #include "dat.h"
4 #include "fns.h"
5 #include "error.h"
6
7 #define debug 0
8
9 /*
10 * Vac file system. This is a simplified version of the same code in Fossil.
11 *
12 * The locking order in the tree is upward: a thread can hold the lock
13 * for a VacFile and then acquire the lock of f->up (the parent),
14 * but not vice-versa.
15 *
16 * A vac file is one or two venti files. Plain data files are one venti file,
17 * while directores are two: a venti data file containing traditional
18 * directory entries, and a venti directory file containing venti
19 * directory entries. The traditional directory entries in the data file
20 * contain integers indexing into the venti directory entry file.
21 * It's a little complicated, but it makes the data usable by standard
22 * tools like venti/copy.
23 *
24 */
25
26 static int filemetaflush(VacFile*, char*);
27
28 struct VacFile
29 {
30 VacFs *fs; /* immutable */
31
32 /* meta data for file: protected by the lk in the parent */
33 int ref; /* holds this data structure up */
34
35 int partial; /* file was never really open */
36 int removed; /* file has been removed */
37 int dirty; /* dir is dirty with respect to meta data in block */
38 u32int boff; /* block offset within msource for this file's metadata */
39 VacDir dir; /* metadata for this file */
40 VacFile *up; /* parent file */
41 VacFile *next; /* sibling */
42
43 RWLock lk; /* lock for the following */
44 VtFile *source; /* actual data */
45 VtFile *msource; /* metadata for children in a directory */
46 VacFile *down; /* children */
47 int mode;
48
49 uvlong qidoffset; /* qid offset */
50 };
51
52 static VacFile*
filealloc(VacFs * fs)53 filealloc(VacFs *fs)
54 {
55 VacFile *f;
56
57 f = vtmallocz(sizeof(VacFile));
58 f->ref = 1;
59 f->fs = fs;
60 f->boff = NilBlock;
61 f->mode = fs->mode;
62 return f;
63 }
64
65 static void
filefree(VacFile * f)66 filefree(VacFile *f)
67 {
68 vtfileclose(f->source);
69 vtfileclose(f->msource);
70 vdcleanup(&f->dir);
71 memset(f, ~0, sizeof *f); /* paranoia */
72 vtfree(f);
73 }
74
75 static int
chksource(VacFile * f)76 chksource(VacFile *f)
77 {
78 if(f->partial)
79 return 0;
80
81 if(f->source == nil
82 || ((f->dir.mode & ModeDir) && f->msource == nil)){
83 werrstr(ERemoved);
84 return -1;
85 }
86 return 0;
87 }
88
89 static int
filelock(VacFile * f)90 filelock(VacFile *f)
91 {
92 wlock(&f->lk);
93 if(chksource(f) < 0){
94 wunlock(&f->lk);
95 return -1;
96 }
97 return 0;
98 }
99
100 static void
fileunlock(VacFile * f)101 fileunlock(VacFile *f)
102 {
103 wunlock(&f->lk);
104 }
105
106 static int
filerlock(VacFile * f)107 filerlock(VacFile *f)
108 {
109 rlock(&f->lk);
110 if(chksource(f) < 0){
111 runlock(&f->lk);
112 return -1;
113 }
114 return 0;
115 }
116
117 static void
filerunlock(VacFile * f)118 filerunlock(VacFile *f)
119 {
120 runlock(&f->lk);
121 }
122
123 /*
124 * The file metadata, like f->dir and f->ref,
125 * are synchronized via the parent's lock.
126 * This is why locking order goes up.
127 */
128 static void
filemetalock(VacFile * f)129 filemetalock(VacFile *f)
130 {
131 assert(f->up != nil);
132 wlock(&f->up->lk);
133 }
134
135 static void
filemetaunlock(VacFile * f)136 filemetaunlock(VacFile *f)
137 {
138 wunlock(&f->up->lk);
139 }
140
141 uvlong
vacfilegetid(VacFile * f)142 vacfilegetid(VacFile *f)
143 {
144 /* immutable */
145 return f->qidoffset + f->dir.qid;
146 }
147
148 uvlong
vacfilegetqidoffset(VacFile * f)149 vacfilegetqidoffset(VacFile *f)
150 {
151 return f->qidoffset;
152 }
153
154 ulong
vacfilegetmcount(VacFile * f)155 vacfilegetmcount(VacFile *f)
156 {
157 ulong mcount;
158
159 filemetalock(f);
160 mcount = f->dir.mcount;
161 filemetaunlock(f);
162 return mcount;
163 }
164
165 ulong
vacfilegetmode(VacFile * f)166 vacfilegetmode(VacFile *f)
167 {
168 ulong mode;
169
170 filemetalock(f);
171 mode = f->dir.mode;
172 filemetaunlock(f);
173 return mode;
174 }
175
176 int
vacfileisdir(VacFile * f)177 vacfileisdir(VacFile *f)
178 {
179 /* immutable */
180 return (f->dir.mode & ModeDir) != 0;
181 }
182
183 int
vacfileisroot(VacFile * f)184 vacfileisroot(VacFile *f)
185 {
186 return f == f->fs->root;
187 }
188
189 /*
190 * The files are reference counted, and while the reference
191 * is bigger than zero, each file can be found in its parent's
192 * f->down list (chains via f->next), so that multiple threads
193 * end up sharing a VacFile* when referring to the same file.
194 *
195 * Each VacFile holds a reference to its parent.
196 */
197 VacFile*
vacfileincref(VacFile * vf)198 vacfileincref(VacFile *vf)
199 {
200 filemetalock(vf);
201 assert(vf->ref > 0);
202 vf->ref++;
203 filemetaunlock(vf);
204 return vf;
205 }
206
207 int
vacfiledecref(VacFile * f)208 vacfiledecref(VacFile *f)
209 {
210 VacFile *p, *q, **qq;
211
212 if(f->up == nil){
213 /* never linked in */
214 assert(f->ref == 1);
215 filefree(f);
216 return 0;
217 }
218
219 filemetalock(f);
220 f->ref--;
221 if(f->ref > 0){
222 filemetaunlock(f);
223 return -1;
224 }
225 assert(f->ref == 0);
226 assert(f->down == nil);
227
228 if(f->source && vtfilelock(f->source, -1) >= 0){
229 vtfileflush(f->source);
230 vtfileunlock(f->source);
231 }
232 if(f->msource && vtfilelock(f->msource, -1) >= 0){
233 vtfileflush(f->msource);
234 vtfileunlock(f->msource);
235 }
236
237 /*
238 * Flush f's directory information to the cache.
239 */
240 filemetaflush(f, nil);
241
242 p = f->up;
243 qq = &p->down;
244 for(q = *qq; q; q = *qq){
245 if(q == f)
246 break;
247 qq = &q->next;
248 }
249 assert(q != nil);
250 *qq = f->next;
251
252 filemetaunlock(f);
253 filefree(f);
254 vacfiledecref(p);
255 return 0;
256 }
257
258
259 /*
260 * Construct a vacfile for the root of a vac tree, given the
261 * venti file for the root information. That venti file is a
262 * directory file containing VtEntries for three more venti files:
263 * the two venti files making up the root directory, and a
264 * third venti file that would be the metadata half of the
265 * "root's parent".
266 *
267 * Fossil generates slightly different vac files, due to a now
268 * impossible-to-change bug, which contain a VtEntry
269 * for just one venti file, that itself contains the expected
270 * three directory entries. Sigh.
271 */
272 VacFile*
_vacfileroot(VacFs * fs,VtFile * r)273 _vacfileroot(VacFs *fs, VtFile *r)
274 {
275 int redirected;
276 char err[ERRMAX];
277 VtBlock *b;
278 VtFile *r0, *r1, *r2;
279 MetaBlock mb;
280 MetaEntry me;
281 VacFile *root, *mr;
282
283 redirected = 0;
284 Top:
285 b = nil;
286 root = nil;
287 mr = nil;
288 r1 = nil;
289 r2 = nil;
290
291 if(vtfilelock(r, -1) < 0)
292 return nil;
293 r0 = vtfileopen(r, 0, fs->mode);
294 if(debug)
295 fprint(2, "r0 %p\n", r0);
296 if(r0 == nil)
297 goto Err;
298 r2 = vtfileopen(r, 2, fs->mode);
299 if(debug)
300 fprint(2, "r2 %p\n", r2);
301 if(r2 == nil){
302 /*
303 * some vac files (e.g., from fossil)
304 * have an extra layer of indirection.
305 */
306 rerrstr(err, sizeof err);
307 if(!redirected && strstr(err, "not active")){
308 redirected = 1;
309 vtfileunlock(r);
310 r = r0;
311 goto Top;
312 }
313 goto Err;
314 }
315 r1 = vtfileopen(r, 1, fs->mode);
316 if(debug)
317 fprint(2, "r1 %p\n", r1);
318 if(r1 == nil)
319 goto Err;
320
321 mr = filealloc(fs);
322 mr->msource = r2;
323 r2 = nil;
324
325 root = filealloc(fs);
326 root->boff = 0;
327 root->up = mr;
328 root->source = r0;
329 r0 = nil;
330 root->msource = r1;
331 r1 = nil;
332
333 mr->down = root;
334 vtfileunlock(r);
335
336 if(vtfilelock(mr->msource, VtOREAD) < 0)
337 goto Err1;
338 b = vtfileblock(mr->msource, 0, VtOREAD);
339 vtfileunlock(mr->msource);
340 if(b == nil)
341 goto Err1;
342
343 if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
344 goto Err1;
345
346 meunpack(&me, &mb, 0);
347 if(vdunpack(&root->dir, &me) < 0)
348 goto Err1;
349 vtblockput(b);
350
351 return root;
352 Err:
353 vtfileunlock(r);
354 Err1:
355 vtblockput(b);
356 if(r0)
357 vtfileclose(r0);
358 if(r1)
359 vtfileclose(r1);
360 if(r2)
361 vtfileclose(r2);
362 if(mr)
363 filefree(mr);
364 if(root)
365 filefree(root);
366
367 return nil;
368 }
369
370 /*
371 * Vac directories are a sequence of metablocks, each of which
372 * contains a bunch of metaentries sorted by file name.
373 * The whole sequence isn't sorted, though, so you still have
374 * to look at every block to find a given name.
375 * Dirlookup looks in f for an element name elem.
376 * It returns a new VacFile with the dir, boff, and mode
377 * filled in, but the sources (venti files) are not, and f is
378 * not yet linked into the tree. These details must be taken
379 * care of by the caller.
380 *
381 * f must be locked, f->msource must not.
382 */
383 static VacFile*
dirlookup(VacFile * f,char * elem)384 dirlookup(VacFile *f, char *elem)
385 {
386 int i;
387 MetaBlock mb;
388 MetaEntry me;
389 VtBlock *b;
390 VtFile *meta;
391 VacFile *ff;
392 u32int bo, nb;
393
394 meta = f->msource;
395 b = nil;
396 if(vtfilelock(meta, -1) < 0)
397 return nil;
398 nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
399 for(bo=0; bo<nb; bo++){
400 b = vtfileblock(meta, bo, VtOREAD);
401 if(b == nil)
402 goto Err;
403 if(mbunpack(&mb, b->data, meta->dsize) < 0)
404 goto Err;
405 if(mbsearch(&mb, elem, &i, &me) >= 0){
406 ff = filealloc(f->fs);
407 if(vdunpack(&ff->dir, &me) < 0){
408 filefree(ff);
409 goto Err;
410 }
411 ff->qidoffset = f->qidoffset + ff->dir.qidoffset;
412 vtfileunlock(meta);
413 vtblockput(b);
414 ff->boff = bo;
415 ff->mode = f->mode;
416 return ff;
417 }
418 vtblockput(b);
419 b = nil;
420 }
421 werrstr(ENoFile);
422 /* fall through */
423 Err:
424 vtfileunlock(meta);
425 vtblockput(b);
426 return nil;
427 }
428
429 /*
430 * Open the venti file at offset in the directory f->source.
431 * f is locked.
432 */
433 static VtFile *
fileopensource(VacFile * f,u32int offset,u32int gen,int dir,uint mode)434 fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
435 {
436 VtFile *r;
437
438 if((r = vtfileopen(f->source, offset, mode)) == nil)
439 return nil;
440 if(r == nil)
441 return nil;
442 if(r->gen != gen){
443 werrstr(ERemoved);
444 vtfileclose(r);
445 return nil;
446 }
447 if(r->dir != dir && r->mode != -1){
448 werrstr(EBadMeta);
449 vtfileclose(r);
450 return nil;
451 }
452 return r;
453 }
454
455 VacFile*
vacfilegetparent(VacFile * f)456 vacfilegetparent(VacFile *f)
457 {
458 if(vacfileisroot(f))
459 return vacfileincref(f);
460 return vacfileincref(f->up);
461 }
462
463 /*
464 * Given an unlocked vacfile (directory) f,
465 * return the vacfile named elem in f.
466 * Interprets . and .. as a convenience to callers.
467 */
468 VacFile*
vacfilewalk(VacFile * f,char * elem)469 vacfilewalk(VacFile *f, char *elem)
470 {
471 VacFile *ff;
472
473 if(elem[0] == 0){
474 werrstr(EBadPath);
475 return nil;
476 }
477
478 if(!vacfileisdir(f)){
479 werrstr(ENotDir);
480 return nil;
481 }
482
483 if(strcmp(elem, ".") == 0)
484 return vacfileincref(f);
485
486 if(strcmp(elem, "..") == 0)
487 return vacfilegetparent(f);
488
489 if(filelock(f) < 0)
490 return nil;
491
492 for(ff = f->down; ff; ff=ff->next){
493 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
494 ff->ref++;
495 goto Exit;
496 }
497 }
498
499 ff = dirlookup(f, elem);
500 if(ff == nil)
501 goto Err;
502
503 if(ff->dir.mode & ModeSnapshot)
504 ff->mode = VtOREAD;
505
506 if(vtfilelock(f->source, f->mode) < 0)
507 goto Err;
508 if(ff->dir.mode & ModeDir){
509 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode);
510 ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode);
511 if(ff->source == nil || ff->msource == nil)
512 goto Err1;
513 }else{
514 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
515 if(ff->source == nil)
516 goto Err1;
517 }
518 vtfileunlock(f->source);
519
520 /* link in and up parent ref count */
521 ff->next = f->down;
522 f->down = ff;
523 ff->up = f;
524 vacfileincref(f);
525 Exit:
526 fileunlock(f);
527 return ff;
528
529 Err1:
530 vtfileunlock(f->source);
531 Err:
532 fileunlock(f);
533 if(ff != nil)
534 vacfiledecref(ff);
535 return nil;
536 }
537
538 /*
539 * Open a path in the vac file system:
540 * just walk each element one at a time.
541 */
542 VacFile*
vacfileopen(VacFs * fs,char * path)543 vacfileopen(VacFs *fs, char *path)
544 {
545 VacFile *f, *ff;
546 char *p, elem[VtMaxStringSize], *opath;
547 int n;
548
549 f = fs->root;
550 vacfileincref(f);
551 opath = path;
552 while(*path != 0){
553 for(p = path; *p && *p != '/'; p++)
554 ;
555 n = p - path;
556 if(n > 0){
557 if(n > VtMaxStringSize){
558 werrstr("%s: element too long", EBadPath);
559 goto Err;
560 }
561 memmove(elem, path, n);
562 elem[n] = 0;
563 ff = vacfilewalk(f, elem);
564 if(ff == nil){
565 werrstr("%.*s: %r", utfnlen(opath, p-opath), opath);
566 goto Err;
567 }
568 vacfiledecref(f);
569 f = ff;
570 }
571 if(*p == '/')
572 p++;
573 path = p;
574 }
575 return f;
576 Err:
577 vacfiledecref(f);
578 return nil;
579 }
580
581 /*
582 * Extract the score for the bn'th block in f.
583 */
584 int
vacfileblockscore(VacFile * f,u32int bn,u8int * score)585 vacfileblockscore(VacFile *f, u32int bn, u8int *score)
586 {
587 VtFile *s;
588 uvlong size;
589 int dsize, ret;
590
591 ret = -1;
592 if(filerlock(f) < 0)
593 return -1;
594 if(vtfilelock(f->source, VtOREAD) < 0)
595 goto out;
596
597 s = f->source;
598 dsize = s->dsize;
599 size = vtfilegetsize(s);
600 if((uvlong)bn*dsize >= size)
601 goto out1;
602 ret = vtfileblockscore(f->source, bn, score);
603
604 out1:
605 vtfileunlock(f->source);
606 out:
607 filerunlock(f);
608 return ret;
609 }
610
611 /*
612 * Read data from f.
613 */
614 int
vacfileread(VacFile * f,void * buf,int cnt,vlong offset)615 vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
616 {
617 int n;
618
619 if(offset < 0){
620 werrstr(EBadOffset);
621 return -1;
622 }
623 if(filerlock(f) < 0)
624 return -1;
625 if(vtfilelock(f->source, VtOREAD) < 0){
626 filerunlock(f);
627 return -1;
628 }
629 n = vtfileread(f->source, buf, cnt, offset);
630 vtfileunlock(f->source);
631 filerunlock(f);
632 return n;
633 }
634
635 static int
getentry(VtFile * f,VtEntry * e)636 getentry(VtFile *f, VtEntry *e)
637 {
638 if(vtfilelock(f, VtOREAD) < 0)
639 return -1;
640 if(vtfilegetentry(f, e) < 0){
641 vtfileunlock(f);
642 return -1;
643 }
644 vtfileunlock(f);
645 if(vtglobaltolocal(e->score) != NilBlock){
646 werrstr("internal error - data not on venti");
647 return -1;
648 }
649 return 0;
650 }
651
652 /*
653 * Get the VtEntries for the data contained in f.
654 */
655 int
vacfilegetentries(VacFile * f,VtEntry * e,VtEntry * me)656 vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me)
657 {
658 if(filerlock(f) < 0)
659 return -1;
660 if(e && getentry(f->source, e) < 0){
661 filerunlock(f);
662 return -1;
663 }
664 if(me){
665 if(f->msource == nil)
666 memset(me, 0, sizeof *me);
667 else if(getentry(f->msource, me) < 0){
668 filerunlock(f);
669 return -1;
670 }
671 }
672 filerunlock(f);
673 return 0;
674 }
675
676 /*
677 * Get the file's size.
678 */
679 int
vacfilegetsize(VacFile * f,uvlong * size)680 vacfilegetsize(VacFile *f, uvlong *size)
681 {
682 if(filerlock(f) < 0)
683 return -1;
684 if(vtfilelock(f->source, VtOREAD) < 0){
685 filerunlock(f);
686 return -1;
687 }
688 *size = vtfilegetsize(f->source);
689 vtfileunlock(f->source);
690 filerunlock(f);
691
692 return 0;
693 }
694
695 /*
696 * Directory reading.
697 *
698 * A VacDirEnum is a buffer containing directory entries.
699 * Directory entries contain malloced strings and need to
700 * be cleaned up with vdcleanup. The invariant in the
701 * VacDirEnum is that the directory entries between
702 * vde->i and vde->n are owned by the vde and need to
703 * be cleaned up if it is closed. Those from 0 up to vde->i
704 * have been handed to the reader, and the reader must
705 * take care of calling vdcleanup as appropriate.
706 */
707 VacDirEnum*
vdeopen(VacFile * f)708 vdeopen(VacFile *f)
709 {
710 VacDirEnum *vde;
711 VacFile *p;
712
713 if(!vacfileisdir(f)){
714 werrstr(ENotDir);
715 return nil;
716 }
717
718 /*
719 * There might be changes to this directory's children
720 * that have not been flushed out into the cache yet.
721 * Those changes are only available if we look at the
722 * VacFile structures directory. But the directory reader
723 * is going to read the cache blocks directly, so update them.
724 */
725 if(filelock(f) < 0)
726 return nil;
727 for(p=f->down; p; p=p->next)
728 filemetaflush(p, nil);
729 fileunlock(f);
730
731 vde = vtmallocz(sizeof(VacDirEnum));
732 vde->file = vacfileincref(f);
733
734 return vde;
735 }
736
737 /*
738 * Figure out the size of the directory entry at offset.
739 * The rest of the metadata is kept in the data half,
740 * but since venti has to track the data size anyway,
741 * we just use that one and avoid updating the directory
742 * each time the file size changes.
743 */
744 static int
direntrysize(VtFile * s,ulong offset,ulong gen,uvlong * size)745 direntrysize(VtFile *s, ulong offset, ulong gen, uvlong *size)
746 {
747 VtBlock *b;
748 ulong bn;
749 VtEntry e;
750 int epb;
751
752 epb = s->dsize/VtEntrySize;
753 bn = offset/epb;
754 offset -= bn*epb;
755
756 b = vtfileblock(s, bn, VtOREAD);
757 if(b == nil)
758 goto Err;
759 if(vtentryunpack(&e, b->data, offset) < 0)
760 goto Err;
761
762 /* dangling entries are returned as zero size */
763 if(!(e.flags & VtEntryActive) || e.gen != gen)
764 *size = 0;
765 else
766 *size = e.size;
767 vtblockput(b);
768 return 0;
769
770 Err:
771 vtblockput(b);
772 return -1;
773 }
774
775 /*
776 * Fill in vde with a new batch of directory entries.
777 */
778 static int
vdefill(VacDirEnum * vde)779 vdefill(VacDirEnum *vde)
780 {
781 int i, n;
782 VtFile *meta, *source;
783 MetaBlock mb;
784 MetaEntry me;
785 VacFile *f;
786 VtBlock *b;
787 VacDir *de;
788
789 /* clean up first */
790 for(i=vde->i; i<vde->n; i++)
791 vdcleanup(vde->buf+i);
792 vtfree(vde->buf);
793 vde->buf = nil;
794 vde->i = 0;
795 vde->n = 0;
796
797 f = vde->file;
798
799 source = f->source;
800 meta = f->msource;
801
802 b = vtfileblock(meta, vde->boff, VtOREAD);
803 if(b == nil)
804 goto Err;
805 if(mbunpack(&mb, b->data, meta->dsize) < 0)
806 goto Err;
807
808 n = mb.nindex;
809 vde->buf = vtmalloc(n * sizeof(VacDir));
810
811 for(i=0; i<n; i++){
812 de = vde->buf + i;
813 meunpack(&me, &mb, i);
814 if(vdunpack(de, &me) < 0)
815 goto Err;
816 vde->n++;
817 if(!(de->mode & ModeDir))
818 if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
819 goto Err;
820 }
821 vde->boff++;
822 vtblockput(b);
823 return 0;
824 Err:
825 vtblockput(b);
826 return -1;
827 }
828
829 /*
830 * Read a single directory entry from vde into de.
831 * Returns -1 on error, 0 on EOF, and 1 on success.
832 * When it returns 1, it becomes the caller's responsibility
833 * to call vdcleanup(de) to free the strings contained
834 * inside, or else to call vdunread to give it back.
835 */
836 int
vderead(VacDirEnum * vde,VacDir * de)837 vderead(VacDirEnum *vde, VacDir *de)
838 {
839 int ret;
840 VacFile *f;
841 u32int nb;
842
843 f = vde->file;
844 if(filerlock(f) < 0)
845 return -1;
846
847 if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
848 filerunlock(f);
849 return -1;
850 }
851
852 nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
853
854 while(vde->i >= vde->n){
855 if(vde->boff >= nb){
856 ret = 0;
857 goto Return;
858 }
859 if(vdefill(vde) < 0){
860 ret = -1;
861 goto Return;
862 }
863 }
864
865 memmove(de, vde->buf + vde->i, sizeof(VacDir));
866 vde->i++;
867 ret = 1;
868
869 Return:
870 vtfileunlock(f->source);
871 vtfileunlock(f->msource);
872 filerunlock(f);
873
874 return ret;
875 }
876
877 /*
878 * "Unread" the last directory entry that was read,
879 * so that the next vderead will return the same one.
880 * If the caller calls vdeunread(vde) it should not call
881 * vdcleanup on the entry being "unread".
882 */
883 int
vdeunread(VacDirEnum * vde)884 vdeunread(VacDirEnum *vde)
885 {
886 if(vde->i > 0){
887 vde->i--;
888 return 0;
889 }
890 return -1;
891 }
892
893 /*
894 * Close the enumerator.
895 */
896 void
vdeclose(VacDirEnum * vde)897 vdeclose(VacDirEnum *vde)
898 {
899 int i;
900 if(vde == nil)
901 return;
902 /* free the strings */
903 for(i=vde->i; i<vde->n; i++)
904 vdcleanup(vde->buf+i);
905 vtfree(vde->buf);
906 vacfiledecref(vde->file);
907 vtfree(vde);
908 }
909
910
911 /*
912 * On to mutation. If the vac file system has been opened
913 * read-write, then the files and directories can all be edited.
914 * Changes are kept in the in-memory cache until flushed out
915 * to venti, so we must be careful to explicitly flush data
916 * that we're not likely to modify again.
917 *
918 * Each VacFile has its own copy of its VacDir directory entry
919 * in f->dir, but otherwise the cache is the authoratative source
920 * for data. Thus, for the most part, it suffices if we just
921 * call vtfileflushbefore and vtfileflush when we modify things.
922 * There are a few places where we have to remember to write
923 * changed VacDirs back into the cache. If f->dir *is* out of sync,
924 * then f->dirty should be set.
925 *
926 * The metadata in a directory is, to venti, a plain data file,
927 * but as mentioned above it is actually a sequence of
928 * MetaBlocks that contain sorted lists of VacDir entries.
929 * The filemetaxxx routines manipulate that stream.
930 */
931
932 /*
933 * Find space in fp for the directory entry dir (not yet written to disk)
934 * and write it to disk, returning NilBlock on failure,
935 * or the block number on success.
936 *
937 * Start is a suggested block number to try.
938 * The caller must have filemetalock'ed f and have
939 * vtfilelock'ed f->up->msource.
940 */
941 static u32int
filemetaalloc(VacFile * fp,VacDir * dir,u32int start)942 filemetaalloc(VacFile *fp, VacDir *dir, u32int start)
943 {
944 u32int nb, bo;
945 VtBlock *b;
946 MetaBlock mb;
947 int nn;
948 uchar *p;
949 int i, n;
950 MetaEntry me;
951 VtFile *ms;
952
953 ms = fp->msource;
954 n = vdsize(dir, VacDirVersion);
955
956 /* Look for a block with room for a new entry of size n. */
957 nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
958 if(start == NilBlock){
959 if(nb > 0)
960 start = nb - 1;
961 else
962 start = 0;
963 }
964
965 if(start > nb)
966 start = nb;
967 for(bo=start; bo<nb; bo++){
968 if((b = vtfileblock(ms, bo, VtOREAD)) == nil)
969 goto Err;
970 if(mbunpack(&mb, b->data, ms->dsize) < 0)
971 goto Err;
972 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
973 if(n <= nn && mb.nindex < mb.maxindex){
974 /* reopen for writing */
975 vtblockput(b);
976 if((b = vtfileblock(ms, bo, VtORDWR)) == nil)
977 goto Err;
978 mbunpack(&mb, b->data, ms->dsize);
979 goto Found;
980 }
981 vtblockput(b);
982 }
983
984 /* No block found, extend the file by one metablock. */
985 vtfileflushbefore(ms, nb*(uvlong)ms->dsize);
986 if((b = vtfileblock(ms, nb, VtORDWR)) == nil)
987 goto Err;
988 vtfilesetsize(ms, (nb+1)*ms->dsize);
989 mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
990
991 Found:
992 /* Now we have a block; allocate space to write the entry. */
993 p = mballoc(&mb, n);
994 if(p == nil){
995 /* mballoc might have changed block */
996 mbpack(&mb);
997 werrstr(EBadMeta);
998 goto Err;
999 }
1000
1001 /* Figure out where to put the index entry, and write it. */
1002 mbsearch(&mb, dir->elem, &i, &me);
1003 assert(me.p == nil); /* not already there */
1004 me.p = p;
1005 me.size = n;
1006 vdpack(dir, &me, VacDirVersion);
1007 mbinsert(&mb, i, &me);
1008 mbpack(&mb);
1009 vtblockput(b);
1010 return bo;
1011
1012 Err:
1013 vtblockput(b);
1014 return NilBlock;
1015 }
1016
1017 /*
1018 * Update f's directory entry in the block cache.
1019 * We look for the directory entry by name;
1020 * if we're trying to rename the file, oelem is the old name.
1021 *
1022 * Assumes caller has filemetalock'ed f.
1023 */
1024 static int
filemetaflush(VacFile * f,char * oelem)1025 filemetaflush(VacFile *f, char *oelem)
1026 {
1027 int i, n;
1028 MetaBlock mb;
1029 MetaEntry me, me2;
1030 VacFile *fp;
1031 VtBlock *b;
1032 u32int bo;
1033
1034 if(!f->dirty)
1035 return 0;
1036
1037 if(oelem == nil)
1038 oelem = f->dir.elem;
1039
1040 /*
1041 * Locate f's old metadata in the parent's metadata file.
1042 * We know which block it was in, but not exactly where
1043 * in the block.
1044 */
1045 fp = f->up;
1046 if(vtfilelock(fp->msource, -1) < 0)
1047 return -1;
1048 /* can happen if source is clri'ed out from under us */
1049 if(f->boff == NilBlock)
1050 goto Err1;
1051 b = vtfileblock(fp->msource, f->boff, VtORDWR);
1052 if(b == nil)
1053 goto Err1;
1054 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
1055 goto Err;
1056 if(mbsearch(&mb, oelem, &i, &me) < 0)
1057 goto Err;
1058
1059 /*
1060 * Check whether we can resize the entry and keep it
1061 * in this block.
1062 */
1063 n = vdsize(&f->dir, VacDirVersion);
1064 if(mbresize(&mb, &me, n) >= 0){
1065 /* Okay, can be done without moving to another block. */
1066
1067 /* Remove old data */
1068 mbdelete(&mb, i, &me);
1069
1070 /* Find new location if renaming */
1071 if(strcmp(f->dir.elem, oelem) != 0)
1072 mbsearch(&mb, f->dir.elem, &i, &me2);
1073
1074 /* Pack new data into new location. */
1075 vdpack(&f->dir, &me, VacDirVersion);
1076 vdunpack(&f->dir, &me);
1077 mbinsert(&mb, i, &me);
1078 mbpack(&mb);
1079
1080 /* Done */
1081 vtblockput(b);
1082 vtfileunlock(fp->msource);
1083 f->dirty = 0;
1084 return 0;
1085 }
1086
1087 /*
1088 * The entry must be moved to another block.
1089 * This can only really happen on renames that
1090 * make the name very long.
1091 */
1092
1093 /* Allocate a spot in a new block. */
1094 if((bo = filemetaalloc(fp, &f->dir, f->boff+1)) == NilBlock){
1095 /* mbresize above might have modified block */
1096 mbpack(&mb);
1097 goto Err;
1098 }
1099 f->boff = bo;
1100
1101 /* Now we're committed. Delete entry in old block. */
1102 mbdelete(&mb, i, &me);
1103 mbpack(&mb);
1104 vtblockput(b);
1105 vtfileunlock(fp->msource);
1106
1107 f->dirty = 0;
1108 return 0;
1109
1110 Err:
1111 vtblockput(b);
1112 Err1:
1113 vtfileunlock(fp->msource);
1114 return -1;
1115 }
1116
1117 /*
1118 * Remove the directory entry for f.
1119 */
1120 static int
filemetaremove(VacFile * f)1121 filemetaremove(VacFile *f)
1122 {
1123 VtBlock *b;
1124 MetaBlock mb;
1125 MetaEntry me;
1126 int i;
1127 VacFile *fp;
1128
1129 b = nil;
1130 fp = f->up;
1131 filemetalock(f);
1132
1133 if(vtfilelock(fp->msource, VtORDWR) < 0)
1134 goto Err;
1135 b = vtfileblock(fp->msource, f->boff, VtORDWR);
1136 if(b == nil)
1137 goto Err;
1138
1139 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
1140 goto Err;
1141 if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
1142 goto Err;
1143 mbdelete(&mb, i, &me);
1144 mbpack(&mb);
1145 vtblockput(b);
1146 vtfileunlock(fp->msource);
1147
1148 f->removed = 1;
1149 f->boff = NilBlock;
1150 f->dirty = 0;
1151
1152 filemetaunlock(f);
1153 return 0;
1154
1155 Err:
1156 vtfileunlock(fp->msource);
1157 vtblockput(b);
1158 filemetaunlock(f);
1159 return -1;
1160 }
1161
1162 /*
1163 * That was far too much effort for directory entries.
1164 * Now we can write code that *does* things.
1165 */
1166
1167 /*
1168 * Flush all data associated with f out of the cache and onto venti.
1169 * If recursive is set, flush f's children too.
1170 * Vacfiledecref knows how to flush source and msource too.
1171 */
1172 int
vacfileflush(VacFile * f,int recursive)1173 vacfileflush(VacFile *f, int recursive)
1174 {
1175 int ret;
1176 VacFile **kids, *p;
1177 int i, nkids;
1178
1179 if(f->mode == VtOREAD)
1180 return 0;
1181
1182 ret = 0;
1183 filemetalock(f);
1184 if(filemetaflush(f, nil) < 0)
1185 ret = -1;
1186 filemetaunlock(f);
1187
1188 if(filelock(f) < 0)
1189 return -1;
1190
1191 /*
1192 * Lock order prevents us from flushing kids while holding
1193 * lock, so make a list and then flush without the lock.
1194 */
1195 nkids = 0;
1196 kids = nil;
1197 if(recursive){
1198 nkids = 0;
1199 for(p=f->down; p; p=p->next)
1200 nkids++;
1201 kids = vtmalloc(nkids*sizeof(VacFile*));
1202 i = 0;
1203 for(p=f->down; p; p=p->next){
1204 kids[i++] = p;
1205 p->ref++;
1206 }
1207 }
1208 if(nkids > 0){
1209 fileunlock(f);
1210 for(i=0; i<nkids; i++){
1211 if(vacfileflush(kids[i], 1) < 0)
1212 ret = -1;
1213 vacfiledecref(kids[i]);
1214 }
1215 filelock(f);
1216 }
1217 free(kids);
1218
1219 /*
1220 * Now we can flush our own data.
1221 */
1222 vtfilelock(f->source, -1);
1223 if(vtfileflush(f->source) < 0)
1224 ret = -1;
1225 vtfileunlock(f->source);
1226 if(f->msource){
1227 vtfilelock(f->msource, -1);
1228 if(vtfileflush(f->msource) < 0)
1229 ret = -1;
1230 vtfileunlock(f->msource);
1231 }
1232 fileunlock(f);
1233
1234 return ret;
1235 }
1236
1237 /*
1238 * Create a new file named elem in fp with the given mode.
1239 * The mode can be changed later except for the ModeDir bit.
1240 */
1241 VacFile*
vacfilecreate(VacFile * fp,char * elem,ulong mode)1242 vacfilecreate(VacFile *fp, char *elem, ulong mode)
1243 {
1244 VacFile *ff;
1245 VacDir *dir;
1246 VtFile *pr, *r, *mr;
1247 int type;
1248 u32int bo;
1249
1250 if(filelock(fp) < 0)
1251 return nil;
1252
1253 /*
1254 * First, look to see that there's not a file in memory
1255 * with the same name.
1256 */
1257 for(ff = fp->down; ff; ff=ff->next){
1258 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
1259 ff = nil;
1260 werrstr(EExists);
1261 goto Err1;
1262 }
1263 }
1264
1265 /*
1266 * Next check the venti blocks.
1267 */
1268 ff = dirlookup(fp, elem);
1269 if(ff != nil){
1270 werrstr(EExists);
1271 goto Err1;
1272 }
1273
1274 /*
1275 * By the way, you can't create in a read-only file system.
1276 */
1277 pr = fp->source;
1278 if(pr->mode != VtORDWR){
1279 werrstr(EReadOnly);
1280 goto Err1;
1281 }
1282
1283 /*
1284 * Okay, time to actually create something. Lock the two
1285 * halves of the directory and create a file.
1286 */
1287 if(vtfilelock2(fp->source, fp->msource, -1) < 0)
1288 goto Err1;
1289 ff = filealloc(fp->fs);
1290 ff->qidoffset = fp->qidoffset; /* hopefully fp->qidoffset == 0 */
1291 type = VtDataType;
1292 if(mode & ModeDir)
1293 type = VtDirType;
1294 mr = nil;
1295 if((r = vtfilecreate(pr, pr->psize, pr->dsize, type)) == nil)
1296 goto Err;
1297 if(mode & ModeDir)
1298 if((mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType)) == nil)
1299 goto Err;
1300
1301 /*
1302 * Fill in the directory entry and write it to disk.
1303 */
1304 dir = &ff->dir;
1305 dir->elem = vtstrdup(elem);
1306 dir->entry = r->offset;
1307 dir->gen = r->gen;
1308 if(mode & ModeDir){
1309 dir->mentry = mr->offset;
1310 dir->mgen = mr->gen;
1311 }
1312 dir->size = 0;
1313 if(_vacfsnextqid(fp->fs, &dir->qid) < 0)
1314 goto Err;
1315 dir->uid = vtstrdup(fp->dir.uid);
1316 dir->gid = vtstrdup(fp->dir.gid);
1317 dir->mid = vtstrdup("");
1318 dir->mtime = time(0L);
1319 dir->mcount = 0;
1320 dir->ctime = dir->mtime;
1321 dir->atime = dir->mtime;
1322 dir->mode = mode;
1323 if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) == NilBlock)
1324 goto Err;
1325
1326 /*
1327 * Now we're committed.
1328 */
1329 vtfileunlock(fp->source);
1330 vtfileunlock(fp->msource);
1331 ff->source = r;
1332 ff->msource = mr;
1333 ff->boff = bo;
1334
1335 /* Link into tree. */
1336 ff->next = fp->down;
1337 fp->down = ff;
1338 ff->up = fp;
1339 vacfileincref(fp);
1340
1341 fileunlock(fp);
1342
1343 filelock(ff);
1344 vtfilelock(ff->source, -1);
1345 vtfileunlock(ff->source);
1346 fileunlock(ff);
1347
1348 return ff;
1349
1350 Err:
1351 vtfileunlock(fp->source);
1352 vtfileunlock(fp->msource);
1353 if(r){
1354 vtfilelock(r, -1);
1355 vtfileremove(r);
1356 }
1357 if(mr){
1358 vtfilelock(mr, -1);
1359 vtfileremove(mr);
1360 }
1361 Err1:
1362 if(ff)
1363 vacfiledecref(ff);
1364 fileunlock(fp);
1365 return nil;
1366 }
1367
1368 /*
1369 * Change the size of the file f.
1370 */
1371 int
vacfilesetsize(VacFile * f,uvlong size)1372 vacfilesetsize(VacFile *f, uvlong size)
1373 {
1374 if(vacfileisdir(f)){
1375 werrstr(ENotFile);
1376 return -1;
1377 }
1378
1379 if(filelock(f) < 0)
1380 return -1;
1381
1382 if(f->source->mode != VtORDWR){
1383 werrstr(EReadOnly);
1384 goto Err;
1385 }
1386 if(vtfilelock(f->source, -1) < 0)
1387 goto Err;
1388 if(vtfilesetsize(f->source, size) < 0){
1389 vtfileunlock(f->source);
1390 goto Err;
1391 }
1392 vtfileunlock(f->source);
1393 fileunlock(f);
1394 return 0;
1395
1396 Err:
1397 fileunlock(f);
1398 return -1;
1399 }
1400
1401 /*
1402 * Write data to f.
1403 */
1404 int
vacfilewrite(VacFile * f,void * buf,int cnt,vlong offset)1405 vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
1406 {
1407 if(vacfileisdir(f)){
1408 werrstr(ENotFile);
1409 return -1;
1410 }
1411 if(filelock(f) < 0)
1412 return -1;
1413 if(f->source->mode != VtORDWR){
1414 werrstr(EReadOnly);
1415 goto Err;
1416 }
1417 if(offset < 0){
1418 werrstr(EBadOffset);
1419 goto Err;
1420 }
1421
1422 if(vtfilelock(f->source, -1) < 0)
1423 goto Err;
1424 if(f->dir.mode & ModeAppend)
1425 offset = vtfilegetsize(f->source);
1426 if(vtfilewrite(f->source, buf, cnt, offset) != cnt
1427 || vtfileflushbefore(f->source, offset) < 0){
1428 vtfileunlock(f->source);
1429 goto Err;
1430 }
1431 vtfileunlock(f->source);
1432 fileunlock(f);
1433 return cnt;
1434
1435 Err:
1436 fileunlock(f);
1437 return -1;
1438 }
1439
1440 /*
1441 * Set (!) the VtEntry for the data contained in f.
1442 * This let's us efficiently copy data from one file to another.
1443 */
1444 int
vacfilesetentries(VacFile * f,VtEntry * e,VtEntry * me)1445 vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
1446 {
1447 int ret;
1448
1449 vacfileflush(f, 0); /* flush blocks to venti, since we won't see them again */
1450
1451 if(!(e->flags&VtEntryActive)){
1452 werrstr("missing entry for source");
1453 return -1;
1454 }
1455 if(me && !(me->flags&VtEntryActive))
1456 me = nil;
1457 if(f->msource && !me){
1458 werrstr("missing entry for msource");
1459 return -1;
1460 }
1461 if(me && !f->msource){
1462 werrstr("no msource to set");
1463 return -1;
1464 }
1465
1466 if(filelock(f) < 0)
1467 return -1;
1468 if(f->source->mode != VtORDWR
1469 || (f->msource && f->msource->mode != VtORDWR)){
1470 werrstr(EReadOnly);
1471 fileunlock(f);
1472 return -1;
1473 }
1474 if(vtfilelock2(f->source, f->msource, -1) < 0){
1475 fileunlock(f);
1476 return -1;
1477 }
1478 ret = 0;
1479 if(vtfilesetentry(f->source, e) < 0)
1480 ret = -1;
1481 else if(me && vtfilesetentry(f->msource, me) < 0)
1482 ret = -1;
1483
1484 vtfileunlock(f->source);
1485 if(f->msource)
1486 vtfileunlock(f->msource);
1487 fileunlock(f);
1488 return ret;
1489 }
1490
1491 /*
1492 * Get the directory entry for f.
1493 */
1494 int
vacfilegetdir(VacFile * f,VacDir * dir)1495 vacfilegetdir(VacFile *f, VacDir *dir)
1496 {
1497 if(filerlock(f) < 0)
1498 return -1;
1499
1500 filemetalock(f);
1501 vdcopy(dir, &f->dir);
1502 filemetaunlock(f);
1503
1504 if(!vacfileisdir(f)){
1505 if(vtfilelock(f->source, VtOREAD) < 0){
1506 filerunlock(f);
1507 return -1;
1508 }
1509 dir->size = vtfilegetsize(f->source);
1510 vtfileunlock(f->source);
1511 }
1512 filerunlock(f);
1513
1514 return 0;
1515 }
1516
1517 /*
1518 * Set the directory entry for f.
1519 */
1520 int
vacfilesetdir(VacFile * f,VacDir * dir)1521 vacfilesetdir(VacFile *f, VacDir *dir)
1522 {
1523 VacFile *ff;
1524 char *oelem;
1525 u32int mask;
1526 u64int size;
1527
1528 /* can not set permissions for the root */
1529 if(vacfileisroot(f)){
1530 werrstr(ERoot);
1531 return -1;
1532 }
1533
1534 if(filelock(f) < 0)
1535 return -1;
1536 filemetalock(f);
1537
1538 if(f->source->mode != VtORDWR){
1539 werrstr(EReadOnly);
1540 goto Err;
1541 }
1542
1543 /* On rename, check new name does not already exist */
1544 if(strcmp(f->dir.elem, dir->elem) != 0){
1545 for(ff = f->up->down; ff; ff=ff->next){
1546 if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
1547 werrstr(EExists);
1548 goto Err;
1549 }
1550 }
1551 ff = dirlookup(f->up, dir->elem);
1552 if(ff != nil){
1553 vacfiledecref(ff);
1554 werrstr(EExists);
1555 goto Err;
1556 }
1557 werrstr(""); /* "failed" dirlookup poisoned it */
1558 }
1559
1560 /* Get ready... */
1561 if(vtfilelock2(f->source, f->msource, -1) < 0)
1562 goto Err;
1563 if(!vacfileisdir(f)){
1564 size = vtfilegetsize(f->source);
1565 if(size != dir->size){
1566 if(vtfilesetsize(f->source, dir->size) < 0){
1567 vtfileunlock(f->source);
1568 if(f->msource)
1569 vtfileunlock(f->msource);
1570 goto Err;
1571 }
1572 }
1573 }
1574 /* ... now commited to changing it. */
1575 vtfileunlock(f->source);
1576 if(f->msource)
1577 vtfileunlock(f->msource);
1578
1579 oelem = nil;
1580 if(strcmp(f->dir.elem, dir->elem) != 0){
1581 oelem = f->dir.elem;
1582 f->dir.elem = vtstrdup(dir->elem);
1583 }
1584
1585 if(strcmp(f->dir.uid, dir->uid) != 0){
1586 vtfree(f->dir.uid);
1587 f->dir.uid = vtstrdup(dir->uid);
1588 }
1589
1590 if(strcmp(f->dir.gid, dir->gid) != 0){
1591 vtfree(f->dir.gid);
1592 f->dir.gid = vtstrdup(dir->gid);
1593 }
1594
1595 if(strcmp(f->dir.mid, dir->mid) != 0){
1596 vtfree(f->dir.mid);
1597 f->dir.mid = vtstrdup(dir->mid);
1598 }
1599
1600 f->dir.mtime = dir->mtime;
1601 f->dir.atime = dir->atime;
1602
1603 mask = ~(ModeDir|ModeSnapshot);
1604 f->dir.mode &= ~mask;
1605 f->dir.mode |= mask & dir->mode;
1606 f->dirty = 1;
1607
1608 if(filemetaflush(f, oelem) < 0){
1609 vtfree(oelem);
1610 goto Err; /* that sucks */
1611 }
1612 vtfree(oelem);
1613
1614 filemetaunlock(f);
1615 fileunlock(f);
1616 return 0;
1617
1618 Err:
1619 filemetaunlock(f);
1620 fileunlock(f);
1621 return -1;
1622 }
1623
1624 /*
1625 * Set the qid space.
1626 */
1627 int
vacfilesetqidspace(VacFile * f,u64int offset,u64int max)1628 vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
1629 {
1630 int ret;
1631
1632 if(filelock(f) < 0)
1633 return -1;
1634 if(f->source->mode != VtORDWR){
1635 fileunlock(f);
1636 werrstr(EReadOnly);
1637 return -1;
1638 }
1639 filemetalock(f);
1640 f->dir.qidspace = 1;
1641 f->dir.qidoffset = offset;
1642 f->dir.qidmax = max;
1643 f->dirty = 1;
1644 ret = filemetaflush(f, nil);
1645 filemetaunlock(f);
1646 fileunlock(f);
1647 return ret;
1648 }
1649
1650 /*
1651 * Check that the file is empty, returning 0 if it is.
1652 * Returns -1 on error (and not being empty is an error).
1653 */
1654 static int
filecheckempty(VacFile * f)1655 filecheckempty(VacFile *f)
1656 {
1657 u32int i, n;
1658 VtBlock *b;
1659 MetaBlock mb;
1660 VtFile *r;
1661
1662 r = f->msource;
1663 n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
1664 for(i=0; i<n; i++){
1665 b = vtfileblock(r, i, VtOREAD);
1666 if(b == nil)
1667 return -1;
1668 if(mbunpack(&mb, b->data, r->dsize) < 0)
1669 goto Err;
1670 if(mb.nindex > 0){
1671 werrstr(ENotEmpty);
1672 goto Err;
1673 }
1674 vtblockput(b);
1675 }
1676 return 0;
1677
1678 Err:
1679 vtblockput(b);
1680 return -1;
1681 }
1682
1683 /*
1684 * Remove the vac file f.
1685 */
1686 int
vacfileremove(VacFile * f)1687 vacfileremove(VacFile *f)
1688 {
1689 VacFile *ff;
1690
1691 /* Cannot remove the root */
1692 if(vacfileisroot(f)){
1693 werrstr(ERoot);
1694 return -1;
1695 }
1696
1697 if(filelock(f) < 0)
1698 return -1;
1699 if(f->source->mode != VtORDWR){
1700 werrstr(EReadOnly);
1701 goto Err1;
1702 }
1703 if(vtfilelock2(f->source, f->msource, -1) < 0)
1704 goto Err1;
1705 if(vacfileisdir(f) && filecheckempty(f)<0)
1706 goto Err;
1707
1708 for(ff=f->down; ff; ff=ff->next)
1709 assert(ff->removed);
1710
1711 vtfileremove(f->source);
1712 f->source = nil;
1713 if(f->msource){
1714 vtfileremove(f->msource);
1715 f->msource = nil;
1716 }
1717 fileunlock(f);
1718
1719 if(filemetaremove(f) < 0)
1720 return -1;
1721 return 0;
1722
1723 Err:
1724 vtfileunlock(f->source);
1725 if(f->msource)
1726 vtfileunlock(f->msource);
1727 Err1:
1728 fileunlock(f);
1729 return -1;
1730 }
1731
1732 /*
1733 * Vac file system format.
1734 */
1735 static char EBadVacFormat[] = "bad format for vac file";
1736
1737 static VacFs *
vacfsalloc(VtConn * z,int bsize,int ncache,int mode)1738 vacfsalloc(VtConn *z, int bsize, int ncache, int mode)
1739 {
1740 VacFs *fs;
1741
1742 fs = vtmallocz(sizeof(VacFs));
1743 fs->z = z;
1744 fs->bsize = bsize;
1745 fs->mode = mode;
1746 fs->cache = vtcachealloc(z, bsize, ncache);
1747 return fs;
1748 }
1749
1750 static int
readscore(int fd,uchar score[VtScoreSize])1751 readscore(int fd, uchar score[VtScoreSize])
1752 {
1753 char buf[45], *pref;
1754 int n;
1755
1756 n = readn(fd, buf, sizeof(buf)-1);
1757 if(n < sizeof(buf)-1) {
1758 werrstr("short read");
1759 return -1;
1760 }
1761 buf[n] = 0;
1762
1763 if(vtparsescore(buf, &pref, score) < 0){
1764 werrstr(EBadVacFormat);
1765 return -1;
1766 }
1767 if(pref==nil || strcmp(pref, "vac") != 0) {
1768 werrstr("not a vac file");
1769 return -1;
1770 }
1771 return 0;
1772 }
1773
1774 VacFs*
vacfsopen(VtConn * z,char * file,int mode,int ncache)1775 vacfsopen(VtConn *z, char *file, int mode, int ncache)
1776 {
1777 int fd;
1778 uchar score[VtScoreSize];
1779 char *prefix;
1780
1781 if(vtparsescore(file, &prefix, score) >= 0){
1782 if(prefix == nil || strcmp(prefix, "vac") != 0){
1783 werrstr("not a vac file");
1784 return nil;
1785 }
1786 }else{
1787 fd = open(file, OREAD);
1788 if(fd < 0)
1789 return nil;
1790 if(readscore(fd, score) < 0){
1791 close(fd);
1792 return nil;
1793 }
1794 close(fd);
1795 }
1796 return vacfsopenscore(z, score, mode, ncache);
1797 }
1798
1799 VacFs*
vacfsopenscore(VtConn * z,u8int * score,int mode,int ncache)1800 vacfsopenscore(VtConn *z, u8int *score, int mode, int ncache)
1801 {
1802 VacFs *fs;
1803 int n;
1804 VtRoot rt;
1805 uchar buf[VtRootSize];
1806 VacFile *root;
1807 VtFile *r;
1808 VtEntry e;
1809
1810 n = vtread(z, score, VtRootType, buf, VtRootSize);
1811 if(n < 0)
1812 return nil;
1813 if(n != VtRootSize){
1814 werrstr("vtread on root too short");
1815 return nil;
1816 }
1817
1818 if(vtrootunpack(&rt, buf) < 0)
1819 return nil;
1820
1821 if(strcmp(rt.type, "vac") != 0) {
1822 werrstr("not a vac root");
1823 return nil;
1824 }
1825
1826 fs = vacfsalloc(z, rt.blocksize, ncache, mode);
1827 memmove(fs->score, score, VtScoreSize);
1828 fs->mode = mode;
1829
1830 memmove(e.score, rt.score, VtScoreSize);
1831 e.gen = 0;
1832 e.psize = rt.blocksize;
1833 e.dsize = rt.blocksize;
1834 e.type = VtDirType;
1835 e.flags = VtEntryActive;
1836 e.size = 3*VtEntrySize;
1837
1838 root = nil;
1839 if((r = vtfileopenroot(fs->cache, &e)) == nil)
1840 goto Err;
1841 if(debug)
1842 fprint(2, "r %p\n", r);
1843 root = _vacfileroot(fs, r);
1844 if(debug)
1845 fprint(2, "root %p\n", root);
1846 vtfileclose(r);
1847 if(root == nil)
1848 goto Err;
1849 fs->root = root;
1850 return fs;
1851 Err:
1852 if(root)
1853 vacfiledecref(root);
1854 vacfsclose(fs);
1855 return nil;
1856 }
1857
1858 int
vacfsmode(VacFs * fs)1859 vacfsmode(VacFs *fs)
1860 {
1861 return fs->mode;
1862 }
1863
1864 VacFile*
vacfsgetroot(VacFs * fs)1865 vacfsgetroot(VacFs *fs)
1866 {
1867 return vacfileincref(fs->root);
1868 }
1869
1870 int
vacfsgetblocksize(VacFs * fs)1871 vacfsgetblocksize(VacFs *fs)
1872 {
1873 return fs->bsize;
1874 }
1875
1876 int
vacfsgetscore(VacFs * fs,u8int * score)1877 vacfsgetscore(VacFs *fs, u8int *score)
1878 {
1879 memmove(score, fs->score, VtScoreSize);
1880 return 0;
1881 }
1882
1883 int
_vacfsnextqid(VacFs * fs,uvlong * qid)1884 _vacfsnextqid(VacFs *fs, uvlong *qid)
1885 {
1886 ++fs->qid;
1887 *qid = fs->qid;
1888 return 0;
1889 }
1890
1891 void
vacfsjumpqid(VacFs * fs,uvlong step)1892 vacfsjumpqid(VacFs *fs, uvlong step)
1893 {
1894 fs->qid += step;
1895 }
1896
1897 /*
1898 * Set *maxqid to the maximum qid expected in this file system.
1899 * In newer vac archives, the maximum qid is stored in the
1900 * qidspace VacDir annotation. In older vac archives, the root
1901 * got created last, so it had the maximum qid.
1902 */
1903 int
vacfsgetmaxqid(VacFs * fs,uvlong * maxqid)1904 vacfsgetmaxqid(VacFs *fs, uvlong *maxqid)
1905 {
1906 VacDir vd;
1907
1908 if(vacfilegetdir(fs->root, &vd) < 0)
1909 return -1;
1910 if(vd.qidspace)
1911 *maxqid = vd.qidmax;
1912 else
1913 *maxqid = vd.qid;
1914 vdcleanup(&vd);
1915 return 0;
1916 }
1917
1918
1919 void
vacfsclose(VacFs * fs)1920 vacfsclose(VacFs *fs)
1921 {
1922 if(fs->root)
1923 vacfiledecref(fs->root);
1924 fs->root = nil;
1925 vtcachefree(fs->cache);
1926 vtfree(fs);
1927 }
1928
1929 /*
1930 * Create a fresh vac fs.
1931 */
1932 VacFs *
vacfscreate(VtConn * z,int bsize,int ncache)1933 vacfscreate(VtConn *z, int bsize, int ncache)
1934 {
1935 VacFs *fs;
1936 VtFile *f;
1937 uchar buf[VtEntrySize], metascore[VtScoreSize];
1938 VtEntry e;
1939 VtBlock *b;
1940 MetaBlock mb;
1941 VacDir vd;
1942 MetaEntry me;
1943 int psize;
1944 int mbsize;
1945
1946 if((fs = vacfsalloc(z, bsize, ncache, VtORDWR)) == nil)
1947 return nil;
1948
1949 /*
1950 * Fake up an empty vac fs.
1951 */
1952 psize = bsize;
1953 f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType);
1954 vtfilelock(f, VtORDWR);
1955
1956 /* Metablocks can't be too big -- they have 16-bit offsets in them. */
1957 mbsize = bsize;
1958 if(mbsize >= 56*1024)
1959 mbsize = 56*1024;
1960
1961 /* Write metablock containing root directory VacDir. */
1962 b = vtcacheallocblock(fs->cache, VtDataType);
1963 mbinit(&mb, b->data, mbsize, mbsize/BytesPerEntry);
1964 memset(&vd, 0, sizeof vd);
1965 vd.elem = "/";
1966 vd.mode = 0777|ModeDir;
1967 vd.uid = "vac";
1968 vd.gid = "vac";
1969 vd.mid = "";
1970 me.size = vdsize(&vd, VacDirVersion);
1971 me.p = mballoc(&mb, me.size);
1972 vdpack(&vd, &me, VacDirVersion);
1973 mbinsert(&mb, 0, &me);
1974 mbpack(&mb);
1975 vtblockwrite(b);
1976 memmove(metascore, b->score, VtScoreSize);
1977 vtblockput(b);
1978
1979 /* First entry: empty venti directory stream. */
1980 memset(&e, 0, sizeof e);
1981 e.flags = VtEntryActive;
1982 e.psize = psize;
1983 e.dsize = bsize;
1984 e.type = VtDirType;
1985 memmove(e.score, vtzeroscore, VtScoreSize);
1986 vtentrypack(&e, buf, 0);
1987 vtfilewrite(f, buf, VtEntrySize, 0);
1988
1989 /* Second entry: empty metadata stream. */
1990 e.type = VtDataType;
1991 e.dsize = mbsize;
1992 vtentrypack(&e, buf, 0);
1993 vtfilewrite(f, buf, VtEntrySize, VtEntrySize);
1994
1995 /* Third entry: metadata stream with root directory. */
1996 memmove(e.score, metascore, VtScoreSize);
1997 e.size = mbsize;
1998 vtentrypack(&e, buf, 0);
1999 vtfilewrite(f, buf, VtEntrySize, VtEntrySize*2);
2000
2001 vtfileflush(f);
2002 vtfileunlock(f);
2003
2004 /* Now open it as a vac fs. */
2005 fs->root = _vacfileroot(fs, f);
2006 if(fs->root == nil){
2007 werrstr("vacfileroot: %r");
2008 vacfsclose(fs);
2009 return nil;
2010 }
2011
2012 return fs;
2013 }
2014
2015 int
vacfssync(VacFs * fs)2016 vacfssync(VacFs *fs)
2017 {
2018 uchar buf[1024];
2019 VtEntry e;
2020 VtFile *f;
2021 VtRoot root;
2022
2023 /* Sync the entire vacfs to disk. */
2024 if(vacfileflush(fs->root, 1) < 0)
2025 return -1;
2026 if(vtfilelock(fs->root->up->msource, -1) < 0)
2027 return -1;
2028 if(vtfileflush(fs->root->up->msource) < 0){
2029 vtfileunlock(fs->root->up->msource);
2030 return -1;
2031 }
2032 vtfileunlock(fs->root->up->msource);
2033
2034 /* Prepare the dir stream for the root block. */
2035 if(getentry(fs->root->source, &e) < 0)
2036 return -1;
2037 vtentrypack(&e, buf, 0);
2038 if(getentry(fs->root->msource, &e) < 0)
2039 return -1;
2040 vtentrypack(&e, buf, 1);
2041 if(getentry(fs->root->up->msource, &e) < 0)
2042 return -1;
2043 vtentrypack(&e, buf, 2);
2044
2045 f = vtfilecreateroot(fs->cache, fs->bsize, fs->bsize, VtDirType);
2046 vtfilelock(f, VtORDWR);
2047 if(vtfilewrite(f, buf, 3*VtEntrySize, 0) < 0
2048 || vtfileflush(f) < 0){
2049 vtfileunlock(f);
2050 vtfileclose(f);
2051 return -1;
2052 }
2053 vtfileunlock(f);
2054 if(getentry(f, &e) < 0){
2055 vtfileclose(f);
2056 return -1;
2057 }
2058 vtfileclose(f);
2059
2060 /* Build a root block. */
2061 memset(&root, 0, sizeof root);
2062 strcpy(root.type, "vac");
2063 strcpy(root.name, fs->name);
2064 memmove(root.score, e.score, VtScoreSize);
2065 root.blocksize = fs->bsize;
2066 memmove(root.prev, fs->score, VtScoreSize);
2067 vtrootpack(&root, buf);
2068 if(vtwrite(fs->z, fs->score, VtRootType, buf, VtRootSize) < 0){
2069 werrstr("writing root: %r");
2070 return -1;
2071 }
2072 if(vtsync(fs->z) < 0)
2073 return -1;
2074 return 0;
2075 }
2076
2077 int
vacfiledsize(VacFile * f)2078 vacfiledsize(VacFile *f)
2079 {
2080 VtEntry e;
2081
2082 if(vacfilegetentries(f,&e,nil) < 0)
2083 return -1;
2084 return e.dsize;
2085 }
2086
2087 /*
2088 * Does block b of f have the same SHA1 hash as the n bytes at buf?
2089 */
2090 int
sha1matches(VacFile * f,ulong b,uchar * buf,int n)2091 sha1matches(VacFile *f, ulong b, uchar *buf, int n)
2092 {
2093 uchar fscore[VtScoreSize];
2094 uchar bufscore[VtScoreSize];
2095
2096 if(vacfileblockscore(f, b, fscore) < 0)
2097 return 0;
2098 n = vtzerotruncate(VtDataType, buf, n);
2099 sha1(buf, n, bufscore, nil);
2100 if(memcmp(bufscore, fscore, VtScoreSize) == 0)
2101 return 1;
2102 return 0;
2103 }
2104
2105