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