xref: /plan9-contrib/sys/src/cmd/fossil/file.c (revision d7aba6c3b511bc618cf0c53345848188fc02611a)
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 
811 	/* can not set permissions for the root */
812 	if(fileIsRoot(f)){
813 		werrstr(ERoot);
814 		return 0;
815 	}
816 
817 	if(!fileLock(f))
818 		return 0;
819 
820 	if(f->source->mode != OReadWrite){
821 		werrstr(EReadOnly);
822 		fileUnlock(f);
823 		return 0;
824 	}
825 
826 	fileMetaLock(f);
827 
828 	/* check new name does not already exist */
829 	if(strcmp(f->dir.elem, dir->elem) != 0){
830 		for(ff = f->up->down; ff; ff=ff->next){
831 			if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
832 				werrstr(EExists);
833 				goto Err;
834 			}
835 		}
836 
837 		ff = dirLookup(f->up, dir->elem);
838 		if(ff != nil){
839 			fileDecRef(ff);
840 			werrstr(EExists);
841 			goto Err;
842 		}
843 	}
844 
845 	if(!sourceLock2(f->source, f->msource, -1))
846 		goto Err;
847 	if(!fileIsDir(f)){
848 		size = sourceGetSize(f->source);
849 		if(size != dir->size){
850 			if(!sourceSetSize(f->source, dir->size)){
851 				sourceUnlock(f->source);
852 				if(f->msource)
853 					sourceUnlock(f->msource);
854 				goto Err;
855 			}
856 			/* commited to changing it now */
857 		}
858 	}
859 	/* commited to changing it now */
860 	if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
861 		fileSetTmp(f, dir->mode&ModeTemporary);
862 	sourceUnlock(f->source);
863 	if(f->msource)
864 		sourceUnlock(f->msource);
865 
866 	oelem = nil;
867 	if(strcmp(f->dir.elem, dir->elem) != 0){
868 		oelem = f->dir.elem;
869 		f->dir.elem = vtstrdup(dir->elem);
870 	}
871 
872 	if(strcmp(f->dir.uid, dir->uid) != 0){
873 		vtfree(f->dir.uid);
874 		f->dir.uid = vtstrdup(dir->uid);
875 	}
876 
877 	if(strcmp(f->dir.gid, dir->gid) != 0){
878 		vtfree(f->dir.gid);
879 		f->dir.gid = vtstrdup(dir->gid);
880 	}
881 
882 	f->dir.mtime = dir->mtime;
883 	f->dir.atime = dir->atime;
884 
885 //fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
886 	mask = ~(ModeDir|ModeSnapshot);
887 	f->dir.mode &= ~mask;
888 	f->dir.mode |= mask & dir->mode;
889 	f->dirty = 1;
890 //fprint(2, "->%x\n", f->dir.mode);
891 
892 	fileMetaFlush2(f, oelem);
893 	vtfree(oelem);
894 
895 	fileMetaUnlock(f);
896 	fileUnlock(f);
897 
898 	fileWAccess(f->up, uid);
899 
900 	return 1;
901 Err:
902 	fileMetaUnlock(f);
903 	fileUnlock(f);
904 	return 0;
905 }
906 
907 int
908 fileSetQidSpace(File *f, u64int offset, u64int max)
909 {
910 	int ret;
911 
912 	if(!fileLock(f))
913 		return 0;
914 	fileMetaLock(f);
915 	f->dir.qidSpace = 1;
916 	f->dir.qidOffset = offset;
917 	f->dir.qidMax = max;
918 	ret = fileMetaFlush2(f, nil)>=0;
919 	fileMetaUnlock(f);
920 	fileUnlock(f);
921 	return ret;
922 }
923 
924 
925 uvlong
926 fileGetId(File *f)
927 {
928 	/* immutable */
929 	return f->dir.qid;
930 }
931 
932 ulong
933 fileGetMcount(File *f)
934 {
935 	ulong mcount;
936 
937 	fileMetaLock(f);
938 	mcount = f->dir.mcount;
939 	fileMetaUnlock(f);
940 	return mcount;
941 }
942 
943 ulong
944 fileGetMode(File *f)
945 {
946 	ulong mode;
947 
948 	fileMetaLock(f);
949 	mode = f->dir.mode;
950 	fileMetaUnlock(f);
951 	return mode;
952 }
953 
954 int
955 fileIsDir(File *f)
956 {
957 	/* immutable */
958 	return (f->dir.mode & ModeDir) != 0;
959 }
960 
961 int
962 fileIsAppend(File *f)
963 {
964 	return (f->dir.mode & ModeAppend) != 0;
965 }
966 
967 int
968 fileIsExclusive(File *f)
969 {
970 	return (f->dir.mode & ModeExclusive) != 0;
971 }
972 
973 int
974 fileIsTemporary(File *f)
975 {
976 	return (f->dir.mode & ModeTemporary) != 0;
977 }
978 
979 int
980 fileIsRoot(File *f)
981 {
982 	return f == f->fs->file;
983 }
984 
985 int
986 fileIsRoFs(File *f)
987 {
988 	return f->fs->mode == OReadOnly;
989 }
990 
991 int
992 fileGetSize(File *f, uvlong *size)
993 {
994 	if(!fileRLock(f))
995 		return 0;
996 	if(!sourceLock(f->source, OReadOnly)){
997 		fileRUnlock(f);
998 		return 0;
999 	}
1000 	*size = sourceGetSize(f->source);
1001 	sourceUnlock(f->source);
1002 	fileRUnlock(f);
1003 
1004 	return 1;
1005 }
1006 
1007 int
1008 fileMetaFlush(File *f, int rec)
1009 {
1010 	File **kids, *p;
1011 	int nkids;
1012 	int i, rv;
1013 
1014 	fileMetaLock(f);
1015 	rv = fileMetaFlush2(f, nil);
1016 	fileMetaUnlock(f);
1017 
1018 	if(!rec || !fileIsDir(f))
1019 		return rv;
1020 
1021 	if(!fileLock(f))
1022 		return rv;
1023 	nkids = 0;
1024 	for(p=f->down; p; p=p->next)
1025 		nkids++;
1026 	kids = vtmalloc(nkids*sizeof(File*));
1027 	i = 0;
1028 	for(p=f->down; p; p=p->next){
1029 		kids[i++] = p;
1030 		p->ref++;
1031 	}
1032 	fileUnlock(f);
1033 
1034 	for(i=0; i<nkids; i++){
1035 		rv |= fileMetaFlush(kids[i], 1);
1036 		fileDecRef(kids[i]);
1037 	}
1038 	vtfree(kids);
1039 	return rv;
1040 }
1041 
1042 /* assumes metaLock is held */
1043 static int
1044 fileMetaFlush2(File *f, char *oelem)
1045 {
1046 	File *fp;
1047 	Block *b, *bb;
1048 	MetaBlock mb;
1049 	MetaEntry me, me2;
1050 	int i, n;
1051 	u32int boff;
1052 
1053 	if(!f->dirty)
1054 		return 0;
1055 
1056 	if(oelem == nil)
1057 		oelem = f->dir.elem;
1058 
1059 //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem);
1060 
1061 	fp = f->up;
1062 
1063 	if(!sourceLock(fp->msource, -1))
1064 		return -1;
1065 	/* can happen if source is clri'ed out from under us */
1066 	if(f->boff == NilBlock)
1067 		goto Err1;
1068 	b = sourceBlock(fp->msource, f->boff, OReadWrite);
1069 	if(b == nil)
1070 		goto Err1;
1071 
1072 	if(!mbUnpack(&mb, b->data, fp->msource->dsize))
1073 		goto Err;
1074 	if(!mbSearch(&mb, oelem, &i, &me))
1075 		goto Err;
1076 
1077 	n = deSize(&f->dir);
1078 if(0)fprint(2, "old size %d new size %d\n", me.size, n);
1079 
1080 	if(mbResize(&mb, &me, n)){
1081 		/* fits in the block */
1082 		mbDelete(&mb, i);
1083 		if(strcmp(f->dir.elem, oelem) != 0)
1084 			mbSearch(&mb, f->dir.elem, &i, &me2);
1085 		dePack(&f->dir, &me);
1086 		mbInsert(&mb, i, &me);
1087 		mbPack(&mb);
1088 		blockDirty(b);
1089 		blockPut(b);
1090 		sourceUnlock(fp->msource);
1091 		f->dirty = 0;
1092 
1093 		return 1;
1094 	}
1095 
1096 	/*
1097 	 * moving entry to another block
1098 	 * it is feasible for the fs to crash leaving two copies
1099 	 * of the directory entry.  This is just too much work to
1100 	 * fix.  Given that entries are only allocated in a block that
1101 	 * is less than PercentageFull, most modifications of meta data
1102 	 * will fit within the block.  i.e. this code should almost
1103 	 * never be executed.
1104 	 */
1105 	boff = fileMetaAlloc(fp, &f->dir, f->boff+1);
1106 	if(boff == NilBlock){
1107 		/* mbResize might have modified block */
1108 		mbPack(&mb);
1109 		blockDirty(b);
1110 		goto Err;
1111 	}
1112 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
1113 	f->boff = boff;
1114 
1115 	/* make sure deletion goes to disk after new entry */
1116 	bb = sourceBlock(fp->msource, f->boff, OReadWrite);
1117 	mbDelete(&mb, i);
1118 	mbPack(&mb);
1119 	blockDependency(b, bb, -1, nil, nil);
1120 	blockPut(bb);
1121 	blockDirty(b);
1122 	blockPut(b);
1123 	sourceUnlock(fp->msource);
1124 
1125 	f->dirty = 0;
1126 
1127 	return 1;
1128 
1129 Err:
1130 	blockPut(b);
1131 Err1:
1132 	sourceUnlock(fp->msource);
1133 	return -1;
1134 }
1135 
1136 static int
1137 fileMetaRemove(File *f, char *uid)
1138 {
1139 	Block *b;
1140 	MetaBlock mb;
1141 	MetaEntry me;
1142 	int i;
1143 	File *up;
1144 
1145 	up = f->up;
1146 
1147 	fileWAccess(up, uid);
1148 
1149 	fileMetaLock(f);
1150 
1151 	sourceLock(up->msource, OReadWrite);
1152 	b = sourceBlock(up->msource, f->boff, OReadWrite);
1153 	if(b == nil)
1154 		goto Err;
1155 
1156 	if(!mbUnpack(&mb, b->data, up->msource->dsize))
1157 {
1158 fprint(2, "U\n");
1159 		goto Err;
1160 }
1161 	if(!mbSearch(&mb, f->dir.elem, &i, &me))
1162 {
1163 fprint(2, "S\n");
1164 		goto Err;
1165 }
1166 	mbDelete(&mb, i);
1167 	mbPack(&mb);
1168 	sourceUnlock(up->msource);
1169 
1170 	blockDirty(b);
1171 	blockPut(b);
1172 
1173 	f->removed = 1;
1174 	f->boff = NilBlock;
1175 	f->dirty = 0;
1176 
1177 	fileMetaUnlock(f);
1178 	return 1;
1179 
1180 Err:
1181 	sourceUnlock(up->msource);
1182 	blockPut(b);
1183 	fileMetaUnlock(f);
1184 	return 0;
1185 }
1186 
1187 /* assume file is locked, assume f->msource is locked */
1188 static int
1189 fileCheckEmpty(File *f)
1190 {
1191 	u32int i, n;
1192 	Block *b;
1193 	MetaBlock mb;
1194 	Source *r;
1195 
1196 	r = f->msource;
1197 	n = (sourceGetSize(r)+r->dsize-1)/r->dsize;
1198 	for(i=0; i<n; i++){
1199 		b = sourceBlock(r, i, OReadOnly);
1200 		if(b == nil)
1201 			goto Err;
1202 		if(!mbUnpack(&mb, b->data, r->dsize))
1203 			goto Err;
1204 		if(mb.nindex > 0){
1205 			werrstr(ENotEmpty);
1206 			goto Err;
1207 		}
1208 		blockPut(b);
1209 	}
1210 	return 1;
1211 Err:
1212 	blockPut(b);
1213 	return 0;
1214 }
1215 
1216 int
1217 fileRemove(File *f, char *uid)
1218 {
1219 	File *ff;
1220 
1221 	/* can not remove the root */
1222 	if(fileIsRoot(f)){
1223 		werrstr(ERoot);
1224 		return 0;
1225 	}
1226 
1227 	if(!fileLock(f))
1228 		return 0;
1229 
1230 	if(f->source->mode != OReadWrite){
1231 		werrstr(EReadOnly);
1232 		goto Err1;
1233 	}
1234 	if(!sourceLock2(f->source, f->msource, -1))
1235 		goto Err1;
1236 	if(fileIsDir(f) && !fileCheckEmpty(f))
1237 		goto Err;
1238 
1239 	for(ff=f->down; ff; ff=ff->next)
1240 		assert(ff->removed);
1241 
1242 	sourceRemove(f->source);
1243 	f->source->file = nil;		/* erase back pointer */
1244 	f->source = nil;
1245 	if(f->msource){
1246 		sourceRemove(f->msource);
1247 		f->msource = nil;
1248 	}
1249 
1250 	fileUnlock(f);
1251 
1252 	if(!fileMetaRemove(f, uid))
1253 		return 0;
1254 
1255 	return 1;
1256 
1257 Err:
1258 	sourceUnlock(f->source);
1259 	if(f->msource)
1260 		sourceUnlock(f->msource);
1261 Err1:
1262 	fileUnlock(f);
1263 	return 0;
1264 }
1265 
1266 static int
1267 clri(File *f, char *uid)
1268 {
1269 	int r;
1270 
1271 	if(f == nil)
1272 		return 0;
1273 	if(f->up->source->mode != OReadWrite){
1274 		werrstr(EReadOnly);
1275 		fileDecRef(f);
1276 		return 0;
1277 	}
1278 	r = fileMetaRemove(f, uid);
1279 	fileDecRef(f);
1280 	return r;
1281 }
1282 
1283 int
1284 fileClriPath(Fs *fs, char *path, char *uid)
1285 {
1286 	return clri(_fileOpen(fs, path, 1), uid);
1287 }
1288 
1289 int
1290 fileClri(File *dir, char *elem, char *uid)
1291 {
1292 	return clri(_fileWalk(dir, elem, 1), uid);
1293 }
1294 
1295 File *
1296 fileIncRef(File *vf)
1297 {
1298 	fileMetaLock(vf);
1299 	assert(vf->ref > 0);
1300 	vf->ref++;
1301 	fileMetaUnlock(vf);
1302 	return vf;
1303 }
1304 
1305 int
1306 fileDecRef(File *f)
1307 {
1308 	File *p, *q, **qq;
1309 
1310 	if(f->up == nil){
1311 		/* never linked in */
1312 		assert(f->ref == 1);
1313 		fileFree(f);
1314 		return 1;
1315 	}
1316 
1317 	fileMetaLock(f);
1318 	f->ref--;
1319 	if(f->ref > 0){
1320 		fileMetaUnlock(f);
1321 		return 0;
1322 	}
1323 	assert(f->ref == 0);
1324 	assert(f->down == nil);
1325 
1326 	fileMetaFlush2(f, nil);
1327 
1328 	p = f->up;
1329 	qq = &p->down;
1330 	for(q = *qq; q; q = *qq){
1331 		if(q == f)
1332 			break;
1333 		qq = &q->next;
1334 	}
1335 	assert(q != nil);
1336 	*qq = f->next;
1337 
1338 	fileMetaUnlock(f);
1339 	fileFree(f);
1340 
1341 	fileDecRef(p);
1342 	return 1;
1343 }
1344 
1345 File *
1346 fileGetParent(File *f)
1347 {
1348 	if(fileIsRoot(f))
1349 		return fileIncRef(f);
1350 	return fileIncRef(f->up);
1351 }
1352 
1353 DirEntryEnum *
1354 deeOpen(File *f)
1355 {
1356 	DirEntryEnum *dee;
1357 	File *p;
1358 
1359 	if(!fileIsDir(f)){
1360 		werrstr(ENotDir);
1361 		fileDecRef(f);
1362 		return nil;
1363 	}
1364 
1365 	/* flush out meta data */
1366 	if(!fileLock(f))
1367 		return nil;
1368 	for(p=f->down; p; p=p->next)
1369 		fileMetaFlush2(p, nil);
1370 	fileUnlock(f);
1371 
1372 	dee = vtmallocz(sizeof(DirEntryEnum));
1373 	dee->file = fileIncRef(f);
1374 
1375 	return dee;
1376 }
1377 
1378 static int
1379 dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
1380 {
1381 	Block *b;
1382 	ulong bn;
1383 	Entry e;
1384 	int epb;
1385 
1386 	epb = s->dsize/VtEntrySize;
1387 	bn = elem/epb;
1388 	elem -= bn*epb;
1389 
1390 	b = sourceBlock(s, bn, OReadOnly);
1391 	if(b == nil)
1392 		goto Err;
1393 	if(!entryUnpack(&e, b->data, elem))
1394 		goto Err;
1395 
1396 	/* hanging entries are returned as zero size */
1397 	if(!(e.flags & VtEntryActive) || e.gen != gen)
1398 		*size = 0;
1399 	else
1400 		*size = e.size;
1401 	blockPut(b);
1402 	return 1;
1403 
1404 Err:
1405 	blockPut(b);
1406 	return 0;
1407 }
1408 
1409 static int
1410 deeFill(DirEntryEnum *dee)
1411 {
1412 	int i, n;
1413 	Source *meta, *source;
1414 	MetaBlock mb;
1415 	MetaEntry me;
1416 	File *f;
1417 	Block *b;
1418 	DirEntry *de;
1419 
1420 	/* clean up first */
1421 	for(i=dee->i; i<dee->n; i++)
1422 		deCleanup(dee->buf+i);
1423 	vtfree(dee->buf);
1424 	dee->buf = nil;
1425 	dee->i = 0;
1426 	dee->n = 0;
1427 
1428 	f = dee->file;
1429 
1430 	source = f->source;
1431 	meta = f->msource;
1432 
1433 	b = sourceBlock(meta, dee->boff, OReadOnly);
1434 	if(b == nil)
1435 		goto Err;
1436 	if(!mbUnpack(&mb, b->data, meta->dsize))
1437 		goto Err;
1438 
1439 	n = mb.nindex;
1440 	dee->buf = vtmalloc(n * sizeof(DirEntry));
1441 
1442 	for(i=0; i<n; i++){
1443 		de = dee->buf + i;
1444 		meUnpack(&me, &mb, i);
1445 		if(!deUnpack(de, &me))
1446 			goto Err;
1447 		dee->n++;
1448 		if(!(de->mode & ModeDir))
1449 		if(!dirEntrySize(source, de->entry, de->gen, &de->size))
1450 			goto Err;
1451 	}
1452 	dee->boff++;
1453 	blockPut(b);
1454 	return 1;
1455 Err:
1456 	blockPut(b);
1457 	return 0;
1458 }
1459 
1460 int
1461 deeRead(DirEntryEnum *dee, DirEntry *de)
1462 {
1463 	int ret, didread;
1464 	File *f;
1465 	u32int nb;
1466 
1467 	if(dee == nil){
1468 		werrstr("cannot happen in deeRead");
1469 		return -1;
1470 	}
1471 
1472 	f = dee->file;
1473 	if(!fileRLock(f))
1474 		return -1;
1475 
1476 	if(!sourceLock2(f->source, f->msource, OReadOnly)){
1477 		fileRUnlock(f);
1478 		return -1;
1479 	}
1480 
1481 	nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
1482 
1483 	didread = 0;
1484 	while(dee->i >= dee->n){
1485 		if(dee->boff >= nb){
1486 			ret = 0;
1487 			goto Return;
1488 		}
1489 		didread = 1;
1490 		if(!deeFill(dee)){
1491 			ret = -1;
1492 			goto Return;
1493 		}
1494 	}
1495 
1496 	memmove(de, dee->buf + dee->i, sizeof(DirEntry));
1497 	dee->i++;
1498 	ret = 1;
1499 
1500 Return:
1501 	sourceUnlock(f->source);
1502 	sourceUnlock(f->msource);
1503 	fileRUnlock(f);
1504 
1505 	if(didread)
1506 		fileRAccess(f);
1507 	return ret;
1508 }
1509 
1510 void
1511 deeClose(DirEntryEnum *dee)
1512 {
1513 	int i;
1514 	if(dee == nil)
1515 		return;
1516 	for(i=dee->i; i<dee->n; i++)
1517 		deCleanup(dee->buf+i);
1518 	vtfree(dee->buf);
1519 	fileDecRef(dee->file);
1520 	vtfree(dee);
1521 }
1522 
1523 /*
1524  * caller must lock f->source and f->msource
1525  * caller must NOT lock the source and msource
1526  * referenced by dir.
1527  */
1528 static u32int
1529 fileMetaAlloc(File *f, DirEntry *dir, u32int start)
1530 {
1531 	u32int nb, bo;
1532 	Block *b, *bb;
1533 	MetaBlock mb;
1534 	int nn;
1535 	uchar *p;
1536 	int i, n, epb;
1537 	MetaEntry me;
1538 	Source *s, *ms;
1539 
1540 	s = f->source;
1541 	ms = f->msource;
1542 
1543 	n = deSize(dir);
1544 	nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize;
1545 	b = nil;
1546 	if(start > nb)
1547 		start = nb;
1548 	for(bo=start; bo<nb; bo++){
1549 		b = sourceBlock(ms, bo, OReadWrite);
1550 		if(b == nil)
1551 			goto Err;
1552 		if(!mbUnpack(&mb, b->data, ms->dsize))
1553 			goto Err;
1554 		nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
1555 		if(n <= nn && mb.nindex < mb.maxindex)
1556 			break;
1557 		blockPut(b);
1558 		b = nil;
1559 	}
1560 
1561 	/* add block to meta file */
1562 	if(b == nil){
1563 		b = sourceBlock(ms, bo, OReadWrite);
1564 		if(b == nil)
1565 			goto Err;
1566 		sourceSetSize(ms, (nb+1)*ms->dsize);
1567 		mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
1568 	}
1569 
1570 	p = mbAlloc(&mb, n);
1571 	if(p == nil){
1572 		/* mbAlloc might have changed block */
1573 		mbPack(&mb);
1574 		blockDirty(b);
1575 		werrstr(EBadMeta);
1576 		goto Err;
1577 	}
1578 
1579 	mbSearch(&mb, dir->elem, &i, &me);
1580 	assert(me.p == nil);
1581 	me.p = p;
1582 	me.size = n;
1583 	dePack(dir, &me);
1584 	mbInsert(&mb, i, &me);
1585 	mbPack(&mb);
1586 
1587 	/* meta block depends on super block for qid ... */
1588 	bb = cacheLocal(b->c, PartSuper, 0, OReadOnly);
1589 	blockDependency(b, bb, -1, nil, nil);
1590 	blockPut(bb);
1591 
1592 	/* ... and one or two dir entries */
1593 	epb = s->dsize/VtEntrySize;
1594 	bb = sourceBlock(s, dir->entry/epb, OReadOnly);
1595 	blockDependency(b, bb, -1, nil, nil);
1596 	blockPut(bb);
1597 	if(dir->mode & ModeDir){
1598 		bb = sourceBlock(s, dir->mentry/epb, OReadOnly);
1599 		blockDependency(b, bb, -1, nil, nil);
1600 		blockPut(bb);
1601 	}
1602 
1603 	blockDirty(b);
1604 	blockPut(b);
1605 	return bo;
1606 Err:
1607 	blockPut(b);
1608 	return NilBlock;
1609 }
1610 
1611 static int
1612 chkSource(File *f)
1613 {
1614 	if(f->partial)
1615 		return 1;
1616 
1617 	if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
1618 		werrstr(ERemoved);
1619 		return 0;
1620 	}
1621 	return 1;
1622 }
1623 
1624 static int
1625 fileRLock(File *f)
1626 {
1627 	assert(!canwlock(&f->fs->elk));
1628 	rlock(&f->lk);
1629 	if(!chkSource(f)){
1630 		fileRUnlock(f);
1631 		return 0;
1632 	}
1633 	return 1;
1634 }
1635 
1636 static void
1637 fileRUnlock(File *f)
1638 {
1639 	runlock(&f->lk);
1640 }
1641 
1642 static int
1643 fileLock(File *f)
1644 {
1645 	assert(!canwlock(&f->fs->elk));
1646 	wlock(&f->lk);
1647 	if(!chkSource(f)){
1648 		fileUnlock(f);
1649 		return 0;
1650 	}
1651 	return 1;
1652 }
1653 
1654 static void
1655 fileUnlock(File *f)
1656 {
1657 	wunlock(&f->lk);
1658 }
1659 
1660 /*
1661  * f->source and f->msource must NOT be locked.
1662  * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
1663  * We have to respect that ordering.
1664  */
1665 static void
1666 fileMetaLock(File *f)
1667 {
1668 if(f->up == nil)
1669 fprint(2, "f->elem = %s\n", f->dir.elem);
1670 	assert(f->up != nil);
1671 	assert(!canwlock(&f->fs->elk));
1672 	wlock(&f->up->lk);
1673 }
1674 
1675 static void
1676 fileMetaUnlock(File *f)
1677 {
1678 	wunlock(&f->up->lk);
1679 }
1680 
1681 /*
1682  * f->source and f->msource must NOT be locked.
1683  * see fileMetaLock.
1684  */
1685 static void
1686 fileRAccess(File* f)
1687 {
1688 	if(f->mode == OReadOnly || f->fs->noatimeupd)
1689 		return;
1690 
1691 	fileMetaLock(f);
1692 	f->dir.atime = time(0L);
1693 	f->dirty = 1;
1694 	fileMetaUnlock(f);
1695 }
1696 
1697 /*
1698  * f->source and f->msource must NOT be locked.
1699  * see fileMetaLock.
1700  */
1701 static void
1702 fileWAccess(File* f, char *mid)
1703 {
1704 	if(f->mode == OReadOnly)
1705 		return;
1706 
1707 	fileMetaLock(f);
1708 	f->dir.atime = f->dir.mtime = time(0L);
1709 	if(strcmp(f->dir.mid, mid) != 0){
1710 		vtfree(f->dir.mid);
1711 		f->dir.mid = vtstrdup(mid);
1712 	}
1713 	f->dir.mcount++;
1714 	f->dirty = 1;
1715 	fileMetaUnlock(f);
1716 
1717 /*RSC: let's try this */
1718 /*presotto - lets not
1719 	if(f->up)
1720 		fileWAccess(f->up, mid);
1721 */
1722 }
1723 
1724 static int
1725 getEntry(Source *r, Entry *e, int checkepoch)
1726 {
1727 	u32int epoch;
1728 	Block *b;
1729 
1730 	if(r == nil){
1731 		memset(&e, 0, sizeof e);
1732 		return 1;
1733 	}
1734 
1735 	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly);
1736 	if(b == nil)
1737 		return 0;
1738 	if(!entryUnpack(e, b->data, r->offset % r->epb)){
1739 		blockPut(b);
1740 		return 0;
1741 	}
1742 	epoch = b->l.epoch;
1743 	blockPut(b);
1744 
1745 	if(checkepoch){
1746 		b = cacheGlobal(r->fs->cache, e->score, entryType(e), e->tag, OReadOnly);
1747 		if(b){
1748 			if(b->l.epoch >= epoch)
1749 				fprint(2, "warning: entry %p epoch not older %#.8ux/%d %V/%d in getEntry\n",
1750 					r, b->addr, b->l.epoch, r->score, epoch);
1751 			blockPut(b);
1752 		}
1753 	}
1754 
1755 	return 1;
1756 }
1757 
1758 static int
1759 setEntry(Source *r, Entry *e)
1760 {
1761 	Block *b;
1762 	Entry oe;
1763 
1764 	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
1765 	if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
1766 	if(b == nil)
1767 		return 0;
1768 	if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
1769 		blockPut(b);
1770 		return 0;
1771 	}
1772 	e->gen = oe.gen;
1773 	entryPack(e, b->data, r->offset % r->epb);
1774 
1775 	/* BUG b should depend on the entry pointer */
1776 
1777 	blockDirty(b);
1778 	blockPut(b);
1779 	return 1;
1780 }
1781 
1782 /* assumes hold elk */
1783 int
1784 fileSnapshot(File *dst, File *src, u32int epoch, int doarchive)
1785 {
1786 	Entry e, ee;
1787 
1788 	/* add link to snapshot */
1789 	if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
1790 		return 0;
1791 
1792 	e.snap = epoch;
1793 	e.archive = doarchive;
1794 	ee.snap = epoch;
1795 	ee.archive = doarchive;
1796 
1797 	if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
1798 		return 0;
1799 	return 1;
1800 }
1801 
1802 int
1803 fileGetSources(File *f, Entry *e, Entry *ee)
1804 {
1805 	if(!getEntry(f->source, e, 0)
1806 	|| !getEntry(f->msource, ee, 0))
1807 		return 0;
1808 	return 1;
1809 }
1810 
1811 /*
1812  * Walk down to the block(s) containing the Entries
1813  * for f->source and f->msource, copying as we go.
1814  */
1815 int
1816 fileWalkSources(File *f)
1817 {
1818 	if(f->mode == OReadOnly){
1819 		fprint(2, "readonly in fileWalkSources\n");
1820 		return 1;
1821 	}
1822 	if(!sourceLock2(f->source, f->msource, OReadWrite)){
1823 		fprint(2, "sourceLock2 failed in fileWalkSources\n");
1824 		return 0;
1825 	}
1826 	sourceUnlock(f->source);
1827 	sourceUnlock(f->msource);
1828 	return 1;
1829 }
1830 
1831 /*
1832  * convert File* to full path name in malloced string.
1833  * this hasn't been as useful as we hoped it would be.
1834  */
1835 char *
1836 fileName(File *f)
1837 {
1838 	char *name, *pname;
1839 	File *p;
1840 	static char root[] = "/";
1841 
1842 	if (f == nil)
1843 		return vtstrdup("/**GOK**");
1844 
1845 	p = fileGetParent(f);
1846 	if (p == f)
1847 		name = vtstrdup(root);
1848 	else {
1849 		pname = fileName(p);
1850 		if (strcmp(pname, root) == 0)
1851 			name = smprint("/%s", f->dir.elem);
1852 		else
1853 			name = smprint("%s/%s", pname, f->dir.elem);
1854 		free(pname);
1855 	}
1856 	fileDecRef(p);
1857 	return name;
1858 }
1859