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