10 * Vac file system. This is a simplified version of the same code in Fossil.
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),
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.
26 static int filemetaflush(VacFile*, char*);
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 */
49 uvlong qidoffset; /* qid offset */
57 f = vtmallocz(sizeof(VacFile));
68 vtfileclose(f->source);
69 vtfileclose(f->msource);
71 memset(f, ~0, sizeof *f); /* paranoia */
82 || ((f->dir.mode & ModeDir) && f->msource == nil)){
101 fileunlock(VacFile *f)
107 filerlock(VacFile *f)
110 if(chksource(f) < 0){
118 filerunlock(VacFile *f)
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.
129 filemetalock(VacFile *f)
131 assert(f->up != nil);
136 filemetaunlock(VacFile *f)
142 vacfilegetid(VacFile *f)
145 return f->qidoffset + f->dir.qid;
149 vacfilegetqidoffset(VacFile *f)
155 vacfilegetmcount(VacFile *f)
160 mcount = f->dir.mcount;
166 vacfilegetmode(VacFile *f)
177 vacfileisdir(VacFile *f)
180 return (f->dir.mode & ModeDir) != 0;
184 vacfileisroot(VacFile *f)
186 return f == f->fs->root;
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.
198 vacfileincref(VacFile *vf)
208 vacfiledecref(VacFile *f)
210 VacFile *p, *q, **qq;
213 /* never linked in */
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);
238 * Flush f's directory information to the cache.
240 filemetaflush(f, nil);
244 for(q = *qq; q; q = *qq){
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
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.
273 _vacfileroot(VacFs *fs, VtFile *r)
278 VtFile *r0, *r1, *r2;
291 if(vtfilelock(r, -1) < 0)
293 r0 = vtfileopen(r, 0, fs->mode);
295 fprint(2, "r0 %p\n", r0);
298 r2 = vtfileopen(r, 2, fs->mode);
300 fprint(2, "r2 %p\n", r2);
303 * some vac files (e.g., from fossil)
304 * have an extra layer of indirection.
306 rerrstr(err, sizeof err);
307 if(!redirected && strstr(err, "not active")){
315 r1 = vtfileopen(r, 1, fs->mode);
317 fprint(2, "r1 %p\n", r1);
325 root = filealloc(fs);
336 if(vtfilelock(mr->msource, VtOREAD) < 0)
338 b = vtfileblock(mr->msource, 0, VtOREAD);
339 vtfileunlock(mr->msource);
343 if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
346 meunpack(&me, &mb, 0);
347 if(vdunpack(&root->dir, &me) < 0)
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.
384 dirlookup(VacFile *f, char *elem)
396 if(vtfilelock(meta, -1) < 0)
398 nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
399 for(bo=0; bo<nb; bo++){
400 b = vtfileblock(meta, bo, VtOREAD);
403 if(mbunpack(&mb, b->data, meta->dsize) < 0)
405 if(mbsearch(&mb, elem, &i, &me) >= 0){
406 ff = filealloc(f->fs);
407 if(vdunpack(&ff->dir, &me) < 0){
411 ff->qidoffset = f->qidoffset + ff->dir.qidoffset;
430 * Open the venti file at offset in the directory f->source.
434 fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
438 if((r = vtfileopen(f->source, offset, mode)) == nil)
447 if(r->dir != dir && r->mode != -1){
456 vacfilegetparent(VacFile *f)
459 return vacfileincref(f);
460 return vacfileincref(f->up);
464 * Given an unlocked vacfile (directory) f,
465 * return the vacfile named elem in f.
466 * Interprets . and .. as a convenience to callers.
469 vacfilewalk(VacFile *f, char *elem)
478 if(!vacfileisdir(f)){
483 if(strcmp(elem, ".") == 0)
484 return vacfileincref(f);
486 if(strcmp(elem, "..") == 0)
487 return vacfilegetparent(f);
492 for(ff = f->down; ff; ff=ff->next){
493 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
499 ff = dirlookup(f, elem);
503 if(ff->dir.mode & ModeSnapshot)
506 if(vtfilelock(f->source, f->mode) < 0)
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)
514 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
515 if(ff->source == nil)
518 vtfileunlock(f->source);
520 /* link in and up parent ref count */
530 vtfileunlock(f->source);
539 * Open a path in the vac file system:
540 * just walk each element one at a time.
543 vacfileopen(VacFs *fs, char *path)
546 char *p, elem[VtMaxStringSize], *opath;
553 for(p = path; *p && *p != '/'; p++)
557 if(n > VtMaxStringSize){
558 werrstr("%s: element too long", EBadPath);
561 memmove(elem, path, n);
563 ff = vacfilewalk(f, elem);
565 werrstr("%.*s: %r", utfnlen(opath, p-opath), opath);
582 * Extract the score for the bn'th block in f.
585 vacfileblockscore(VacFile *f, u32int bn, u8int *score)
594 if(vtfilelock(f->source, VtOREAD) < 0)
599 size = vtfilegetsize(s);
600 if((uvlong)bn*dsize >= size)
602 ret = vtfileblockscore(f->source, bn, score);
605 vtfileunlock(f->source);
615 vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
625 if(vtfilelock(f->source, VtOREAD) < 0){
629 n = vtfileread(f->source, buf, cnt, offset);
630 vtfileunlock(f->source);
636 getentry(VtFile *f, VtEntry *e)
638 if(vtfilelock(f, VtOREAD) < 0)
640 if(vtfilegetentry(f, e) < 0){
645 if(vtglobaltolocal(e->score) != NilBlock){
646 werrstr("internal error - data not on venti");
653 * Get the VtEntries for the data contained in f.
656 vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me)
660 if(e && getentry(f->source, e) < 0){
665 if(f->msource == nil)
666 memset(me, 0, sizeof *me);
667 else if(getentry(f->msource, me) < 0){
677 * Get the file's size.
680 vacfilegetsize(VacFile *f, uvlong *size)
684 if(vtfilelock(f->source, VtOREAD) < 0){
688 *size = vtfilegetsize(f->source);
689 vtfileunlock(f->source);
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.
713 if(!vacfileisdir(f)){
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.
727 for(p=f->down; p; p=p->next)
728 filemetaflush(p, nil);
731 vde = vtmallocz(sizeof(VacDirEnum));
732 vde->file = vacfileincref(f);
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.
745 direntrysize(VtFile *s, ulong offset, ulong gen, uvlong *size)
752 epb = s->dsize/VtEntrySize;
756 b = vtfileblock(s, bn, VtOREAD);
759 if(vtentryunpack(&e, b->data, offset) < 0)
762 /* dangling entries are returned as zero size */
763 if(!(e.flags & VtEntryActive) || e.gen != gen)
776 * Fill in vde with a new batch of directory entries.
779 vdefill(VacDirEnum *vde)
782 VtFile *meta, *source;
790 for(i=vde->i; i<vde->n; i++)
791 vdcleanup(vde->buf+i);
802 b = vtfileblock(meta, vde->boff, VtOREAD);
805 if(mbunpack(&mb, b->data, meta->dsize) < 0)
809 vde->buf = vtmalloc(n * sizeof(VacDir));
813 meunpack(&me, &mb, i);
814 if(vdunpack(de, &me) < 0)
817 if(!(de->mode & ModeDir))
818 if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
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.
837 vderead(VacDirEnum *vde, VacDir *de)
847 if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
852 nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
854 while(vde->i >= vde->n){
859 if(vdefill(vde) < 0){
865 memmove(de, vde->buf + vde->i, sizeof(VacDir));
870 vtfileunlock(f->source);
871 vtfileunlock(f->msource);
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".
884 vdeunread(VacDirEnum *vde)
894 * Close the enumerator.
897 vdeclose(VacDirEnum *vde)
902 /* free the strings */
903 for(i=vde->i; i<vde->n; i++)
904 vdcleanup(vde->buf+i);
906 vacfiledecref(vde->file);
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.
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.
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.
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.
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.
942 filemetaalloc(VacFile *fp, VacDir *dir, u32int start)
954 n = vdsize(dir, VacDirVersion);
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){
967 for(bo=start; bo<nb; bo++){
968 if((b = vtfileblock(ms, bo, VtOREAD)) == nil)
970 if(mbunpack(&mb, b->data, ms->dsize) < 0)
972 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
973 if(n <= nn && mb.nindex < mb.maxindex){
974 /* reopen for writing */
976 if((b = vtfileblock(ms, bo, VtORDWR)) == nil)
978 mbunpack(&mb, b->data, ms->dsize);
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)
988 vtfilesetsize(ms, (nb+1)*ms->dsize);
989 mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
992 /* Now we have a block; allocate space to write the entry. */
995 /* mballoc might have changed block */
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 */
1006 vdpack(dir, &me, VacDirVersion);
1007 mbinsert(&mb, i, &me);
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.
1022 * Assumes caller has filemetalock'ed f.
1025 filemetaflush(VacFile *f, char *oelem)
1038 oelem = f->dir.elem;
1041 * Locate f's old metadata in the parent's metadata file.
1042 * We know which block it was in, but not exactly where
1046 if(vtfilelock(fp->msource, -1) < 0)
1048 /* can happen if source is clri'ed out from under us */
1049 if(f->boff == NilBlock)
1051 b = vtfileblock(fp->msource, f->boff, VtORDWR);
1054 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
1056 if(mbsearch(&mb, oelem, &i, &me) < 0)
1060 * Check whether we can resize the entry and keep it
1063 n = vdsize(&f->dir, VacDirVersion);
1064 if(mbresize(&mb, &me, n) >= 0){
1065 /* Okay, can be done without moving to another block. */
1067 /* Remove old data */
1068 mbdelete(&mb, i, &me);
1070 /* Find new location if renaming */
1071 if(strcmp(f->dir.elem, oelem) != 0)
1072 mbsearch(&mb, f->dir.elem, &i, &me2);
1074 /* Pack new data into new location. */
1075 vdpack(&f->dir, &me, VacDirVersion);
1076 vdunpack(&f->dir, &me);
1077 mbinsert(&mb, i, &me);
1082 vtfileunlock(fp->msource);
1088 * The entry must be moved to another block.
1089 * This can only really happen on renames that
1090 * make the name very long.
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 */
1101 /* Now we're committed. Delete entry in old block. */
1102 mbdelete(&mb, i, &me);
1105 vtfileunlock(fp->msource);
1113 vtfileunlock(fp->msource);
1118 * Remove the directory entry for f.
1121 filemetaremove(VacFile *f)
1133 if(vtfilelock(fp->msource, VtORDWR) < 0)
1135 b = vtfileblock(fp->msource, f->boff, VtORDWR);
1139 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
1141 if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
1143 mbdelete(&mb, i, &me);
1146 vtfileunlock(fp->msource);
1156 vtfileunlock(fp->msource);
1163 * That was far too much effort for directory entries.
1164 * Now we can write code that *does* things.
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.
1173 vacfileflush(VacFile *f, int recursive)
1179 if(f->mode == VtOREAD)
1184 if(filemetaflush(f, nil) < 0)
1192 * Lock order prevents us from flushing kids while holding
1193 * lock, so make a list and then flush without the lock.
1199 for(p=f->down; p; p=p->next)
1201 kids = vtmalloc(nkids*sizeof(VacFile*));
1203 for(p=f->down; p; p=p->next){
1210 for(i=0; i<nkids; i++){
1211 if(vacfileflush(kids[i], 1) < 0)
1213 vacfiledecref(kids[i]);
1220 * Now we can flush our own data.
1222 vtfilelock(f->source, -1);
1223 if(vtfileflush(f->source) < 0)
1225 vtfileunlock(f->source);
1227 vtfilelock(f->msource, -1);
1228 if(vtfileflush(f->msource) < 0)
1230 vtfileunlock(f->msource);
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.
1242 vacfilecreate(VacFile *fp, char *elem, ulong mode)
1246 VtFile *pr, *r, *mr;
1250 if(filelock(fp) < 0)
1254 * First, look to see that there's not a file in memory
1255 * with the same name.
1257 for(ff = fp->down; ff; ff=ff->next){
1258 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
1266 * Next check the venti blocks.
1268 ff = dirlookup(fp, elem);
1275 * By the way, you can't create in a read-only file system.
1278 if(pr->mode != VtORDWR){
1284 * Okay, time to actually create something. Lock the two
1285 * halves of the directory and create a file.
1287 if(vtfilelock2(fp->source, fp->msource, -1) < 0)
1289 ff = filealloc(fp->fs);
1290 ff->qidoffset = fp->qidoffset; /* hopefully fp->qidoffset == 0 */
1295 if((r = vtfilecreate(pr, pr->psize, pr->dsize, type)) == nil)
1298 if((mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType)) == nil)
1302 * Fill in the directory entry and write it to disk.
1305 dir->elem = vtstrdup(elem);
1306 dir->entry = r->offset;
1309 dir->mentry = mr->offset;
1310 dir->mgen = mr->gen;
1313 if(_vacfsnextqid(fp->fs, &dir->qid) < 0)
1315 dir->uid = vtstrdup(fp->dir.uid);
1316 dir->gid = vtstrdup(fp->dir.gid);
1317 dir->mid = vtstrdup("");
1318 dir->mtime = time(0L);
1320 dir->ctime = dir->mtime;
1321 dir->atime = dir->mtime;
1323 if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) == NilBlock)
1327 * Now we're committed.
1329 vtfileunlock(fp->source);
1330 vtfileunlock(fp->msource);
1335 /* Link into tree. */
1336 ff->next = fp->down;
1344 vtfilelock(ff->source, -1);
1345 vtfileunlock(ff->source);
1351 vtfileunlock(fp->source);
1352 vtfileunlock(fp->msource);
1369 * Change the size of the file f.
1372 vacfilesetsize(VacFile *f, uvlong size)
1374 if(vacfileisdir(f)){
1382 if(f->source->mode != VtORDWR){
1386 if(vtfilelock(f->source, -1) < 0)
1388 if(vtfilesetsize(f->source, size) < 0){
1389 vtfileunlock(f->source);
1392 vtfileunlock(f->source);
1405 vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
1407 if(vacfileisdir(f)){
1413 if(f->source->mode != VtORDWR){
1418 werrstr(EBadOffset);
1422 if(vtfilelock(f->source, -1) < 0)
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);
1431 vtfileunlock(f->source);
1441 * Set (!) the VtEntry for the data contained in f.
1442 * This let's us efficiently copy data from one file to another.
1445 vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
1449 vacfileflush(f, 0); /* flush blocks to venti, since we won't see them again */
1451 if(!(e->flags&VtEntryActive)){
1452 werrstr("missing entry for source");
1455 if(me && !(me->flags&VtEntryActive))
1457 if(f->msource && !me){
1458 werrstr("missing entry for msource");
1461 if(me && !f->msource){
1462 werrstr("no msource to set");
1468 if(f->source->mode != VtORDWR
1469 || (f->msource && f->msource->mode != VtORDWR)){
1474 if(vtfilelock2(f->source, f->msource, -1) < 0){
1479 if(vtfilesetentry(f->source, e) < 0)
1481 else if(me && vtfilesetentry(f->msource, me) < 0)
1484 vtfileunlock(f->source);
1486 vtfileunlock(f->msource);
1492 * Get the directory entry for f.
1495 vacfilegetdir(VacFile *f, VacDir *dir)
1497 if(filerlock(f) < 0)
1501 vdcopy(dir, &f->dir);
1504 if(!vacfileisdir(f)){
1505 if(vtfilelock(f->source, VtOREAD) < 0){
1509 dir->size = vtfilegetsize(f->source);
1510 vtfileunlock(f->source);
1518 * Set the directory entry for f.
1521 vacfilesetdir(VacFile *f, VacDir *dir)
1528 /* can not set permissions for the root */
1529 if(vacfileisroot(f)){
1538 if(f->source->mode != VtORDWR){
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){
1551 ff = dirlookup(f->up, dir->elem);
1557 werrstr(""); /* "failed" dirlookup poisoned it */
1561 if(vtfilelock2(f->source, f->msource, -1) < 0)
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);
1569 vtfileunlock(f->msource);
1574 /* ... now commited to changing it. */
1575 vtfileunlock(f->source);
1577 vtfileunlock(f->msource);
1580 if(strcmp(f->dir.elem, dir->elem) != 0){
1581 oelem = f->dir.elem;
1582 f->dir.elem = vtstrdup(dir->elem);
1585 if(strcmp(f->dir.uid, dir->uid) != 0){
1587 f->dir.uid = vtstrdup(dir->uid);
1590 if(strcmp(f->dir.gid, dir->gid) != 0){
1592 f->dir.gid = vtstrdup(dir->gid);
1595 f->dir.mtime = dir->mtime;
1596 f->dir.atime = dir->atime;
1598 mask = ~(ModeDir|ModeSnapshot);
1599 f->dir.mode &= ~mask;
1600 f->dir.mode |= mask & dir->mode;
1603 if(filemetaflush(f, oelem) < 0){
1605 goto Err; /* that sucks */
1620 * Set the qid space.
1623 vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
1629 if(f->source->mode != VtORDWR){
1635 f->dir.qidspace = 1;
1636 f->dir.qidoffset = offset;
1637 f->dir.qidmax = max;
1639 ret = filemetaflush(f, nil);
1646 * Check that the file is empty, returning 0 if it is.
1647 * Returns -1 on error (and not being empty is an error).
1650 filecheckempty(VacFile *f)
1658 n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
1660 b = vtfileblock(r, i, VtOREAD);
1663 if(mbunpack(&mb, b->data, r->dsize) < 0)
1679 * Remove the vac file f.
1682 vacfileremove(VacFile *f)
1686 /* Cannot remove the root */
1687 if(vacfileisroot(f)){
1694 if(f->source->mode != VtORDWR){
1698 if(vtfilelock2(f->source, f->msource, -1) < 0)
1700 if(vacfileisdir(f) && filecheckempty(f)<0)
1703 for(ff=f->down; ff; ff=ff->next)
1704 assert(ff->removed);
1706 vtfileremove(f->source);
1709 vtfileremove(f->msource);
1714 if(filemetaremove(f) < 0)
1719 vtfileunlock(f->source);
1721 vtfileunlock(f->msource);
1728 * Vac file system format.
1730 static char EBadVacFormat[] = "bad format for vac file";
1733 vacfsalloc(VtConn *z, int bsize, ulong cachemem, int mode)
1737 fs = vtmallocz(sizeof(VacFs));
1741 fs->cache = vtcachealloc(z, cachemem);
1746 readscore(int fd, uchar score[VtScoreSize])
1748 char buf[45], *pref;
1751 n = readn(fd, buf, sizeof(buf)-1);
1752 if(n < sizeof(buf)-1) {
1753 werrstr("short read");
1758 if(vtparsescore(buf, &pref, score) < 0){
1759 werrstr(EBadVacFormat);
1762 if(pref==nil || strcmp(pref, "vac") != 0) {
1763 werrstr("not a vac file");
1770 vacfsopen(VtConn *z, char *file, int mode, ulong cachemem)
1773 uchar score[VtScoreSize];
1776 if(vtparsescore(file, &prefix, score) >= 0){
1777 if(strcmp(prefix, "vac") != 0){
1778 werrstr("not a vac file");
1782 fd = open(file, OREAD);
1785 if(readscore(fd, score) < 0){
1791 if(debug) fprint(2, "vacfsopen %V\n", score);
1792 return vacfsopenscore(z, score, mode, cachemem);
1796 vacfsopenscore(VtConn *z, u8int *score, int mode, ulong cachemem)
1801 uchar buf[VtRootSize];
1806 n = vtread(z, score, VtRootType, buf, VtRootSize);
1808 if(debug) fprint(2, "read %r\n");
1811 if(n != VtRootSize){
1812 werrstr("vtread on root too short");
1813 if(debug) fprint(2, "size %d\n", n);
1817 if(vtrootunpack(&rt, buf) < 0) {
1818 if(debug) fprint(2, "unpack: %r\n");
1822 if(strcmp(rt.type, "vac") != 0) {
1823 if(debug) fprint(2, "bad type %s\n", rt.type);
1824 werrstr("not a vac root");
1828 fs = vacfsalloc(z, rt.blocksize, cachemem, mode);
1829 memmove(fs->score, score, VtScoreSize);
1832 memmove(e.score, rt.score, VtScoreSize);
1835 // Don't waste cache memory on pointer blocks
1836 // when rt.blocksize is large.
1837 e.psize = (rt.blocksize/VtEntrySize)*VtEntrySize;
1839 e.psize = (60000/VtEntrySize)*VtEntrySize;
1841 e.dsize = rt.blocksize;
1842 if(debug) fprint(2, "openscore %d psize %d dsize %d\n", (int)rt.blocksize, (int)e.psize, (int)e.dsize);
1844 e.flags = VtEntryActive;
1845 e.size = 3*VtEntrySize;
1848 if((r = vtfileopenroot(fs->cache, &e)) == nil)
1851 fprint(2, "r %p\n", r);
1852 root = _vacfileroot(fs, r);
1854 fprint(2, "root %p\n", root);
1862 vacfiledecref(root);
1868 vacfsmode(VacFs *fs)
1874 vacfsgetroot(VacFs *fs)
1876 return vacfileincref(fs->root);
1880 vacfsgetblocksize(VacFs *fs)
1886 vacfsgetscore(VacFs *fs, u8int *score)
1888 memmove(score, fs->score, VtScoreSize);
1893 _vacfsnextqid(VacFs *fs, uvlong *qid)
1901 vacfsjumpqid(VacFs *fs, uvlong step)
1907 * Set *maxqid to the maximum qid expected in this file system.
1908 * In newer vac archives, the maximum qid is stored in the
1909 * qidspace VacDir annotation. In older vac archives, the root
1910 * got created last, so it had the maximum qid.
1913 vacfsgetmaxqid(VacFs *fs, uvlong *maxqid)
1917 if(vacfilegetdir(fs->root, &vd) < 0)
1920 *maxqid = vd.qidmax;
1929 vacfsclose(VacFs *fs)
1932 vacfiledecref(fs->root);
1934 vtcachefree(fs->cache);
1939 * Create a fresh vac fs.
1942 vacfscreate(VtConn *z, int bsize, ulong cachemem)
1946 uchar buf[VtEntrySize], metascore[VtScoreSize];
1954 if((fs = vacfsalloc(z, bsize, cachemem, VtORDWR)) == nil)
1958 * Fake up an empty vac fs.
1960 psize = bsize/VtScoreSize*VtScoreSize;
1962 psize = 60000/VtScoreSize*VtScoreSize;
1963 if(debug) fprint(2, "create bsize %d psize %d\n", bsize, psize);
1965 f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType);
1967 sysfatal("vtfilecreateroot: %r");
1968 vtfilelock(f, VtORDWR);
1970 /* Write metablock containing root directory VacDir. */
1971 b = vtcacheallocblock(fs->cache, VtDataType, bsize);
1972 mbinit(&mb, b->data, bsize, bsize/BytesPerEntry);
1973 memset(&vd, 0, sizeof vd);
1975 vd.mode = 0777|ModeDir;
1979 me.size = vdsize(&vd, VacDirVersion);
1980 me.p = mballoc(&mb, me.size);
1981 vdpack(&vd, &me, VacDirVersion);
1982 mbinsert(&mb, 0, &me);
1985 memmove(metascore, b->score, VtScoreSize);
1988 /* First entry: empty venti directory stream. */
1989 memset(&e, 0, sizeof e);
1990 e.flags = VtEntryActive;
1994 memmove(e.score, vtzeroscore, VtScoreSize);
1995 vtentrypack(&e, buf, 0);
1996 vtfilewrite(f, buf, VtEntrySize, 0);
1998 /* Second entry: empty metadata stream. */
1999 e.type = VtDataType;
2000 vtentrypack(&e, buf, 0);
2001 vtfilewrite(f, buf, VtEntrySize, VtEntrySize);
2003 /* Third entry: metadata stream with root directory. */
2004 memmove(e.score, metascore, VtScoreSize);
2006 vtentrypack(&e, buf, 0);
2007 vtfilewrite(f, buf, VtEntrySize, VtEntrySize*2);
2012 /* Now open it as a vac fs. */
2013 fs->root = _vacfileroot(fs, f);
2014 if(fs->root == nil){
2015 werrstr("vacfileroot: %r");
2024 vacfssync(VacFs *fs)
2031 /* Sync the entire vacfs to disk. */
2032 if(vacfileflush(fs->root, 1) < 0)
2034 if(vtfilelock(fs->root->up->msource, -1) < 0)
2036 if(vtfileflush(fs->root->up->msource) < 0){
2037 vtfileunlock(fs->root->up->msource);
2040 vtfileunlock(fs->root->up->msource);
2042 /* Prepare the dir stream for the root block. */
2043 if(getentry(fs->root->source, &e) < 0)
2045 vtentrypack(&e, buf, 0);
2046 if(getentry(fs->root->msource, &e) < 0)
2048 vtentrypack(&e, buf, 1);
2049 if(getentry(fs->root->up->msource, &e) < 0)
2051 vtentrypack(&e, buf, 2);
2053 f = vtfilecreateroot(fs->cache, fs->bsize, fs->bsize, VtDirType);
2054 vtfilelock(f, VtORDWR);
2055 if(vtfilewrite(f, buf, 3*VtEntrySize, 0) < 0
2056 || vtfileflush(f) < 0){
2062 if(getentry(f, &e) < 0){
2068 /* Build a root block. */
2069 memset(&root, 0, sizeof root);
2070 strcpy(root.type, "vac");
2071 strcpy(root.name, fs->name);
2072 memmove(root.score, e.score, VtScoreSize);
2073 root.blocksize = fs->bsize;
2074 memmove(root.prev, fs->score, VtScoreSize);
2075 vtrootpack(&root, buf);
2076 if(vtwrite(fs->z, fs->score, VtRootType, buf, VtRootSize) < 0){
2077 werrstr("writing root: %r");
2080 if(vtsync(fs->z) < 0)
2086 vacfiledsize(VacFile *f)
2090 if(vacfilegetentries(f,&e,nil) < 0)
2096 * Does block b of f have the same SHA1 hash as the n bytes at buf?
2099 sha1matches(VacFile *f, ulong b, uchar *buf, int n)
2101 uchar fscore[VtScoreSize];
2102 uchar bufscore[VtScoreSize];
2104 if(vacfileblockscore(f, b, fscore) < 0)
2106 n = vtzerotruncate(VtDataType, buf, n);
2107 sha1(buf, n, bufscore, nil);
2108 if(memcmp(bufscore, fscore, VtScoreSize) == 0)