xref: /plan9/sys/src/cmd/fossil/file.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
1 #include "stdinc.h"
2 #include "dat.h"
3 #include "fns.h"
4 #include "error.h"
5 
6 /*
7  * locking order is upwards.  A thread can hold the lock for a File
8  * and then acquire the lock of its parent
9  */
10 
11 struct File {
12 	Fs	*fs;		/* immutable */
13 
14 	/* meta data for file: protected by the lk in the parent */
15 	int	ref;		/* holds this data structure up */
16 
17 	int	partial;	/* file was never really open */
18 	int	removed;	/* file has been removed */
19 	int	dirty;		/* dir is dirty with respect to meta data in block */
20 	u32int	boff;		/* block offset within msource for this file's meta data */
21 
22 	DirEntry dir;		/* meta data for this file */
23 
24 	File	*up;		/* parent file */
25 	File	*next;		/* sibling */
26 
27 	/* data for file */
28 	VtLock	*lk;		/* lock for the following */
29 	Source	*source;
30 	Source	*msource;	/* for directories: meta data for children */
31 	File	*down;		/* children */
32 
33 	int	mode;
34 	int	issnapshot;
35 };
36 
37 static int fileMetaFlush2(File*, char*);
38 static u32int fileMetaAlloc(File*, DirEntry*, u32int);
39 static int fileRLock(File*);
40 static void fileRUnlock(File*);
41 static int fileLock(File*);
42 static void fileUnlock(File*);
43 static void fileMetaLock(File*);
44 static void fileMetaUnlock(File*);
45 static void fileRAccess(File*);
46 static void fileWAccess(File*, char*);
47 
48 static File *
49 fileAlloc(Fs *fs)
50 {
51 	File *f;
52 
53 	f = vtMemAllocZ(sizeof(File));
54 	f->lk = vtLockAlloc();
55 	f->ref = 1;
56 	f->fs = fs;
57 	f->boff = NilBlock;
58 	f->mode = fs->mode;
59 	return f;
60 }
61 
62 static void
63 fileFree(File *f)
64 {
65 	sourceClose(f->source);
66 	vtLockFree(f->lk);
67 	sourceClose(f->msource);
68 	deCleanup(&f->dir);
69 
70 	memset(f, ~0, sizeof(File));
71 	vtMemFree(f);
72 }
73 
74 /*
75  * the file is locked already
76  * f->msource is unlocked
77  */
78 static File *
79 dirLookup(File *f, char *elem)
80 {
81 	int i;
82 	MetaBlock mb;
83 	MetaEntry me;
84 	Block *b;
85 	Source *meta;
86 	File *ff;
87 	u32int bo, nb;
88 
89 	meta = f->msource;
90 	b = nil;
91 	if(!sourceLock(meta, -1))
92 		return nil;
93 	nb = (sourceGetSize(meta)+meta->dsize-1)/meta->dsize;
94 	for(bo=0; bo<nb; bo++){
95 		b = sourceBlock(meta, bo, OReadOnly);
96 		if(b == nil)
97 			goto Err;
98 		if(!mbUnpack(&mb, b->data, meta->dsize))
99 			goto Err;
100 		if(mbSearch(&mb, elem, &i, &me)){
101 			ff = fileAlloc(f->fs);
102 			if(!deUnpack(&ff->dir, &me)){
103 				fileFree(ff);
104 				goto Err;
105 			}
106 			sourceUnlock(meta);
107 			blockPut(b);
108 			ff->boff = bo;
109 			ff->mode = f->mode;
110 			ff->issnapshot = f->issnapshot;
111 			return ff;
112 		}
113 
114 		blockPut(b);
115 		b = nil;
116 	}
117 	vtSetError(ENoFile);
118 	/* fall through */
119 Err:
120 	sourceUnlock(meta);
121 	blockPut(b);
122 	return nil;
123 }
124 
125 File *
126 fileRoot(Source *r)
127 {
128 	Block *b;
129 	Source *r0, *r1, *r2;
130 	MetaBlock mb;
131 	MetaEntry me;
132 	File *root, *mr;
133 	Fs *fs;
134 
135 	b = nil;
136 	root = nil;
137 	mr = nil;
138 	r1 = nil;
139 	r2 = nil;
140 
141 	fs = r->fs;
142 	if(!sourceLock(r, -1))
143 		return nil;
144 	r0 = sourceOpen(r, 0, fs->mode, 0);
145 	if(r0 == nil)
146 		goto Err;
147 	r1 = sourceOpen(r, 1, fs->mode, 0);
148 	if(r1 == nil)
149 		goto Err;
150 	r2 = sourceOpen(r, 2, fs->mode, 0);
151 	if(r2 == nil)
152 		goto Err;
153 
154 	mr = fileAlloc(fs);
155 	mr->msource = r2;
156 	r2 = nil;
157 
158 	root = fileAlloc(fs);
159 	root->boff = 0;
160 	root->up = mr;
161 	root->source = r0;
162 	r0 = nil;
163 	root->msource = r1;
164 	r1 = nil;
165 
166 	mr->down = root;
167 
168 	if(!sourceLock(mr->msource, -1))
169 		goto Err;
170 	b = sourceBlock(mr->msource, 0, OReadOnly);
171 	sourceUnlock(mr->msource);
172 	if(b == nil)
173 		goto Err;
174 
175 	if(!mbUnpack(&mb, b->data, mr->msource->dsize))
176 		goto Err;
177 
178 	meUnpack(&me, &mb, 0);
179 	if(!deUnpack(&root->dir, &me))
180 		goto Err;
181 	blockPut(b);
182 	sourceUnlock(r);
183 	fileRAccess(root);
184 
185 	return root;
186 Err:
187 	blockPut(b);
188 	if(r0)
189 		sourceClose(r0);
190 	if(r1)
191 		sourceClose(r1);
192 	if(r2)
193 		sourceClose(r2);
194 	if(mr)
195 		fileFree(mr);
196 	if(root)
197 		fileFree(root);
198 	sourceUnlock(r);
199 
200 	return nil;
201 }
202 
203 static Source *
204 fileOpenSource(File *f, u32int offset, u32int gen, int dir, uint mode, int issnapshot)
205 {
206 	Source *r;
207 
208 	if(!sourceLock(f->source, mode))
209 		return nil;
210 	r = sourceOpen(f->source, offset, mode, issnapshot);
211 	sourceUnlock(f->source);
212 	if(r == nil)
213 		return nil;
214 	if(r->gen != gen){
215 		vtSetError(ERemoved);
216 		goto Err;
217 	}
218 	if(r->dir != dir && r->mode != -1){
219 fprint(2, "fileOpenSource: dir mismatch %d %d\n", r->dir, dir);
220 		vtSetError(EBadMeta);
221 		goto Err;
222 	}
223 	return r;
224 Err:
225 	sourceClose(r);
226 	return nil;
227 }
228 
229 File *
230 _fileWalk(File *f, char *elem, int partial)
231 {
232 	File *ff;
233 
234 	fileRAccess(f);
235 
236 	if(elem[0] == 0){
237 		vtSetError(EBadPath);
238 		return nil;
239 	}
240 
241 	if(!fileIsDir(f)){
242 		vtSetError(ENotDir);
243 		return nil;
244 	}
245 
246 	if(strcmp(elem, ".") == 0){
247 		return fileIncRef(f);
248 	}
249 
250 	if(strcmp(elem, "..") == 0){
251 		if(fileIsRoot(f))
252 			return fileIncRef(f);
253 		return fileIncRef(f->up);
254 	}
255 
256 	if(!fileLock(f))
257 		return nil;
258 
259 	for(ff = f->down; ff; ff=ff->next){
260 		if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
261 			ff->ref++;
262 			goto Exit;
263 		}
264 	}
265 
266 	ff = dirLookup(f, elem);
267 	if(ff == nil)
268 		goto Err;
269 
270 	if(ff->dir.mode & ModeSnapshot){
271 		ff->mode = OReadOnly;
272 		ff->issnapshot = 1;
273 	}
274 
275 	if(partial){
276 		/*
277 		 * Do nothing.  We're opening this file only so we can clri it.
278 		 * Usually the sources can't be opened, hence we won't even bother.
279 		 * Be VERY careful with the returned file.  If you hand it to a routine
280 		 * expecting ff->source and/or ff->msource to be non-nil, we're
281 		 * likely to dereference nil.  FileClri should be the only routine
282 		 * setting partial.
283 		 */
284 		ff->partial = 1;
285 	}else if(ff->dir.mode & ModeDir){
286 		ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode, ff->issnapshot);
287 		ff->msource = fileOpenSource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode, ff->issnapshot);
288 		if(ff->source == nil || ff->msource == nil)
289 			goto Err;
290 	}else{
291 		ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode, ff->issnapshot);
292 		if(ff->source == nil)
293 			goto Err;
294 	}
295 
296 	/* link in and up parent ref count */
297 	ff->next = f->down;
298 	f->down = ff;
299 	ff->up = f;
300 	fileIncRef(f);
301 Exit:
302 	fileUnlock(f);
303 	return ff;
304 Err:
305 	fileUnlock(f);
306 	if(ff != nil)
307 		fileDecRef(ff);
308 	return nil;
309 }
310 
311 File *
312 fileWalk(File *f, char *elem)
313 {
314 	return _fileWalk(f, elem, 0);
315 }
316 
317 File *
318 _fileOpen(Fs *fs, char *path, int partial)
319 {
320 	File *f, *ff;
321 	char *p, elem[VtMaxStringSize], *opath;
322 	int n;
323 
324 	f = fs->file;
325 	fileIncRef(f);
326 	opath = path;
327 	while(*path != 0){
328 		for(p = path; *p && *p != '/'; p++)
329 			;
330 		n = p - path;
331 		if(n > 0){
332 			if(n > VtMaxStringSize){
333 				vtSetError("%s: element too long", EBadPath);
334 				goto Err;
335 			}
336 			memmove(elem, path, n);
337 			elem[n] = 0;
338 			ff = _fileWalk(f, elem, partial && *p=='\0');
339 			if(ff == nil){
340 				vtSetError("%.*s: %R", utfnlen(opath, p-opath), opath);
341 				goto Err;
342 			}
343 			fileDecRef(f);
344 			f = ff;
345 		}
346 		if(*p == '/')
347 			p++;
348 		path = p;
349 	}
350 	return f;
351 Err:
352 	fileDecRef(f);
353 	return nil;
354 }
355 
356 File*
357 fileOpen(Fs *fs, char *path)
358 {
359 	return _fileOpen(fs, path, 0);
360 }
361 
362 static void
363 fileSetTmp(File *f, int istmp)
364 {
365 	int i;
366 	Entry e;
367 	Source *r;
368 
369 	for(i=0; i<2; i++){
370 		if(i==0)
371 			r = f->source;
372 		else
373 			r = f->msource;
374 		if(r == nil)
375 			continue;
376 		if(!sourceGetEntry(r, &e)){
377 			fprint(2, "sourceGetEntry failed (cannot happen): %r\n");
378 			continue;
379 		}
380 		if(istmp)
381 			e.flags |= VtEntryNoArchive;
382 		else
383 			e.flags &= ~VtEntryNoArchive;
384 		if(!sourceSetEntry(r, &e)){
385 			fprint(2, "sourceSetEntry failed (cannot happen): %r\n");
386 			continue;
387 		}
388 	}
389 }
390 
391 File *
392 fileCreate(File *f, char *elem, ulong mode, char *uid)
393 {
394 	File *ff;
395 	DirEntry *dir;
396 	Source *pr, *r, *mr;
397 	int isdir;
398 
399 	if(!fileLock(f))
400 		return nil;
401 
402 	r = nil;
403 	mr = nil;
404 	for(ff = f->down; ff; ff=ff->next){
405 		if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
406 			ff = nil;
407 			vtSetError(EExists);
408 			goto Err1;
409 		}
410 	}
411 
412 	ff = dirLookup(f, elem);
413 	if(ff != nil){
414 		vtSetError(EExists);
415 		goto Err1;
416 	}
417 
418 	pr = f->source;
419 	if(pr->mode != OReadWrite){
420 		vtSetError(EReadOnly);
421 		goto Err1;
422 	}
423 
424 	if(!sourceLock2(f->source, f->msource, -1))
425 		goto Err1;
426 
427 	ff = fileAlloc(f->fs);
428 	isdir = mode & ModeDir;
429 
430 	r = sourceCreate(pr, pr->dsize, isdir, 0);
431 	if(r == nil)
432 		goto Err;
433 	if(isdir){
434 		mr = sourceCreate(pr, pr->dsize, 0, r->offset);
435 		if(mr == nil)
436 			goto Err;
437 	}
438 
439 	dir = &ff->dir;
440 	dir->elem = vtStrDup(elem);
441 	dir->entry = r->offset;
442 	dir->gen = r->gen;
443 	if(isdir){
444 		dir->mentry = mr->offset;
445 		dir->mgen = mr->gen;
446 	}
447 	dir->size = 0;
448 	if(!fsNextQid(f->fs, &dir->qid))
449 		goto Err;
450 	dir->uid = vtStrDup(uid);
451 	dir->gid = vtStrDup(f->dir.gid);
452 	dir->mid = vtStrDup(uid);
453 	dir->mtime = time(0L);
454 	dir->mcount = 0;
455 	dir->ctime = dir->mtime;
456 	dir->atime = dir->mtime;
457 	dir->mode = mode;
458 
459 	ff->boff = fileMetaAlloc(f, dir, 0);
460 	if(ff->boff == NilBlock)
461 		goto Err;
462 
463 	sourceUnlock(f->source);
464 	sourceUnlock(f->msource);
465 
466 	ff->source = r;
467 	ff->msource = mr;
468 
469 	if(mode&ModeTemporary){
470 		if(!sourceLock2(r, mr, -1))
471 			goto Err1;
472 		fileSetTmp(ff, 1);
473 		sourceUnlock(r);
474 		if(mr)
475 			sourceUnlock(mr);
476 	}
477 
478 	/* committed */
479 
480 	/* link in and up parent ref count */
481 	ff->next = f->down;
482 	f->down = ff;
483 	ff->up = f;
484 	fileIncRef(f);
485 
486 	fileWAccess(f, uid);
487 
488 	fileUnlock(f);
489 	return ff;
490 
491 Err:
492 	sourceUnlock(f->source);
493 	sourceUnlock(f->msource);
494 Err1:
495 	if(r){
496 		sourceLock(r, -1);
497 		sourceRemove(r);
498 	}
499 	if(mr){
500 		sourceLock(mr, -1);
501 		sourceRemove(mr);
502 	}
503 	if(ff)
504 		fileDecRef(ff);
505 	fileUnlock(f);
506 	return 0;
507 }
508 
509 int
510 fileRead(File *f, void *buf, int cnt, vlong offset)
511 {
512 	Source *s;
513 	uvlong size;
514 	u32int bn;
515 	int off, dsize, n, nn;
516 	Block *b;
517 	uchar *p;
518 
519 if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
520 
521 	if(!fileRLock(f))
522 		return -1;
523 
524 	if(offset < 0){
525 		vtSetError(EBadOffset);
526 		goto Err1;
527 	}
528 
529 	fileRAccess(f);
530 
531 	if(!sourceLock(f->source, OReadOnly))
532 		goto Err1;
533 
534 	s = f->source;
535 	dsize = s->dsize;
536 	size = sourceGetSize(s);
537 
538 	if(offset >= size)
539 		offset = size;
540 
541 	if(cnt > size-offset)
542 		cnt = size-offset;
543 	bn = offset/dsize;
544 	off = offset%dsize;
545 	p = buf;
546 	while(cnt > 0){
547 		b = sourceBlock(s, bn, OReadOnly);
548 		if(b == nil)
549 			goto Err;
550 		n = cnt;
551 		if(n > dsize-off)
552 			n = dsize-off;
553 		nn = dsize-off;
554 		if(nn > n)
555 			nn = n;
556 		memmove(p, b->data+off, nn);
557 		memset(p+nn, 0, nn-n);
558 		off = 0;
559 		bn++;
560 		cnt -= n;
561 		p += n;
562 		blockPut(b);
563 	}
564 	sourceUnlock(s);
565 	fileRUnlock(f);
566 	return p-(uchar*)buf;
567 
568 Err:
569 	sourceUnlock(s);
570 Err1:
571 	fileRUnlock(f);
572 	return -1;
573 }
574 
575 /*
576  * Changes the file block bn to be the given block score.
577  * Very sneaky.  Only used by flfmt.
578  */
579 int
580 fileMapBlock(File *f, ulong bn, uchar score[VtScoreSize], ulong tag)
581 {
582 	Block *b;
583 	Entry e;
584 	Source *s;
585 
586 	if(!fileLock(f))
587 		return 0;
588 
589 	s = nil;
590 	if(f->dir.mode & ModeDir){
591 		vtSetError(ENotFile);
592 		goto Err;
593 	}
594 
595 	if(f->source->mode != OReadWrite){
596 		vtSetError(EReadOnly);
597 		goto Err;
598 	}
599 
600 	if(!sourceLock(f->source, -1))
601 		goto Err;
602 
603 	s = f->source;
604 	b = _sourceBlock(s, bn, OReadWrite, 1, tag);
605 	if(b == nil)
606 		goto Err;
607 
608 	if(!sourceGetEntry(s, &e))
609 		goto Err;
610 	if(b->l.type == BtDir){
611 		memmove(e.score, score, VtScoreSize);
612 		assert(e.tag == tag || e.tag == 0);
613 		e.tag = tag;
614 		e.flags |= VtEntryLocal;
615 		entryPack(&e, b->data, f->source->offset % f->source->epb);
616 	}else
617 		memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize);
618 	blockDirty(b);
619 	blockPut(b);
620 	sourceUnlock(s);
621 	fileUnlock(f);
622 	return 1;
623 
624 Err:
625 	if(s)
626 		sourceUnlock(s);
627 	fileUnlock(f);
628 	return 0;
629 }
630 
631 int
632 fileSetSize(File *f, uvlong size)
633 {
634 	int r;
635 
636 	if(!fileLock(f))
637 		return 0;
638 	r = 0;
639 	if(f->dir.mode & ModeDir){
640 		vtSetError(ENotFile);
641 		goto Err;
642 	}
643 	if(f->source->mode != OReadWrite){
644 		vtSetError(EReadOnly);
645 		goto Err;
646 	}
647 	if(!sourceLock(f->source, -1))
648 		goto Err;
649 	r = sourceSetSize(f->source, size);
650 	sourceUnlock(f->source);
651 Err:
652 	fileUnlock(f);
653 	return r;
654 }
655 
656 int
657 fileWrite(File *f, void *buf, int cnt, vlong offset, char *uid)
658 {
659 	Source *s;
660 	ulong bn;
661 	int off, dsize, n;
662 	Block *b;
663 	uchar *p;
664 	vlong eof;
665 
666 if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
667 
668 	if(!fileLock(f))
669 		return -1;
670 
671 	s = nil;
672 	if(f->dir.mode & ModeDir){
673 		vtSetError(ENotFile);
674 		goto Err;
675 	}
676 
677 	if(f->source->mode != OReadWrite){
678 		vtSetError(EReadOnly);
679 		goto Err;
680 	}
681 	if(offset < 0){
682 		vtSetError(EBadOffset);
683 		goto Err;
684 	}
685 
686 	fileWAccess(f, uid);
687 
688 	if(!sourceLock(f->source, -1))
689 		goto Err;
690 	s = f->source;
691 	dsize = s->dsize;
692 
693 	eof = sourceGetSize(s);
694 	if(f->dir.mode & ModeAppend)
695 		offset = eof;
696 	bn = offset/dsize;
697 	off = offset%dsize;
698 	p = buf;
699 	while(cnt > 0){
700 		n = cnt;
701 		if(n > dsize-off)
702 			n = dsize-off;
703 		b = sourceBlock(s, bn, n<dsize?OReadWrite:OOverWrite);
704 		if(b == nil){
705 			if(offset > eof)
706 				sourceSetSize(s, offset);
707 			goto Err;
708 		}
709 		memmove(b->data+off, p, n);
710 		off = 0;
711 		cnt -= n;
712 		p += n;
713 		offset += n;
714 		bn++;
715 		blockDirty(b);
716 		blockPut(b);
717 	}
718 	if(offset > eof && !sourceSetSize(s, offset))
719 		goto Err;
720 	sourceUnlock(s);
721 	fileUnlock(f);
722 	return p-(uchar*)buf;
723 Err:
724 	if(s)
725 		sourceUnlock(s);
726 	fileUnlock(f);
727 	return -1;
728 }
729 
730 int
731 fileGetDir(File *f, DirEntry *dir)
732 {
733 	if(!fileRLock(f))
734 		return 0;
735 
736 	fileMetaLock(f);
737 	deCopy(dir, &f->dir);
738 	fileMetaUnlock(f);
739 
740 	if(!fileIsDir(f)){
741 		if(!sourceLock(f->source, OReadOnly)){
742 			fileRUnlock(f);
743 			return 0;
744 		}
745 		dir->size = sourceGetSize(f->source);
746 		sourceUnlock(f->source);
747 	}
748 	fileRUnlock(f);
749 
750 	return 1;
751 }
752 
753 int
754 fileTruncate(File *f, char *uid)
755 {
756 	if(fileIsDir(f)){
757 		vtSetError(ENotFile);
758 		return 0;
759 	}
760 
761 	if(!fileLock(f))
762 		return 0;
763 
764 	if(f->source->mode != OReadWrite){
765 		vtSetError(EReadOnly);
766 		fileUnlock(f);
767 		return 0;
768 	}
769 	if(!sourceLock(f->source, -1)){
770 		fileUnlock(f);
771 		return 0;
772 	}
773 	if(!sourceTruncate(f->source)){
774 		sourceUnlock(f->source);
775 		fileUnlock(f);
776 		return 0;
777 	}
778 	sourceUnlock(f->source);
779 	fileUnlock(f);
780 
781 	fileWAccess(f, uid);
782 
783 	return 1;
784 }
785 
786 int
787 fileSetDir(File *f, DirEntry *dir, char *uid)
788 {
789 	File *ff;
790 	char *oelem;
791 	u32int mask;
792 	u64int size;
793 
794 	/* can not set permissions for the root */
795 	if(fileIsRoot(f)){
796 		vtSetError(ERoot);
797 		return 0;
798 	}
799 
800 	if(!fileLock(f))
801 		return 0;
802 
803 	if(f->source->mode != OReadWrite){
804 		vtSetError(EReadOnly);
805 		fileUnlock(f);
806 		return 0;
807 	}
808 
809 	fileMetaLock(f);
810 
811 	/* check new name does not already exist */
812 	if(strcmp(f->dir.elem, dir->elem) != 0){
813 		for(ff = f->up->down; ff; ff=ff->next){
814 			if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
815 				vtSetError(EExists);
816 				goto Err;
817 			}
818 		}
819 
820 		ff = dirLookup(f->up, dir->elem);
821 		if(ff != nil){
822 			fileDecRef(ff);
823 			vtSetError(EExists);
824 			goto Err;
825 		}
826 	}
827 
828 	if(!sourceLock2(f->source, f->msource, -1))
829 		goto Err;
830 	if(!fileIsDir(f)){
831 		size = sourceGetSize(f->source);
832 		if(size != dir->size){
833 			if(!sourceSetSize(f->source, dir->size)){
834 				sourceUnlock(f->source);
835 				if(f->msource)
836 					sourceUnlock(f->msource);
837 				goto Err;
838 			}
839 			/* commited to changing it now */
840 		}
841 	}
842 	/* commited to changing it now */
843 	if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
844 		fileSetTmp(f, dir->mode&ModeTemporary);
845 	sourceUnlock(f->source);
846 	if(f->msource)
847 		sourceUnlock(f->msource);
848 
849 	oelem = nil;
850 	if(strcmp(f->dir.elem, dir->elem) != 0){
851 		oelem = f->dir.elem;
852 		f->dir.elem = vtStrDup(dir->elem);
853 	}
854 
855 	if(strcmp(f->dir.uid, dir->uid) != 0){
856 		vtMemFree(f->dir.uid);
857 		f->dir.uid = vtStrDup(dir->uid);
858 	}
859 
860 	if(strcmp(f->dir.gid, dir->gid) != 0){
861 		vtMemFree(f->dir.gid);
862 		f->dir.gid = vtStrDup(dir->gid);
863 	}
864 
865 	f->dir.mtime = dir->mtime;
866 	f->dir.atime = dir->atime;
867 
868 //fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
869 	mask = ~(ModeDir|ModeSnapshot);
870 	f->dir.mode &= ~mask;
871 	f->dir.mode |= mask & dir->mode;
872 	f->dirty = 1;
873 //fprint(2, "->%x\n", f->dir.mode);
874 
875 	fileMetaFlush2(f, oelem);
876 	vtMemFree(oelem);
877 
878 	fileMetaUnlock(f);
879 	fileUnlock(f);
880 
881 	fileWAccess(f->up, uid);
882 
883 	return 1;
884 Err:
885 	fileMetaUnlock(f);
886 	fileUnlock(f);
887 	return 0;
888 }
889 
890 int
891 fileSetQidSpace(File *f, u64int offset, u64int max)
892 {
893 	int ret;
894 
895 	if(!fileLock(f))
896 		return 0;
897 	fileMetaLock(f);
898 	f->dir.qidSpace = 1;
899 	f->dir.qidOffset = offset;
900 	f->dir.qidMax = max;
901 	ret = fileMetaFlush2(f, nil)>=0;
902 	fileMetaUnlock(f);
903 	fileUnlock(f);
904 	return ret;
905 }
906 
907 
908 uvlong
909 fileGetId(File *f)
910 {
911 	/* immutable */
912 	return f->dir.qid;
913 }
914 
915 ulong
916 fileGetMcount(File *f)
917 {
918 	ulong mcount;
919 
920 	fileMetaLock(f);
921 	mcount = f->dir.mcount;
922 	fileMetaUnlock(f);
923 	return mcount;
924 }
925 
926 ulong
927 fileGetMode(File *f)
928 {
929 	ulong mode;
930 
931 	fileMetaLock(f);
932 	mode = f->dir.mode;
933 	fileMetaUnlock(f);
934 	return mode;
935 }
936 
937 int
938 fileIsDir(File *f)
939 {
940 	/* immutable */
941 	return (f->dir.mode & ModeDir) != 0;
942 }
943 
944 int
945 fileIsRoot(File *f)
946 {
947 	return f == f->fs->file;
948 }
949 
950 int
951 fileIsRoFs(File *f)
952 {
953 	return f->fs->mode == OReadOnly;
954 }
955 
956 int
957 fileGetSize(File *f, uvlong *size)
958 {
959 	if(!fileRLock(f))
960 		return 0;
961 	if(!sourceLock(f->source, OReadOnly)){
962 		fileRUnlock(f);
963 		return 0;
964 	}
965 	*size = sourceGetSize(f->source);
966 	sourceUnlock(f->source);
967 	fileRUnlock(f);
968 
969 	return 1;
970 }
971 
972 int
973 fileMetaFlush(File *f, int rec)
974 {
975 	File **kids, *p;
976 	int nkids;
977 	int i, rv;
978 
979 	fileMetaLock(f);
980 	rv = fileMetaFlush2(f, nil);
981 	fileMetaUnlock(f);
982 
983 	if(!rec || !fileIsDir(f))
984 		return rv;
985 
986 	if(!fileLock(f))
987 		return rv;
988 	nkids = 0;
989 	for(p=f->down; p; p=p->next)
990 		nkids++;
991 	kids = vtMemAlloc(nkids*sizeof(File*));
992 	i = 0;
993 	for(p=f->down; p; p=p->next){
994 		kids[i++] = p;
995 		p->ref++;
996 	}
997 	fileUnlock(f);
998 
999 	for(i=0; i<nkids; i++){
1000 		rv |= fileMetaFlush(kids[i], 1);
1001 		fileDecRef(kids[i]);
1002 	}
1003 	vtMemFree(kids);
1004 	return rv;
1005 }
1006 
1007 /* assumes metaLock is held */
1008 static int
1009 fileMetaFlush2(File *f, char *oelem)
1010 {
1011 	File *fp;
1012 	Block *b, *bb;
1013 	MetaBlock mb;
1014 	MetaEntry me, me2;
1015 	int i, n;
1016 	u32int boff;
1017 
1018 	if(!f->dirty)
1019 		return 0;
1020 
1021 	if(oelem == nil)
1022 		oelem = f->dir.elem;
1023 
1024 //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem);
1025 
1026 	fp = f->up;
1027 
1028 	if(!sourceLock(fp->msource, -1))
1029 		return -1;
1030 	/* can happen if source is clri'ed out from under us */
1031 	if(f->boff == NilBlock)
1032 		goto Err1;
1033 	b = sourceBlock(fp->msource, f->boff, OReadWrite);
1034 	if(b == nil)
1035 		goto Err1;
1036 
1037 	if(!mbUnpack(&mb, b->data, fp->msource->dsize))
1038 		goto Err;
1039 	if(!mbSearch(&mb, oelem, &i, &me))
1040 		goto Err;
1041 
1042 	n = deSize(&f->dir);
1043 if(0)fprint(2, "old size %d new size %d\n", me.size, n);
1044 
1045 	if(mbResize(&mb, &me, n)){
1046 		/* fits in the block */
1047 		mbDelete(&mb, i);
1048 		if(strcmp(f->dir.elem, oelem) != 0)
1049 			mbSearch(&mb, f->dir.elem, &i, &me2);
1050 		dePack(&f->dir, &me);
1051 		mbInsert(&mb, i, &me);
1052 		mbPack(&mb);
1053 		blockDirty(b);
1054 		blockPut(b);
1055 		sourceUnlock(fp->msource);
1056 		f->dirty = 0;
1057 
1058 		return 1;
1059 	}
1060 
1061 	/*
1062 	 * moving entry to another block
1063 	 * it is feasible for the fs to crash leaving two copies
1064 	 * of the directory entry.  This is just too much work to
1065 	 * fix.  Given that entries are only allocated in a block that
1066 	 * is less than PercentageFull, most modifications of meta data
1067 	 * will fit within the block.  i.e. this code should almost
1068 	 * never be executed.
1069 	 */
1070 	boff = fileMetaAlloc(fp, &f->dir, f->boff+1);
1071 	if(boff == NilBlock){
1072 		/* mbResize might have modified block */
1073 		mbPack(&mb);
1074 		blockDirty(b);
1075 		goto Err;
1076 	}
1077 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
1078 	f->boff = boff;
1079 
1080 	/* make sure deletion goes to disk after new entry */
1081 	bb = sourceBlock(fp->msource, f->boff, OReadWrite);
1082 	mbDelete(&mb, i);
1083 	mbPack(&mb);
1084 	blockDependency(b, bb, -1, nil, nil);
1085 	blockPut(bb);
1086 	blockDirty(b);
1087 	blockPut(b);
1088 	sourceUnlock(fp->msource);
1089 
1090 	f->dirty = 0;
1091 
1092 	return 1;
1093 
1094 Err:
1095 	blockPut(b);
1096 Err1:
1097 	sourceUnlock(fp->msource);
1098 	return -1;
1099 }
1100 
1101 static int
1102 fileMetaRemove(File *f, char *uid)
1103 {
1104 	Block *b;
1105 	MetaBlock mb;
1106 	MetaEntry me;
1107 	int i;
1108 	File *up;
1109 
1110 	up = f->up;
1111 
1112 	fileWAccess(up, uid);
1113 
1114 	fileMetaLock(f);
1115 
1116 	sourceLock(up->msource, OReadWrite);
1117 	b = sourceBlock(up->msource, f->boff, OReadWrite);
1118 	if(b == nil)
1119 		goto Err;
1120 
1121 	if(!mbUnpack(&mb, b->data, up->msource->dsize))
1122 {
1123 fprint(2, "U\n");
1124 		goto Err;
1125 }
1126 	if(!mbSearch(&mb, f->dir.elem, &i, &me))
1127 {
1128 fprint(2, "S\n");
1129 		goto Err;
1130 }
1131 	mbDelete(&mb, i);
1132 	mbPack(&mb);
1133 	sourceUnlock(up->msource);
1134 
1135 	blockDirty(b);
1136 	blockPut(b);
1137 
1138 	f->removed = 1;
1139 	f->boff = NilBlock;
1140 	f->dirty = 0;
1141 
1142 	fileMetaUnlock(f);
1143 	return 1;
1144 
1145 Err:
1146 	sourceUnlock(up->msource);
1147 	blockPut(b);
1148 	fileMetaUnlock(f);
1149 	return 0;
1150 }
1151 
1152 /* assume file is locked, assume f->msource is locked */
1153 static int
1154 fileCheckEmpty(File *f)
1155 {
1156 	u32int i, n;
1157 	Block *b;
1158 	MetaBlock mb;
1159 	Source *r;
1160 
1161 	r = f->msource;
1162 	n = (sourceGetSize(r)+r->dsize-1)/r->dsize;
1163 	for(i=0; i<n; i++){
1164 		b = sourceBlock(r, i, OReadOnly);
1165 		if(b == nil)
1166 			goto Err;
1167 		if(!mbUnpack(&mb, b->data, r->dsize))
1168 			goto Err;
1169 		if(mb.nindex > 0){
1170 			vtSetError(ENotEmpty);
1171 			goto Err;
1172 		}
1173 		blockPut(b);
1174 	}
1175 	return 1;
1176 Err:
1177 	blockPut(b);
1178 	return 0;
1179 }
1180 
1181 int
1182 fileRemove(File *f, char *uid)
1183 {
1184 	File *ff;
1185 
1186 	/* can not remove the root */
1187 	if(fileIsRoot(f)){
1188 		vtSetError(ERoot);
1189 		return 0;
1190 	}
1191 
1192 	if(!fileLock(f))
1193 		return 0;
1194 
1195 	if(f->source->mode != OReadWrite){
1196 		vtSetError(EReadOnly);
1197 		goto Err1;
1198 	}
1199 	if(!sourceLock2(f->source, f->msource, -1))
1200 		goto Err1;
1201 	if(fileIsDir(f) && !fileCheckEmpty(f))
1202 		goto Err;
1203 
1204 	for(ff=f->down; ff; ff=ff->next)
1205 		assert(ff->removed);
1206 
1207 	sourceRemove(f->source);
1208 	f->source = nil;
1209 	if(f->msource){
1210 		sourceRemove(f->msource);
1211 		f->msource = nil;
1212 	}
1213 
1214 	fileUnlock(f);
1215 
1216 	if(!fileMetaRemove(f, uid))
1217 		return 0;
1218 
1219 	return 1;
1220 
1221 Err:
1222 	sourceUnlock(f->source);
1223 	if(f->msource)
1224 		sourceUnlock(f->msource);
1225 Err1:
1226 	fileUnlock(f);
1227 	return 0;
1228 }
1229 
1230 static int
1231 clri(File *f, char *uid)
1232 {
1233 	int r;
1234 
1235 	if(f == nil)
1236 		return 0;
1237 	if(f->up->source->mode != OReadWrite){
1238 		vtSetError(EReadOnly);
1239 		fileDecRef(f);
1240 		return 0;
1241 	}
1242 	r = fileMetaRemove(f, uid);
1243 	fileDecRef(f);
1244 	return r;
1245 }
1246 
1247 int
1248 fileClriPath(Fs *fs, char *path, char *uid)
1249 {
1250 	return clri(_fileOpen(fs, path, 1), uid);
1251 }
1252 
1253 int
1254 fileClri(File *dir, char *elem, char *uid)
1255 {
1256 	return clri(_fileWalk(dir, elem, 1), uid);
1257 }
1258 
1259 File *
1260 fileIncRef(File *vf)
1261 {
1262 	fileMetaLock(vf);
1263 	assert(vf->ref > 0);
1264 	vf->ref++;
1265 	fileMetaUnlock(vf);
1266 	return vf;
1267 }
1268 
1269 int
1270 fileDecRef(File *f)
1271 {
1272 	File *p, *q, **qq;
1273 
1274 	if(f->up == nil){
1275 		/* never linked in */
1276 		assert(f->ref == 1);
1277 		fileFree(f);
1278 		return 1;
1279 	}
1280 
1281 	fileMetaLock(f);
1282 	f->ref--;
1283 	if(f->ref > 0){
1284 		fileMetaUnlock(f);
1285 		return 0;
1286 	}
1287 	assert(f->ref == 0);
1288 	assert(f->down == nil);
1289 
1290 	fileMetaFlush2(f, nil);
1291 
1292 	p = f->up;
1293 	qq = &p->down;
1294 	for(q = *qq; q; q = *qq){
1295 		if(q == f)
1296 			break;
1297 		qq = &q->next;
1298 	}
1299 	assert(q != nil);
1300 	*qq = f->next;
1301 
1302 	fileMetaUnlock(f);
1303 	fileFree(f);
1304 
1305 	fileDecRef(p);
1306 	return 1;
1307 }
1308 
1309 File *
1310 fileGetParent(File *f)
1311 {
1312 	if(fileIsRoot(f))
1313 		return fileIncRef(f);
1314 	return fileIncRef(f->up);
1315 }
1316 
1317 DirEntryEnum *
1318 deeOpen(File *f)
1319 {
1320 	DirEntryEnum *dee;
1321 	File *p;
1322 
1323 	if(!fileIsDir(f)){
1324 		vtSetError(ENotDir);
1325 		fileDecRef(f);
1326 		return nil;
1327 	}
1328 
1329 	/* flush out meta data */
1330 	if(!fileLock(f))
1331 		return nil;
1332 	for(p=f->down; p; p=p->next)
1333 		fileMetaFlush2(p, nil);
1334 	fileUnlock(f);
1335 
1336 	dee = vtMemAllocZ(sizeof(DirEntryEnum));
1337 	dee->file = fileIncRef(f);
1338 
1339 	return dee;
1340 }
1341 
1342 static int
1343 dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
1344 {
1345 	Block *b;
1346 	ulong bn;
1347 	Entry e;
1348 	int epb;
1349 
1350 	epb = s->dsize/VtEntrySize;
1351 	bn = elem/epb;
1352 	elem -= bn*epb;
1353 
1354 	b = sourceBlock(s, bn, OReadOnly);
1355 	if(b == nil)
1356 		goto Err;
1357 	if(!entryUnpack(&e, b->data, elem))
1358 		goto Err;
1359 
1360 	/* hanging entries are returned as zero size */
1361 	if(!(e.flags & VtEntryActive) || e.gen != gen)
1362 		*size = 0;
1363 	else
1364 		*size = e.size;
1365 	blockPut(b);
1366 	return 1;
1367 
1368 Err:
1369 	blockPut(b);
1370 	return 0;
1371 }
1372 
1373 static int
1374 deeFill(DirEntryEnum *dee)
1375 {
1376 	int i, n;
1377 	Source *meta, *source;
1378 	MetaBlock mb;
1379 	MetaEntry me;
1380 	File *f;
1381 	Block *b;
1382 	DirEntry *de;
1383 
1384 	/* clean up first */
1385 	for(i=dee->i; i<dee->n; i++)
1386 		deCleanup(dee->buf+i);
1387 	vtMemFree(dee->buf);
1388 	dee->buf = nil;
1389 	dee->i = 0;
1390 	dee->n = 0;
1391 
1392 	f = dee->file;
1393 
1394 	source = f->source;
1395 	meta = f->msource;
1396 
1397 	b = sourceBlock(meta, dee->boff, OReadOnly);
1398 	if(b == nil)
1399 		goto Err;
1400 	if(!mbUnpack(&mb, b->data, meta->dsize))
1401 		goto Err;
1402 
1403 	n = mb.nindex;
1404 	dee->buf = vtMemAlloc(n * sizeof(DirEntry));
1405 
1406 	for(i=0; i<n; i++){
1407 		de = dee->buf + i;
1408 		meUnpack(&me, &mb, i);
1409 		if(!deUnpack(de, &me))
1410 			goto Err;
1411 		dee->n++;
1412 		if(!(de->mode & ModeDir))
1413 		if(!dirEntrySize(source, de->entry, de->gen, &de->size))
1414 			goto Err;
1415 	}
1416 	dee->boff++;
1417 	blockPut(b);
1418 	return 1;
1419 Err:
1420 	blockPut(b);
1421 	return 0;
1422 }
1423 
1424 int
1425 deeRead(DirEntryEnum *dee, DirEntry *de)
1426 {
1427 	int ret, didread;
1428 	File *f;
1429 	u32int nb;
1430 
1431 	if(dee == nil){
1432 		vtSetError("cannot happen in deeRead");
1433 		return -1;
1434 	}
1435 
1436 	f = dee->file;
1437 	if(!fileRLock(f))
1438 		return -1;
1439 
1440 	if(!sourceLock2(f->source, f->msource, OReadOnly)){
1441 		fileRUnlock(f);
1442 		return -1;
1443 	}
1444 
1445 	nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
1446 
1447 	didread = 0;
1448 	while(dee->i >= dee->n){
1449 		if(dee->boff >= nb){
1450 			ret = 0;
1451 			goto Return;
1452 		}
1453 		didread = 1;
1454 		if(!deeFill(dee)){
1455 			ret = -1;
1456 			goto Return;
1457 		}
1458 	}
1459 
1460 	memmove(de, dee->buf + dee->i, sizeof(DirEntry));
1461 	dee->i++;
1462 	ret = 1;
1463 
1464 Return:
1465 	sourceUnlock(f->source);
1466 	sourceUnlock(f->msource);
1467 	fileRUnlock(f);
1468 
1469 	if(didread)
1470 		fileRAccess(f);
1471 	return ret;
1472 }
1473 
1474 void
1475 deeClose(DirEntryEnum *dee)
1476 {
1477 	int i;
1478 	if(dee == nil)
1479 		return;
1480 	for(i=dee->i; i<dee->n; i++)
1481 		deCleanup(dee->buf+i);
1482 	vtMemFree(dee->buf);
1483 	fileDecRef(dee->file);
1484 	vtMemFree(dee);
1485 }
1486 
1487 /*
1488  * caller must lock f->source and f->msource
1489  * caller must NOT lock the source and msource
1490  * referenced by dir.
1491  */
1492 static u32int
1493 fileMetaAlloc(File *f, DirEntry *dir, u32int start)
1494 {
1495 	u32int nb, bo;
1496 	Block *b, *bb;
1497 	MetaBlock mb;
1498 	int nn;
1499 	uchar *p;
1500 	int i, n, epb;
1501 	MetaEntry me;
1502 	Source *s, *ms;
1503 
1504 	s = f->source;
1505 	ms = f->msource;
1506 
1507 	n = deSize(dir);
1508 	nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize;
1509 	b = nil;
1510 	if(start > nb)
1511 		start = nb;
1512 	for(bo=start; bo<nb; bo++){
1513 		b = sourceBlock(ms, bo, OReadWrite);
1514 		if(b == nil)
1515 			goto Err;
1516 		if(!mbUnpack(&mb, b->data, ms->dsize))
1517 			goto Err;
1518 		nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
1519 		if(n <= nn && mb.nindex < mb.maxindex)
1520 			break;
1521 		blockPut(b);
1522 		b = nil;
1523 	}
1524 
1525 	/* add block to meta file */
1526 	if(b == nil){
1527 		b = sourceBlock(ms, bo, OReadWrite);
1528 		if(b == nil)
1529 			goto Err;
1530 		sourceSetSize(ms, (nb+1)*ms->dsize);
1531 		mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
1532 	}
1533 
1534 	p = mbAlloc(&mb, n);
1535 	if(p == nil){
1536 		/* mbAlloc might have changed block */
1537 		mbPack(&mb);
1538 		blockDirty(b);
1539 		vtSetError(EBadMeta);
1540 		goto Err;
1541 	}
1542 
1543 	mbSearch(&mb, dir->elem, &i, &me);
1544 	assert(me.p == nil);
1545 	me.p = p;
1546 	me.size = n;
1547 	dePack(dir, &me);
1548 	mbInsert(&mb, i, &me);
1549 	mbPack(&mb);
1550 
1551 	/* meta block depends on super block for qid ... */
1552 	bb = cacheLocal(b->c, PartSuper, 0, OReadOnly);
1553 	blockDependency(b, bb, -1, nil, nil);
1554 	blockPut(bb);
1555 
1556 	/* ... and one or two dir entries */
1557 	epb = s->dsize/VtEntrySize;
1558 	bb = sourceBlock(s, dir->entry/epb, OReadOnly);
1559 	blockDependency(b, bb, -1, nil, nil);
1560 	blockPut(bb);
1561 	if(dir->mode & ModeDir){
1562 		bb = sourceBlock(s, dir->mentry/epb, OReadOnly);
1563 		blockDependency(b, bb, -1, nil, nil);
1564 		blockPut(bb);
1565 	}
1566 
1567 	blockDirty(b);
1568 	blockPut(b);
1569 	return bo;
1570 Err:
1571 	blockPut(b);
1572 	return NilBlock;
1573 }
1574 
1575 static int
1576 chkSource(File *f)
1577 {
1578 	if(f->partial)
1579 		return 1;
1580 
1581 	if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
1582 		vtSetError(ERemoved);
1583 		return 0;
1584 	}
1585 	return 1;
1586 }
1587 
1588 static int
1589 fileRLock(File *f)
1590 {
1591 	assert(!vtCanLock(f->fs->elk));
1592 	vtRLock(f->lk);
1593 	if(!chkSource(f)){
1594 		fileRUnlock(f);
1595 		return 0;
1596 	}
1597 	return 1;
1598 }
1599 
1600 static void
1601 fileRUnlock(File *f)
1602 {
1603 	vtRUnlock(f->lk);
1604 }
1605 
1606 static int
1607 fileLock(File *f)
1608 {
1609 	assert(!vtCanLock(f->fs->elk));
1610 	vtLock(f->lk);
1611 	if(!chkSource(f)){
1612 		fileUnlock(f);
1613 		return 0;
1614 	}
1615 	return 1;
1616 }
1617 
1618 static void
1619 fileUnlock(File *f)
1620 {
1621 	vtUnlock(f->lk);
1622 }
1623 
1624 /*
1625  * f->source and f->msource must NOT be locked.
1626  * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
1627  * We have to respect that ordering.
1628  */
1629 static void
1630 fileMetaLock(File *f)
1631 {
1632 if(f->up == nil)
1633 fprint(2, "f->elem = %s\n", f->dir.elem);
1634 	assert(f->up != nil);
1635 	assert(!vtCanLock(f->fs->elk));
1636 	vtLock(f->up->lk);
1637 }
1638 
1639 static void
1640 fileMetaUnlock(File *f)
1641 {
1642 	vtUnlock(f->up->lk);
1643 }
1644 
1645 /*
1646  * f->source and f->msource must NOT be locked.
1647  * see fileMetaLock.
1648  */
1649 static void
1650 fileRAccess(File* f)
1651 {
1652 	if(f->mode == OReadOnly)
1653 		return;
1654 
1655 	fileMetaLock(f);
1656 	f->dir.atime = time(0L);
1657 	f->dirty = 1;
1658 	fileMetaUnlock(f);
1659 }
1660 
1661 /*
1662  * f->source and f->msource must NOT be locked.
1663  * see fileMetaLock.
1664  */
1665 static void
1666 fileWAccess(File* f, char *mid)
1667 {
1668 	if(f->mode == OReadOnly)
1669 		return;
1670 
1671 	fileMetaLock(f);
1672 	f->dir.atime = f->dir.mtime = time(0L);
1673 	if(strcmp(f->dir.mid, mid) != 0){
1674 		vtMemFree(f->dir.mid);
1675 		f->dir.mid = vtStrDup(mid);
1676 	}
1677 	f->dir.mcount++;
1678 	f->dirty = 1;
1679 	fileMetaUnlock(f);
1680 
1681 /*RSC: let's try this */
1682 /*presotto - lets not
1683 	if(f->up)
1684 		fileWAccess(f->up, mid);
1685 */
1686 }
1687 
1688 static int
1689 getEntry(Source *r, Entry *e, int checkepoch)
1690 {
1691 	u32int epoch;
1692 	Block *b;
1693 
1694 	if(r == nil){
1695 		memset(&e, 0, sizeof e);
1696 		return 1;
1697 	}
1698 
1699 	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly);
1700 	if(b == nil)
1701 		return 0;
1702 	if(!entryUnpack(e, b->data, r->offset % r->epb)){
1703 		blockPut(b);
1704 		return 0;
1705 	}
1706 	epoch = b->l.epoch;
1707 	blockPut(b);
1708 
1709 	if(checkepoch){
1710 		b = cacheGlobal(r->fs->cache, e->score, entryType(e), e->tag, OReadOnly);
1711 		if(b){
1712 			if(b->l.epoch >= epoch)
1713 				fprint(2, "warning: entry %p epoch not older %#.8ux/%d %V/%d in getEntry\n",
1714 					r, b->addr, b->l.epoch, r->score, epoch);
1715 			blockPut(b);
1716 		}
1717 	}
1718 
1719 	return 1;
1720 }
1721 
1722 static int
1723 setEntry(Source *r, Entry *e)
1724 {
1725 	Block *b;
1726 	Entry oe;
1727 
1728 	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
1729 	if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
1730 	if(b == nil)
1731 		return 0;
1732 	if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
1733 		blockPut(b);
1734 		return 0;
1735 	}
1736 	e->gen = oe.gen;
1737 	entryPack(e, b->data, r->offset % r->epb);
1738 
1739 	/* BUG b should depend on the entry pointer */
1740 
1741 	blockDirty(b);
1742 	blockPut(b);
1743 	return 1;
1744 }
1745 
1746 /* assumes hold elk */
1747 int
1748 fileSnapshot(File *dst, File *src, u32int epoch, int doarchive)
1749 {
1750 	Entry e, ee;
1751 
1752 	/* add link to snapshot */
1753 	if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
1754 		return 0;
1755 
1756 	e.snap = epoch;
1757 	e.archive = doarchive;
1758 	ee.snap = epoch;
1759 	ee.archive = doarchive;
1760 
1761 	if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
1762 		return 0;
1763 	return 1;
1764 }
1765 
1766 int
1767 fileGetSources(File *f, Entry *e, Entry *ee)
1768 {
1769 	if(!getEntry(f->source, e, 0)
1770 	|| !getEntry(f->msource, ee, 0))
1771 		return 0;
1772 	return 1;
1773 }
1774 
1775 /*
1776  * Walk down to the block(s) containing the Entries
1777  * for f->source and f->msource, copying as we go.
1778  */
1779 int
1780 fileWalkSources(File *f)
1781 {
1782 	if(f->mode == OReadOnly){
1783 		fprint(2, "readonly in fileWalkSources\n");
1784 		return 1;
1785 	}
1786 	if(!sourceLock2(f->source, f->msource, OReadWrite)){
1787 		fprint(2, "sourceLock2 failed in fileWalkSources\n");
1788 		return 0;
1789 	}
1790 	sourceUnlock(f->source);
1791 	sourceUnlock(f->msource);
1792 	return 1;
1793 }
1794