Blob


1 #include "stdinc.h"
2 #include "vac.h"
3 #include "dat.h"
4 #include "fns.h"
5 #include "error.h"
7 #define debug 0
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 */
26 static int filemetaflush(VacFile*, char*);
28 struct VacFile
29 {
30 VacFs *fs; /* immutable */
32 /* meta data for file: protected by the lk in the parent */
33 int ref; /* holds this data structure up */
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 */
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;
49 uvlong qidoffset; /* qid offset */
50 };
52 static VacFile*
53 filealloc(VacFs *fs)
54 {
55 VacFile *f;
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 }
65 static void
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 }
75 static int
76 chksource(VacFile *f)
77 {
78 if(f->partial)
79 return 0;
81 if(f->source == nil
82 || ((f->dir.mode & ModeDir) && f->msource == nil)){
83 werrstr(ERemoved);
84 return -1;
85 }
86 return 0;
87 }
89 static int
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 }
100 static void
101 fileunlock(VacFile *f)
103 wunlock(&f->lk);
106 static int
107 filerlock(VacFile *f)
109 rlock(&f->lk);
110 if(chksource(f) < 0){
111 runlock(&f->lk);
112 return -1;
114 return 0;
117 static void
118 filerunlock(VacFile *f)
120 runlock(&f->lk);
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
129 filemetalock(VacFile *f)
131 assert(f->up != nil);
132 wlock(&f->up->lk);
135 static void
136 filemetaunlock(VacFile *f)
138 wunlock(&f->up->lk);
141 uvlong
142 vacfilegetid(VacFile *f)
144 /* immutable */
145 return f->qidoffset + f->dir.qid;
148 uvlong
149 vacfilegetqidoffset(VacFile *f)
151 return f->qidoffset;
154 ulong
155 vacfilegetmcount(VacFile *f)
157 ulong mcount;
159 filemetalock(f);
160 mcount = f->dir.mcount;
161 filemetaunlock(f);
162 return mcount;
165 ulong
166 vacfilegetmode(VacFile *f)
168 ulong mode;
170 filemetalock(f);
171 mode = f->dir.mode;
172 filemetaunlock(f);
173 return mode;
176 int
177 vacfileisdir(VacFile *f)
179 /* immutable */
180 return (f->dir.mode & ModeDir) != 0;
183 int
184 vacfileisroot(VacFile *f)
186 return f == f->fs->root;
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.
195 * Each VacFile holds a reference to its parent.
196 */
197 VacFile*
198 vacfileincref(VacFile *vf)
200 filemetalock(vf);
201 assert(vf->ref > 0);
202 vf->ref++;
203 filemetaunlock(vf);
204 return vf;
207 int
208 vacfiledecref(VacFile *f)
210 VacFile *p, *q, **qq;
212 if(f->up == nil){
213 /* never linked in */
214 assert(f->ref == 1);
215 filefree(f);
216 return 0;
219 filemetalock(f);
220 f->ref--;
221 if(f->ref > 0){
222 filemetaunlock(f);
223 return -1;
225 assert(f->ref == 0);
226 assert(f->down == nil);
228 if(f->source && vtfilelock(f->source, -1) >= 0){
229 vtfileflush(f->source);
230 vtfileunlock(f->source);
232 if(f->msource && vtfilelock(f->msource, -1) >= 0){
233 vtfileflush(f->msource);
234 vtfileunlock(f->msource);
237 /*
238 * Flush f's directory information to the cache.
239 */
240 filemetaflush(f, nil);
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;
249 assert(q != nil);
250 *qq = f->next;
252 filemetaunlock(f);
253 filefree(f);
254 vacfiledecref(p);
255 return 0;
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".
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*
273 _vacfileroot(VacFs *fs, VtFile *r)
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;
283 redirected = 0;
284 Top:
285 b = nil;
286 root = nil;
287 mr = nil;
288 r1 = nil;
289 r2 = nil;
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;
313 goto Err;
315 r1 = vtfileopen(r, 1, fs->mode);
316 if(debug)
317 fprint(2, "r1 %p\n", r1);
318 if(r1 == nil)
319 goto Err;
321 mr = filealloc(fs);
322 mr->msource = r2;
323 r2 = nil;
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;
333 mr->down = root;
334 vtfileunlock(r);
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;
343 if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
344 goto Err1;
346 meunpack(&me, &mb, 0);
347 if(vdunpack(&root->dir, &me) < 0)
348 goto Err1;
349 vtblockput(b);
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);
367 return nil;
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.
381 * f must be locked, f->msource must not.
382 */
383 static VacFile*
384 dirlookup(VacFile *f, char *elem)
386 int i;
387 MetaBlock mb;
388 MetaEntry me;
389 VtBlock *b;
390 VtFile *meta;
391 VacFile *ff;
392 u32int bo, nb;
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;
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;
418 vtblockput(b);
419 b = nil;
421 werrstr(ENoFile);
422 /* fall through */
423 Err:
424 vtfileunlock(meta);
425 vtblockput(b);
426 return nil;
429 /*
430 * Open the venti file at offset in the directory f->source.
431 * f is locked.
432 */
433 static VtFile *
434 fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
436 VtFile *r;
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;
447 if(r->dir != dir && r->mode != -1){
448 werrstr(EBadMeta);
449 vtfileclose(r);
450 return nil;
452 return r;
455 VacFile*
456 vacfilegetparent(VacFile *f)
458 if(vacfileisroot(f))
459 return vacfileincref(f);
460 return vacfileincref(f->up);
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*
469 vacfilewalk(VacFile *f, char *elem)
471 VacFile *ff;
473 if(elem[0] == 0){
474 werrstr(EBadPath);
475 return nil;
478 if(!vacfileisdir(f)){
479 werrstr(ENotDir);
480 return nil;
483 if(strcmp(elem, ".") == 0)
484 return vacfileincref(f);
486 if(strcmp(elem, "..") == 0)
487 return vacfilegetparent(f);
489 if(filelock(f) < 0)
490 return nil;
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;
499 ff = dirlookup(f, elem);
500 if(ff == nil)
501 goto Err;
503 if(ff->dir.mode & ModeSnapshot)
504 ff->mode = VtOREAD;
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;
518 vtfileunlock(f->source);
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;
529 Err1:
530 vtfileunlock(f->source);
531 Err:
532 fileunlock(f);
533 if(ff != nil)
534 vacfiledecref(ff);
535 return nil;
538 /*
539 * Open a path in the vac file system:
540 * just walk each element one at a time.
541 */
542 VacFile*
543 vacfileopen(VacFs *fs, char *path)
545 VacFile *f, *ff;
546 char *p, elem[VtMaxStringSize], *opath;
547 int n;
549 f = fs->root;
550 vacfileincref(f);
551 opath = path;
552 while(*path != 0){
553 for(p = path; *p && *p != '/'; p++)
555 n = p - path;
556 if(n > 0){
557 if(n > VtMaxStringSize){
558 werrstr("%s: element too long", EBadPath);
559 goto Err;
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;
568 vacfiledecref(f);
569 f = ff;
571 if(*p == '/')
572 p++;
573 path = p;
575 return f;
576 Err:
577 vacfiledecref(f);
578 return nil;
581 /*
582 * Extract the score for the bn'th block in f.
583 */
584 int
585 vacfileblockscore(VacFile *f, u32int bn, u8int *score)
587 VtFile *s;
588 uvlong size;
589 int dsize, ret;
591 ret = -1;
592 if(filerlock(f) < 0)
593 return -1;
594 if(vtfilelock(f->source, VtOREAD) < 0)
595 goto out;
597 s = f->source;
598 dsize = s->dsize;
599 size = vtfilegetsize(s);
600 if((uvlong)bn*dsize >= size)
601 goto out;
602 ret = vtfileblockscore(f->source, bn, score);
604 out:
605 vtfileunlock(f->source);
606 filerunlock(f);
607 return ret;
610 /*
611 * Read data from f.
612 */
613 int
614 vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
616 int n;
618 if(offset < 0){
619 werrstr(EBadOffset);
620 return -1;
622 if(filerlock(f) < 0)
623 return -1;
624 if(vtfilelock(f->source, VtOREAD) < 0){
625 filerunlock(f);
626 return -1;
628 n = vtfileread(f->source, buf, cnt, offset);
629 vtfileunlock(f->source);
630 filerunlock(f);
631 return n;
634 static int
635 getentry(VtFile *f, VtEntry *e)
637 if(vtfilelock(f, VtOREAD) < 0)
638 return -1;
639 if(vtfilegetentry(f, e) < 0){
640 vtfileunlock(f);
641 return -1;
643 vtfileunlock(f);
644 if(vtglobaltolocal(e->score) != NilBlock){
645 werrstr("internal error - data not on venti");
646 return -1;
648 return 0;
651 /*
652 * Get the VtEntries for the data contained in f.
653 */
654 int
655 vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me)
657 if(filerlock(f) < 0)
658 return -1;
659 if(e && getentry(f->source, e) < 0){
660 filerunlock(f);
661 return -1;
663 if(me){
664 if(f->msource == nil)
665 memset(me, 0, sizeof *me);
666 else if(getentry(f->msource, me) < 0){
667 filerunlock(f);
668 return -1;
671 filerunlock(f);
672 return 0;
675 /*
676 * Get the file's size.
677 */
678 int
679 vacfilegetsize(VacFile *f, uvlong *size)
681 if(filerlock(f) < 0)
682 return -1;
683 if(vtfilelock(f->source, VtOREAD) < 0){
684 filerunlock(f);
685 return -1;
687 *size = vtfilegetsize(f->source);
688 vtfileunlock(f->source);
689 filerunlock(f);
691 return 0;
694 /*
695 * Directory reading.
697 * A VacDirEnum is a buffer containing directory entries.
698 * Directory entries contain malloced strings and need to
699 * be cleaned up with vdcleanup. The invariant in the
700 * VacDirEnum is that the directory entries between
701 * vde->i and vde->n are owned by the vde and need to
702 * be cleaned up if it is closed. Those from 0 up to vde->i
703 * have been handed to the reader, and the reader must
704 * take care of calling vdcleanup as appropriate.
705 */
706 VacDirEnum*
707 vdeopen(VacFile *f)
709 VacDirEnum *vde;
710 VacFile *p;
712 if(!vacfileisdir(f)){
713 werrstr(ENotDir);
714 return nil;
717 /*
718 * There might be changes to this directory's children
719 * that have not been flushed out into the cache yet.
720 * Those changes are only available if we look at the
721 * VacFile structures directory. But the directory reader
722 * is going to read the cache blocks directly, so update them.
723 */
724 if(filelock(f) < 0)
725 return nil;
726 for(p=f->down; p; p=p->next)
727 filemetaflush(p, nil);
728 fileunlock(f);
730 vde = vtmallocz(sizeof(VacDirEnum));
731 vde->file = vacfileincref(f);
733 return vde;
736 /*
737 * Figure out the size of the directory entry at offset.
738 * The rest of the metadata is kept in the data half,
739 * but since venti has to track the data size anyway,
740 * we just use that one and avoid updating the directory
741 * each time the file size changes.
742 */
743 static int
744 direntrysize(VtFile *s, ulong offset, ulong gen, uvlong *size)
746 VtBlock *b;
747 ulong bn;
748 VtEntry e;
749 int epb;
751 epb = s->dsize/VtEntrySize;
752 bn = offset/epb;
753 offset -= bn*epb;
755 b = vtfileblock(s, bn, VtOREAD);
756 if(b == nil)
757 goto Err;
758 if(vtentryunpack(&e, b->data, offset) < 0)
759 goto Err;
761 /* dangling entries are returned as zero size */
762 if(!(e.flags & VtEntryActive) || e.gen != gen)
763 *size = 0;
764 else
765 *size = e.size;
766 vtblockput(b);
767 return 0;
769 Err:
770 vtblockput(b);
771 return -1;
774 /*
775 * Fill in vde with a new batch of directory entries.
776 */
777 static int
778 vdefill(VacDirEnum *vde)
780 int i, n;
781 VtFile *meta, *source;
782 MetaBlock mb;
783 MetaEntry me;
784 VacFile *f;
785 VtBlock *b;
786 VacDir *de;
788 /* clean up first */
789 for(i=vde->i; i<vde->n; i++)
790 vdcleanup(vde->buf+i);
791 vtfree(vde->buf);
792 vde->buf = nil;
793 vde->i = 0;
794 vde->n = 0;
796 f = vde->file;
798 source = f->source;
799 meta = f->msource;
801 b = vtfileblock(meta, vde->boff, VtOREAD);
802 if(b == nil)
803 goto Err;
804 if(mbunpack(&mb, b->data, meta->dsize) < 0)
805 goto Err;
807 n = mb.nindex;
808 vde->buf = vtmalloc(n * sizeof(VacDir));
810 for(i=0; i<n; i++){
811 de = vde->buf + i;
812 meunpack(&me, &mb, i);
813 if(vdunpack(de, &me) < 0)
814 goto Err;
815 vde->n++;
816 if(!(de->mode & ModeDir))
817 if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
818 goto Err;
820 vde->boff++;
821 vtblockput(b);
822 return 0;
823 Err:
824 vtblockput(b);
825 return -1;
828 /*
829 * Read a single directory entry from vde into de.
830 * Returns -1 on error, 0 on EOF, and 1 on success.
831 * When it returns 1, it becomes the caller's responsibility
832 * to call vdcleanup(de) to free the strings contained
833 * inside, or else to call vdunread to give it back.
834 */
835 int
836 vderead(VacDirEnum *vde, VacDir *de)
838 int ret;
839 VacFile *f;
840 u32int nb;
842 f = vde->file;
843 if(filerlock(f) < 0)
844 return -1;
846 if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
847 filerunlock(f);
848 return -1;
851 nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
853 while(vde->i >= vde->n){
854 if(vde->boff >= nb){
855 ret = 0;
856 goto Return;
858 if(vdefill(vde) < 0){
859 ret = -1;
860 goto Return;
864 memmove(de, vde->buf + vde->i, sizeof(VacDir));
865 vde->i++;
866 ret = 1;
868 Return:
869 vtfileunlock(f->source);
870 vtfileunlock(f->msource);
871 filerunlock(f);
873 return ret;
876 /*
877 * "Unread" the last directory entry that was read,
878 * so that the next vderead will return the same one.
879 * If the caller calls vdeunread(vde) it should not call
880 * vdcleanup on the entry being "unread".
881 */
882 int
883 vdeunread(VacDirEnum *vde)
885 if(vde->i > 0){
886 vde->i--;
887 return 0;
889 return -1;
892 /*
893 * Close the enumerator.
894 */
895 void
896 vdeclose(VacDirEnum *vde)
898 int i;
899 if(vde == nil)
900 return;
901 /* free the strings */
902 for(i=vde->i; i<vde->n; i++)
903 vdcleanup(vde->buf+i);
904 vtfree(vde->buf);
905 vacfiledecref(vde->file);
906 vtfree(vde);
910 /*
911 * On to mutation. If the vac file system has been opened
912 * read-write, then the files and directories can all be edited.
913 * Changes are kept in the in-memory cache until flushed out
914 * to venti, so we must be careful to explicitly flush data
915 * that we're not likely to modify again.
917 * Each VacFile has its own copy of its VacDir directory entry
918 * in f->dir, but otherwise the cache is the authoratative source
919 * for data. Thus, for the most part, it suffices if we just
920 * call vtfileflushbefore and vtfileflush when we modify things.
921 * There are a few places where we have to remember to write
922 * changed VacDirs back into the cache. If f->dir *is* out of sync,
923 * then f->dirty should be set.
925 * The metadata in a directory is, to venti, a plain data file,
926 * but as mentioned above it is actually a sequence of
927 * MetaBlocks that contain sorted lists of VacDir entries.
928 * The filemetaxxx routines manipulate that stream.
929 */
931 /*
932 * Find space in fp for the directory entry dir (not yet written to disk)
933 * and write it to disk, returning NilBlock on failure,
934 * or the block number on success.
936 * Start is a suggested block number to try.
937 * The caller must have filemetalock'ed f and have
938 * vtfilelock'ed f->up->msource.
939 */
940 static u32int
941 filemetaalloc(VacFile *fp, VacDir *dir, u32int start)
943 u32int nb, bo;
944 VtBlock *b;
945 MetaBlock mb;
946 int nn;
947 uchar *p;
948 int i, n;
949 MetaEntry me;
950 VtFile *ms;
952 ms = fp->msource;
953 n = vdsize(dir, VacDirVersion);
955 /* Look for a block with room for a new entry of size n. */
956 nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
957 if(start == NilBlock){
958 if(nb > 0)
959 start = nb - 1;
960 else
961 start = 0;
964 if(start > nb)
965 start = nb;
966 for(bo=start; bo<nb; bo++){
967 if((b = vtfileblock(ms, bo, VtOREAD)) == nil)
968 goto Err;
969 if(mbunpack(&mb, b->data, ms->dsize) < 0)
970 goto Err;
971 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
972 if(n <= nn && mb.nindex < mb.maxindex){
973 /* reopen for writing */
974 vtblockput(b);
975 if((b = vtfileblock(ms, bo, VtORDWR)) == nil)
976 goto Err;
977 mbunpack(&mb, b->data, ms->dsize);
978 goto Found;
980 vtblockput(b);
983 /* No block found, extend the file by one metablock. */
984 vtfileflushbefore(ms, nb*(uvlong)ms->dsize);
985 if((b = vtfileblock(ms, nb, VtORDWR)) == nil)
986 goto Err;
987 vtfilesetsize(ms, (nb+1)*ms->dsize);
988 mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
990 Found:
991 /* Now we have a block; allocate space to write the entry. */
992 p = mballoc(&mb, n);
993 if(p == nil){
994 /* mballoc might have changed block */
995 mbpack(&mb);
996 werrstr(EBadMeta);
997 goto Err;
1000 /* Figure out where to put the index entry, and write it. */
1001 mbsearch(&mb, dir->elem, &i, &me);
1002 assert(me.p == nil); /* not already there */
1003 me.p = p;
1004 me.size = n;
1005 vdpack(dir, &me, VacDirVersion);
1006 mbinsert(&mb, i, &me);
1007 mbpack(&mb);
1008 vtblockput(b);
1009 return bo;
1011 Err:
1012 vtblockput(b);
1013 return NilBlock;
1017 * Update f's directory entry in the block cache.
1018 * We look for the directory entry by name;
1019 * if we're trying to rename the file, oelem is the old name.
1021 * Assumes caller has filemetalock'ed f.
1023 static int
1024 filemetaflush(VacFile *f, char *oelem)
1026 int i, n;
1027 MetaBlock mb;
1028 MetaEntry me, me2;
1029 VacFile *fp;
1030 VtBlock *b;
1031 u32int bo;
1033 if(!f->dirty)
1034 return 0;
1036 if(oelem == nil)
1037 oelem = f->dir.elem;
1040 * Locate f's old metadata in the parent's metadata file.
1041 * We know which block it was in, but not exactly where
1042 * in the block.
1044 fp = f->up;
1045 if(vtfilelock(fp->msource, -1) < 0)
1046 return -1;
1047 /* can happen if source is clri'ed out from under us */
1048 if(f->boff == NilBlock)
1049 goto Err1;
1050 b = vtfileblock(fp->msource, f->boff, VtORDWR);
1051 if(b == nil)
1052 goto Err1;
1053 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
1054 goto Err;
1055 if(mbsearch(&mb, oelem, &i, &me) < 0)
1056 goto Err;
1059 * Check whether we can resize the entry and keep it
1060 * in this block.
1062 n = vdsize(&f->dir, VacDirVersion);
1063 if(mbresize(&mb, &me, n) >= 0){
1064 /* Okay, can be done without moving to another block. */
1066 /* Remove old data */
1067 mbdelete(&mb, i, &me);
1069 /* Find new location if renaming */
1070 if(strcmp(f->dir.elem, oelem) != 0)
1071 mbsearch(&mb, f->dir.elem, &i, &me2);
1073 /* Pack new data into new location. */
1074 vdpack(&f->dir, &me, VacDirVersion);
1075 vdunpack(&f->dir, &me);
1076 mbinsert(&mb, i, &me);
1077 mbpack(&mb);
1079 /* Done */
1080 vtblockput(b);
1081 vtfileunlock(fp->msource);
1082 f->dirty = 0;
1083 return 0;
1087 * The entry must be moved to another block.
1088 * This can only really happen on renames that
1089 * make the name very long.
1092 /* Allocate a spot in a new block. */
1093 if((bo = filemetaalloc(fp, &f->dir, f->boff+1)) == NilBlock){
1094 /* mbresize above might have modified block */
1095 mbpack(&mb);
1096 goto Err;
1098 f->boff = bo;
1100 /* Now we're committed. Delete entry in old block. */
1101 mbdelete(&mb, i, &me);
1102 mbpack(&mb);
1103 vtblockput(b);
1104 vtfileunlock(fp->msource);
1106 f->dirty = 0;
1107 return 0;
1109 Err:
1110 vtblockput(b);
1111 Err1:
1112 vtfileunlock(fp->msource);
1113 return -1;
1117 * Remove the directory entry for f.
1119 static int
1120 filemetaremove(VacFile *f)
1122 VtBlock *b;
1123 MetaBlock mb;
1124 MetaEntry me;
1125 int i;
1126 VacFile *fp;
1128 b = nil;
1129 fp = f->up;
1130 filemetalock(f);
1132 if(vtfilelock(fp->msource, VtORDWR) < 0)
1133 goto Err;
1134 b = vtfileblock(fp->msource, f->boff, VtORDWR);
1135 if(b == nil)
1136 goto Err;
1138 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
1139 goto Err;
1140 if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
1141 goto Err;
1142 mbdelete(&mb, i, &me);
1143 mbpack(&mb);
1144 vtblockput(b);
1145 vtfileunlock(fp->msource);
1147 f->removed = 1;
1148 f->boff = NilBlock;
1149 f->dirty = 0;
1151 filemetaunlock(f);
1152 return 0;
1154 Err:
1155 vtfileunlock(fp->msource);
1156 vtblockput(b);
1157 filemetaunlock(f);
1158 return -1;
1162 * That was far too much effort for directory entries.
1163 * Now we can write code that *does* things.
1167 * Flush all data associated with f out of the cache and onto venti.
1168 * If recursive is set, flush f's children too.
1169 * Vacfiledecref knows how to flush source and msource too.
1171 int
1172 vacfileflush(VacFile *f, int recursive)
1174 int ret;
1175 VacFile **kids, *p;
1176 int i, nkids;
1178 if(f->mode == VtOREAD)
1179 return 0;
1181 ret = 0;
1182 filemetalock(f);
1183 if(filemetaflush(f, nil) < 0)
1184 ret = -1;
1185 filemetaunlock(f);
1187 if(filelock(f) < 0)
1188 return -1;
1191 * Lock order prevents us from flushing kids while holding
1192 * lock, so make a list and then flush without the lock.
1194 nkids = 0;
1195 kids = nil;
1196 if(recursive){
1197 nkids = 0;
1198 for(p=f->down; p; p=p->next)
1199 nkids++;
1200 kids = vtmalloc(nkids*sizeof(VacFile*));
1201 i = 0;
1202 for(p=f->down; p; p=p->next){
1203 kids[i++] = p;
1204 p->ref++;
1207 if(nkids > 0){
1208 fileunlock(f);
1209 for(i=0; i<nkids; i++){
1210 if(vacfileflush(kids[i], 1) < 0)
1211 ret = -1;
1212 vacfiledecref(kids[i]);
1214 filelock(f);
1216 free(kids);
1219 * Now we can flush our own data.
1221 vtfilelock(f->source, -1);
1222 if(vtfileflush(f->source) < 0)
1223 ret = -1;
1224 vtfileunlock(f->source);
1225 if(f->msource){
1226 vtfilelock(f->msource, -1);
1227 if(vtfileflush(f->msource) < 0)
1228 ret = -1;
1229 vtfileunlock(f->msource);
1231 fileunlock(f);
1233 return ret;
1237 * Create a new file named elem in fp with the given mode.
1238 * The mode can be changed later except for the ModeDir bit.
1240 VacFile*
1241 vacfilecreate(VacFile *fp, char *elem, ulong mode)
1243 VacFile *ff;
1244 VacDir *dir;
1245 VtFile *pr, *r, *mr;
1246 int type;
1247 u32int bo;
1249 if(filelock(fp) < 0)
1250 return nil;
1253 * First, look to see that there's not a file in memory
1254 * with the same name.
1256 for(ff = fp->down; ff; ff=ff->next){
1257 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
1258 ff = nil;
1259 werrstr(EExists);
1260 goto Err1;
1265 * Next check the venti blocks.
1267 ff = dirlookup(fp, elem);
1268 if(ff != nil){
1269 werrstr(EExists);
1270 goto Err1;
1274 * By the way, you can't create in a read-only file system.
1276 pr = fp->source;
1277 if(pr->mode != VtORDWR){
1278 werrstr(EReadOnly);
1279 goto Err1;
1283 * Okay, time to actually create something. Lock the two
1284 * halves of the directory and create a file.
1286 if(vtfilelock2(fp->source, fp->msource, -1) < 0)
1287 goto Err1;
1288 ff = filealloc(fp->fs);
1289 ff->qidoffset = fp->qidoffset; /* hopefully fp->qidoffset == 0 */
1290 type = VtDataType;
1291 if(mode & ModeDir)
1292 type = VtDirType;
1293 mr = nil;
1294 if((r = vtfilecreate(pr, pr->psize, pr->dsize, type)) == nil)
1295 goto Err;
1296 if(mode & ModeDir)
1297 if((mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType)) == nil)
1298 goto Err;
1301 * Fill in the directory entry and write it to disk.
1303 dir = &ff->dir;
1304 dir->elem = vtstrdup(elem);
1305 dir->entry = r->offset;
1306 dir->gen = r->gen;
1307 if(mode & ModeDir){
1308 dir->mentry = mr->offset;
1309 dir->mgen = mr->gen;
1311 dir->size = 0;
1312 if(_vacfsnextqid(fp->fs, &dir->qid) < 0)
1313 goto Err;
1314 dir->uid = vtstrdup(fp->dir.uid);
1315 dir->gid = vtstrdup(fp->dir.gid);
1316 dir->mid = vtstrdup("");
1317 dir->mtime = time(0L);
1318 dir->mcount = 0;
1319 dir->ctime = dir->mtime;
1320 dir->atime = dir->mtime;
1321 dir->mode = mode;
1322 if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) == NilBlock)
1323 goto Err;
1326 * Now we're committed.
1328 vtfileunlock(fp->source);
1329 vtfileunlock(fp->msource);
1330 ff->source = r;
1331 ff->msource = mr;
1332 ff->boff = bo;
1334 /* Link into tree. */
1335 ff->next = fp->down;
1336 fp->down = ff;
1337 ff->up = fp;
1338 vacfileincref(fp);
1340 fileunlock(fp);
1342 filelock(ff);
1343 vtfilelock(ff->source, -1);
1344 vtfileunlock(ff->source);
1345 fileunlock(ff);
1347 return ff;
1349 Err:
1350 vtfileunlock(fp->source);
1351 vtfileunlock(fp->msource);
1352 if(r){
1353 vtfilelock(r, -1);
1354 vtfileremove(r);
1356 if(mr){
1357 vtfilelock(mr, -1);
1358 vtfileremove(mr);
1360 Err1:
1361 if(ff)
1362 vacfiledecref(ff);
1363 fileunlock(fp);
1364 return nil;
1368 * Change the size of the file f.
1370 int
1371 vacfilesetsize(VacFile *f, uvlong size)
1373 if(vacfileisdir(f)){
1374 werrstr(ENotFile);
1375 return -1;
1378 if(filelock(f) < 0)
1379 return -1;
1381 if(f->source->mode != VtORDWR){
1382 werrstr(EReadOnly);
1383 goto Err;
1385 if(vtfilelock(f->source, -1) < 0)
1386 goto Err;
1387 if(vtfilesetsize(f->source, size) < 0){
1388 vtfileunlock(f->source);
1389 goto Err;
1391 vtfileunlock(f->source);
1392 fileunlock(f);
1393 return 0;
1395 Err:
1396 fileunlock(f);
1397 return -1;
1401 * Write data to f.
1403 int
1404 vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
1406 if(vacfileisdir(f)){
1407 werrstr(ENotFile);
1408 return -1;
1410 if(filelock(f) < 0)
1411 return -1;
1412 if(f->source->mode != VtORDWR){
1413 werrstr(EReadOnly);
1414 goto Err;
1416 if(offset < 0){
1417 werrstr(EBadOffset);
1418 goto Err;
1421 if(vtfilelock(f->source, -1) < 0)
1422 goto Err;
1423 if(f->dir.mode & ModeAppend)
1424 offset = vtfilegetsize(f->source);
1425 if(vtfilewrite(f->source, buf, cnt, offset) != cnt
1426 || vtfileflushbefore(f->source, offset) < 0){
1427 vtfileunlock(f->source);
1428 goto Err;
1430 vtfileunlock(f->source);
1431 fileunlock(f);
1432 return cnt;
1434 Err:
1435 fileunlock(f);
1436 return -1;
1440 * Set (!) the VtEntry for the data contained in f.
1441 * This let's us efficiently copy data from one file to another.
1443 int
1444 vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
1446 int ret;
1448 vacfileflush(f, 0); /* flush blocks to venti, since we won't see them again */
1450 if(!(e->flags&VtEntryActive)){
1451 werrstr("missing entry for source");
1452 return -1;
1454 if(me && !(me->flags&VtEntryActive))
1455 me = nil;
1456 if(f->msource && !me){
1457 werrstr("missing entry for msource");
1458 return -1;
1460 if(me && !f->msource){
1461 werrstr("no msource to set");
1462 return -1;
1465 if(filelock(f) < 0)
1466 return -1;
1467 if(f->source->mode != VtORDWR
1468 || (f->msource && f->msource->mode != VtORDWR)){
1469 werrstr(EReadOnly);
1470 fileunlock(f);
1471 return -1;
1473 if(vtfilelock2(f->source, f->msource, -1) < 0){
1474 fileunlock(f);
1475 return -1;
1477 ret = 0;
1478 if(vtfilesetentry(f->source, e) < 0)
1479 ret = -1;
1480 else if(me && vtfilesetentry(f->msource, me) < 0)
1481 ret = -1;
1483 vtfileunlock(f->source);
1484 if(f->msource)
1485 vtfileunlock(f->msource);
1486 fileunlock(f);
1487 return ret;
1491 * Get the directory entry for f.
1493 int
1494 vacfilegetdir(VacFile *f, VacDir *dir)
1496 if(filerlock(f) < 0)
1497 return -1;
1499 filemetalock(f);
1500 vdcopy(dir, &f->dir);
1501 filemetaunlock(f);
1503 if(!vacfileisdir(f)){
1504 if(vtfilelock(f->source, VtOREAD) < 0){
1505 filerunlock(f);
1506 return -1;
1508 dir->size = vtfilegetsize(f->source);
1509 vtfileunlock(f->source);
1511 filerunlock(f);
1513 return 0;
1517 * Set the directory entry for f.
1519 int
1520 vacfilesetdir(VacFile *f, VacDir *dir)
1522 VacFile *ff;
1523 char *oelem;
1524 u32int mask;
1525 u64int size;
1527 /* can not set permissions for the root */
1528 if(vacfileisroot(f)){
1529 werrstr(ERoot);
1530 return -1;
1533 if(filelock(f) < 0)
1534 return -1;
1535 filemetalock(f);
1537 if(f->source->mode != VtORDWR){
1538 werrstr(EReadOnly);
1539 goto Err;
1542 /* On rename, check new name does not already exist */
1543 if(strcmp(f->dir.elem, dir->elem) != 0){
1544 for(ff = f->up->down; ff; ff=ff->next){
1545 if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
1546 werrstr(EExists);
1547 goto Err;
1550 ff = dirlookup(f->up, dir->elem);
1551 if(ff != nil){
1552 vacfiledecref(ff);
1553 werrstr(EExists);
1554 goto Err;
1556 werrstr(""); /* "failed" dirlookup poisoned it */
1559 /* Get ready... */
1560 if(vtfilelock2(f->source, f->msource, -1) < 0)
1561 goto Err;
1562 if(!vacfileisdir(f)){
1563 size = vtfilegetsize(f->source);
1564 if(size != dir->size){
1565 if(vtfilesetsize(f->source, dir->size) < 0){
1566 vtfileunlock(f->source);
1567 if(f->msource)
1568 vtfileunlock(f->msource);
1569 goto Err;
1573 /* ... now commited to changing it. */
1574 vtfileunlock(f->source);
1575 if(f->msource)
1576 vtfileunlock(f->msource);
1578 oelem = nil;
1579 if(strcmp(f->dir.elem, dir->elem) != 0){
1580 oelem = f->dir.elem;
1581 f->dir.elem = vtstrdup(dir->elem);
1584 if(strcmp(f->dir.uid, dir->uid) != 0){
1585 vtfree(f->dir.uid);
1586 f->dir.uid = vtstrdup(dir->uid);
1589 if(strcmp(f->dir.gid, dir->gid) != 0){
1590 vtfree(f->dir.gid);
1591 f->dir.gid = vtstrdup(dir->gid);
1594 f->dir.mtime = dir->mtime;
1595 f->dir.atime = dir->atime;
1597 mask = ~(ModeDir|ModeSnapshot);
1598 f->dir.mode &= ~mask;
1599 f->dir.mode |= mask & dir->mode;
1600 f->dirty = 1;
1602 if(filemetaflush(f, oelem) < 0){
1603 vtfree(oelem);
1604 goto Err; /* that sucks */
1606 vtfree(oelem);
1608 filemetaunlock(f);
1609 fileunlock(f);
1610 return 0;
1612 Err:
1613 filemetaunlock(f);
1614 fileunlock(f);
1615 return -1;
1619 * Set the qid space.
1621 int
1622 vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
1624 int ret;
1626 if(filelock(f) < 0)
1627 return -1;
1628 if(f->source->mode != VtORDWR){
1629 fileunlock(f);
1630 werrstr(EReadOnly);
1631 return -1;
1633 filemetalock(f);
1634 f->dir.qidspace = 1;
1635 f->dir.qidoffset = offset;
1636 f->dir.qidmax = max;
1637 f->dirty = 1;
1638 ret = filemetaflush(f, nil);
1639 filemetaunlock(f);
1640 fileunlock(f);
1641 return ret;
1645 * Check that the file is empty, returning 0 if it is.
1646 * Returns -1 on error (and not being empty is an error).
1648 static int
1649 filecheckempty(VacFile *f)
1651 u32int i, n;
1652 VtBlock *b;
1653 MetaBlock mb;
1654 VtFile *r;
1656 r = f->msource;
1657 n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
1658 for(i=0; i<n; i++){
1659 b = vtfileblock(r, i, VtOREAD);
1660 if(b == nil)
1661 return -1;
1662 if(mbunpack(&mb, b->data, r->dsize) < 0)
1663 goto Err;
1664 if(mb.nindex > 0){
1665 werrstr(ENotEmpty);
1666 goto Err;
1668 vtblockput(b);
1670 return 0;
1672 Err:
1673 vtblockput(b);
1674 return -1;
1678 * Remove the vac file f.
1680 int
1681 vacfileremove(VacFile *f)
1683 VacFile *ff;
1685 /* Cannot remove the root */
1686 if(vacfileisroot(f)){
1687 werrstr(ERoot);
1688 return -1;
1691 if(filelock(f) < 0)
1692 return -1;
1693 if(f->source->mode != VtORDWR){
1694 werrstr(EReadOnly);
1695 goto Err1;
1697 if(vtfilelock2(f->source, f->msource, -1) < 0)
1698 goto Err1;
1699 if(vacfileisdir(f) && filecheckempty(f)<0)
1700 goto Err;
1702 for(ff=f->down; ff; ff=ff->next)
1703 assert(ff->removed);
1705 vtfileremove(f->source);
1706 f->source = nil;
1707 if(f->msource){
1708 vtfileremove(f->msource);
1709 f->msource = nil;
1711 fileunlock(f);
1713 if(filemetaremove(f) < 0)
1714 return -1;
1715 return 0;
1717 Err:
1718 vtfileunlock(f->source);
1719 if(f->msource)
1720 vtfileunlock(f->msource);
1721 Err1:
1722 fileunlock(f);
1723 return -1;
1727 * Vac file system format.
1729 static char EBadVacFormat[] = "bad format for vac file";
1731 static VacFs *
1732 vacfsalloc(VtConn *z, int bsize, int ncache, int mode)
1734 VacFs *fs;
1736 fs = vtmallocz(sizeof(VacFs));
1737 fs->z = z;
1738 fs->bsize = bsize;
1739 fs->mode = mode;
1740 fs->cache = vtcachealloc(z, bsize, ncache);
1741 return fs;
1744 static int
1745 readscore(int fd, uchar score[VtScoreSize])
1747 char buf[45], *pref;
1748 int n;
1750 n = readn(fd, buf, sizeof(buf)-1);
1751 if(n < sizeof(buf)-1) {
1752 werrstr("short read");
1753 return -1;
1755 buf[n] = 0;
1757 if(vtparsescore(buf, &pref, score) < 0){
1758 werrstr(EBadVacFormat);
1759 return -1;
1761 if(pref==nil || strcmp(pref, "vac") != 0) {
1762 werrstr("not a vac file");
1763 return -1;
1765 return 0;
1768 VacFs*
1769 vacfsopen(VtConn *z, char *file, int mode, int ncache)
1771 int fd;
1772 uchar score[VtScoreSize];
1773 char *prefix;
1775 if(vtparsescore(file, &prefix, score) >= 0){
1776 if(strcmp(prefix, "vac") != 0){
1777 werrstr("not a vac file");
1778 return nil;
1780 }else{
1781 fd = open(file, OREAD);
1782 if(fd < 0)
1783 return nil;
1784 if(readscore(fd, score) < 0){
1785 close(fd);
1786 return nil;
1788 close(fd);
1790 return vacfsopenscore(z, score, mode, ncache);
1793 VacFs*
1794 vacfsopenscore(VtConn *z, u8int *score, int mode, int ncache)
1796 VacFs *fs;
1797 int n;
1798 VtRoot rt;
1799 uchar buf[VtRootSize];
1800 VacFile *root;
1801 VtFile *r;
1802 VtEntry e;
1804 n = vtread(z, score, VtRootType, buf, VtRootSize);
1805 if(n < 0)
1806 return nil;
1807 if(n != VtRootSize){
1808 werrstr("vtread on root too short");
1809 return nil;
1812 if(vtrootunpack(&rt, buf) < 0)
1813 return nil;
1815 if(strcmp(rt.type, "vac") != 0) {
1816 werrstr("not a vac root");
1817 return nil;
1820 fs = vacfsalloc(z, rt.blocksize, ncache, mode);
1821 memmove(fs->score, score, VtScoreSize);
1822 fs->mode = mode;
1824 memmove(e.score, rt.score, VtScoreSize);
1825 e.gen = 0;
1826 e.psize = (rt.blocksize/VtEntrySize)*VtEntrySize;
1827 e.dsize = rt.blocksize;
1828 e.type = VtDirType;
1829 e.flags = VtEntryActive;
1830 e.size = 3*VtEntrySize;
1832 root = nil;
1833 if((r = vtfileopenroot(fs->cache, &e)) == nil)
1834 goto Err;
1835 if(debug)
1836 fprint(2, "r %p\n", r);
1837 root = _vacfileroot(fs, r);
1838 if(debug)
1839 fprint(2, "root %p\n", root);
1840 vtfileclose(r);
1841 if(root == nil)
1842 goto Err;
1843 fs->root = root;
1844 return fs;
1845 Err:
1846 if(root)
1847 vacfiledecref(root);
1848 vacfsclose(fs);
1849 return nil;
1852 int
1853 vacfsmode(VacFs *fs)
1855 return fs->mode;
1858 VacFile*
1859 vacfsgetroot(VacFs *fs)
1861 return vacfileincref(fs->root);
1864 int
1865 vacfsgetblocksize(VacFs *fs)
1867 return fs->bsize;
1870 int
1871 vacfsgetscore(VacFs *fs, u8int *score)
1873 memmove(score, fs->score, VtScoreSize);
1874 return 0;
1877 int
1878 _vacfsnextqid(VacFs *fs, uvlong *qid)
1880 ++fs->qid;
1881 *qid = fs->qid;
1882 return 0;
1885 void
1886 vacfsjumpqid(VacFs *fs, uvlong step)
1888 fs->qid += step;
1892 * Set *maxqid to the maximum qid expected in this file system.
1893 * In newer vac archives, the maximum qid is stored in the
1894 * qidspace VacDir annotation. In older vac archives, the root
1895 * got created last, so it had the maximum qid.
1897 int
1898 vacfsgetmaxqid(VacFs *fs, uvlong *maxqid)
1900 VacDir vd;
1902 if(vacfilegetdir(fs->root, &vd) < 0)
1903 return -1;
1904 if(vd.qidspace)
1905 *maxqid = vd.qidmax;
1906 else
1907 *maxqid = vd.qid;
1908 vdcleanup(&vd);
1909 return 0;
1913 void
1914 vacfsclose(VacFs *fs)
1916 if(fs->root)
1917 vacfiledecref(fs->root);
1918 fs->root = nil;
1919 vtcachefree(fs->cache);
1920 vtfree(fs);
1924 * Create a fresh vac fs.
1926 VacFs *
1927 vacfscreate(VtConn *z, int bsize, int ncache)
1929 VacFs *fs;
1930 VtFile *f;
1931 uchar buf[VtEntrySize], metascore[VtScoreSize];
1932 VtEntry e;
1933 VtBlock *b;
1934 MetaBlock mb;
1935 VacDir vd;
1936 MetaEntry me;
1937 int psize;
1939 if((fs = vacfsalloc(z, bsize, ncache, VtORDWR)) == nil)
1940 return nil;
1943 * Fake up an empty vac fs.
1945 psize = bsize/VtEntrySize*VtEntrySize;
1946 f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType);
1947 vtfilelock(f, VtORDWR);
1949 /* Write metablock containing root directory VacDir. */
1950 b = vtcacheallocblock(fs->cache, VtDataType);
1951 mbinit(&mb, b->data, bsize, bsize/BytesPerEntry);
1952 memset(&vd, 0, sizeof vd);
1953 vd.elem = "/";
1954 vd.mode = 0777|ModeDir;
1955 vd.uid = "vac";
1956 vd.gid = "vac";
1957 vd.mid = "";
1958 me.size = vdsize(&vd, VacDirVersion);
1959 me.p = mballoc(&mb, me.size);
1960 vdpack(&vd, &me, VacDirVersion);
1961 mbinsert(&mb, 0, &me);
1962 mbpack(&mb);
1963 vtblockwrite(b);
1964 memmove(metascore, b->score, VtScoreSize);
1965 vtblockput(b);
1967 /* First entry: empty venti directory stream. */
1968 memset(&e, 0, sizeof e);
1969 e.flags = VtEntryActive;
1970 e.psize = psize;
1971 e.dsize = bsize;
1972 e.type = VtDirType;
1973 memmove(e.score, vtzeroscore, VtScoreSize);
1974 vtentrypack(&e, buf, 0);
1975 vtfilewrite(f, buf, VtEntrySize, 0);
1977 /* Second entry: empty metadata stream. */
1978 e.type = VtDataType;
1979 vtentrypack(&e, buf, 0);
1980 vtfilewrite(f, buf, VtEntrySize, VtEntrySize);
1982 /* Third entry: metadata stream with root directory. */
1983 memmove(e.score, metascore, VtScoreSize);
1984 e.size = bsize;
1985 vtentrypack(&e, buf, 0);
1986 vtfilewrite(f, buf, VtEntrySize, VtEntrySize*2);
1988 vtfileflush(f);
1989 vtfileunlock(f);
1991 /* Now open it as a vac fs. */
1992 fs->root = _vacfileroot(fs, f);
1993 if(fs->root == nil){
1994 werrstr("vacfileroot: %r");
1995 vacfsclose(fs);
1996 return nil;
1999 return fs;
2002 int
2003 vacfssync(VacFs *fs)
2005 uchar buf[1024];
2006 VtEntry e;
2007 VtFile *f;
2008 VtRoot root;
2010 /* Sync the entire vacfs to disk. */
2011 if(vacfileflush(fs->root, 1) < 0)
2012 return -1;
2013 if(vtfilelock(fs->root->up->msource, -1) < 0)
2014 return -1;
2015 if(vtfileflush(fs->root->up->msource) < 0){
2016 vtfileunlock(fs->root->up->msource);
2017 return -1;
2019 vtfileunlock(fs->root->up->msource);
2021 /* Prepare the dir stream for the root block. */
2022 if(getentry(fs->root->source, &e) < 0)
2023 return -1;
2024 vtentrypack(&e, buf, 0);
2025 if(getentry(fs->root->msource, &e) < 0)
2026 return -1;
2027 vtentrypack(&e, buf, 1);
2028 if(getentry(fs->root->up->msource, &e) < 0)
2029 return -1;
2030 vtentrypack(&e, buf, 2);
2032 f = vtfilecreateroot(fs->cache, fs->bsize, fs->bsize, VtDirType);
2033 vtfilelock(f, VtORDWR);
2034 if(vtfilewrite(f, buf, 3*VtEntrySize, 0) < 0
2035 || vtfileflush(f) < 0){
2036 vtfileunlock(f);
2037 vtfileclose(f);
2038 return -1;
2040 vtfileunlock(f);
2041 if(getentry(f, &e) < 0){
2042 vtfileclose(f);
2043 return -1;
2045 vtfileclose(f);
2047 /* Build a root block. */
2048 memset(&root, 0, sizeof root);
2049 strcpy(root.type, "vac");
2050 strcpy(root.name, fs->name);
2051 memmove(root.score, e.score, VtScoreSize);
2052 root.blocksize = fs->bsize;
2053 memmove(root.prev, fs->score, VtScoreSize);
2054 vtrootpack(&root, buf);
2055 if(vtwrite(fs->z, fs->score, VtRootType, buf, VtRootSize) < 0){
2056 werrstr("writing root: %r");
2057 return -1;
2059 if(vtsync(fs->z) < 0)
2060 return -1;
2061 return 0;