Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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*
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
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
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
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
101
fileunlock(VacFile *f)
102
{
103
	wunlock(&f->lk);
104
}
105
 
106
static int
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
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
129
filemetalock(VacFile *f)
130
{
131
	assert(f->up != nil);
132
	wlock(&f->up->lk);
133
}
134
 
135
static void
136
filemetaunlock(VacFile *f)
137
{
138
	wunlock(&f->up->lk);
139
}
140
 
141
uvlong
142
vacfilegetid(VacFile *f)
143
{
144
	/* immutable */
145
	return f->qidoffset + f->dir.qid;
146
}
147
 
148
uvlong
149
vacfilegetqidoffset(VacFile *f)
150
{
151
	return f->qidoffset;
152
}
153
 
154
ulong
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
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
177
vacfileisdir(VacFile *f)
178
{
179
	/* immutable */
180
	return (f->dir.mode & ModeDir) != 0;
181
}
182
 
183
int
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*
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
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*
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*
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 *
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*
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*
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*
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
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
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
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
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
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*
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
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
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
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
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
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
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
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
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
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*
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
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
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
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
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
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
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
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
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 *
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
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*
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*
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
1859
vacfsmode(VacFs *fs)
1860
{
1861
	return fs->mode;
1862
}
1863
 
1864
VacFile*
1865
vacfsgetroot(VacFs *fs)
1866
{
1867
	return vacfileincref(fs->root);
1868
}
1869
 
1870
int
1871
vacfsgetblocksize(VacFs *fs)
1872
{
1873
	return fs->bsize;
1874
}
1875
 
1876
int
1877
vacfsgetscore(VacFs *fs, u8int *score)
1878
{
1879
	memmove(score, fs->score, VtScoreSize);
1880
	return 0;
1881
}
1882
 
1883
int
1884
_vacfsnextqid(VacFs *fs, uvlong *qid)
1885
{
1886
	++fs->qid;
1887
	*qid = fs->qid;
1888
	return 0;
1889
}
1890
 
1891
void
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
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
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 *
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
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
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
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