xref: /plan9/sys/src/cmd/vac/file.c (revision fba43d71b4443b968ad4ef4774f3628e4d3f4ddd)
19a747e4fSDavid du Colombier #include "stdinc.h"
29a747e4fSDavid du Colombier #include "vac.h"
39a747e4fSDavid du Colombier #include "dat.h"
49a747e4fSDavid du Colombier #include "fns.h"
59a747e4fSDavid du Colombier #include "error.h"
69a747e4fSDavid du Colombier 
73be74836SDavid du Colombier #define debug 0
83be74836SDavid du Colombier 
9d9306527SDavid du Colombier /*
103be74836SDavid du Colombier  * Vac file system.  This is a simplified version of the same code in Fossil.
113be74836SDavid du Colombier  *
123be74836SDavid du Colombier  * The locking order in the tree is upward: a thread can hold the lock
133be74836SDavid du Colombier  * for a VacFile and then acquire the lock of f->up (the parent),
143be74836SDavid du Colombier  * but not vice-versa.
153be74836SDavid du Colombier  *
163be74836SDavid du Colombier  * A vac file is one or two venti files.  Plain data files are one venti file,
173be74836SDavid du Colombier  * while directores are two: a venti data file containing traditional
183be74836SDavid du Colombier  * directory entries, and a venti directory file containing venti
193be74836SDavid du Colombier  * directory entries.  The traditional directory entries in the data file
203be74836SDavid du Colombier  * contain integers indexing into the venti directory entry file.
213be74836SDavid du Colombier  * It's a little complicated, but it makes the data usable by standard
223be74836SDavid du Colombier  * tools like venti/copy.
233be74836SDavid du Colombier  *
24d9306527SDavid du Colombier  */
25d9306527SDavid du Colombier 
263be74836SDavid du Colombier static int filemetaflush(VacFile*, char*);
273be74836SDavid du Colombier 
283be74836SDavid du Colombier struct VacFile
293be74836SDavid du Colombier {
303be74836SDavid du Colombier 	VacFs	*fs;	/* immutable */
313be74836SDavid du Colombier 
32d9306527SDavid du Colombier 	/* meta data for file: protected by the lk in the parent */
33d9306527SDavid du Colombier 	int		ref;	/* holds this data structure up */
34d9306527SDavid du Colombier 
353be74836SDavid du Colombier 	int		partial;	/* file was never really open */
36d9306527SDavid du Colombier 	int		removed;	/* file has been removed */
37d9306527SDavid du Colombier 	int		dirty;	/* dir is dirty with respect to meta data in block */
383be74836SDavid du Colombier 	u32int	boff;		/* block offset within msource for this file's metadata */
39d9306527SDavid du Colombier 	VacDir	dir;		/* metadata for this file */
40d9306527SDavid du Colombier 	VacFile	*up;		/* parent file */
41d9306527SDavid du Colombier 	VacFile	*next;	/* sibling */
42d9306527SDavid du Colombier 
433be74836SDavid du Colombier 	RWLock	lk;		/* lock for the following */
443be74836SDavid du Colombier 	VtFile	*source;	/* actual data */
453be74836SDavid du Colombier 	VtFile	*msource;	/* metadata for children in a directory */
46d9306527SDavid du Colombier 	VacFile	*down;	/* children */
473be74836SDavid du Colombier 	int		mode;
483be74836SDavid du Colombier 
493be74836SDavid du Colombier 	uvlong	qidoffset;	/* qid offset */
50d9306527SDavid du Colombier };
51d9306527SDavid du Colombier 
529a747e4fSDavid du Colombier static VacFile*
filealloc(VacFs * fs)533be74836SDavid du Colombier filealloc(VacFs *fs)
549a747e4fSDavid du Colombier {
553be74836SDavid du Colombier 	VacFile *f;
569a747e4fSDavid du Colombier 
573be74836SDavid du Colombier 	f = vtmallocz(sizeof(VacFile));
583be74836SDavid du Colombier 	f->ref = 1;
593be74836SDavid du Colombier 	f->fs = fs;
603be74836SDavid du Colombier 	f->boff = NilBlock;
613be74836SDavid du Colombier 	f->mode = fs->mode;
623be74836SDavid du Colombier 	return f;
633be74836SDavid du Colombier }
643be74836SDavid du Colombier 
653be74836SDavid du Colombier static void
filefree(VacFile * f)663be74836SDavid du Colombier filefree(VacFile *f)
673be74836SDavid du Colombier {
683be74836SDavid du Colombier 	vtfileclose(f->source);
693be74836SDavid du Colombier 	vtfileclose(f->msource);
703be74836SDavid du Colombier 	vdcleanup(&f->dir);
713be74836SDavid du Colombier 	memset(f, ~0, sizeof *f);	/* paranoia */
723be74836SDavid du Colombier 	vtfree(f);
733be74836SDavid du Colombier }
743be74836SDavid du Colombier 
753be74836SDavid du Colombier static int
chksource(VacFile * f)763be74836SDavid du Colombier chksource(VacFile *f)
773be74836SDavid du Colombier {
783be74836SDavid du Colombier 	if(f->partial)
793be74836SDavid du Colombier 		return 0;
803be74836SDavid du Colombier 
813be74836SDavid du Colombier 	if(f->source == nil
823be74836SDavid du Colombier 	|| ((f->dir.mode & ModeDir) && f->msource == nil)){
833be74836SDavid du Colombier 		werrstr(ERemoved);
843be74836SDavid du Colombier 		return -1;
853be74836SDavid du Colombier 	}
863be74836SDavid du Colombier 	return 0;
873be74836SDavid du Colombier }
883be74836SDavid du Colombier 
893be74836SDavid du Colombier static int
filelock(VacFile * f)903be74836SDavid du Colombier filelock(VacFile *f)
913be74836SDavid du Colombier {
923be74836SDavid du Colombier 	wlock(&f->lk);
933be74836SDavid du Colombier 	if(chksource(f) < 0){
943be74836SDavid du Colombier 		wunlock(&f->lk);
953be74836SDavid du Colombier 		return -1;
963be74836SDavid du Colombier 	}
973be74836SDavid du Colombier 	return 0;
983be74836SDavid du Colombier }
993be74836SDavid du Colombier 
1003be74836SDavid du Colombier static void
fileunlock(VacFile * f)1013be74836SDavid du Colombier fileunlock(VacFile *f)
1023be74836SDavid du Colombier {
1033be74836SDavid du Colombier 	wunlock(&f->lk);
1043be74836SDavid du Colombier }
1053be74836SDavid du Colombier 
1063be74836SDavid du Colombier static int
filerlock(VacFile * f)1073be74836SDavid du Colombier filerlock(VacFile *f)
1083be74836SDavid du Colombier {
1093be74836SDavid du Colombier 	rlock(&f->lk);
1103be74836SDavid du Colombier 	if(chksource(f) < 0){
1113be74836SDavid du Colombier 		runlock(&f->lk);
1123be74836SDavid du Colombier 		return -1;
1133be74836SDavid du Colombier 	}
1143be74836SDavid du Colombier 	return 0;
1153be74836SDavid du Colombier }
1163be74836SDavid du Colombier 
1173be74836SDavid du Colombier static void
filerunlock(VacFile * f)1183be74836SDavid du Colombier filerunlock(VacFile *f)
1193be74836SDavid du Colombier {
1203be74836SDavid du Colombier 	runlock(&f->lk);
1213be74836SDavid du Colombier }
1223be74836SDavid du Colombier 
1233be74836SDavid du Colombier /*
1243be74836SDavid du Colombier  * The file metadata, like f->dir and f->ref,
1253be74836SDavid du Colombier  * are synchronized via the parent's lock.
1263be74836SDavid du Colombier  * This is why locking order goes up.
1273be74836SDavid du Colombier  */
1283be74836SDavid du Colombier static void
filemetalock(VacFile * f)1293be74836SDavid du Colombier filemetalock(VacFile *f)
1303be74836SDavid du Colombier {
1313be74836SDavid du Colombier 	assert(f->up != nil);
1323be74836SDavid du Colombier 	wlock(&f->up->lk);
1333be74836SDavid du Colombier }
1343be74836SDavid du Colombier 
1353be74836SDavid du Colombier static void
filemetaunlock(VacFile * f)1363be74836SDavid du Colombier filemetaunlock(VacFile *f)
1373be74836SDavid du Colombier {
1383be74836SDavid du Colombier 	wunlock(&f->up->lk);
1393be74836SDavid du Colombier }
1403be74836SDavid du Colombier 
1413be74836SDavid du Colombier uvlong
vacfilegetid(VacFile * f)1423be74836SDavid du Colombier vacfilegetid(VacFile *f)
1433be74836SDavid du Colombier {
1443be74836SDavid du Colombier 	/* immutable */
1453be74836SDavid du Colombier 	return f->qidoffset + f->dir.qid;
1463be74836SDavid du Colombier }
1473be74836SDavid du Colombier 
1483be74836SDavid du Colombier uvlong
vacfilegetqidoffset(VacFile * f)1493be74836SDavid du Colombier vacfilegetqidoffset(VacFile *f)
1503be74836SDavid du Colombier {
1513be74836SDavid du Colombier 	return f->qidoffset;
1523be74836SDavid du Colombier }
1533be74836SDavid du Colombier 
1543be74836SDavid du Colombier ulong
vacfilegetmcount(VacFile * f)1553be74836SDavid du Colombier vacfilegetmcount(VacFile *f)
1563be74836SDavid du Colombier {
1573be74836SDavid du Colombier 	ulong mcount;
1583be74836SDavid du Colombier 
1593be74836SDavid du Colombier 	filemetalock(f);
1603be74836SDavid du Colombier 	mcount = f->dir.mcount;
1613be74836SDavid du Colombier 	filemetaunlock(f);
1623be74836SDavid du Colombier 	return mcount;
1633be74836SDavid du Colombier }
1643be74836SDavid du Colombier 
1653be74836SDavid du Colombier ulong
vacfilegetmode(VacFile * f)1663be74836SDavid du Colombier vacfilegetmode(VacFile *f)
1673be74836SDavid du Colombier {
1683be74836SDavid du Colombier 	ulong mode;
1693be74836SDavid du Colombier 
1703be74836SDavid du Colombier 	filemetalock(f);
1713be74836SDavid du Colombier 	mode = f->dir.mode;
1723be74836SDavid du Colombier 	filemetaunlock(f);
1733be74836SDavid du Colombier 	return mode;
1743be74836SDavid du Colombier }
1753be74836SDavid du Colombier 
1763be74836SDavid du Colombier int
vacfileisdir(VacFile * f)1773be74836SDavid du Colombier vacfileisdir(VacFile *f)
1783be74836SDavid du Colombier {
1793be74836SDavid du Colombier 	/* immutable */
1803be74836SDavid du Colombier 	return (f->dir.mode & ModeDir) != 0;
1813be74836SDavid du Colombier }
1823be74836SDavid du Colombier 
1833be74836SDavid du Colombier int
vacfileisroot(VacFile * f)1843be74836SDavid du Colombier vacfileisroot(VacFile *f)
1853be74836SDavid du Colombier {
1863be74836SDavid du Colombier 	return f == f->fs->root;
1873be74836SDavid du Colombier }
1883be74836SDavid du Colombier 
1893be74836SDavid du Colombier /*
1903be74836SDavid du Colombier  * The files are reference counted, and while the reference
1913be74836SDavid du Colombier  * is bigger than zero, each file can be found in its parent's
1923be74836SDavid du Colombier  * f->down list (chains via f->next), so that multiple threads
1933be74836SDavid du Colombier  * end up sharing a VacFile* when referring to the same file.
1943be74836SDavid du Colombier  *
1953be74836SDavid du Colombier  * Each VacFile holds a reference to its parent.
1963be74836SDavid du Colombier  */
1973be74836SDavid du Colombier VacFile*
vacfileincref(VacFile * vf)1983be74836SDavid du Colombier vacfileincref(VacFile *vf)
1993be74836SDavid du Colombier {
2003be74836SDavid du Colombier 	filemetalock(vf);
2013be74836SDavid du Colombier 	assert(vf->ref > 0);
2023be74836SDavid du Colombier 	vf->ref++;
2033be74836SDavid du Colombier 	filemetaunlock(vf);
2049a747e4fSDavid du Colombier 	return vf;
2059a747e4fSDavid du Colombier }
2069a747e4fSDavid du Colombier 
2073be74836SDavid du Colombier int
vacfiledecref(VacFile * f)2083be74836SDavid du Colombier vacfiledecref(VacFile *f)
209d9306527SDavid du Colombier {
2103be74836SDavid du Colombier 	VacFile *p, *q, **qq;
211d9306527SDavid du Colombier 
2123be74836SDavid du Colombier 	if(f->up == nil){
2133be74836SDavid du Colombier 		/* never linked in */
2143be74836SDavid du Colombier 		assert(f->ref == 1);
2153be74836SDavid du Colombier 		filefree(f);
2163be74836SDavid du Colombier 		return 0;
217d9306527SDavid du Colombier 	}
218d9306527SDavid du Colombier 
2193be74836SDavid du Colombier 	filemetalock(f);
2203be74836SDavid du Colombier 	f->ref--;
2213be74836SDavid du Colombier 	if(f->ref > 0){
2223be74836SDavid du Colombier 		filemetaunlock(f);
2233be74836SDavid du Colombier 		return -1;
224e538c934SDavid du Colombier 	}
2253be74836SDavid du Colombier 	assert(f->ref == 0);
2263be74836SDavid du Colombier 	assert(f->down == nil);
2273be74836SDavid du Colombier 
2283be74836SDavid du Colombier 	if(f->source && vtfilelock(f->source, -1) >= 0){
2293be74836SDavid du Colombier 		vtfileflush(f->source);
2303be74836SDavid du Colombier 		vtfileunlock(f->source);
2313be74836SDavid du Colombier 	}
2323be74836SDavid du Colombier 	if(f->msource && vtfilelock(f->msource, -1) >= 0){
2333be74836SDavid du Colombier 		vtfileflush(f->msource);
2343be74836SDavid du Colombier 		vtfileunlock(f->msource);
235d9306527SDavid du Colombier 	}
236d9306527SDavid du Colombier 
2373be74836SDavid du Colombier 	/*
2383be74836SDavid du Colombier 	 * Flush f's directory information to the cache.
2393be74836SDavid du Colombier 	 */
2403be74836SDavid du Colombier 	filemetaflush(f, nil);
2413be74836SDavid du Colombier 
2423be74836SDavid du Colombier 	p = f->up;
2433be74836SDavid du Colombier 	qq = &p->down;
2443be74836SDavid du Colombier 	for(q = *qq; q; q = *qq){
2453be74836SDavid du Colombier 		if(q == f)
2463be74836SDavid du Colombier 			break;
2473be74836SDavid du Colombier 		qq = &q->next;
248d9306527SDavid du Colombier 	}
2493be74836SDavid du Colombier 	assert(q != nil);
2503be74836SDavid du Colombier 	*qq = f->next;
2513be74836SDavid du Colombier 
2523be74836SDavid du Colombier 	filemetaunlock(f);
2533be74836SDavid du Colombier 	filefree(f);
2543be74836SDavid du Colombier 	vacfiledecref(p);
2553be74836SDavid du Colombier 	return 0;
256d9306527SDavid du Colombier }
2579a747e4fSDavid du Colombier 
258aedc1c01SDavid du Colombier 
2593be74836SDavid du Colombier /*
2603be74836SDavid du Colombier  * Construct a vacfile for the root of a vac tree, given the
2613be74836SDavid du Colombier  * venti file for the root information.  That venti file is a
2623be74836SDavid du Colombier  * directory file containing VtEntries for three more venti files:
2633be74836SDavid du Colombier  * the two venti files making up the root directory, and a
2643be74836SDavid du Colombier  * third venti file that would be the metadata half of the
2653be74836SDavid du Colombier  * "root's parent".
2663be74836SDavid du Colombier  *
2673be74836SDavid du Colombier  * Fossil generates slightly different vac files, due to a now
2683be74836SDavid du Colombier  * impossible-to-change bug, which contain a VtEntry
2693be74836SDavid du Colombier  * for just one venti file, that itself contains the expected
2703be74836SDavid du Colombier  * three directory entries.  Sigh.
2713be74836SDavid du Colombier  */
2729a747e4fSDavid du Colombier VacFile*
_vacfileroot(VacFs * fs,VtFile * r)2733be74836SDavid du Colombier _vacfileroot(VacFs *fs, VtFile *r)
2749a747e4fSDavid du Colombier {
2753be74836SDavid du Colombier 	int redirected;
2763be74836SDavid du Colombier 	char err[ERRMAX];
2773be74836SDavid du Colombier 	VtBlock *b;
2783be74836SDavid du Colombier 	VtFile *r0, *r1, *r2;
2799a747e4fSDavid du Colombier 	MetaBlock mb;
280d9306527SDavid du Colombier 	MetaEntry me;
281d9306527SDavid du Colombier 	VacFile *root, *mr;
2829a747e4fSDavid du Colombier 
2833be74836SDavid du Colombier 	redirected = 0;
2843be74836SDavid du Colombier Top:
2853be74836SDavid du Colombier 	b = nil;
286d9306527SDavid du Colombier 	root = nil;
287d9306527SDavid du Colombier 	mr = nil;
2889a747e4fSDavid du Colombier 	r1 = nil;
2899a747e4fSDavid du Colombier 	r2 = nil;
2909a747e4fSDavid du Colombier 
2913be74836SDavid du Colombier 	if(vtfilelock(r, -1) < 0)
2923be74836SDavid du Colombier 		return nil;
2933be74836SDavid du Colombier 	r0 = vtfileopen(r, 0, fs->mode);
2943be74836SDavid du Colombier 	if(debug)
2953be74836SDavid du Colombier 		fprint(2, "r0 %p\n", r0);
2969a747e4fSDavid du Colombier 	if(r0 == nil)
2979a747e4fSDavid du Colombier 		goto Err;
2983be74836SDavid du Colombier 	r2 = vtfileopen(r, 2, fs->mode);
2993be74836SDavid du Colombier 	if(debug)
3003be74836SDavid du Colombier 		fprint(2, "r2 %p\n", r2);
3013be74836SDavid du Colombier 	if(r2 == nil){
3023be74836SDavid du Colombier 		/*
3033be74836SDavid du Colombier 		 * some vac files (e.g., from fossil)
3043be74836SDavid du Colombier 		 * have an extra layer of indirection.
3053be74836SDavid du Colombier 		 */
3063be74836SDavid du Colombier 		rerrstr(err, sizeof err);
3073be74836SDavid du Colombier 		if(!redirected && strstr(err, "not active")){
3083be74836SDavid du Colombier 			redirected = 1;
3093be74836SDavid du Colombier 			vtfileunlock(r);
3103be74836SDavid du Colombier 			r = r0;
3113be74836SDavid du Colombier 			goto Top;
3123be74836SDavid du Colombier 		}
31349223a73SDavid du Colombier 		goto Err;
31449223a73SDavid du Colombier 	}
3153be74836SDavid du Colombier 	r1 = vtfileopen(r, 1, fs->mode);
3163be74836SDavid du Colombier 	if(debug)
3173be74836SDavid du Colombier 		fprint(2, "r1 %p\n", r1);
3183be74836SDavid du Colombier 	if(r1 == nil)
31949223a73SDavid du Colombier 		goto Err;
3209a747e4fSDavid du Colombier 
3213be74836SDavid du Colombier 	mr = filealloc(fs);
322d9306527SDavid du Colombier 	mr->msource = r2;
323d9306527SDavid du Colombier 	r2 = nil;
324d9306527SDavid du Colombier 
3253be74836SDavid du Colombier 	root = filealloc(fs);
3263be74836SDavid du Colombier 	root->boff = 0;
327d9306527SDavid du Colombier 	root->up = mr;
328d9306527SDavid du Colombier 	root->source = r0;
329d9306527SDavid du Colombier 	r0 = nil;
330d9306527SDavid du Colombier 	root->msource = r1;
331d9306527SDavid du Colombier 	r1 = nil;
332d9306527SDavid du Colombier 
333d9306527SDavid du Colombier 	mr->down = root;
3343be74836SDavid du Colombier 	vtfileunlock(r);
335d9306527SDavid du Colombier 
3363be74836SDavid du Colombier 	if(vtfilelock(mr->msource, VtOREAD) < 0)
3373be74836SDavid du Colombier 		goto Err1;
3383be74836SDavid du Colombier 	b = vtfileblock(mr->msource, 0, VtOREAD);
3393be74836SDavid du Colombier 	vtfileunlock(mr->msource);
3403be74836SDavid du Colombier 	if(b == nil)
3413be74836SDavid du Colombier 		goto Err1;
3429a747e4fSDavid du Colombier 
3433be74836SDavid du Colombier 	if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
3443be74836SDavid du Colombier 		goto Err1;
345e5fc48fbSDavid du Colombier 
3463be74836SDavid du Colombier 	meunpack(&me, &mb, 0);
3473be74836SDavid du Colombier 	if(vdunpack(&root->dir, &me) < 0)
3483be74836SDavid du Colombier 		goto Err1;
3493be74836SDavid du Colombier 	vtblockput(b);
3509a747e4fSDavid du Colombier 
3519a747e4fSDavid du Colombier 	return root;
3529a747e4fSDavid du Colombier Err:
3533be74836SDavid du Colombier 	vtfileunlock(r);
3543be74836SDavid du Colombier Err1:
3553be74836SDavid du Colombier 	vtblockput(b);
3569a747e4fSDavid du Colombier 	if(r0)
3573be74836SDavid du Colombier 		vtfileclose(r0);
3589a747e4fSDavid du Colombier 	if(r1)
3593be74836SDavid du Colombier 		vtfileclose(r1);
3609a747e4fSDavid du Colombier 	if(r2)
3613be74836SDavid du Colombier 		vtfileclose(r2);
362d9306527SDavid du Colombier 	if(mr)
3633be74836SDavid du Colombier 		filefree(mr);
3649a747e4fSDavid du Colombier 	if(root)
3653be74836SDavid du Colombier 		filefree(root);
3669a747e4fSDavid du Colombier 
3679a747e4fSDavid du Colombier 	return nil;
3689a747e4fSDavid du Colombier }
3699a747e4fSDavid du Colombier 
3703be74836SDavid du Colombier /*
3713be74836SDavid du Colombier  * Vac directories are a sequence of metablocks, each of which
3723be74836SDavid du Colombier  * contains a bunch of metaentries sorted by file name.
3733be74836SDavid du Colombier  * The whole sequence isn't sorted, though, so you still have
3743be74836SDavid du Colombier  * to look at every block to find a given name.
3753be74836SDavid du Colombier  * Dirlookup looks in f for an element name elem.
3763be74836SDavid du Colombier  * It returns a new VacFile with the dir, boff, and mode
3773be74836SDavid du Colombier  * filled in, but the sources (venti files) are not, and f is
3783be74836SDavid du Colombier  * not yet linked into the tree.  These details must be taken
3793be74836SDavid du Colombier  * care of by the caller.
3803be74836SDavid du Colombier  *
3813be74836SDavid du Colombier  * f must be locked, f->msource must not.
3823be74836SDavid du Colombier  */
3833be74836SDavid du Colombier static VacFile*
dirlookup(VacFile * f,char * elem)3843be74836SDavid du Colombier dirlookup(VacFile *f, char *elem)
3853be74836SDavid du Colombier {
3863be74836SDavid du Colombier 	int i;
3873be74836SDavid du Colombier 	MetaBlock mb;
3883be74836SDavid du Colombier 	MetaEntry me;
3893be74836SDavid du Colombier 	VtBlock *b;
3903be74836SDavid du Colombier 	VtFile *meta;
3913be74836SDavid du Colombier 	VacFile *ff;
3923be74836SDavid du Colombier 	u32int bo, nb;
3933be74836SDavid du Colombier 
3943be74836SDavid du Colombier 	meta = f->msource;
3953be74836SDavid du Colombier 	b = nil;
3963be74836SDavid du Colombier 	if(vtfilelock(meta, -1) < 0)
3973be74836SDavid du Colombier 		return nil;
3983be74836SDavid du Colombier 	nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
3993be74836SDavid du Colombier 	for(bo=0; bo<nb; bo++){
4003be74836SDavid du Colombier 		b = vtfileblock(meta, bo, VtOREAD);
4013be74836SDavid du Colombier 		if(b == nil)
4023be74836SDavid du Colombier 			goto Err;
4033be74836SDavid du Colombier 		if(mbunpack(&mb, b->data, meta->dsize) < 0)
4043be74836SDavid du Colombier 			goto Err;
4053be74836SDavid du Colombier 		if(mbsearch(&mb, elem, &i, &me) >= 0){
4063be74836SDavid du Colombier 			ff = filealloc(f->fs);
4073be74836SDavid du Colombier 			if(vdunpack(&ff->dir, &me) < 0){
4083be74836SDavid du Colombier 				filefree(ff);
4093be74836SDavid du Colombier 				goto Err;
4103be74836SDavid du Colombier 			}
4113be74836SDavid du Colombier 			ff->qidoffset = f->qidoffset + ff->dir.qidoffset;
4123be74836SDavid du Colombier 			vtfileunlock(meta);
4133be74836SDavid du Colombier 			vtblockput(b);
4143be74836SDavid du Colombier 			ff->boff = bo;
4153be74836SDavid du Colombier 			ff->mode = f->mode;
4163be74836SDavid du Colombier 			return ff;
4173be74836SDavid du Colombier 		}
4183be74836SDavid du Colombier 		vtblockput(b);
4193be74836SDavid du Colombier 		b = nil;
4203be74836SDavid du Colombier 	}
4213be74836SDavid du Colombier 	werrstr(ENoFile);
4223be74836SDavid du Colombier 	/* fall through */
4233be74836SDavid du Colombier Err:
4243be74836SDavid du Colombier 	vtfileunlock(meta);
4253be74836SDavid du Colombier 	vtblockput(b);
4263be74836SDavid du Colombier 	return nil;
4273be74836SDavid du Colombier }
4283be74836SDavid du Colombier 
4293be74836SDavid du Colombier /*
4303be74836SDavid du Colombier  * Open the venti file at offset in the directory f->source.
4313be74836SDavid du Colombier  * f is locked.
4323be74836SDavid du Colombier  */
4333be74836SDavid du Colombier static VtFile *
fileopensource(VacFile * f,u32int offset,u32int gen,int dir,uint mode)4343be74836SDavid du Colombier fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
4353be74836SDavid du Colombier {
4363be74836SDavid du Colombier 	VtFile *r;
4373be74836SDavid du Colombier 
4383be74836SDavid du Colombier 	if((r = vtfileopen(f->source, offset, mode)) == nil)
4393be74836SDavid du Colombier 		return nil;
4403be74836SDavid du Colombier 	if(r == nil)
4413be74836SDavid du Colombier 		return nil;
4423be74836SDavid du Colombier 	if(r->gen != gen){
4433be74836SDavid du Colombier 		werrstr(ERemoved);
4443be74836SDavid du Colombier 		vtfileclose(r);
4453be74836SDavid du Colombier 		return nil;
4463be74836SDavid du Colombier 	}
4473be74836SDavid du Colombier 	if(r->dir != dir && r->mode != -1){
4483be74836SDavid du Colombier 		werrstr(EBadMeta);
4493be74836SDavid du Colombier 		vtfileclose(r);
4503be74836SDavid du Colombier 		return nil;
4513be74836SDavid du Colombier 	}
4523be74836SDavid du Colombier 	return r;
4533be74836SDavid du Colombier }
4543be74836SDavid du Colombier 
4559a747e4fSDavid du Colombier VacFile*
vacfilegetparent(VacFile * f)4563be74836SDavid du Colombier vacfilegetparent(VacFile *f)
4579a747e4fSDavid du Colombier {
4583be74836SDavid du Colombier 	if(vacfileisroot(f))
4593be74836SDavid du Colombier 		return vacfileincref(f);
4603be74836SDavid du Colombier 	return vacfileincref(f->up);
4613be74836SDavid du Colombier }
4629a747e4fSDavid du Colombier 
4633be74836SDavid du Colombier /*
4643be74836SDavid du Colombier  * Given an unlocked vacfile (directory) f,
4653be74836SDavid du Colombier  * return the vacfile named elem in f.
4663be74836SDavid du Colombier  * Interprets . and .. as a convenience to callers.
4673be74836SDavid du Colombier  */
4683be74836SDavid du Colombier VacFile*
vacfilewalk(VacFile * f,char * elem)4693be74836SDavid du Colombier vacfilewalk(VacFile *f, char *elem)
4703be74836SDavid du Colombier {
4713be74836SDavid du Colombier 	VacFile *ff;
472d9306527SDavid du Colombier 
4739a747e4fSDavid du Colombier 	if(elem[0] == 0){
4743be74836SDavid du Colombier 		werrstr(EBadPath);
475d9306527SDavid du Colombier 		return nil;
4769a747e4fSDavid du Colombier 	}
4779a747e4fSDavid du Colombier 
4783be74836SDavid du Colombier 	if(!vacfileisdir(f)){
4793be74836SDavid du Colombier 		werrstr(ENotDir);
4803be74836SDavid du Colombier 		return nil;
4819a747e4fSDavid du Colombier 	}
4829a747e4fSDavid du Colombier 
4833be74836SDavid du Colombier 	if(strcmp(elem, ".") == 0)
4843be74836SDavid du Colombier 		return vacfileincref(f);
4859a747e4fSDavid du Colombier 
4863be74836SDavid du Colombier 	if(strcmp(elem, "..") == 0)
4873be74836SDavid du Colombier 		return vacfilegetparent(f);
4883be74836SDavid du Colombier 
4893be74836SDavid du Colombier 	if(filelock(f) < 0)
490d9306527SDavid du Colombier 		return nil;
491d9306527SDavid du Colombier 
4923be74836SDavid du Colombier 	for(ff = f->down; ff; ff=ff->next){
4933be74836SDavid du Colombier 		if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
4943be74836SDavid du Colombier 			ff->ref++;
495d9306527SDavid du Colombier 			goto Exit;
4969a747e4fSDavid du Colombier 		}
4979a747e4fSDavid du Colombier 	}
4989a747e4fSDavid du Colombier 
4993be74836SDavid du Colombier 	ff = dirlookup(f, elem);
5003be74836SDavid du Colombier 	if(ff == nil)
5019a747e4fSDavid du Colombier 		goto Err;
5023be74836SDavid du Colombier 
5033be74836SDavid du Colombier 	if(ff->dir.mode & ModeSnapshot)
5043be74836SDavid du Colombier 		ff->mode = VtOREAD;
5053be74836SDavid du Colombier 
5063be74836SDavid du Colombier 	if(vtfilelock(f->source, f->mode) < 0)
5079a747e4fSDavid du Colombier 		goto Err;
5083be74836SDavid du Colombier 	if(ff->dir.mode & ModeDir){
5093be74836SDavid du Colombier 		ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode);
5103be74836SDavid du Colombier 		ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode);
5113be74836SDavid du Colombier 		if(ff->source == nil || ff->msource == nil)
5123be74836SDavid du Colombier 			goto Err1;
5133be74836SDavid du Colombier 	}else{
5143be74836SDavid du Colombier 		ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
5153be74836SDavid du Colombier 		if(ff->source == nil)
5163be74836SDavid du Colombier 			goto Err1;
5179a747e4fSDavid du Colombier 	}
5183be74836SDavid du Colombier 	vtfileunlock(f->source);
519d9306527SDavid du Colombier 
520d9306527SDavid du Colombier 	/* link in and up parent ref count */
5213be74836SDavid du Colombier 	ff->next = f->down;
5223be74836SDavid du Colombier 	f->down = ff;
5233be74836SDavid du Colombier 	ff->up = f;
5243be74836SDavid du Colombier 	vacfileincref(f);
525d9306527SDavid du Colombier Exit:
5263be74836SDavid du Colombier 	fileunlock(f);
5273be74836SDavid du Colombier 	return ff;
5283be74836SDavid du Colombier 
5293be74836SDavid du Colombier Err1:
5303be74836SDavid du Colombier 	vtfileunlock(f->source);
5319a747e4fSDavid du Colombier Err:
5323be74836SDavid du Colombier 	fileunlock(f);
5333be74836SDavid du Colombier 	if(ff != nil)
5343be74836SDavid du Colombier 		vacfiledecref(ff);
5359a747e4fSDavid du Colombier 	return nil;
5369a747e4fSDavid du Colombier }
5379a747e4fSDavid du Colombier 
5383be74836SDavid du Colombier /*
5393be74836SDavid du Colombier  * Open a path in the vac file system:
5403be74836SDavid du Colombier  * just walk each element one at a time.
5413be74836SDavid du Colombier  */
5429a747e4fSDavid du Colombier VacFile*
vacfileopen(VacFs * fs,char * path)5433be74836SDavid du Colombier vacfileopen(VacFs *fs, char *path)
5449a747e4fSDavid du Colombier {
5453be74836SDavid du Colombier 	VacFile *f, *ff;
5463be74836SDavid du Colombier 	char *p, elem[VtMaxStringSize], *opath;
5479a747e4fSDavid du Colombier 	int n;
5489a747e4fSDavid du Colombier 
5493be74836SDavid du Colombier 	f = fs->root;
5503be74836SDavid du Colombier 	vacfileincref(f);
5513be74836SDavid du Colombier 	opath = path;
5529a747e4fSDavid du Colombier 	while(*path != 0){
5539a747e4fSDavid du Colombier 		for(p = path; *p && *p != '/'; p++)
5549a747e4fSDavid du Colombier 			;
5559a747e4fSDavid du Colombier 		n = p - path;
5569a747e4fSDavid du Colombier 		if(n > 0){
5579a747e4fSDavid du Colombier 			if(n > VtMaxStringSize){
5583be74836SDavid du Colombier 				werrstr("%s: element too long", EBadPath);
5599a747e4fSDavid du Colombier 				goto Err;
5609a747e4fSDavid du Colombier 			}
5619a747e4fSDavid du Colombier 			memmove(elem, path, n);
5629a747e4fSDavid du Colombier 			elem[n] = 0;
5633be74836SDavid du Colombier 			ff = vacfilewalk(f, elem);
5643be74836SDavid du Colombier 			if(ff == nil){
5653be74836SDavid du Colombier 				werrstr("%.*s: %r", utfnlen(opath, p-opath), opath);
5669a747e4fSDavid du Colombier 				goto Err;
5673be74836SDavid du Colombier 			}
5683be74836SDavid du Colombier 			vacfiledecref(f);
5693be74836SDavid du Colombier 			f = ff;
5709a747e4fSDavid du Colombier 		}
5719a747e4fSDavid du Colombier 		if(*p == '/')
5729a747e4fSDavid du Colombier 			p++;
5739a747e4fSDavid du Colombier 		path = p;
5749a747e4fSDavid du Colombier 	}
5753be74836SDavid du Colombier 	return f;
5769a747e4fSDavid du Colombier Err:
5773be74836SDavid du Colombier 	vacfiledecref(f);
5789a747e4fSDavid du Colombier 	return nil;
5799a747e4fSDavid du Colombier }
5809a747e4fSDavid du Colombier 
5813be74836SDavid du Colombier /*
5823be74836SDavid du Colombier  * Extract the score for the bn'th block in f.
5833be74836SDavid du Colombier  */
5843be74836SDavid du Colombier int
vacfileblockscore(VacFile * f,u32int bn,u8int * score)5853be74836SDavid du Colombier vacfileblockscore(VacFile *f, u32int bn, u8int *score)
586e538c934SDavid du Colombier {
5873be74836SDavid du Colombier 	VtFile *s;
5883be74836SDavid du Colombier 	uvlong size;
5893be74836SDavid du Colombier 	int dsize, ret;
5903be74836SDavid du Colombier 
5913be74836SDavid du Colombier 	ret = -1;
5923be74836SDavid du Colombier 	if(filerlock(f) < 0)
5933be74836SDavid du Colombier 		return -1;
5943be74836SDavid du Colombier 	if(vtfilelock(f->source, VtOREAD) < 0)
5953be74836SDavid du Colombier 		goto out;
5963be74836SDavid du Colombier 
5973be74836SDavid du Colombier 	s = f->source;
5983be74836SDavid du Colombier 	dsize = s->dsize;
5993be74836SDavid du Colombier 	size = vtfilegetsize(s);
6003be74836SDavid du Colombier 	if((uvlong)bn*dsize >= size)
6013be74836SDavid du Colombier 		goto out1;
6023be74836SDavid du Colombier 	ret = vtfileblockscore(f->source, bn, score);
6033be74836SDavid du Colombier 
6043be74836SDavid du Colombier out1:
6053be74836SDavid du Colombier 	vtfileunlock(f->source);
6063be74836SDavid du Colombier out:
6073be74836SDavid du Colombier 	filerunlock(f);
6083be74836SDavid du Colombier 	return ret;
6093be74836SDavid du Colombier }
6103be74836SDavid du Colombier 
6113be74836SDavid du Colombier /*
6123be74836SDavid du Colombier  * Read data from f.
6133be74836SDavid du Colombier  */
6143be74836SDavid du Colombier int
vacfileread(VacFile * f,void * buf,int cnt,vlong offset)6153be74836SDavid du Colombier vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
6163be74836SDavid du Colombier {
6173be74836SDavid du Colombier 	int n;
6183be74836SDavid du Colombier 
6193be74836SDavid du Colombier 	if(offset < 0){
6203be74836SDavid du Colombier 		werrstr(EBadOffset);
6213be74836SDavid du Colombier 		return -1;
6223be74836SDavid du Colombier 	}
6233be74836SDavid du Colombier 	if(filerlock(f) < 0)
6243be74836SDavid du Colombier 		return -1;
6253be74836SDavid du Colombier 	if(vtfilelock(f->source, VtOREAD) < 0){
6263be74836SDavid du Colombier 		filerunlock(f);
6273be74836SDavid du Colombier 		return -1;
6283be74836SDavid du Colombier 	}
6293be74836SDavid du Colombier 	n = vtfileread(f->source, buf, cnt, offset);
6303be74836SDavid du Colombier 	vtfileunlock(f->source);
6313be74836SDavid du Colombier 	filerunlock(f);
6323be74836SDavid du Colombier 	return n;
6333be74836SDavid du Colombier }
6343be74836SDavid du Colombier 
6353be74836SDavid du Colombier static int
getentry(VtFile * f,VtEntry * e)6363be74836SDavid du Colombier getentry(VtFile *f, VtEntry *e)
6373be74836SDavid du Colombier {
6383be74836SDavid du Colombier 	if(vtfilelock(f, VtOREAD) < 0)
6393be74836SDavid du Colombier 		return -1;
6403be74836SDavid du Colombier 	if(vtfilegetentry(f, e) < 0){
6413be74836SDavid du Colombier 		vtfileunlock(f);
6423be74836SDavid du Colombier 		return -1;
6433be74836SDavid du Colombier 	}
6443be74836SDavid du Colombier 	vtfileunlock(f);
6453be74836SDavid du Colombier 	if(vtglobaltolocal(e->score) != NilBlock){
6463be74836SDavid du Colombier 		werrstr("internal error - data not on venti");
6473be74836SDavid du Colombier 		return -1;
6483be74836SDavid du Colombier 	}
6493be74836SDavid du Colombier 	return 0;
6503be74836SDavid du Colombier }
6513be74836SDavid du Colombier 
6523be74836SDavid du Colombier /*
6533be74836SDavid du Colombier  * Get the VtEntries for the data contained in f.
6543be74836SDavid du Colombier  */
6553be74836SDavid du Colombier int
vacfilegetentries(VacFile * f,VtEntry * e,VtEntry * me)6563be74836SDavid du Colombier vacfilegetentries(VacFile *f, VtEntry *e, VtEntry *me)
6573be74836SDavid du Colombier {
6583be74836SDavid du Colombier 	if(filerlock(f) < 0)
6593be74836SDavid du Colombier 		return -1;
6603be74836SDavid du Colombier 	if(e && getentry(f->source, e) < 0){
6613be74836SDavid du Colombier 		filerunlock(f);
6623be74836SDavid du Colombier 		return -1;
6633be74836SDavid du Colombier 	}
6643be74836SDavid du Colombier 	if(me){
6653be74836SDavid du Colombier 		if(f->msource == nil)
6663be74836SDavid du Colombier 			memset(me, 0, sizeof *me);
6673be74836SDavid du Colombier 		else if(getentry(f->msource, me) < 0){
6683be74836SDavid du Colombier 			filerunlock(f);
6693be74836SDavid du Colombier 			return -1;
6703be74836SDavid du Colombier 		}
6713be74836SDavid du Colombier 	}
6723be74836SDavid du Colombier 	filerunlock(f);
6733be74836SDavid du Colombier 	return 0;
6743be74836SDavid du Colombier }
6753be74836SDavid du Colombier 
6763be74836SDavid du Colombier /*
6773be74836SDavid du Colombier  * Get the file's size.
6783be74836SDavid du Colombier  */
6793be74836SDavid du Colombier int
vacfilegetsize(VacFile * f,uvlong * size)6803be74836SDavid du Colombier vacfilegetsize(VacFile *f, uvlong *size)
6813be74836SDavid du Colombier {
6823be74836SDavid du Colombier 	if(filerlock(f) < 0)
6833be74836SDavid du Colombier 		return -1;
6843be74836SDavid du Colombier 	if(vtfilelock(f->source, VtOREAD) < 0){
6853be74836SDavid du Colombier 		filerunlock(f);
6863be74836SDavid du Colombier 		return -1;
6873be74836SDavid du Colombier 	}
6883be74836SDavid du Colombier 	*size = vtfilegetsize(f->source);
6893be74836SDavid du Colombier 	vtfileunlock(f->source);
6903be74836SDavid du Colombier 	filerunlock(f);
6913be74836SDavid du Colombier 
6923be74836SDavid du Colombier 	return 0;
6933be74836SDavid du Colombier }
6943be74836SDavid du Colombier 
6953be74836SDavid du Colombier /*
6963be74836SDavid du Colombier  * Directory reading.
6973be74836SDavid du Colombier  *
6983be74836SDavid du Colombier  * A VacDirEnum is a buffer containing directory entries.
6993be74836SDavid du Colombier  * Directory entries contain malloced strings and need to
7003be74836SDavid du Colombier  * be cleaned up with vdcleanup.  The invariant in the
7013be74836SDavid du Colombier  * VacDirEnum is that the directory entries between
7023be74836SDavid du Colombier  * vde->i and vde->n are owned by the vde and need to
7033be74836SDavid du Colombier  * be cleaned up if it is closed.  Those from 0 up to vde->i
7043be74836SDavid du Colombier  * have been handed to the reader, and the reader must
7053be74836SDavid du Colombier  * take care of calling vdcleanup as appropriate.
7063be74836SDavid du Colombier  */
7073be74836SDavid du Colombier VacDirEnum*
vdeopen(VacFile * f)7083be74836SDavid du Colombier vdeopen(VacFile *f)
7093be74836SDavid du Colombier {
7103be74836SDavid du Colombier 	VacDirEnum *vde;
7113be74836SDavid du Colombier 	VacFile *p;
7123be74836SDavid du Colombier 
7133be74836SDavid du Colombier 	if(!vacfileisdir(f)){
7143be74836SDavid du Colombier 		werrstr(ENotDir);
7153be74836SDavid du Colombier 		return nil;
7163be74836SDavid du Colombier 	}
7173be74836SDavid du Colombier 
7183be74836SDavid du Colombier 	/*
7193be74836SDavid du Colombier 	 * There might be changes to this directory's children
7203be74836SDavid du Colombier 	 * that have not been flushed out into the cache yet.
7213be74836SDavid du Colombier 	 * Those changes are only available if we look at the
7223be74836SDavid du Colombier 	 * VacFile structures directory.  But the directory reader
7233be74836SDavid du Colombier 	 * is going to read the cache blocks directly, so update them.
7243be74836SDavid du Colombier 	 */
7253be74836SDavid du Colombier 	if(filelock(f) < 0)
7263be74836SDavid du Colombier 		return nil;
7273be74836SDavid du Colombier 	for(p=f->down; p; p=p->next)
7283be74836SDavid du Colombier 		filemetaflush(p, nil);
7293be74836SDavid du Colombier 	fileunlock(f);
7303be74836SDavid du Colombier 
7313be74836SDavid du Colombier 	vde = vtmallocz(sizeof(VacDirEnum));
7323be74836SDavid du Colombier 	vde->file = vacfileincref(f);
7333be74836SDavid du Colombier 
7343be74836SDavid du Colombier 	return vde;
7353be74836SDavid du Colombier }
7363be74836SDavid du Colombier 
7373be74836SDavid du Colombier /*
7383be74836SDavid du Colombier  * Figure out the size of the directory entry at offset.
7393be74836SDavid du Colombier  * The rest of the metadata is kept in the data half,
7403be74836SDavid du Colombier  * but since venti has to track the data size anyway,
7413be74836SDavid du Colombier  * we just use that one and avoid updating the directory
7423be74836SDavid du Colombier  * each time the file size changes.
7433be74836SDavid du Colombier  */
7443be74836SDavid du Colombier static int
direntrysize(VtFile * s,ulong offset,ulong gen,uvlong * size)7453be74836SDavid du Colombier direntrysize(VtFile *s, ulong offset, ulong gen, uvlong *size)
7463be74836SDavid du Colombier {
7473be74836SDavid du Colombier 	VtBlock *b;
7483be74836SDavid du Colombier 	ulong bn;
7493be74836SDavid du Colombier 	VtEntry e;
7503be74836SDavid du Colombier 	int epb;
7513be74836SDavid du Colombier 
7523be74836SDavid du Colombier 	epb = s->dsize/VtEntrySize;
7533be74836SDavid du Colombier 	bn = offset/epb;
7543be74836SDavid du Colombier 	offset -= bn*epb;
7553be74836SDavid du Colombier 
7563be74836SDavid du Colombier 	b = vtfileblock(s, bn, VtOREAD);
7573be74836SDavid du Colombier 	if(b == nil)
7583be74836SDavid du Colombier 		goto Err;
7593be74836SDavid du Colombier 	if(vtentryunpack(&e, b->data, offset) < 0)
7603be74836SDavid du Colombier 		goto Err;
7613be74836SDavid du Colombier 
7623be74836SDavid du Colombier 	/* dangling entries are returned as zero size */
7633be74836SDavid du Colombier 	if(!(e.flags & VtEntryActive) || e.gen != gen)
7643be74836SDavid du Colombier 		*size = 0;
7653be74836SDavid du Colombier 	else
7663be74836SDavid du Colombier 		*size = e.size;
7673be74836SDavid du Colombier 	vtblockput(b);
7683be74836SDavid du Colombier 	return 0;
7693be74836SDavid du Colombier 
7703be74836SDavid du Colombier Err:
7713be74836SDavid du Colombier 	vtblockput(b);
7723be74836SDavid du Colombier 	return -1;
7733be74836SDavid du Colombier }
7743be74836SDavid du Colombier 
7753be74836SDavid du Colombier /*
7763be74836SDavid du Colombier  * Fill in vde with a new batch of directory entries.
7773be74836SDavid du Colombier  */
7783be74836SDavid du Colombier static int
vdefill(VacDirEnum * vde)7793be74836SDavid du Colombier vdefill(VacDirEnum *vde)
7803be74836SDavid du Colombier {
7813be74836SDavid du Colombier 	int i, n;
7823be74836SDavid du Colombier 	VtFile *meta, *source;
78349223a73SDavid du Colombier 	MetaBlock mb;
78449223a73SDavid du Colombier 	MetaEntry me;
7853be74836SDavid du Colombier 	VacFile *f;
7863be74836SDavid du Colombier 	VtBlock *b;
7873be74836SDavid du Colombier 	VacDir *de;
788e538c934SDavid du Colombier 
7893be74836SDavid du Colombier 	/* clean up first */
7903be74836SDavid du Colombier 	for(i=vde->i; i<vde->n; i++)
7913be74836SDavid du Colombier 		vdcleanup(vde->buf+i);
7923be74836SDavid du Colombier 	vtfree(vde->buf);
7933be74836SDavid du Colombier 	vde->buf = nil;
7943be74836SDavid du Colombier 	vde->i = 0;
7953be74836SDavid du Colombier 	vde->n = 0;
7963be74836SDavid du Colombier 
7973be74836SDavid du Colombier 	f = vde->file;
7983be74836SDavid du Colombier 
7993be74836SDavid du Colombier 	source = f->source;
8003be74836SDavid du Colombier 	meta = f->msource;
8013be74836SDavid du Colombier 
8023be74836SDavid du Colombier 	b = vtfileblock(meta, vde->boff, VtOREAD);
8033be74836SDavid du Colombier 	if(b == nil)
8043be74836SDavid du Colombier 		goto Err;
8053be74836SDavid du Colombier 	if(mbunpack(&mb, b->data, meta->dsize) < 0)
8063be74836SDavid du Colombier 		goto Err;
8073be74836SDavid du Colombier 
8083be74836SDavid du Colombier 	n = mb.nindex;
8093be74836SDavid du Colombier 	vde->buf = vtmalloc(n * sizeof(VacDir));
8103be74836SDavid du Colombier 
8113be74836SDavid du Colombier 	for(i=0; i<n; i++){
8123be74836SDavid du Colombier 		de = vde->buf + i;
8133be74836SDavid du Colombier 		meunpack(&me, &mb, i);
8143be74836SDavid du Colombier 		if(vdunpack(de, &me) < 0)
8153be74836SDavid du Colombier 			goto Err;
8163be74836SDavid du Colombier 		vde->n++;
8173be74836SDavid du Colombier 		if(!(de->mode & ModeDir))
8183be74836SDavid du Colombier 		if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
8193be74836SDavid du Colombier 			goto Err;
8203be74836SDavid du Colombier 	}
8213be74836SDavid du Colombier 	vde->boff++;
8223be74836SDavid du Colombier 	vtblockput(b);
8233be74836SDavid du Colombier 	return 0;
8243be74836SDavid du Colombier Err:
8253be74836SDavid du Colombier 	vtblockput(b);
8263be74836SDavid du Colombier 	return -1;
8273be74836SDavid du Colombier }
8283be74836SDavid du Colombier 
8293be74836SDavid du Colombier /*
8303be74836SDavid du Colombier  * Read a single directory entry from vde into de.
8313be74836SDavid du Colombier  * Returns -1 on error, 0 on EOF, and 1 on success.
8323be74836SDavid du Colombier  * When it returns 1, it becomes the caller's responsibility
8333be74836SDavid du Colombier  * to call vdcleanup(de) to free the strings contained
8343be74836SDavid du Colombier  * inside, or else to call vdunread to give it back.
8353be74836SDavid du Colombier  */
8363be74836SDavid du Colombier int
vderead(VacDirEnum * vde,VacDir * de)8373be74836SDavid du Colombier vderead(VacDirEnum *vde, VacDir *de)
8383be74836SDavid du Colombier {
8393be74836SDavid du Colombier 	int ret;
8403be74836SDavid du Colombier 	VacFile *f;
8413be74836SDavid du Colombier 	u32int nb;
8423be74836SDavid du Colombier 
8433be74836SDavid du Colombier 	f = vde->file;
8443be74836SDavid du Colombier 	if(filerlock(f) < 0)
8453be74836SDavid du Colombier 		return -1;
8463be74836SDavid du Colombier 
8473be74836SDavid du Colombier 	if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
8483be74836SDavid du Colombier 		filerunlock(f);
8493be74836SDavid du Colombier 		return -1;
8503be74836SDavid du Colombier 	}
8513be74836SDavid du Colombier 
8523be74836SDavid du Colombier 	nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
8533be74836SDavid du Colombier 
8543be74836SDavid du Colombier 	while(vde->i >= vde->n){
8553be74836SDavid du Colombier 		if(vde->boff >= nb){
8563be74836SDavid du Colombier 			ret = 0;
8573be74836SDavid du Colombier 			goto Return;
8583be74836SDavid du Colombier 		}
8593be74836SDavid du Colombier 		if(vdefill(vde) < 0){
8603be74836SDavid du Colombier 			ret = -1;
8613be74836SDavid du Colombier 			goto Return;
8623be74836SDavid du Colombier 		}
8633be74836SDavid du Colombier 	}
8643be74836SDavid du Colombier 
8653be74836SDavid du Colombier 	memmove(de, vde->buf + vde->i, sizeof(VacDir));
8663be74836SDavid du Colombier 	vde->i++;
8673be74836SDavid du Colombier 	ret = 1;
8683be74836SDavid du Colombier 
8693be74836SDavid du Colombier Return:
8703be74836SDavid du Colombier 	vtfileunlock(f->source);
8713be74836SDavid du Colombier 	vtfileunlock(f->msource);
8723be74836SDavid du Colombier 	filerunlock(f);
8733be74836SDavid du Colombier 
8743be74836SDavid du Colombier 	return ret;
8753be74836SDavid du Colombier }
8763be74836SDavid du Colombier 
8773be74836SDavid du Colombier /*
8783be74836SDavid du Colombier  * "Unread" the last directory entry that was read,
8793be74836SDavid du Colombier  * so that the next vderead will return the same one.
8803be74836SDavid du Colombier  * If the caller calls vdeunread(vde) it should not call
8813be74836SDavid du Colombier  * vdcleanup on the entry being "unread".
8823be74836SDavid du Colombier  */
8833be74836SDavid du Colombier int
vdeunread(VacDirEnum * vde)8843be74836SDavid du Colombier vdeunread(VacDirEnum *vde)
8853be74836SDavid du Colombier {
8863be74836SDavid du Colombier 	if(vde->i > 0){
8873be74836SDavid du Colombier 		vde->i--;
8883be74836SDavid du Colombier 		return 0;
8893be74836SDavid du Colombier 	}
8903be74836SDavid du Colombier 	return -1;
8913be74836SDavid du Colombier }
8923be74836SDavid du Colombier 
8933be74836SDavid du Colombier /*
8943be74836SDavid du Colombier  * Close the enumerator.
8953be74836SDavid du Colombier  */
8963be74836SDavid du Colombier void
vdeclose(VacDirEnum * vde)8973be74836SDavid du Colombier vdeclose(VacDirEnum *vde)
8983be74836SDavid du Colombier {
8993be74836SDavid du Colombier 	int i;
9003be74836SDavid du Colombier 	if(vde == nil)
9013be74836SDavid du Colombier 		return;
9023be74836SDavid du Colombier 	/* free the strings */
9033be74836SDavid du Colombier 	for(i=vde->i; i<vde->n; i++)
9043be74836SDavid du Colombier 		vdcleanup(vde->buf+i);
9053be74836SDavid du Colombier 	vtfree(vde->buf);
9063be74836SDavid du Colombier 	vacfiledecref(vde->file);
9073be74836SDavid du Colombier 	vtfree(vde);
9083be74836SDavid du Colombier }
9093be74836SDavid du Colombier 
9103be74836SDavid du Colombier 
9113be74836SDavid du Colombier /*
9123be74836SDavid du Colombier  * On to mutation.  If the vac file system has been opened
9133be74836SDavid du Colombier  * read-write, then the files and directories can all be edited.
9143be74836SDavid du Colombier  * Changes are kept in the in-memory cache until flushed out
9153be74836SDavid du Colombier  * to venti, so we must be careful to explicitly flush data
9163be74836SDavid du Colombier  * that we're not likely to modify again.
9173be74836SDavid du Colombier  *
9183be74836SDavid du Colombier  * Each VacFile has its own copy of its VacDir directory entry
9193be74836SDavid du Colombier  * in f->dir, but otherwise the cache is the authoratative source
9203be74836SDavid du Colombier  * for data.  Thus, for the most part, it suffices if we just
9213be74836SDavid du Colombier  * call vtfileflushbefore and vtfileflush when we modify things.
9223be74836SDavid du Colombier  * There are a few places where we have to remember to write
9233be74836SDavid du Colombier  * changed VacDirs back into the cache.  If f->dir *is* out of sync,
9243be74836SDavid du Colombier  * then f->dirty should be set.
9253be74836SDavid du Colombier  *
9263be74836SDavid du Colombier  * The metadata in a directory is, to venti, a plain data file,
9273be74836SDavid du Colombier  * but as mentioned above it is actually a sequence of
9283be74836SDavid du Colombier  * MetaBlocks that contain sorted lists of VacDir entries.
9293be74836SDavid du Colombier  * The filemetaxxx routines manipulate that stream.
9303be74836SDavid du Colombier  */
9313be74836SDavid du Colombier 
9323be74836SDavid du Colombier /*
9333be74836SDavid du Colombier  * Find space in fp for the directory entry dir (not yet written to disk)
9343be74836SDavid du Colombier  * and write it to disk, returning NilBlock on failure,
9353be74836SDavid du Colombier  * or the block number on success.
9363be74836SDavid du Colombier  *
9373be74836SDavid du Colombier  * Start is a suggested block number to try.
9383be74836SDavid du Colombier  * The caller must have filemetalock'ed f and have
9393be74836SDavid du Colombier  * vtfilelock'ed f->up->msource.
9403be74836SDavid du Colombier  */
9413be74836SDavid du Colombier static u32int
filemetaalloc(VacFile * fp,VacDir * dir,u32int start)9423be74836SDavid du Colombier filemetaalloc(VacFile *fp, VacDir *dir, u32int start)
9433be74836SDavid du Colombier {
9443be74836SDavid du Colombier 	u32int nb, bo;
9453be74836SDavid du Colombier 	VtBlock *b;
9463be74836SDavid du Colombier 	MetaBlock mb;
9473be74836SDavid du Colombier 	int nn;
9483be74836SDavid du Colombier 	uchar *p;
9493be74836SDavid du Colombier 	int i, n;
9503be74836SDavid du Colombier 	MetaEntry me;
9513be74836SDavid du Colombier 	VtFile *ms;
9523be74836SDavid du Colombier 
9533be74836SDavid du Colombier 	ms = fp->msource;
9543be74836SDavid du Colombier 	n = vdsize(dir, VacDirVersion);
9553be74836SDavid du Colombier 
9563be74836SDavid du Colombier 	/* Look for a block with room for a new entry of size n. */
9573be74836SDavid du Colombier 	nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
9583be74836SDavid du Colombier 	if(start == NilBlock){
9593be74836SDavid du Colombier 		if(nb > 0)
9603be74836SDavid du Colombier 			start = nb - 1;
9613be74836SDavid du Colombier 		else
9623be74836SDavid du Colombier 			start = 0;
9633be74836SDavid du Colombier 	}
9643be74836SDavid du Colombier 
9653be74836SDavid du Colombier 	if(start > nb)
9663be74836SDavid du Colombier 		start = nb;
9673be74836SDavid du Colombier 	for(bo=start; bo<nb; bo++){
9683be74836SDavid du Colombier 		if((b = vtfileblock(ms, bo, VtOREAD)) == nil)
9693be74836SDavid du Colombier 			goto Err;
9703be74836SDavid du Colombier 		if(mbunpack(&mb, b->data, ms->dsize) < 0)
9713be74836SDavid du Colombier 			goto Err;
9723be74836SDavid du Colombier 		nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
9733be74836SDavid du Colombier 		if(n <= nn && mb.nindex < mb.maxindex){
9743be74836SDavid du Colombier 			/* reopen for writing */
9753be74836SDavid du Colombier 			vtblockput(b);
9763be74836SDavid du Colombier 			if((b = vtfileblock(ms, bo, VtORDWR)) == nil)
9773be74836SDavid du Colombier 				goto Err;
9783be74836SDavid du Colombier 			mbunpack(&mb, b->data, ms->dsize);
9793be74836SDavid du Colombier 			goto Found;
9803be74836SDavid du Colombier 		}
9813be74836SDavid du Colombier 		vtblockput(b);
9823be74836SDavid du Colombier 	}
9833be74836SDavid du Colombier 
9843be74836SDavid du Colombier 	/* No block found, extend the file by one metablock. */
9853be74836SDavid du Colombier 	vtfileflushbefore(ms, nb*(uvlong)ms->dsize);
9863be74836SDavid du Colombier 	if((b = vtfileblock(ms, nb, VtORDWR)) == nil)
9873be74836SDavid du Colombier 		goto Err;
9883be74836SDavid du Colombier 	vtfilesetsize(ms, (nb+1)*ms->dsize);
9893be74836SDavid du Colombier 	mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
9903be74836SDavid du Colombier 
9913be74836SDavid du Colombier Found:
9923be74836SDavid du Colombier 	/* Now we have a block; allocate space to write the entry. */
9933be74836SDavid du Colombier 	p = mballoc(&mb, n);
9943be74836SDavid du Colombier 	if(p == nil){
9953be74836SDavid du Colombier 		/* mballoc might have changed block */
9963be74836SDavid du Colombier 		mbpack(&mb);
9973be74836SDavid du Colombier 		werrstr(EBadMeta);
9983be74836SDavid du Colombier 		goto Err;
9993be74836SDavid du Colombier 	}
10003be74836SDavid du Colombier 
10013be74836SDavid du Colombier 	/* Figure out where to put the index entry, and write it. */
10023be74836SDavid du Colombier 	mbsearch(&mb, dir->elem, &i, &me);
10033be74836SDavid du Colombier 	assert(me.p == nil);	/* not already there */
10043be74836SDavid du Colombier 	me.p = p;
10053be74836SDavid du Colombier 	me.size = n;
10063be74836SDavid du Colombier 	vdpack(dir, &me, VacDirVersion);
10073be74836SDavid du Colombier 	mbinsert(&mb, i, &me);
10083be74836SDavid du Colombier 	mbpack(&mb);
10093be74836SDavid du Colombier 	vtblockput(b);
10103be74836SDavid du Colombier 	return bo;
10113be74836SDavid du Colombier 
10123be74836SDavid du Colombier Err:
10133be74836SDavid du Colombier 	vtblockput(b);
10143be74836SDavid du Colombier 	return NilBlock;
10153be74836SDavid du Colombier }
10163be74836SDavid du Colombier 
10173be74836SDavid du Colombier /*
10183be74836SDavid du Colombier  * Update f's directory entry in the block cache.
10193be74836SDavid du Colombier  * We look for the directory entry by name;
10203be74836SDavid du Colombier  * if we're trying to rename the file, oelem is the old name.
10213be74836SDavid du Colombier  *
10223be74836SDavid du Colombier  * Assumes caller has filemetalock'ed f.
10233be74836SDavid du Colombier  */
10243be74836SDavid du Colombier static int
filemetaflush(VacFile * f,char * oelem)10253be74836SDavid du Colombier filemetaflush(VacFile *f, char *oelem)
10263be74836SDavid du Colombier {
10273be74836SDavid du Colombier 	int i, n;
10283be74836SDavid du Colombier 	MetaBlock mb;
10293be74836SDavid du Colombier 	MetaEntry me, me2;
10303be74836SDavid du Colombier 	VacFile *fp;
10313be74836SDavid du Colombier 	VtBlock *b;
10323be74836SDavid du Colombier 	u32int bo;
10333be74836SDavid du Colombier 
10343be74836SDavid du Colombier 	if(!f->dirty)
10353be74836SDavid du Colombier 		return 0;
10363be74836SDavid du Colombier 
10373be74836SDavid du Colombier 	if(oelem == nil)
10383be74836SDavid du Colombier 		oelem = f->dir.elem;
10393be74836SDavid du Colombier 
10403be74836SDavid du Colombier 	/*
10413be74836SDavid du Colombier 	 * Locate f's old metadata in the parent's metadata file.
10423be74836SDavid du Colombier 	 * We know which block it was in, but not exactly where
10433be74836SDavid du Colombier 	 * in the block.
10443be74836SDavid du Colombier 	 */
10453be74836SDavid du Colombier 	fp = f->up;
10463be74836SDavid du Colombier 	if(vtfilelock(fp->msource, -1) < 0)
10473be74836SDavid du Colombier 		return -1;
10483be74836SDavid du Colombier 	/* can happen if source is clri'ed out from under us */
10493be74836SDavid du Colombier 	if(f->boff == NilBlock)
10503be74836SDavid du Colombier 		goto Err1;
10513be74836SDavid du Colombier 	b = vtfileblock(fp->msource, f->boff, VtORDWR);
10523be74836SDavid du Colombier 	if(b == nil)
10533be74836SDavid du Colombier 		goto Err1;
10543be74836SDavid du Colombier 	if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
10553be74836SDavid du Colombier 		goto Err;
10563be74836SDavid du Colombier 	if(mbsearch(&mb, oelem, &i, &me) < 0)
10573be74836SDavid du Colombier 		goto Err;
10583be74836SDavid du Colombier 
10593be74836SDavid du Colombier 	/*
10603be74836SDavid du Colombier 	 * Check whether we can resize the entry and keep it
10613be74836SDavid du Colombier 	 * in this block.
10623be74836SDavid du Colombier 	 */
10633be74836SDavid du Colombier 	n = vdsize(&f->dir, VacDirVersion);
10643be74836SDavid du Colombier 	if(mbresize(&mb, &me, n) >= 0){
10653be74836SDavid du Colombier 		/* Okay, can be done without moving to another block. */
10663be74836SDavid du Colombier 
10673be74836SDavid du Colombier 		/* Remove old data */
10683be74836SDavid du Colombier 		mbdelete(&mb, i, &me);
10693be74836SDavid du Colombier 
10703be74836SDavid du Colombier 		/* Find new location if renaming */
10713be74836SDavid du Colombier 		if(strcmp(f->dir.elem, oelem) != 0)
10723be74836SDavid du Colombier 			mbsearch(&mb, f->dir.elem, &i, &me2);
10733be74836SDavid du Colombier 
10743be74836SDavid du Colombier 		/* Pack new data into new location. */
10753be74836SDavid du Colombier 		vdpack(&f->dir, &me, VacDirVersion);
10763be74836SDavid du Colombier vdunpack(&f->dir, &me);
10773be74836SDavid du Colombier 		mbinsert(&mb, i, &me);
10783be74836SDavid du Colombier 		mbpack(&mb);
10793be74836SDavid du Colombier 
10803be74836SDavid du Colombier 		/* Done */
10813be74836SDavid du Colombier 		vtblockput(b);
10823be74836SDavid du Colombier 		vtfileunlock(fp->msource);
10833be74836SDavid du Colombier 		f->dirty = 0;
10843be74836SDavid du Colombier 		return 0;
10853be74836SDavid du Colombier 	}
10863be74836SDavid du Colombier 
10873be74836SDavid du Colombier 	/*
10883be74836SDavid du Colombier 	 * The entry must be moved to another block.
10893be74836SDavid du Colombier 	 * This can only really happen on renames that
10903be74836SDavid du Colombier 	 * make the name very long.
10913be74836SDavid du Colombier 	 */
10923be74836SDavid du Colombier 
10933be74836SDavid du Colombier 	/* Allocate a spot in a new block. */
10943be74836SDavid du Colombier 	if((bo = filemetaalloc(fp, &f->dir, f->boff+1)) == NilBlock){
10953be74836SDavid du Colombier 		/* mbresize above might have modified block */
10963be74836SDavid du Colombier 		mbpack(&mb);
10973be74836SDavid du Colombier 		goto Err;
10983be74836SDavid du Colombier 	}
10993be74836SDavid du Colombier 	f->boff = bo;
11003be74836SDavid du Colombier 
11013be74836SDavid du Colombier 	/* Now we're committed.  Delete entry in old block. */
11023be74836SDavid du Colombier 	mbdelete(&mb, i, &me);
11033be74836SDavid du Colombier 	mbpack(&mb);
11043be74836SDavid du Colombier 	vtblockput(b);
11053be74836SDavid du Colombier 	vtfileunlock(fp->msource);
11063be74836SDavid du Colombier 
11073be74836SDavid du Colombier 	f->dirty = 0;
11083be74836SDavid du Colombier 	return 0;
11093be74836SDavid du Colombier 
11103be74836SDavid du Colombier Err:
11113be74836SDavid du Colombier 	vtblockput(b);
11123be74836SDavid du Colombier Err1:
11133be74836SDavid du Colombier 	vtfileunlock(fp->msource);
11143be74836SDavid du Colombier 	return -1;
11153be74836SDavid du Colombier }
11163be74836SDavid du Colombier 
11173be74836SDavid du Colombier /*
11183be74836SDavid du Colombier  * Remove the directory entry for f.
11193be74836SDavid du Colombier  */
11203be74836SDavid du Colombier static int
filemetaremove(VacFile * f)11213be74836SDavid du Colombier filemetaremove(VacFile *f)
11223be74836SDavid du Colombier {
11233be74836SDavid du Colombier 	VtBlock *b;
11243be74836SDavid du Colombier 	MetaBlock mb;
11253be74836SDavid du Colombier 	MetaEntry me;
11263be74836SDavid du Colombier 	int i;
11273be74836SDavid du Colombier 	VacFile *fp;
11283be74836SDavid du Colombier 
11293be74836SDavid du Colombier 	b = nil;
11303be74836SDavid du Colombier 	fp = f->up;
11313be74836SDavid du Colombier 	filemetalock(f);
11323be74836SDavid du Colombier 
11333be74836SDavid du Colombier 	if(vtfilelock(fp->msource, VtORDWR) < 0)
11343be74836SDavid du Colombier 		goto Err;
11353be74836SDavid du Colombier 	b = vtfileblock(fp->msource, f->boff, VtORDWR);
11363be74836SDavid du Colombier 	if(b == nil)
11373be74836SDavid du Colombier 		goto Err;
11383be74836SDavid du Colombier 
11393be74836SDavid du Colombier 	if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
11403be74836SDavid du Colombier 		goto Err;
11413be74836SDavid du Colombier 	if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
11423be74836SDavid du Colombier 		goto Err;
11433be74836SDavid du Colombier 	mbdelete(&mb, i, &me);
11443be74836SDavid du Colombier 	mbpack(&mb);
11453be74836SDavid du Colombier 	vtblockput(b);
11463be74836SDavid du Colombier 	vtfileunlock(fp->msource);
11473be74836SDavid du Colombier 
11483be74836SDavid du Colombier 	f->removed = 1;
11493be74836SDavid du Colombier 	f->boff = NilBlock;
11503be74836SDavid du Colombier 	f->dirty = 0;
11513be74836SDavid du Colombier 
11523be74836SDavid du Colombier 	filemetaunlock(f);
11533be74836SDavid du Colombier 	return 0;
11543be74836SDavid du Colombier 
11553be74836SDavid du Colombier Err:
11563be74836SDavid du Colombier 	vtfileunlock(fp->msource);
11573be74836SDavid du Colombier 	vtblockput(b);
11583be74836SDavid du Colombier 	filemetaunlock(f);
11593be74836SDavid du Colombier 	return -1;
11603be74836SDavid du Colombier }
11613be74836SDavid du Colombier 
11623be74836SDavid du Colombier /*
11633be74836SDavid du Colombier  * That was far too much effort for directory entries.
11643be74836SDavid du Colombier  * Now we can write code that *does* things.
11653be74836SDavid du Colombier  */
11663be74836SDavid du Colombier 
11673be74836SDavid du Colombier /*
11683be74836SDavid du Colombier  * Flush all data associated with f out of the cache and onto venti.
11693be74836SDavid du Colombier  * If recursive is set, flush f's children too.
11703be74836SDavid du Colombier  * Vacfiledecref knows how to flush source and msource too.
11713be74836SDavid du Colombier  */
11723be74836SDavid du Colombier int
vacfileflush(VacFile * f,int recursive)11733be74836SDavid du Colombier vacfileflush(VacFile *f, int recursive)
11743be74836SDavid du Colombier {
11753be74836SDavid du Colombier 	int ret;
11763be74836SDavid du Colombier 	VacFile **kids, *p;
11773be74836SDavid du Colombier 	int i, nkids;
11783be74836SDavid du Colombier 
11793be74836SDavid du Colombier 	if(f->mode == VtOREAD)
11803be74836SDavid du Colombier 		return 0;
11813be74836SDavid du Colombier 
11823be74836SDavid du Colombier 	ret = 0;
11833be74836SDavid du Colombier 	filemetalock(f);
11843be74836SDavid du Colombier 	if(filemetaflush(f, nil) < 0)
11853be74836SDavid du Colombier 		ret = -1;
11863be74836SDavid du Colombier 	filemetaunlock(f);
11873be74836SDavid du Colombier 
11883be74836SDavid du Colombier 	if(filelock(f) < 0)
11893be74836SDavid du Colombier 		return -1;
11903be74836SDavid du Colombier 
11913be74836SDavid du Colombier 	/*
11923be74836SDavid du Colombier 	 * Lock order prevents us from flushing kids while holding
11933be74836SDavid du Colombier 	 * lock, so make a list and then flush without the lock.
11943be74836SDavid du Colombier 	 */
11953be74836SDavid du Colombier 	nkids = 0;
11963be74836SDavid du Colombier 	kids = nil;
11973be74836SDavid du Colombier 	if(recursive){
11983be74836SDavid du Colombier 		nkids = 0;
11993be74836SDavid du Colombier 		for(p=f->down; p; p=p->next)
12003be74836SDavid du Colombier 			nkids++;
12013be74836SDavid du Colombier 		kids = vtmalloc(nkids*sizeof(VacFile*));
12023be74836SDavid du Colombier 		i = 0;
12033be74836SDavid du Colombier 		for(p=f->down; p; p=p->next){
12043be74836SDavid du Colombier 			kids[i++] = p;
12053be74836SDavid du Colombier 			p->ref++;
12063be74836SDavid du Colombier 		}
12073be74836SDavid du Colombier 	}
12083be74836SDavid du Colombier 	if(nkids > 0){
12093be74836SDavid du Colombier 		fileunlock(f);
12103be74836SDavid du Colombier 		for(i=0; i<nkids; i++){
12113be74836SDavid du Colombier 			if(vacfileflush(kids[i], 1) < 0)
12123be74836SDavid du Colombier 				ret = -1;
12133be74836SDavid du Colombier 			vacfiledecref(kids[i]);
12143be74836SDavid du Colombier 		}
12153be74836SDavid du Colombier 		filelock(f);
12163be74836SDavid du Colombier 	}
12173be74836SDavid du Colombier 	free(kids);
12183be74836SDavid du Colombier 
12193be74836SDavid du Colombier 	/*
12203be74836SDavid du Colombier 	 * Now we can flush our own data.
12213be74836SDavid du Colombier 	 */
12223be74836SDavid du Colombier 	vtfilelock(f->source, -1);
12233be74836SDavid du Colombier 	if(vtfileflush(f->source) < 0)
12243be74836SDavid du Colombier 		ret = -1;
12253be74836SDavid du Colombier 	vtfileunlock(f->source);
12263be74836SDavid du Colombier 	if(f->msource){
12273be74836SDavid du Colombier 		vtfilelock(f->msource, -1);
12283be74836SDavid du Colombier 		if(vtfileflush(f->msource) < 0)
12293be74836SDavid du Colombier 			ret = -1;
12303be74836SDavid du Colombier 		vtfileunlock(f->msource);
12313be74836SDavid du Colombier 	}
12323be74836SDavid du Colombier 	fileunlock(f);
12333be74836SDavid du Colombier 
12343be74836SDavid du Colombier 	return ret;
12353be74836SDavid du Colombier }
12363be74836SDavid du Colombier 
12373be74836SDavid du Colombier /*
12383be74836SDavid du Colombier  * Create a new file named elem in fp with the given mode.
12393be74836SDavid du Colombier  * The mode can be changed later except for the ModeDir bit.
12403be74836SDavid du Colombier  */
12413be74836SDavid du Colombier VacFile*
vacfilecreate(VacFile * fp,char * elem,ulong mode)12423be74836SDavid du Colombier vacfilecreate(VacFile *fp, char *elem, ulong mode)
12433be74836SDavid du Colombier {
12443be74836SDavid du Colombier 	VacFile *ff;
12453be74836SDavid du Colombier 	VacDir *dir;
12463be74836SDavid du Colombier 	VtFile *pr, *r, *mr;
12473be74836SDavid du Colombier 	int type;
12483be74836SDavid du Colombier 	u32int bo;
12493be74836SDavid du Colombier 
12503be74836SDavid du Colombier 	if(filelock(fp) < 0)
1251d9306527SDavid du Colombier 		return nil;
1252d9306527SDavid du Colombier 
12533be74836SDavid du Colombier 	/*
12543be74836SDavid du Colombier 	 * First, look to see that there's not a file in memory
12553be74836SDavid du Colombier 	 * with the same name.
12563be74836SDavid du Colombier 	 */
12573be74836SDavid du Colombier 	for(ff = fp->down; ff; ff=ff->next){
12583be74836SDavid du Colombier 		if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
12593be74836SDavid du Colombier 			ff = nil;
12603be74836SDavid du Colombier 			werrstr(EExists);
12613be74836SDavid du Colombier 			goto Err1;
12623be74836SDavid du Colombier 		}
12633be74836SDavid du Colombier 	}
12643be74836SDavid du Colombier 
12653be74836SDavid du Colombier 	/*
12663be74836SDavid du Colombier 	 * Next check the venti blocks.
12673be74836SDavid du Colombier 	 */
12683be74836SDavid du Colombier 	ff = dirlookup(fp, elem);
12693be74836SDavid du Colombier 	if(ff != nil){
12703be74836SDavid du Colombier 		werrstr(EExists);
12713be74836SDavid du Colombier 		goto Err1;
12723be74836SDavid du Colombier 	}
12733be74836SDavid du Colombier 
12743be74836SDavid du Colombier 	/*
12753be74836SDavid du Colombier 	 * By the way, you can't create in a read-only file system.
12763be74836SDavid du Colombier 	 */
12773be74836SDavid du Colombier 	pr = fp->source;
12783be74836SDavid du Colombier 	if(pr->mode != VtORDWR){
12793be74836SDavid du Colombier 		werrstr(EReadOnly);
12803be74836SDavid du Colombier 		goto Err1;
12813be74836SDavid du Colombier 	}
12823be74836SDavid du Colombier 
12833be74836SDavid du Colombier 	/*
12843be74836SDavid du Colombier 	 * Okay, time to actually create something.  Lock the two
12853be74836SDavid du Colombier 	 * halves of the directory and create a file.
12863be74836SDavid du Colombier 	 */
12873be74836SDavid du Colombier 	if(vtfilelock2(fp->source, fp->msource, -1) < 0)
12883be74836SDavid du Colombier 		goto Err1;
12893be74836SDavid du Colombier 	ff = filealloc(fp->fs);
12903be74836SDavid du Colombier 	ff->qidoffset = fp->qidoffset;	/* hopefully fp->qidoffset == 0 */
12913be74836SDavid du Colombier 	type = VtDataType;
12923be74836SDavid du Colombier 	if(mode & ModeDir)
12933be74836SDavid du Colombier 		type = VtDirType;
1294d9306527SDavid du Colombier 	mr = nil;
12953be74836SDavid du Colombier 	if((r = vtfilecreate(pr, pr->psize, pr->dsize, type)) == nil)
129649223a73SDavid du Colombier 		goto Err;
12973be74836SDavid du Colombier 	if(mode & ModeDir)
12983be74836SDavid du Colombier 	if((mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType)) == nil)
129949223a73SDavid du Colombier 		goto Err;
130049223a73SDavid du Colombier 
13013be74836SDavid du Colombier 	/*
13023be74836SDavid du Colombier 	 * Fill in the directory entry and write it to disk.
13033be74836SDavid du Colombier 	 */
13043be74836SDavid du Colombier 	dir = &ff->dir;
13053be74836SDavid du Colombier 	dir->elem = vtstrdup(elem);
13063be74836SDavid du Colombier 	dir->entry = r->offset;
1307d9306527SDavid du Colombier 	dir->gen = r->gen;
13083be74836SDavid du Colombier 	if(mode & ModeDir){
13093be74836SDavid du Colombier 		dir->mentry = mr->offset;
1310d9306527SDavid du Colombier 		dir->mgen = mr->gen;
1311d9306527SDavid du Colombier 	}
1312d9306527SDavid du Colombier 	dir->size = 0;
13133be74836SDavid du Colombier 	if(_vacfsnextqid(fp->fs, &dir->qid) < 0)
13143be74836SDavid du Colombier 		goto Err;
13153be74836SDavid du Colombier 	dir->uid = vtstrdup(fp->dir.uid);
13163be74836SDavid du Colombier 	dir->gid = vtstrdup(fp->dir.gid);
13173be74836SDavid du Colombier 	dir->mid = vtstrdup("");
1318d9306527SDavid du Colombier 	dir->mtime = time(0L);
1319d9306527SDavid du Colombier 	dir->mcount = 0;
1320d9306527SDavid du Colombier 	dir->ctime = dir->mtime;
1321d9306527SDavid du Colombier 	dir->atime = dir->mtime;
1322d9306527SDavid du Colombier 	dir->mode = mode;
13233be74836SDavid du Colombier 	if((bo = filemetaalloc(fp, &ff->dir, NilBlock)) == NilBlock)
1324d9306527SDavid du Colombier 		goto Err;
1325d9306527SDavid du Colombier 
13263be74836SDavid du Colombier 	/*
13273be74836SDavid du Colombier 	 * Now we're committed.
13283be74836SDavid du Colombier 	 */
13293be74836SDavid du Colombier 	vtfileunlock(fp->source);
13303be74836SDavid du Colombier 	vtfileunlock(fp->msource);
13313be74836SDavid du Colombier 	ff->source = r;
13323be74836SDavid du Colombier 	ff->msource = mr;
13333be74836SDavid du Colombier 	ff->boff = bo;
1334d9306527SDavid du Colombier 
13353be74836SDavid du Colombier 	/* Link into tree. */
13363be74836SDavid du Colombier 	ff->next = fp->down;
13373be74836SDavid du Colombier 	fp->down = ff;
13383be74836SDavid du Colombier 	ff->up = fp;
13393be74836SDavid du Colombier 	vacfileincref(fp);
1340d9306527SDavid du Colombier 
13413be74836SDavid du Colombier 	fileunlock(fp);
134249223a73SDavid du Colombier 
13433be74836SDavid du Colombier 	filelock(ff);
13443be74836SDavid du Colombier 	vtfilelock(ff->source, -1);
13453be74836SDavid du Colombier 	vtfileunlock(ff->source);
13463be74836SDavid du Colombier 	fileunlock(ff);
134749223a73SDavid du Colombier 
13483be74836SDavid du Colombier 	return ff;
1349d9306527SDavid du Colombier 
1350d9306527SDavid du Colombier Err:
13513be74836SDavid du Colombier 	vtfileunlock(fp->source);
13523be74836SDavid du Colombier 	vtfileunlock(fp->msource);
13533be74836SDavid du Colombier 	if(r){
13543be74836SDavid du Colombier 		vtfilelock(r, -1);
13553be74836SDavid du Colombier 		vtfileremove(r);
1356e538c934SDavid du Colombier 	}
13573be74836SDavid du Colombier 	if(mr){
13583be74836SDavid du Colombier 		vtfilelock(mr, -1);
13593be74836SDavid du Colombier 		vtfileremove(mr);
1360e538c934SDavid du Colombier 	}
13613be74836SDavid du Colombier Err1:
13623be74836SDavid du Colombier 	if(ff)
13633be74836SDavid du Colombier 		vacfiledecref(ff);
13643be74836SDavid du Colombier 	fileunlock(fp);
136549223a73SDavid du Colombier 	return nil;
136649223a73SDavid du Colombier }
136749223a73SDavid du Colombier 
1368e538c934SDavid du Colombier /*
13693be74836SDavid du Colombier  * Change the size of the file f.
1370e538c934SDavid du Colombier  */
13713be74836SDavid du Colombier int
vacfilesetsize(VacFile * f,uvlong size)13723be74836SDavid du Colombier vacfilesetsize(VacFile *f, uvlong size)
1373e538c934SDavid du Colombier {
13743be74836SDavid du Colombier 	if(vacfileisdir(f)){
13753be74836SDavid du Colombier 		werrstr(ENotFile);
13763be74836SDavid du Colombier 		return -1;
13773be74836SDavid du Colombier 	}
1378e538c934SDavid du Colombier 
13793be74836SDavid du Colombier 	if(filelock(f) < 0)
13803be74836SDavid du Colombier 		return -1;
13813be74836SDavid du Colombier 
13823be74836SDavid du Colombier 	if(f->source->mode != VtORDWR){
13833be74836SDavid du Colombier 		werrstr(EReadOnly);
13843be74836SDavid du Colombier 		goto Err;
1385e538c934SDavid du Colombier 	}
13863be74836SDavid du Colombier 	if(vtfilelock(f->source, -1) < 0)
13873be74836SDavid du Colombier 		goto Err;
13883be74836SDavid du Colombier 	if(vtfilesetsize(f->source, size) < 0){
13893be74836SDavid du Colombier 		vtfileunlock(f->source);
13903be74836SDavid du Colombier 		goto Err;
13913be74836SDavid du Colombier 	}
13923be74836SDavid du Colombier 	vtfileunlock(f->source);
13933be74836SDavid du Colombier 	fileunlock(f);
13943be74836SDavid du Colombier 	return 0;
13953be74836SDavid du Colombier 
13963be74836SDavid du Colombier Err:
13973be74836SDavid du Colombier 	fileunlock(f);
13983be74836SDavid du Colombier 	return -1;
13993be74836SDavid du Colombier }
14003be74836SDavid du Colombier 
14013be74836SDavid du Colombier /*
14023be74836SDavid du Colombier  * Write data to f.
14033be74836SDavid du Colombier  */
14043be74836SDavid du Colombier int
vacfilewrite(VacFile * f,void * buf,int cnt,vlong offset)14053be74836SDavid du Colombier vacfilewrite(VacFile *f, void *buf, int cnt, vlong offset)
14063be74836SDavid du Colombier {
14073be74836SDavid du Colombier 	if(vacfileisdir(f)){
14083be74836SDavid du Colombier 		werrstr(ENotFile);
14093be74836SDavid du Colombier 		return -1;
14103be74836SDavid du Colombier 	}
14113be74836SDavid du Colombier 	if(filelock(f) < 0)
14123be74836SDavid du Colombier 		return -1;
14133be74836SDavid du Colombier 	if(f->source->mode != VtORDWR){
14143be74836SDavid du Colombier 		werrstr(EReadOnly);
14153be74836SDavid du Colombier 		goto Err;
14163be74836SDavid du Colombier 	}
14173be74836SDavid du Colombier 	if(offset < 0){
14183be74836SDavid du Colombier 		werrstr(EBadOffset);
14193be74836SDavid du Colombier 		goto Err;
14203be74836SDavid du Colombier 	}
14213be74836SDavid du Colombier 
14223be74836SDavid du Colombier 	if(vtfilelock(f->source, -1) < 0)
14233be74836SDavid du Colombier 		goto Err;
14243be74836SDavid du Colombier 	if(f->dir.mode & ModeAppend)
14253be74836SDavid du Colombier 		offset = vtfilegetsize(f->source);
14263be74836SDavid du Colombier 	if(vtfilewrite(f->source, buf, cnt, offset) != cnt
14273be74836SDavid du Colombier 	|| vtfileflushbefore(f->source, offset) < 0){
14283be74836SDavid du Colombier 		vtfileunlock(f->source);
14293be74836SDavid du Colombier 		goto Err;
14303be74836SDavid du Colombier 	}
14313be74836SDavid du Colombier 	vtfileunlock(f->source);
14323be74836SDavid du Colombier 	fileunlock(f);
14333be74836SDavid du Colombier 	return cnt;
14343be74836SDavid du Colombier 
14353be74836SDavid du Colombier Err:
14363be74836SDavid du Colombier 	fileunlock(f);
14373be74836SDavid du Colombier 	return -1;
14383be74836SDavid du Colombier }
14393be74836SDavid du Colombier 
14403be74836SDavid du Colombier /*
14413be74836SDavid du Colombier  * Set (!) the VtEntry for the data contained in f.
14423be74836SDavid du Colombier  * This let's us efficiently copy data from one file to another.
14433be74836SDavid du Colombier  */
14443be74836SDavid du Colombier int
vacfilesetentries(VacFile * f,VtEntry * e,VtEntry * me)14453be74836SDavid du Colombier vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me)
14463be74836SDavid du Colombier {
14473be74836SDavid du Colombier 	int ret;
14483be74836SDavid du Colombier 
14493be74836SDavid du Colombier 	vacfileflush(f, 0);	/* flush blocks to venti, since we won't see them again */
14503be74836SDavid du Colombier 
14513be74836SDavid du Colombier 	if(!(e->flags&VtEntryActive)){
14523be74836SDavid du Colombier 		werrstr("missing entry for source");
14533be74836SDavid du Colombier 		return -1;
14543be74836SDavid du Colombier 	}
14553be74836SDavid du Colombier 	if(me && !(me->flags&VtEntryActive))
14563be74836SDavid du Colombier 		me = nil;
14573be74836SDavid du Colombier 	if(f->msource && !me){
14583be74836SDavid du Colombier 		werrstr("missing entry for msource");
14593be74836SDavid du Colombier 		return -1;
14603be74836SDavid du Colombier 	}
14613be74836SDavid du Colombier 	if(me && !f->msource){
14623be74836SDavid du Colombier 		werrstr("no msource to set");
14633be74836SDavid du Colombier 		return -1;
14643be74836SDavid du Colombier 	}
14653be74836SDavid du Colombier 
14663be74836SDavid du Colombier 	if(filelock(f) < 0)
14673be74836SDavid du Colombier 		return -1;
14683be74836SDavid du Colombier 	if(f->source->mode != VtORDWR
14693be74836SDavid du Colombier 	|| (f->msource && f->msource->mode != VtORDWR)){
14703be74836SDavid du Colombier 		werrstr(EReadOnly);
14713be74836SDavid du Colombier 		fileunlock(f);
14723be74836SDavid du Colombier 		return -1;
14733be74836SDavid du Colombier 	}
14743be74836SDavid du Colombier 	if(vtfilelock2(f->source, f->msource, -1) < 0){
14753be74836SDavid du Colombier 		fileunlock(f);
14763be74836SDavid du Colombier 		return -1;
14773be74836SDavid du Colombier 	}
14783be74836SDavid du Colombier 	ret = 0;
14793be74836SDavid du Colombier 	if(vtfilesetentry(f->source, e) < 0)
14803be74836SDavid du Colombier 		ret = -1;
14813be74836SDavid du Colombier 	else if(me && vtfilesetentry(f->msource, me) < 0)
14823be74836SDavid du Colombier 		ret = -1;
14833be74836SDavid du Colombier 
14843be74836SDavid du Colombier 	vtfileunlock(f->source);
14853be74836SDavid du Colombier 	if(f->msource)
14863be74836SDavid du Colombier 		vtfileunlock(f->msource);
14873be74836SDavid du Colombier 	fileunlock(f);
14883be74836SDavid du Colombier 	return ret;
14893be74836SDavid du Colombier }
14903be74836SDavid du Colombier 
14913be74836SDavid du Colombier /*
14923be74836SDavid du Colombier  * Get the directory entry for f.
14933be74836SDavid du Colombier  */
14943be74836SDavid du Colombier int
vacfilegetdir(VacFile * f,VacDir * dir)14953be74836SDavid du Colombier vacfilegetdir(VacFile *f, VacDir *dir)
14963be74836SDavid du Colombier {
14973be74836SDavid du Colombier 	if(filerlock(f) < 0)
14983be74836SDavid du Colombier 		return -1;
14993be74836SDavid du Colombier 
15003be74836SDavid du Colombier 	filemetalock(f);
15013be74836SDavid du Colombier 	vdcopy(dir, &f->dir);
15023be74836SDavid du Colombier 	filemetaunlock(f);
15033be74836SDavid du Colombier 
15043be74836SDavid du Colombier 	if(!vacfileisdir(f)){
15053be74836SDavid du Colombier 		if(vtfilelock(f->source, VtOREAD) < 0){
15063be74836SDavid du Colombier 			filerunlock(f);
15073be74836SDavid du Colombier 			return -1;
15083be74836SDavid du Colombier 		}
15093be74836SDavid du Colombier 		dir->size = vtfilegetsize(f->source);
15103be74836SDavid du Colombier 		vtfileunlock(f->source);
15113be74836SDavid du Colombier 	}
15123be74836SDavid du Colombier 	filerunlock(f);
15133be74836SDavid du Colombier 
15143be74836SDavid du Colombier 	return 0;
15153be74836SDavid du Colombier }
15163be74836SDavid du Colombier 
15173be74836SDavid du Colombier /*
15183be74836SDavid du Colombier  * Set the directory entry for f.
15193be74836SDavid du Colombier  */
15203be74836SDavid du Colombier int
vacfilesetdir(VacFile * f,VacDir * dir)15213be74836SDavid du Colombier vacfilesetdir(VacFile *f, VacDir *dir)
15223be74836SDavid du Colombier {
15233be74836SDavid du Colombier 	VacFile *ff;
15243be74836SDavid du Colombier 	char *oelem;
15253be74836SDavid du Colombier 	u32int mask;
15263be74836SDavid du Colombier 	u64int size;
15273be74836SDavid du Colombier 
15283be74836SDavid du Colombier 	/* can not set permissions for the root */
15293be74836SDavid du Colombier 	if(vacfileisroot(f)){
15303be74836SDavid du Colombier 		werrstr(ERoot);
15313be74836SDavid du Colombier 		return -1;
15323be74836SDavid du Colombier 	}
15333be74836SDavid du Colombier 
15343be74836SDavid du Colombier 	if(filelock(f) < 0)
15353be74836SDavid du Colombier 		return -1;
15363be74836SDavid du Colombier 	filemetalock(f);
15373be74836SDavid du Colombier 
15383be74836SDavid du Colombier 	if(f->source->mode != VtORDWR){
15393be74836SDavid du Colombier 		werrstr(EReadOnly);
15403be74836SDavid du Colombier 		goto Err;
15413be74836SDavid du Colombier 	}
15423be74836SDavid du Colombier 
15433be74836SDavid du Colombier 	/* On rename, check new name does not already exist */
15443be74836SDavid du Colombier 	if(strcmp(f->dir.elem, dir->elem) != 0){
15453be74836SDavid du Colombier 		for(ff = f->up->down; ff; ff=ff->next){
15463be74836SDavid du Colombier 			if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
15473be74836SDavid du Colombier 				werrstr(EExists);
15483be74836SDavid du Colombier 				goto Err;
15493be74836SDavid du Colombier 			}
15503be74836SDavid du Colombier 		}
15513be74836SDavid du Colombier 		ff = dirlookup(f->up, dir->elem);
15523be74836SDavid du Colombier 		if(ff != nil){
15533be74836SDavid du Colombier 			vacfiledecref(ff);
15543be74836SDavid du Colombier 			werrstr(EExists);
15553be74836SDavid du Colombier 			goto Err;
15563be74836SDavid du Colombier 		}
15573be74836SDavid du Colombier 		werrstr("");	/* "failed" dirlookup poisoned it */
15583be74836SDavid du Colombier 	}
15593be74836SDavid du Colombier 
15603be74836SDavid du Colombier 	/* Get ready... */
15613be74836SDavid du Colombier 	if(vtfilelock2(f->source, f->msource, -1) < 0)
15623be74836SDavid du Colombier 		goto Err;
15633be74836SDavid du Colombier 	if(!vacfileisdir(f)){
15643be74836SDavid du Colombier 		size = vtfilegetsize(f->source);
15653be74836SDavid du Colombier 		if(size != dir->size){
15663be74836SDavid du Colombier 			if(vtfilesetsize(f->source, dir->size) < 0){
15673be74836SDavid du Colombier 				vtfileunlock(f->source);
15683be74836SDavid du Colombier 				if(f->msource)
15693be74836SDavid du Colombier 					vtfileunlock(f->msource);
15703be74836SDavid du Colombier 				goto Err;
15713be74836SDavid du Colombier 			}
15723be74836SDavid du Colombier 		}
15733be74836SDavid du Colombier 	}
15743be74836SDavid du Colombier 	/* ... now commited to changing it. */
15753be74836SDavid du Colombier 	vtfileunlock(f->source);
15763be74836SDavid du Colombier 	if(f->msource)
15773be74836SDavid du Colombier 		vtfileunlock(f->msource);
15783be74836SDavid du Colombier 
15793be74836SDavid du Colombier 	oelem = nil;
15803be74836SDavid du Colombier 	if(strcmp(f->dir.elem, dir->elem) != 0){
15813be74836SDavid du Colombier 		oelem = f->dir.elem;
15823be74836SDavid du Colombier 		f->dir.elem = vtstrdup(dir->elem);
15833be74836SDavid du Colombier 	}
15843be74836SDavid du Colombier 
15853be74836SDavid du Colombier 	if(strcmp(f->dir.uid, dir->uid) != 0){
15863be74836SDavid du Colombier 		vtfree(f->dir.uid);
15873be74836SDavid du Colombier 		f->dir.uid = vtstrdup(dir->uid);
15883be74836SDavid du Colombier 	}
15893be74836SDavid du Colombier 
15903be74836SDavid du Colombier 	if(strcmp(f->dir.gid, dir->gid) != 0){
15913be74836SDavid du Colombier 		vtfree(f->dir.gid);
15923be74836SDavid du Colombier 		f->dir.gid = vtstrdup(dir->gid);
15933be74836SDavid du Colombier 	}
15943be74836SDavid du Colombier 
1595*fba43d71SDavid du Colombier 	if(strcmp(f->dir.mid, dir->mid) != 0){
1596*fba43d71SDavid du Colombier 		vtfree(f->dir.mid);
1597*fba43d71SDavid du Colombier 		f->dir.mid = vtstrdup(dir->mid);
1598*fba43d71SDavid du Colombier 	}
1599*fba43d71SDavid du Colombier 
16003be74836SDavid du Colombier 	f->dir.mtime = dir->mtime;
16013be74836SDavid du Colombier 	f->dir.atime = dir->atime;
16023be74836SDavid du Colombier 
16033be74836SDavid du Colombier 	mask = ~(ModeDir|ModeSnapshot);
16043be74836SDavid du Colombier 	f->dir.mode &= ~mask;
16053be74836SDavid du Colombier 	f->dir.mode |= mask & dir->mode;
16063be74836SDavid du Colombier 	f->dirty = 1;
16073be74836SDavid du Colombier 
16083be74836SDavid du Colombier 	if(filemetaflush(f, oelem) < 0){
16093be74836SDavid du Colombier 		vtfree(oelem);
16103be74836SDavid du Colombier 		goto Err;	/* that sucks */
16113be74836SDavid du Colombier 	}
16123be74836SDavid du Colombier 	vtfree(oelem);
16133be74836SDavid du Colombier 
16143be74836SDavid du Colombier 	filemetaunlock(f);
16153be74836SDavid du Colombier 	fileunlock(f);
16163be74836SDavid du Colombier 	return 0;
16173be74836SDavid du Colombier 
16183be74836SDavid du Colombier Err:
16193be74836SDavid du Colombier 	filemetaunlock(f);
16203be74836SDavid du Colombier 	fileunlock(f);
16213be74836SDavid du Colombier 	return -1;
16223be74836SDavid du Colombier }
16233be74836SDavid du Colombier 
16243be74836SDavid du Colombier /*
16253be74836SDavid du Colombier  * Set the qid space.
16263be74836SDavid du Colombier  */
16273be74836SDavid du Colombier int
vacfilesetqidspace(VacFile * f,u64int offset,u64int max)16283be74836SDavid du Colombier vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
16293be74836SDavid du Colombier {
16303be74836SDavid du Colombier 	int ret;
16313be74836SDavid du Colombier 
16323be74836SDavid du Colombier 	if(filelock(f) < 0)
16333be74836SDavid du Colombier 		return -1;
16343be74836SDavid du Colombier 	if(f->source->mode != VtORDWR){
16353be74836SDavid du Colombier 		fileunlock(f);
16363be74836SDavid du Colombier 		werrstr(EReadOnly);
16373be74836SDavid du Colombier 		return -1;
16383be74836SDavid du Colombier 	}
16393be74836SDavid du Colombier 	filemetalock(f);
16403be74836SDavid du Colombier 	f->dir.qidspace = 1;
16413be74836SDavid du Colombier 	f->dir.qidoffset = offset;
16423be74836SDavid du Colombier 	f->dir.qidmax = max;
16433be74836SDavid du Colombier 	f->dirty = 1;
16443be74836SDavid du Colombier 	ret = filemetaflush(f, nil);
16453be74836SDavid du Colombier 	filemetaunlock(f);
16463be74836SDavid du Colombier 	fileunlock(f);
16473be74836SDavid du Colombier 	return ret;
16483be74836SDavid du Colombier }
16493be74836SDavid du Colombier 
16503be74836SDavid du Colombier /*
16513be74836SDavid du Colombier  * Check that the file is empty, returning 0 if it is.
16523be74836SDavid du Colombier  * Returns -1 on error (and not being empty is an error).
16533be74836SDavid du Colombier  */
16543be74836SDavid du Colombier static int
filecheckempty(VacFile * f)16553be74836SDavid du Colombier filecheckempty(VacFile *f)
16563be74836SDavid du Colombier {
16573be74836SDavid du Colombier 	u32int i, n;
16583be74836SDavid du Colombier 	VtBlock *b;
16593be74836SDavid du Colombier 	MetaBlock mb;
16603be74836SDavid du Colombier 	VtFile *r;
16613be74836SDavid du Colombier 
16623be74836SDavid du Colombier 	r = f->msource;
16633be74836SDavid du Colombier 	n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
16643be74836SDavid du Colombier 	for(i=0; i<n; i++){
16653be74836SDavid du Colombier 		b = vtfileblock(r, i, VtOREAD);
16663be74836SDavid du Colombier 		if(b == nil)
16673be74836SDavid du Colombier 			return -1;
16683be74836SDavid du Colombier 		if(mbunpack(&mb, b->data, r->dsize) < 0)
16693be74836SDavid du Colombier 			goto Err;
16703be74836SDavid du Colombier 		if(mb.nindex > 0){
16713be74836SDavid du Colombier 			werrstr(ENotEmpty);
16723be74836SDavid du Colombier 			goto Err;
16733be74836SDavid du Colombier 		}
16743be74836SDavid du Colombier 		vtblockput(b);
16753be74836SDavid du Colombier 	}
16763be74836SDavid du Colombier 	return 0;
16773be74836SDavid du Colombier 
16783be74836SDavid du Colombier Err:
16793be74836SDavid du Colombier 	vtblockput(b);
16803be74836SDavid du Colombier 	return -1;
16813be74836SDavid du Colombier }
16823be74836SDavid du Colombier 
16833be74836SDavid du Colombier /*
16843be74836SDavid du Colombier  * Remove the vac file f.
16853be74836SDavid du Colombier  */
16863be74836SDavid du Colombier int
vacfileremove(VacFile * f)16873be74836SDavid du Colombier vacfileremove(VacFile *f)
16883be74836SDavid du Colombier {
16893be74836SDavid du Colombier 	VacFile *ff;
16903be74836SDavid du Colombier 
16913be74836SDavid du Colombier 	/* Cannot remove the root */
16923be74836SDavid du Colombier 	if(vacfileisroot(f)){
16933be74836SDavid du Colombier 		werrstr(ERoot);
16943be74836SDavid du Colombier 		return -1;
16953be74836SDavid du Colombier 	}
16963be74836SDavid du Colombier 
16973be74836SDavid du Colombier 	if(filelock(f) < 0)
16983be74836SDavid du Colombier 		return -1;
16993be74836SDavid du Colombier 	if(f->source->mode != VtORDWR){
17003be74836SDavid du Colombier 		werrstr(EReadOnly);
17013be74836SDavid du Colombier 		goto Err1;
17023be74836SDavid du Colombier 	}
17033be74836SDavid du Colombier 	if(vtfilelock2(f->source, f->msource, -1) < 0)
17043be74836SDavid du Colombier 		goto Err1;
17053be74836SDavid du Colombier 	if(vacfileisdir(f) && filecheckempty(f)<0)
17063be74836SDavid du Colombier 		goto Err;
17073be74836SDavid du Colombier 
17083be74836SDavid du Colombier 	for(ff=f->down; ff; ff=ff->next)
17093be74836SDavid du Colombier 		assert(ff->removed);
17103be74836SDavid du Colombier 
17113be74836SDavid du Colombier 	vtfileremove(f->source);
17123be74836SDavid du Colombier 	f->source = nil;
17133be74836SDavid du Colombier 	if(f->msource){
17143be74836SDavid du Colombier 		vtfileremove(f->msource);
17153be74836SDavid du Colombier 		f->msource = nil;
17163be74836SDavid du Colombier 	}
17173be74836SDavid du Colombier 	fileunlock(f);
17183be74836SDavid du Colombier 
17193be74836SDavid du Colombier 	if(filemetaremove(f) < 0)
17203be74836SDavid du Colombier 		return -1;
17213be74836SDavid du Colombier 	return 0;
17223be74836SDavid du Colombier 
17233be74836SDavid du Colombier Err:
17243be74836SDavid du Colombier 	vtfileunlock(f->source);
17253be74836SDavid du Colombier 	if(f->msource)
17263be74836SDavid du Colombier 		vtfileunlock(f->msource);
17273be74836SDavid du Colombier Err1:
17283be74836SDavid du Colombier 	fileunlock(f);
17293be74836SDavid du Colombier 	return -1;
17303be74836SDavid du Colombier }
17313be74836SDavid du Colombier 
17323be74836SDavid du Colombier /*
17333be74836SDavid du Colombier  * Vac file system format.
17343be74836SDavid du Colombier  */
17353be74836SDavid du Colombier static char EBadVacFormat[] = "bad format for vac file";
17363be74836SDavid du Colombier 
17373be74836SDavid du Colombier static VacFs *
vacfsalloc(VtConn * z,int bsize,int ncache,int mode)17383be74836SDavid du Colombier vacfsalloc(VtConn *z, int bsize, int ncache, int mode)
17393be74836SDavid du Colombier {
17403be74836SDavid du Colombier 	VacFs *fs;
17413be74836SDavid du Colombier 
17423be74836SDavid du Colombier 	fs = vtmallocz(sizeof(VacFs));
17433be74836SDavid du Colombier 	fs->z = z;
17443be74836SDavid du Colombier 	fs->bsize = bsize;
17453be74836SDavid du Colombier 	fs->mode = mode;
17463be74836SDavid du Colombier 	fs->cache = vtcachealloc(z, bsize, ncache);
17473be74836SDavid du Colombier 	return fs;
17483be74836SDavid du Colombier }
17493be74836SDavid du Colombier 
17503be74836SDavid du Colombier static int
readscore(int fd,uchar score[VtScoreSize])17513be74836SDavid du Colombier readscore(int fd, uchar score[VtScoreSize])
17523be74836SDavid du Colombier {
17533be74836SDavid du Colombier 	char buf[45], *pref;
17543be74836SDavid du Colombier 	int n;
17553be74836SDavid du Colombier 
17563be74836SDavid du Colombier 	n = readn(fd, buf, sizeof(buf)-1);
17573be74836SDavid du Colombier 	if(n < sizeof(buf)-1) {
17583be74836SDavid du Colombier 		werrstr("short read");
17593be74836SDavid du Colombier 		return -1;
17603be74836SDavid du Colombier 	}
17613be74836SDavid du Colombier 	buf[n] = 0;
17623be74836SDavid du Colombier 
17633be74836SDavid du Colombier 	if(vtparsescore(buf, &pref, score) < 0){
17643be74836SDavid du Colombier 		werrstr(EBadVacFormat);
17653be74836SDavid du Colombier 		return -1;
17663be74836SDavid du Colombier 	}
17673be74836SDavid du Colombier 	if(pref==nil || strcmp(pref, "vac") != 0) {
17683be74836SDavid du Colombier 		werrstr("not a vac file");
17693be74836SDavid du Colombier 		return -1;
17703be74836SDavid du Colombier 	}
17713be74836SDavid du Colombier 	return 0;
17723be74836SDavid du Colombier }
17733be74836SDavid du Colombier 
17743be74836SDavid du Colombier VacFs*
vacfsopen(VtConn * z,char * file,int mode,int ncache)17753be74836SDavid du Colombier vacfsopen(VtConn *z, char *file, int mode, int ncache)
17763be74836SDavid du Colombier {
17773be74836SDavid du Colombier 	int fd;
17783be74836SDavid du Colombier 	uchar score[VtScoreSize];
17793be74836SDavid du Colombier 	char *prefix;
17803be74836SDavid du Colombier 
17813be74836SDavid du Colombier 	if(vtparsescore(file, &prefix, score) >= 0){
178242ae7379SDavid du Colombier 		if(prefix == nil || strcmp(prefix, "vac") != 0){
17833be74836SDavid du Colombier 			werrstr("not a vac file");
17843be74836SDavid du Colombier 			return nil;
17853be74836SDavid du Colombier 		}
17863be74836SDavid du Colombier 	}else{
17873be74836SDavid du Colombier 		fd = open(file, OREAD);
17883be74836SDavid du Colombier 		if(fd < 0)
17893be74836SDavid du Colombier 			return nil;
17903be74836SDavid du Colombier 		if(readscore(fd, score) < 0){
17913be74836SDavid du Colombier 			close(fd);
17923be74836SDavid du Colombier 			return nil;
17933be74836SDavid du Colombier 		}
17943be74836SDavid du Colombier 		close(fd);
17953be74836SDavid du Colombier 	}
17963be74836SDavid du Colombier 	return vacfsopenscore(z, score, mode, ncache);
17973be74836SDavid du Colombier }
17983be74836SDavid du Colombier 
17993be74836SDavid du Colombier VacFs*
vacfsopenscore(VtConn * z,u8int * score,int mode,int ncache)18003be74836SDavid du Colombier vacfsopenscore(VtConn *z, u8int *score, int mode, int ncache)
18013be74836SDavid du Colombier {
18023be74836SDavid du Colombier 	VacFs *fs;
18033be74836SDavid du Colombier 	int n;
18043be74836SDavid du Colombier 	VtRoot rt;
18053be74836SDavid du Colombier 	uchar buf[VtRootSize];
18063be74836SDavid du Colombier 	VacFile *root;
18073be74836SDavid du Colombier 	VtFile *r;
18083be74836SDavid du Colombier 	VtEntry e;
18093be74836SDavid du Colombier 
18103be74836SDavid du Colombier 	n = vtread(z, score, VtRootType, buf, VtRootSize);
18113be74836SDavid du Colombier 	if(n < 0)
18123be74836SDavid du Colombier 		return nil;
18133be74836SDavid du Colombier 	if(n != VtRootSize){
18143be74836SDavid du Colombier 		werrstr("vtread on root too short");
18153be74836SDavid du Colombier 		return nil;
18163be74836SDavid du Colombier 	}
18173be74836SDavid du Colombier 
18183be74836SDavid du Colombier 	if(vtrootunpack(&rt, buf) < 0)
18193be74836SDavid du Colombier 		return nil;
18203be74836SDavid du Colombier 
18213be74836SDavid du Colombier 	if(strcmp(rt.type, "vac") != 0) {
18223be74836SDavid du Colombier 		werrstr("not a vac root");
18233be74836SDavid du Colombier 		return nil;
18243be74836SDavid du Colombier 	}
18253be74836SDavid du Colombier 
18263be74836SDavid du Colombier 	fs = vacfsalloc(z, rt.blocksize, ncache, mode);
18273be74836SDavid du Colombier 	memmove(fs->score, score, VtScoreSize);
18283be74836SDavid du Colombier 	fs->mode = mode;
18293be74836SDavid du Colombier 
18303be74836SDavid du Colombier 	memmove(e.score, rt.score, VtScoreSize);
18313be74836SDavid du Colombier 	e.gen = 0;
18323be74836SDavid du Colombier 	e.psize = rt.blocksize;
18333be74836SDavid du Colombier 	e.dsize = rt.blocksize;
18343be74836SDavid du Colombier 	e.type = VtDirType;
18353be74836SDavid du Colombier 	e.flags = VtEntryActive;
18363be74836SDavid du Colombier 	e.size = 3*VtEntrySize;
18373be74836SDavid du Colombier 
18383be74836SDavid du Colombier 	root = nil;
18393be74836SDavid du Colombier 	if((r = vtfileopenroot(fs->cache, &e)) == nil)
18403be74836SDavid du Colombier 		goto Err;
18413be74836SDavid du Colombier 	if(debug)
18423be74836SDavid du Colombier 		fprint(2, "r %p\n", r);
18433be74836SDavid du Colombier 	root = _vacfileroot(fs, r);
18443be74836SDavid du Colombier 	if(debug)
18453be74836SDavid du Colombier 		fprint(2, "root %p\n", root);
18463be74836SDavid du Colombier 	vtfileclose(r);
18473be74836SDavid du Colombier 	if(root == nil)
18483be74836SDavid du Colombier 		goto Err;
18493be74836SDavid du Colombier 	fs->root = root;
18503be74836SDavid du Colombier 	return fs;
18513be74836SDavid du Colombier Err:
18523be74836SDavid du Colombier 	if(root)
18533be74836SDavid du Colombier 		vacfiledecref(root);
18543be74836SDavid du Colombier 	vacfsclose(fs);
18553be74836SDavid du Colombier 	return nil;
18563be74836SDavid du Colombier }
18573be74836SDavid du Colombier 
18583be74836SDavid du Colombier int
vacfsmode(VacFs * fs)18593be74836SDavid du Colombier vacfsmode(VacFs *fs)
18603be74836SDavid du Colombier {
18613be74836SDavid du Colombier 	return fs->mode;
18623be74836SDavid du Colombier }
18633be74836SDavid du Colombier 
18643be74836SDavid du Colombier VacFile*
vacfsgetroot(VacFs * fs)18653be74836SDavid du Colombier vacfsgetroot(VacFs *fs)
18663be74836SDavid du Colombier {
18673be74836SDavid du Colombier 	return vacfileincref(fs->root);
18683be74836SDavid du Colombier }
18693be74836SDavid du Colombier 
18703be74836SDavid du Colombier int
vacfsgetblocksize(VacFs * fs)18713be74836SDavid du Colombier vacfsgetblocksize(VacFs *fs)
18723be74836SDavid du Colombier {
18733be74836SDavid du Colombier 	return fs->bsize;
18743be74836SDavid du Colombier }
18753be74836SDavid du Colombier 
18763be74836SDavid du Colombier int
vacfsgetscore(VacFs * fs,u8int * score)18773be74836SDavid du Colombier vacfsgetscore(VacFs *fs, u8int *score)
18783be74836SDavid du Colombier {
18793be74836SDavid du Colombier 	memmove(score, fs->score, VtScoreSize);
18803be74836SDavid du Colombier 	return 0;
18813be74836SDavid du Colombier }
18823be74836SDavid du Colombier 
18833be74836SDavid du Colombier int
_vacfsnextqid(VacFs * fs,uvlong * qid)18843be74836SDavid du Colombier _vacfsnextqid(VacFs *fs, uvlong *qid)
18853be74836SDavid du Colombier {
18863be74836SDavid du Colombier 	++fs->qid;
18873be74836SDavid du Colombier 	*qid = fs->qid;
18883be74836SDavid du Colombier 	return 0;
18893be74836SDavid du Colombier }
18903be74836SDavid du Colombier 
18913be74836SDavid du Colombier void
vacfsjumpqid(VacFs * fs,uvlong step)18923be74836SDavid du Colombier vacfsjumpqid(VacFs *fs, uvlong step)
18933be74836SDavid du Colombier {
18943be74836SDavid du Colombier 	fs->qid += step;
18953be74836SDavid du Colombier }
18963be74836SDavid du Colombier 
18973be74836SDavid du Colombier /*
18983be74836SDavid du Colombier  * Set *maxqid to the maximum qid expected in this file system.
18993be74836SDavid du Colombier  * In newer vac archives, the maximum qid is stored in the
19003be74836SDavid du Colombier  * qidspace VacDir annotation.  In older vac archives, the root
19013be74836SDavid du Colombier  * got created last, so it had the maximum qid.
19023be74836SDavid du Colombier  */
19033be74836SDavid du Colombier int
vacfsgetmaxqid(VacFs * fs,uvlong * maxqid)19043be74836SDavid du Colombier vacfsgetmaxqid(VacFs *fs, uvlong *maxqid)
19053be74836SDavid du Colombier {
19063be74836SDavid du Colombier 	VacDir vd;
19073be74836SDavid du Colombier 
19083be74836SDavid du Colombier 	if(vacfilegetdir(fs->root, &vd) < 0)
19093be74836SDavid du Colombier 		return -1;
19103be74836SDavid du Colombier 	if(vd.qidspace)
19113be74836SDavid du Colombier 		*maxqid = vd.qidmax;
191249223a73SDavid du Colombier 	else
19133be74836SDavid du Colombier 		*maxqid = vd.qid;
19143be74836SDavid du Colombier 	vdcleanup(&vd);
19153be74836SDavid du Colombier 	return 0;
1916aedc1c01SDavid du Colombier }
19173be74836SDavid du Colombier 
19183be74836SDavid du Colombier 
19193be74836SDavid du Colombier void
vacfsclose(VacFs * fs)19203be74836SDavid du Colombier vacfsclose(VacFs *fs)
19213be74836SDavid du Colombier {
19223be74836SDavid du Colombier 	if(fs->root)
19233be74836SDavid du Colombier 		vacfiledecref(fs->root);
19243be74836SDavid du Colombier 	fs->root = nil;
19253be74836SDavid du Colombier 	vtcachefree(fs->cache);
19263be74836SDavid du Colombier 	vtfree(fs);
19273be74836SDavid du Colombier }
19283be74836SDavid du Colombier 
19293be74836SDavid du Colombier /*
19303be74836SDavid du Colombier  * Create a fresh vac fs.
19313be74836SDavid du Colombier  */
19323be74836SDavid du Colombier VacFs *
vacfscreate(VtConn * z,int bsize,int ncache)19333be74836SDavid du Colombier vacfscreate(VtConn *z, int bsize, int ncache)
19343be74836SDavid du Colombier {
19353be74836SDavid du Colombier 	VacFs *fs;
19363be74836SDavid du Colombier 	VtFile *f;
19373be74836SDavid du Colombier 	uchar buf[VtEntrySize], metascore[VtScoreSize];
19383be74836SDavid du Colombier 	VtEntry e;
19393be74836SDavid du Colombier 	VtBlock *b;
19403be74836SDavid du Colombier 	MetaBlock mb;
19413be74836SDavid du Colombier 	VacDir vd;
19423be74836SDavid du Colombier 	MetaEntry me;
19433be74836SDavid du Colombier 	int psize;
19443be74836SDavid du Colombier 	int mbsize;
19453be74836SDavid du Colombier 
19463be74836SDavid du Colombier 	if((fs = vacfsalloc(z, bsize, ncache, VtORDWR)) == nil)
19473be74836SDavid du Colombier 		return nil;
19483be74836SDavid du Colombier 
19493be74836SDavid du Colombier 	/*
19503be74836SDavid du Colombier 	 * Fake up an empty vac fs.
19513be74836SDavid du Colombier 	 */
19523be74836SDavid du Colombier 	psize = bsize;
19533be74836SDavid du Colombier 	f = vtfilecreateroot(fs->cache, psize, bsize, VtDirType);
19543be74836SDavid du Colombier 	vtfilelock(f, VtORDWR);
19553be74836SDavid du Colombier 
19563be74836SDavid du Colombier 	/* Metablocks can't be too big -- they have 16-bit offsets in them. */
19573be74836SDavid du Colombier 	mbsize = bsize;
19583be74836SDavid du Colombier 	if(mbsize >= 56*1024)
19593be74836SDavid du Colombier 		mbsize = 56*1024;
19603be74836SDavid du Colombier 
19613be74836SDavid du Colombier 	/* Write metablock containing root directory VacDir. */
19623be74836SDavid du Colombier 	b = vtcacheallocblock(fs->cache, VtDataType);
19633be74836SDavid du Colombier 	mbinit(&mb, b->data, mbsize, mbsize/BytesPerEntry);
19643be74836SDavid du Colombier 	memset(&vd, 0, sizeof vd);
19653be74836SDavid du Colombier 	vd.elem = "/";
19663be74836SDavid du Colombier 	vd.mode = 0777|ModeDir;
19673be74836SDavid du Colombier 	vd.uid = "vac";
19683be74836SDavid du Colombier 	vd.gid = "vac";
19693be74836SDavid du Colombier 	vd.mid = "";
19703be74836SDavid du Colombier 	me.size = vdsize(&vd, VacDirVersion);
19713be74836SDavid du Colombier 	me.p = mballoc(&mb, me.size);
19723be74836SDavid du Colombier 	vdpack(&vd, &me, VacDirVersion);
19733be74836SDavid du Colombier 	mbinsert(&mb, 0, &me);
19743be74836SDavid du Colombier 	mbpack(&mb);
19753be74836SDavid du Colombier 	vtblockwrite(b);
19763be74836SDavid du Colombier 	memmove(metascore, b->score, VtScoreSize);
19773be74836SDavid du Colombier 	vtblockput(b);
19783be74836SDavid du Colombier 
19793be74836SDavid du Colombier 	/* First entry: empty venti directory stream. */
19803be74836SDavid du Colombier 	memset(&e, 0, sizeof e);
19813be74836SDavid du Colombier 	e.flags = VtEntryActive;
19823be74836SDavid du Colombier 	e.psize = psize;
19833be74836SDavid du Colombier 	e.dsize = bsize;
19843be74836SDavid du Colombier 	e.type = VtDirType;
19853be74836SDavid du Colombier 	memmove(e.score, vtzeroscore, VtScoreSize);
19863be74836SDavid du Colombier 	vtentrypack(&e, buf, 0);
19873be74836SDavid du Colombier 	vtfilewrite(f, buf, VtEntrySize, 0);
19883be74836SDavid du Colombier 
19893be74836SDavid du Colombier 	/* Second entry: empty metadata stream. */
19903be74836SDavid du Colombier 	e.type = VtDataType;
19913be74836SDavid du Colombier 	e.dsize = mbsize;
19923be74836SDavid du Colombier 	vtentrypack(&e, buf, 0);
19933be74836SDavid du Colombier 	vtfilewrite(f, buf, VtEntrySize, VtEntrySize);
19943be74836SDavid du Colombier 
19953be74836SDavid du Colombier 	/* Third entry: metadata stream with root directory. */
19963be74836SDavid du Colombier 	memmove(e.score, metascore, VtScoreSize);
19973be74836SDavid du Colombier 	e.size = mbsize;
19983be74836SDavid du Colombier 	vtentrypack(&e, buf, 0);
19993be74836SDavid du Colombier 	vtfilewrite(f, buf, VtEntrySize, VtEntrySize*2);
20003be74836SDavid du Colombier 
20013be74836SDavid du Colombier 	vtfileflush(f);
20023be74836SDavid du Colombier 	vtfileunlock(f);
20033be74836SDavid du Colombier 
20043be74836SDavid du Colombier 	/* Now open it as a vac fs. */
20053be74836SDavid du Colombier 	fs->root = _vacfileroot(fs, f);
20063be74836SDavid du Colombier 	if(fs->root == nil){
20073be74836SDavid du Colombier 		werrstr("vacfileroot: %r");
20083be74836SDavid du Colombier 		vacfsclose(fs);
20093be74836SDavid du Colombier 		return nil;
20103be74836SDavid du Colombier 	}
20113be74836SDavid du Colombier 
20123be74836SDavid du Colombier 	return fs;
20133be74836SDavid du Colombier }
20143be74836SDavid du Colombier 
20153be74836SDavid du Colombier int
vacfssync(VacFs * fs)20163be74836SDavid du Colombier vacfssync(VacFs *fs)
20173be74836SDavid du Colombier {
20183be74836SDavid du Colombier 	uchar buf[1024];
20193be74836SDavid du Colombier 	VtEntry e;
20203be74836SDavid du Colombier 	VtFile *f;
20213be74836SDavid du Colombier 	VtRoot root;
20223be74836SDavid du Colombier 
20233be74836SDavid du Colombier 	/* Sync the entire vacfs to disk. */
20243be74836SDavid du Colombier 	if(vacfileflush(fs->root, 1) < 0)
20253be74836SDavid du Colombier 		return -1;
20263be74836SDavid du Colombier 	if(vtfilelock(fs->root->up->msource, -1) < 0)
20273be74836SDavid du Colombier 		return -1;
20283be74836SDavid du Colombier 	if(vtfileflush(fs->root->up->msource) < 0){
20293be74836SDavid du Colombier 		vtfileunlock(fs->root->up->msource);
20303be74836SDavid du Colombier 		return -1;
20313be74836SDavid du Colombier 	}
20323be74836SDavid du Colombier 	vtfileunlock(fs->root->up->msource);
20333be74836SDavid du Colombier 
20343be74836SDavid du Colombier 	/* Prepare the dir stream for the root block. */
20353be74836SDavid du Colombier 	if(getentry(fs->root->source, &e) < 0)
20363be74836SDavid du Colombier 		return -1;
20373be74836SDavid du Colombier 	vtentrypack(&e, buf, 0);
20383be74836SDavid du Colombier 	if(getentry(fs->root->msource, &e) < 0)
20393be74836SDavid du Colombier 		return -1;
20403be74836SDavid du Colombier 	vtentrypack(&e, buf, 1);
20413be74836SDavid du Colombier 	if(getentry(fs->root->up->msource, &e) < 0)
20423be74836SDavid du Colombier 		return -1;
20433be74836SDavid du Colombier 	vtentrypack(&e, buf, 2);
20443be74836SDavid du Colombier 
20453be74836SDavid du Colombier 	f = vtfilecreateroot(fs->cache, fs->bsize, fs->bsize, VtDirType);
20463be74836SDavid du Colombier 	vtfilelock(f, VtORDWR);
20473be74836SDavid du Colombier 	if(vtfilewrite(f, buf, 3*VtEntrySize, 0) < 0
20483be74836SDavid du Colombier 	|| vtfileflush(f) < 0){
20493be74836SDavid du Colombier 		vtfileunlock(f);
20503be74836SDavid du Colombier 		vtfileclose(f);
20513be74836SDavid du Colombier 		return -1;
20523be74836SDavid du Colombier 	}
20533be74836SDavid du Colombier 	vtfileunlock(f);
20543be74836SDavid du Colombier 	if(getentry(f, &e) < 0){
20553be74836SDavid du Colombier 		vtfileclose(f);
20563be74836SDavid du Colombier 		return -1;
20573be74836SDavid du Colombier 	}
20583be74836SDavid du Colombier 	vtfileclose(f);
20593be74836SDavid du Colombier 
20603be74836SDavid du Colombier 	/* Build a root block. */
20613be74836SDavid du Colombier 	memset(&root, 0, sizeof root);
20623be74836SDavid du Colombier 	strcpy(root.type, "vac");
20633be74836SDavid du Colombier 	strcpy(root.name, fs->name);
20643be74836SDavid du Colombier 	memmove(root.score, e.score, VtScoreSize);
20653be74836SDavid du Colombier 	root.blocksize = fs->bsize;
20663be74836SDavid du Colombier 	memmove(root.prev, fs->score, VtScoreSize);
20673be74836SDavid du Colombier 	vtrootpack(&root, buf);
20683be74836SDavid du Colombier 	if(vtwrite(fs->z, fs->score, VtRootType, buf, VtRootSize) < 0){
20693be74836SDavid du Colombier 		werrstr("writing root: %r");
20703be74836SDavid du Colombier 		return -1;
20713be74836SDavid du Colombier 	}
20723be74836SDavid du Colombier 	if(vtsync(fs->z) < 0)
20733be74836SDavid du Colombier 		return -1;
20743be74836SDavid du Colombier 	return 0;
20753be74836SDavid du Colombier }
20763be74836SDavid du Colombier 
20773be74836SDavid du Colombier int
vacfiledsize(VacFile * f)20783be74836SDavid du Colombier vacfiledsize(VacFile *f)
20793be74836SDavid du Colombier {
20803be74836SDavid du Colombier 	VtEntry e;
20813be74836SDavid du Colombier 
20823be74836SDavid du Colombier 	if(vacfilegetentries(f,&e,nil) < 0)
20833be74836SDavid du Colombier 		return -1;
20843be74836SDavid du Colombier 	return e.dsize;
20853be74836SDavid du Colombier }
20863be74836SDavid du Colombier 
20873be74836SDavid du Colombier /*
20883be74836SDavid du Colombier  * Does block b of f have the same SHA1 hash as the n bytes at buf?
20893be74836SDavid du Colombier  */
20903be74836SDavid du Colombier int
sha1matches(VacFile * f,ulong b,uchar * buf,int n)20913be74836SDavid du Colombier sha1matches(VacFile *f, ulong b, uchar *buf, int n)
20923be74836SDavid du Colombier {
20933be74836SDavid du Colombier 	uchar fscore[VtScoreSize];
20943be74836SDavid du Colombier 	uchar bufscore[VtScoreSize];
20953be74836SDavid du Colombier 
20963be74836SDavid du Colombier 	if(vacfileblockscore(f, b, fscore) < 0)
20973be74836SDavid du Colombier 		return 0;
20983be74836SDavid du Colombier 	n = vtzerotruncate(VtDataType, buf, n);
20993be74836SDavid du Colombier 	sha1(buf, n, bufscore, nil);
21003be74836SDavid du Colombier 	if(memcmp(bufscore, fscore, VtScoreSize) == 0)
21013be74836SDavid du Colombier 		return 1;
21023be74836SDavid du Colombier 	return 0;
21033be74836SDavid du Colombier }
21043be74836SDavid du Colombier 
2105