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