xref: /plan9-contrib/sys/src/cmd/fossil/file.c (revision a6a9e07217f318acf170f99684a55fba5200524f)
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 File *
359 fileCreate(File *f, char *elem, ulong mode, char *uid)
360 {
361 	File *ff;
362 	DirEntry *dir;
363 	Source *pr, *r, *mr;
364 	int isdir;
365 
366 	if(!fileLock(f))
367 		return nil;
368 
369 	r = nil;
370 	mr = nil;
371 	for(ff = f->down; ff; ff=ff->next){
372 		if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
373 			ff = nil;
374 			vtSetError(EExists);
375 			goto Err1;
376 		}
377 	}
378 
379 	ff = dirLookup(f, elem);
380 	if(ff != nil){
381 		vtSetError(EExists);
382 		goto Err1;
383 	}
384 
385 	pr = f->source;
386 	if(pr->mode != OReadWrite){
387 		vtSetError(EReadOnly);
388 		goto Err1;
389 	}
390 
391 	if(!sourceLock2(f->source, f->msource, -1))
392 		goto Err1;
393 
394 	ff = fileAlloc(f->fs);
395 	isdir = mode & ModeDir;
396 
397 	r = sourceCreate(pr, pr->dsize, isdir, 0);
398 	if(r == nil)
399 		goto Err;
400 	if(isdir){
401 		mr = sourceCreate(pr, pr->dsize, 0, r->offset);
402 		if(mr == nil)
403 			goto Err;
404 	}
405 
406 	dir = &ff->dir;
407 	dir->elem = vtStrDup(elem);
408 	dir->entry = r->offset;
409 	dir->gen = r->gen;
410 	if(isdir){
411 		dir->mentry = mr->offset;
412 		dir->mgen = mr->gen;
413 	}
414 	dir->size = 0;
415 	if(!fsNextQid(f->fs, &dir->qid))
416 		goto Err;
417 	dir->uid = vtStrDup(uid);
418 	dir->gid = vtStrDup(f->dir.gid);
419 	dir->mid = vtStrDup(uid);
420 	dir->mtime = time(0L);
421 	dir->mcount = 0;
422 	dir->ctime = dir->mtime;
423 	dir->atime = dir->mtime;
424 	dir->mode = mode;
425 
426 	ff->boff = fileMetaAlloc(f, dir, 0);
427 	if(ff->boff == NilBlock)
428 		goto Err;
429 
430 	sourceUnlock(f->source);
431 	sourceUnlock(f->msource);
432 
433 	ff->source = r;
434 	ff->msource = mr;
435 
436 	/* link in and up parent ref count */
437 	ff->next = f->down;
438 	f->down = ff;
439 	ff->up = f;
440 	fileIncRef(f);
441 
442 	fileWAccess(f, uid);
443 
444 	fileUnlock(f);
445 	return ff;
446 
447 Err:
448 	sourceUnlock(f->source);
449 	sourceUnlock(f->msource);
450 Err1:
451 	if(r)
452 		sourceRemove(r);
453 	if(mr)
454 		sourceRemove(mr);
455 	if(ff)
456 		fileDecRef(ff);
457 	fileUnlock(f);
458 	return 0;
459 }
460 
461 int
462 fileRead(File *f, void *buf, int cnt, vlong offset)
463 {
464 	Source *s;
465 	uvlong size;
466 	u32int bn;
467 	int off, dsize, n, nn;
468 	Block *b;
469 	uchar *p;
470 
471 if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
472 
473 	if(!fileRLock(f))
474 		return -1;
475 
476 	if(offset < 0){
477 		vtSetError(EBadOffset);
478 		goto Err1;
479 	}
480 
481 	fileRAccess(f);
482 
483 	if(!sourceLock(f->source, OReadOnly))
484 		goto Err1;
485 
486 	s = f->source;
487 	dsize = s->dsize;
488 	size = sourceGetSize(s);
489 
490 	if(offset >= size)
491 		offset = size;
492 
493 	if(cnt > size-offset)
494 		cnt = size-offset;
495 	bn = offset/dsize;
496 	off = offset%dsize;
497 	p = buf;
498 	while(cnt > 0){
499 		b = sourceBlock(s, bn, OReadOnly);
500 		if(b == nil)
501 			goto Err;
502 		n = cnt;
503 		if(n > dsize-off)
504 			n = dsize-off;
505 		nn = dsize-off;
506 		if(nn > n)
507 			nn = n;
508 		memmove(p, b->data+off, nn);
509 		memset(p+nn, 0, nn-n);
510 		off = 0;
511 		bn++;
512 		cnt -= n;
513 		p += n;
514 		blockPut(b);
515 	}
516 	sourceUnlock(s);
517 	fileRUnlock(f);
518 	return p-(uchar*)buf;
519 
520 Err:
521 	sourceUnlock(s);
522 Err1:
523 	fileRUnlock(f);
524 	return -1;
525 }
526 
527 int
528 fileWrite(File *f, void *buf, int cnt, vlong offset, char *uid)
529 {
530 	Source *s;
531 	ulong bn;
532 	int off, dsize, n;
533 	Block *b;
534 	uchar *p;
535 	vlong eof;
536 
537 if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
538 
539 	if(!fileLock(f))
540 		return -1;
541 
542 	s = nil;
543 	if(f->dir.mode & ModeDir){
544 		vtSetError(ENotFile);
545 		goto Err;
546 	}
547 
548 	if(f->source->mode != OReadWrite){
549 		vtSetError(EReadOnly);
550 		goto Err;
551 	}
552 	if(offset < 0){
553 		vtSetError(EBadOffset);
554 		goto Err;
555 	}
556 
557 	fileWAccess(f, uid);
558 
559 	if(!sourceLock(f->source, -1))
560 		goto Err;
561 	s = f->source;
562 	dsize = s->dsize;
563 
564 	eof = sourceGetSize(s);
565 	if(f->dir.mode & ModeAppend)
566 		offset = eof;
567 	bn = offset/dsize;
568 	off = offset%dsize;
569 	p = buf;
570 	while(cnt > 0){
571 		n = cnt;
572 		if(n > dsize-off)
573 			n = dsize-off;
574 		b = sourceBlock(s, bn, n<dsize?OReadWrite:OOverWrite);
575 		if(b == nil){
576 			if(offset > eof)
577 				sourceSetSize(s, offset);
578 			goto Err;
579 		}
580 		memmove(b->data+off, p, n);
581 		off = 0;
582 		cnt -= n;
583 		p += n;
584 		offset += n;
585 		bn++;
586 		blockDirty(b);
587 		blockPut(b);
588 	}
589 	if(offset > eof && !sourceSetSize(s, offset))
590 		goto Err;
591 	sourceUnlock(s);
592 	fileUnlock(f);
593 	return p-(uchar*)buf;
594 Err:
595 	if(s)
596 		sourceUnlock(s);
597 	fileUnlock(f);
598 	return -1;
599 }
600 
601 int
602 fileGetDir(File *f, DirEntry *dir)
603 {
604 	if(!fileRLock(f))
605 		return 0;
606 
607 	fileMetaLock(f);
608 	deCopy(dir, &f->dir);
609 	fileMetaUnlock(f);
610 
611 	if(!fileIsDir(f)){
612 		if(!sourceLock(f->source, OReadOnly)){
613 			fileRUnlock(f);
614 			return 0;
615 		}
616 		dir->size = sourceGetSize(f->source);
617 		sourceUnlock(f->source);
618 	}
619 	fileRUnlock(f);
620 
621 	return 1;
622 }
623 
624 int
625 fileTruncate(File *f, char *uid)
626 {
627 	if(fileIsDir(f)){
628 		vtSetError(ENotFile);
629 		return 0;
630 	}
631 
632 	if(!fileLock(f))
633 		return 0;
634 
635 	if(f->source->mode != OReadWrite){
636 		vtSetError(EReadOnly);
637 		fileUnlock(f);
638 		return 0;
639 	}
640 	if(!sourceLock(f->source, -1)){
641 		fileUnlock(f);
642 		return 0;
643 	}
644 	if(!sourceTruncate(f->source)){
645 		sourceUnlock(f->source);
646 		fileUnlock(f);
647 		return 0;
648 	}
649 	sourceUnlock(f->source);
650 	fileUnlock(f);
651 
652 	fileWAccess(f->up, uid);
653 
654 	return 1;
655 }
656 
657 int
658 fileSetDir(File *f, DirEntry *dir, char *uid)
659 {
660 	File *ff;
661 	char *oelem;
662 	u32int mask;
663 	u64int size;
664 
665 	/* can not set permissions for the root */
666 	if(fileIsRoot(f)){
667 		vtSetError(ERoot);
668 		return 0;
669 	}
670 
671 	if(!fileLock(f))
672 		return 0;
673 
674 	if(f->source->mode != OReadWrite){
675 		vtSetError(EReadOnly);
676 		fileUnlock(f);
677 		return 0;
678 	}
679 
680 	fileMetaLock(f);
681 
682 	/* check new name does not already exist */
683 	if(strcmp(f->dir.elem, dir->elem) != 0){
684 		for(ff = f->up->down; ff; ff=ff->next){
685 			if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
686 				vtSetError(EExists);
687 				goto Err;
688 			}
689 		}
690 
691 		ff = dirLookup(f->up, dir->elem);
692 		if(ff != nil){
693 			fileDecRef(ff);
694 			vtSetError(EExists);
695 			goto Err;
696 		}
697 	}
698 
699 	if(!fileIsDir(f)){
700 		if(!sourceLock(f->source, -1))
701 			goto Err;
702 		size = sourceGetSize(f->source);
703 		if(size != dir->size){
704 			if(!sourceSetSize(f->source, dir->size)){
705 				sourceUnlock(f->source);
706 				goto Err;
707 			}
708 			/* commited to changing it now */
709 		}
710 		sourceUnlock(f->source);
711 	}
712 
713 	/* commited to changing it now */
714 	oelem = nil;
715 	if(strcmp(f->dir.elem, dir->elem) != 0){
716 		oelem = f->dir.elem;
717 		f->dir.elem = vtStrDup(dir->elem);
718 	}
719 
720 	if(strcmp(f->dir.uid, dir->uid) != 0){
721 		vtMemFree(f->dir.uid);
722 		f->dir.uid = vtStrDup(dir->uid);
723 	}
724 
725 	if(strcmp(f->dir.gid, dir->gid) != 0){
726 		vtMemFree(f->dir.gid);
727 		f->dir.gid = vtStrDup(dir->gid);
728 	}
729 
730 	f->dir.mtime = dir->mtime;
731 	f->dir.atime = dir->atime;
732 
733 //fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
734 	mask = ~(ModeDir|ModeSnapshot);
735 	f->dir.mode &= ~mask;
736 	f->dir.mode |= mask & dir->mode;
737 	f->dirty = 1;
738 //fprint(2, "->%x\n", f->dir.mode);
739 
740 	fileMetaFlush2(f, oelem);
741 	vtMemFree(oelem);
742 
743 	fileMetaUnlock(f);
744 	fileUnlock(f);
745 
746 	fileWAccess(f->up, uid);
747 
748 	return 1;
749 Err:
750 	fileMetaUnlock(f);
751 	fileUnlock(f);
752 	return 0;
753 }
754 
755 int
756 fileSetQidSpace(File *f, u64int offset, u64int max)
757 {
758 	int ret;
759 
760 	if(!fileLock(f))
761 		return 0;
762 	fileMetaLock(f);
763 	f->dir.qidSpace = 1;
764 	f->dir.qidOffset = offset;
765 	f->dir.qidMax = max;
766 	ret = fileMetaFlush2(f, nil);
767 	fileMetaUnlock(f);
768 	fileUnlock(f);
769 	return ret;
770 }
771 
772 
773 uvlong
774 fileGetId(File *f)
775 {
776 	/* immutable */
777 	return f->dir.qid;
778 }
779 
780 ulong
781 fileGetMcount(File *f)
782 {
783 	ulong mcount;
784 
785 	fileMetaLock(f);
786 	mcount = f->dir.mcount;
787 	fileMetaUnlock(f);
788 	return mcount;
789 }
790 
791 ulong
792 fileGetMode(File *f)
793 {
794 	ulong mode;
795 
796 	fileMetaLock(f);
797 	mode = f->dir.mode;
798 	fileMetaUnlock(f);
799 	return mode;
800 }
801 
802 int
803 fileIsDir(File *f)
804 {
805 	/* immutable */
806 	return (f->dir.mode & ModeDir) != 0;
807 }
808 
809 int
810 fileIsRoot(File *f)
811 {
812 	return f == f->fs->file;
813 }
814 
815 int
816 fileIsRoFs(File *f)
817 {
818 	return f->fs->mode == OReadOnly;
819 }
820 
821 int
822 fileGetSize(File *f, uvlong *size)
823 {
824 	if(!fileRLock(f))
825 		return 0;
826 	if(!sourceLock(f->source, OReadOnly)){
827 		fileRUnlock(f);
828 		return 0;
829 	}
830 	*size = sourceGetSize(f->source);
831 	sourceUnlock(f->source);
832 	fileRUnlock(f);
833 
834 	return 1;
835 }
836 
837 void
838 fileMetaFlush(File *f, int rec)
839 {
840 	File **kids, *p;
841 	int nkids;
842 	int i;
843 
844 	fileMetaLock(f);
845 	fileMetaFlush2(f, nil);
846 	fileMetaUnlock(f);
847 
848 	if(!rec || !fileIsDir(f))
849 		return;
850 
851 	if(!fileLock(f))
852 		return;
853 	nkids = 0;
854 	for(p=f->down; p; p=p->next)
855 		nkids++;
856 	kids = vtMemAlloc(nkids*sizeof(File*));
857 	i = 0;
858 	for(p=f->down; p; p=p->next){
859 		kids[i++] = p;
860 		p->ref++;
861 	}
862 	fileUnlock(f);
863 
864 	for(i=0; i<nkids; i++){
865 		fileMetaFlush(kids[i], 1);
866 		fileDecRef(kids[i]);
867 	}
868 	vtMemFree(kids);
869 }
870 
871 /* assumes metaLock is held */
872 static int
873 fileMetaFlush2(File *f, char *oelem)
874 {
875 	File *fp;
876 	Block *b, *bb;
877 	MetaBlock mb;
878 	MetaEntry me, me2;
879 	int i, n;
880 	u32int boff;
881 
882 	if(!f->dirty)
883 		return 1;
884 
885 	if(oelem == nil)
886 		oelem = f->dir.elem;
887 
888 //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem);
889 
890 	fp = f->up;
891 
892 	if(!sourceLock(fp->msource, -1))
893 		return 0;
894 	b = sourceBlock(fp->msource, f->boff, OReadWrite);
895 	if(b == nil)
896 		goto Err1;
897 
898 	if(!mbUnpack(&mb, b->data, fp->msource->dsize))
899 		goto Err;
900 	if(!mbSearch(&mb, oelem, &i, &me))
901 		goto Err;
902 
903 	n = deSize(&f->dir);
904 if(0)fprint(2, "old size %d new size %d\n", me.size, n);
905 
906 	if(mbResize(&mb, &me, n)){
907 		/* fits in the block */
908 		mbDelete(&mb, i);
909 		if(strcmp(f->dir.elem, oelem) != 0)
910 			mbSearch(&mb, f->dir.elem, &i, &me2);
911 		dePack(&f->dir, &me);
912 		mbInsert(&mb, i, &me);
913 		mbPack(&mb);
914 		blockDirty(b);
915 		blockPut(b);
916 		sourceUnlock(fp->msource);
917 		f->dirty = 0;
918 
919 		return 1;
920 	}
921 
922 	/*
923 	 * moving entry to another block
924 	 * it is feasible for the fs to crash leaving two copies
925 	 * of the directory entry.  This is just too much work to
926 	 * fix.  Given that entries are only allocated in a block that
927 	 * is less than PercentageFull, most modifications of meta data
928 	 * will fit within the block.  i.e. this code should almost
929 	 * never be executed.
930 	 */
931 	boff = fileMetaAlloc(fp, &f->dir, f->boff+1);
932 	if(boff == NilBlock){
933 		/* mbResize might have modified block */
934 		mbPack(&mb);
935 		blockDirty(b);
936 		goto Err;
937 	}
938 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
939 	f->boff = boff;
940 
941 	/* make sure deletion goes to disk after new entry */
942 	bb = sourceBlock(fp->msource, f->boff, OReadWrite);
943 	mbDelete(&mb, i);
944 	mbPack(&mb);
945 	blockDependency(b, bb, -1, nil, nil);
946 	blockPut(bb);
947 	blockDirty(b);
948 	blockPut(b);
949 	sourceUnlock(fp->msource);
950 
951 	f->dirty = 0;
952 
953 	return 1;
954 
955 Err:
956 	blockPut(b);
957 Err1:
958 	sourceUnlock(fp->msource);
959 	return 0;
960 }
961 
962 static int
963 fileMetaRemove(File *f, char *uid)
964 {
965 	Block *b;
966 	MetaBlock mb;
967 	MetaEntry me;
968 	int i;
969 	File *up;
970 
971 	up = f->up;
972 
973 	fileWAccess(up, uid);
974 
975 	fileMetaLock(f);
976 
977 	sourceLock(up->msource, OReadWrite);
978 	b = sourceBlock(up->msource, f->boff, OReadWrite);
979 	if(b == nil)
980 		goto Err;
981 
982 	if(!mbUnpack(&mb, b->data, up->msource->dsize))
983 {
984 fprint(2, "U\n");
985 		goto Err;
986 }
987 	if(!mbSearch(&mb, f->dir.elem, &i, &me))
988 {
989 fprint(2, "S\n");
990 		goto Err;
991 }
992 	mbDelete(&mb, i);
993 	mbPack(&mb);
994 	sourceUnlock(up->msource);
995 
996 	blockDirty(b);
997 	blockPut(b);
998 
999 	f->removed = 1;
1000 	f->boff = NilBlock;
1001 	f->dirty = 0;
1002 
1003 	fileMetaUnlock(f);
1004 	return 1;
1005 
1006 Err:
1007 	sourceUnlock(up->msource);
1008 	blockPut(b);
1009 	fileMetaUnlock(f);
1010 	return 0;
1011 }
1012 
1013 /* assume file is locked, assume f->msource is locked */
1014 static int
1015 fileCheckEmpty(File *f)
1016 {
1017 	u32int i, n;
1018 	Block *b;
1019 	MetaBlock mb;
1020 	Source *r;
1021 
1022 	r = f->msource;
1023 	n = (sourceGetSize(r)+r->dsize-1)/r->dsize;
1024 	for(i=0; i<n; i++){
1025 		b = sourceBlock(r, i, OReadOnly);
1026 		if(b == nil)
1027 			goto Err;
1028 		if(!mbUnpack(&mb, b->data, r->dsize))
1029 			goto Err;
1030 		if(mb.nindex > 0){
1031 			vtSetError(ENotEmpty);
1032 			goto Err;
1033 		}
1034 		blockPut(b);
1035 	}
1036 	return 1;
1037 Err:
1038 	blockPut(b);
1039 	return 0;
1040 }
1041 
1042 int
1043 fileRemove(File *f, char *uid)
1044 {
1045 	File *ff;
1046 
1047 	/* can not remove the root */
1048 	if(fileIsRoot(f)){
1049 		vtSetError(ERoot);
1050 		return 0;
1051 	}
1052 
1053 	if(!fileLock(f))
1054 		return 0;
1055 
1056 	if(f->source->mode != OReadWrite){
1057 		vtSetError(EReadOnly);
1058 		goto Err1;
1059 	}
1060 	if(!sourceLock2(f->source, f->msource, -1))
1061 		goto Err1;
1062 	if(fileIsDir(f) && !fileCheckEmpty(f))
1063 		goto Err;
1064 
1065 	for(ff=f->down; ff; ff=ff->next)
1066 		assert(ff->removed);
1067 
1068 	sourceRemove(f->source);
1069 	f->source = nil;
1070 	if(f->msource){
1071 		sourceRemove(f->msource);
1072 		f->msource = nil;
1073 	}
1074 
1075 	fileUnlock(f);
1076 
1077 	if(!fileMetaRemove(f, uid))
1078 		return 0;
1079 
1080 	return 1;
1081 
1082 Err:
1083 	sourceUnlock(f->source);
1084 	if(f->msource)
1085 		sourceUnlock(f->msource);
1086 Err1:
1087 	fileUnlock(f);
1088 	return 0;
1089 }
1090 
1091 int
1092 fileClri(Fs *fs, char *path, char *uid)
1093 {
1094 	int r;
1095 	File *f;
1096 
1097 	f = _fileOpen(fs, path, 1);
1098 	if(f == nil)
1099 		return 0;
1100 	if(f->up->source->mode != OReadWrite){
1101 		vtSetError(EReadOnly);
1102 		fileDecRef(f);
1103 		return 0;
1104 	}
1105 	r = fileMetaRemove(f, uid);
1106 	fileDecRef(f);
1107 	return r;
1108 }
1109 
1110 File *
1111 fileIncRef(File *vf)
1112 {
1113 	fileMetaLock(vf);
1114 	assert(vf->ref > 0);
1115 	vf->ref++;
1116 	fileMetaUnlock(vf);
1117 	return vf;
1118 }
1119 
1120 int
1121 fileDecRef(File *f)
1122 {
1123 	File *p, *q, **qq;
1124 
1125 	if(f->up == nil){
1126 		/* never linked in */
1127 		assert(f->ref == 1);
1128 		fileFree(f);
1129 		return 1;
1130 	}
1131 
1132 	fileMetaLock(f);
1133 	f->ref--;
1134 	if(f->ref > 0){
1135 		fileMetaUnlock(f);
1136 		return 0;
1137 	}
1138 	assert(f->ref == 0);
1139 	assert(f->down == nil);
1140 
1141 	fileMetaFlush2(f, nil);
1142 
1143 	p = f->up;
1144 	qq = &p->down;
1145 	for(q = *qq; q; q = *qq){
1146 		if(q == f)
1147 			break;
1148 		qq = &q->next;
1149 	}
1150 	assert(q != nil);
1151 	*qq = f->next;
1152 
1153 	fileMetaUnlock(f);
1154 	fileFree(f);
1155 
1156 	fileDecRef(p);
1157 	return 1;
1158 }
1159 
1160 File *
1161 fileGetParent(File *f)
1162 {
1163 	if(fileIsRoot(f))
1164 		return fileIncRef(f);
1165 	return fileIncRef(f->up);
1166 }
1167 
1168 DirEntryEnum *
1169 deeOpen(File *f)
1170 {
1171 	DirEntryEnum *dee;
1172 	File *p;
1173 
1174 	if(!fileIsDir(f)){
1175 		vtSetError(ENotDir);
1176 		fileDecRef(f);
1177 		return nil;
1178 	}
1179 
1180 	/* flush out meta data */
1181 	if(!fileLock(f))
1182 		return nil;
1183 	for(p=f->down; p; p=p->next)
1184 		fileMetaFlush2(p, nil);
1185 	fileUnlock(f);
1186 
1187 	dee = vtMemAllocZ(sizeof(DirEntryEnum));
1188 	dee->file = fileIncRef(f);
1189 
1190 	return dee;
1191 }
1192 
1193 static int
1194 dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
1195 {
1196 	Block *b;
1197 	ulong bn;
1198 	Entry e;
1199 	int epb;
1200 
1201 	epb = s->dsize/VtEntrySize;
1202 	bn = elem/epb;
1203 	elem -= bn*epb;
1204 
1205 	b = sourceBlock(s, bn, OReadOnly);
1206 	if(b == nil)
1207 		goto Err;
1208 	if(!entryUnpack(&e, b->data, elem))
1209 		goto Err;
1210 
1211 	/* hanging entries are returned as zero size */
1212 	if(!(e.flags & VtEntryActive) || e.gen != gen)
1213 		*size = 0;
1214 	else
1215 		*size = e.size;
1216 	blockPut(b);
1217 	return 1;
1218 
1219 Err:
1220 	blockPut(b);
1221 	return 0;
1222 }
1223 
1224 static int
1225 deeFill(DirEntryEnum *dee)
1226 {
1227 	int i, n;
1228 	Source *meta, *source;
1229 	MetaBlock mb;
1230 	MetaEntry me;
1231 	File *f;
1232 	Block *b;
1233 	DirEntry *de;
1234 
1235 	/* clean up first */
1236 	for(i=dee->i; i<dee->n; i++)
1237 		deCleanup(dee->buf+i);
1238 	vtMemFree(dee->buf);
1239 	dee->buf = nil;
1240 	dee->i = 0;
1241 	dee->n = 0;
1242 
1243 	f = dee->file;
1244 
1245 	source = f->source;
1246 	meta = f->msource;
1247 
1248 	b = sourceBlock(meta, dee->boff, OReadOnly);
1249 	if(b == nil)
1250 		goto Err;
1251 	if(!mbUnpack(&mb, b->data, meta->dsize))
1252 		goto Err;
1253 
1254 	n = mb.nindex;
1255 	dee->buf = vtMemAlloc(n * sizeof(DirEntry));
1256 
1257 	for(i=0; i<n; i++){
1258 		de = dee->buf + i;
1259 		meUnpack(&me, &mb, i);
1260 		if(!deUnpack(de, &me))
1261 			goto Err;
1262 		dee->n++;
1263 		if(!(de->mode & ModeDir))
1264 		if(!dirEntrySize(source, de->entry, de->gen, &de->size))
1265 			goto Err;
1266 	}
1267 	dee->boff++;
1268 	blockPut(b);
1269 	return 1;
1270 Err:
1271 	blockPut(b);
1272 	return 0;
1273 }
1274 
1275 int
1276 deeRead(DirEntryEnum *dee, DirEntry *de)
1277 {
1278 	int ret, didread;
1279 	File *f;
1280 	u32int nb;
1281 
1282 	f = dee->file;
1283 	if(!fileRLock(f))
1284 		return -1;
1285 
1286 	if(!sourceLock2(f->source, f->msource, OReadOnly)){
1287 		fileRUnlock(f);
1288 		return -1;
1289 	}
1290 
1291 	nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
1292 
1293 	didread = 0;
1294 	while(dee->i >= dee->n){
1295 		if(dee->boff >= nb){
1296 			ret = 0;
1297 			goto Return;
1298 		}
1299 		didread = 1;
1300 		if(!deeFill(dee)){
1301 			ret = -1;
1302 			goto Return;
1303 		}
1304 	}
1305 
1306 	memmove(de, dee->buf + dee->i, sizeof(DirEntry));
1307 	dee->i++;
1308 	ret = 1;
1309 
1310 Return:
1311 	sourceUnlock(f->source);
1312 	sourceUnlock(f->msource);
1313 	fileRUnlock(f);
1314 
1315 	if(didread)
1316 		fileRAccess(f);
1317 	return ret;
1318 }
1319 
1320 void
1321 deeClose(DirEntryEnum *dee)
1322 {
1323 	int i;
1324 	if(dee == nil)
1325 		return;
1326 	for(i=dee->i; i<dee->n; i++)
1327 		deCleanup(dee->buf+i);
1328 	vtMemFree(dee->buf);
1329 	fileDecRef(dee->file);
1330 	vtMemFree(dee);
1331 }
1332 
1333 /*
1334  * caller must lock f->source and f->msource
1335  * caller must NOT lock the source and msource
1336  * referenced by dir.
1337  */
1338 static u32int
1339 fileMetaAlloc(File *f, DirEntry *dir, u32int start)
1340 {
1341 	u32int nb, bo;
1342 	Block *b, *bb;
1343 	MetaBlock mb;
1344 	int nn;
1345 	uchar *p;
1346 	int i, n, epb;
1347 	MetaEntry me;
1348 	Source *s, *ms;
1349 
1350 	s = f->source;
1351 	ms = f->msource;
1352 
1353 	n = deSize(dir);
1354 	nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize;
1355 	b = nil;
1356 	if(start > nb)
1357 		start = nb;
1358 	for(bo=start; bo<nb; bo++){
1359 		b = sourceBlock(ms, bo, OReadWrite);
1360 		if(b == nil)
1361 			goto Err;
1362 		if(!mbUnpack(&mb, b->data, ms->dsize))
1363 			goto Err;
1364 		nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
1365 		if(n <= nn && mb.nindex < mb.maxindex)
1366 			break;
1367 		blockPut(b);
1368 		b = nil;
1369 	}
1370 
1371 	/* add block to meta file */
1372 	if(b == nil){
1373 		b = sourceBlock(ms, bo, OReadWrite);
1374 		if(b == nil)
1375 			goto Err;
1376 		sourceSetSize(ms, (nb+1)*ms->dsize);
1377 		mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
1378 	}
1379 
1380 	p = mbAlloc(&mb, n);
1381 	if(p == nil){
1382 		/* mbAlloc might have changed block */
1383 		mbPack(&mb);
1384 		blockDirty(b);
1385 		vtSetError(EBadMeta);
1386 		goto Err;
1387 	}
1388 
1389 	mbSearch(&mb, dir->elem, &i, &me);
1390 	assert(me.p == nil);
1391 	me.p = p;
1392 	me.size = n;
1393 	dePack(dir, &me);
1394 	mbInsert(&mb, i, &me);
1395 	mbPack(&mb);
1396 
1397 	/* meta block depends on super block for qid ... */
1398 	bb = cacheLocal(b->c, PartSuper, 0, OReadOnly);
1399 	blockDependency(b, bb, -1, nil, nil);
1400 	blockPut(bb);
1401 
1402 	/* ... and one or two dir entries */
1403 	epb = s->dsize/VtEntrySize;
1404 	bb = sourceBlock(s, dir->entry/epb, OReadOnly);
1405 	blockDependency(b, bb, -1, nil, nil);
1406 	blockPut(bb);
1407 	if(dir->mode & ModeDir){
1408 		bb = sourceBlock(s, dir->mentry/epb, OReadOnly);
1409 		blockDependency(b, bb, -1, nil, nil);
1410 		blockPut(bb);
1411 	}
1412 
1413 	blockDirty(b);
1414 	blockPut(b);
1415 	return bo;
1416 Err:
1417 	blockPut(b);
1418 	return NilBlock;
1419 }
1420 
1421 static int
1422 chkSource(File *f)
1423 {
1424 	if(f->partial)
1425 		return 1;
1426 
1427 	if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
1428 		vtSetError(ERemoved);
1429 		return 0;
1430 	}
1431 	return 1;
1432 }
1433 
1434 static int
1435 fileRLock(File *f)
1436 {
1437 	assert(!vtCanLock(f->fs->elk));
1438 	vtRLock(f->lk);
1439 	if(!chkSource(f)){
1440 		fileRUnlock(f);
1441 		return 0;
1442 	}
1443 	return 1;
1444 }
1445 
1446 static void
1447 fileRUnlock(File *f)
1448 {
1449 	vtRUnlock(f->lk);
1450 }
1451 
1452 static int
1453 fileLock(File *f)
1454 {
1455 	assert(!vtCanLock(f->fs->elk));
1456 	vtLock(f->lk);
1457 	if(!chkSource(f)){
1458 		fileUnlock(f);
1459 		return 0;
1460 	}
1461 	return 1;
1462 }
1463 
1464 static void
1465 fileUnlock(File *f)
1466 {
1467 	vtUnlock(f->lk);
1468 }
1469 
1470 /*
1471  * f->source and f->msource must NOT be locked.
1472  * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
1473  * We have to respect that ordering.
1474  */
1475 static void
1476 fileMetaLock(File *f)
1477 {
1478 if(f->up == nil)
1479 fprint(2, "f->elem = %s\n", f->dir.elem);
1480 	assert(f->up != nil);
1481 	assert(!vtCanLock(f->fs->elk));
1482 	vtLock(f->up->lk);
1483 }
1484 
1485 static void
1486 fileMetaUnlock(File *f)
1487 {
1488 	vtUnlock(f->up->lk);
1489 }
1490 
1491 /*
1492  * f->source and f->msource must NOT be locked.
1493  * see fileMetaLock.
1494  */
1495 static void
1496 fileRAccess(File* f)
1497 {
1498 	if(f->mode == OReadOnly)
1499 		return;
1500 
1501 	fileMetaLock(f);
1502 	f->dir.atime = time(0L);
1503 	f->dirty = 1;
1504 	fileMetaUnlock(f);
1505 }
1506 
1507 /*
1508  * f->source and f->msource must NOT be locked.
1509  * see fileMetaLock.
1510  */
1511 static void
1512 fileWAccess(File* f, char *mid)
1513 {
1514 	if(f->mode == OReadOnly)
1515 		return;
1516 
1517 	fileMetaLock(f);
1518 	f->dir.atime = f->dir.mtime = time(0L);
1519 	if(strcmp(f->dir.mid, mid) != 0){
1520 		vtMemFree(f->dir.mid);
1521 		f->dir.mid = vtStrDup(mid);
1522 	}
1523 	f->dir.mcount++;
1524 	f->dirty = 1;
1525 	fileMetaUnlock(f);
1526 }
1527 
1528 static void
1529 markCopied(Block *b)
1530 {
1531 	Block *lb;
1532 	Label l;
1533 
1534 	if(globalToLocal(b->score) == NilBlock)
1535 		return;
1536 
1537 	if(!(b->l.state & BsCopied)){
1538 		/*
1539 		 * We need to record that there are now pointers in
1540 		 * b that are not unique to b.  We do this by marking
1541 		 * b as copied.  Since we don't return the label block,
1542 		 * the caller can't get the dependencies right.  So we have
1543 		 * to flush the block ourselves.  This is a rare occurrence.
1544 		 */
1545 		l = b->l;
1546 		l.state |= BsCopied;
1547 		lb = _blockSetLabel(b, &l);
1548 	WriteAgain:
1549 		while(!blockWrite(lb)){
1550 			fprint(2, "getEntry: could not write label block\n");
1551 			sleep(10*1000);
1552 		}
1553 		while(lb->iostate != BioClean && lb->iostate != BioDirty){
1554 			assert(lb->iostate == BioWriting);
1555 			vtSleep(lb->ioready);
1556 		}
1557 		if(lb->iostate == BioDirty)
1558 			goto WriteAgain;
1559 		blockPut(lb);
1560 	}
1561 }
1562 
1563 static int
1564 getEntry(Source *r, Entry *e, int mark)
1565 {
1566 	Block *b;
1567 
1568 	if(r == nil){
1569 		memset(&e, 0, sizeof e);
1570 		return 1;
1571 	}
1572 
1573 	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly);
1574 	if(b == nil)
1575 		return 0;
1576 	if(!entryUnpack(e, b->data, r->offset % r->epb)){
1577 		blockPut(b);
1578 		return 0;
1579 	}
1580 
1581 	if(mark)
1582 		markCopied(b);
1583 	blockPut(b);
1584 	return 1;
1585 }
1586 
1587 static int
1588 setEntry(Source *r, Entry *e)
1589 {
1590 	Block *b;
1591 	Entry oe;
1592 
1593 	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
1594 	if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
1595 	if(b == nil)
1596 		return 0;
1597 	if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
1598 		blockPut(b);
1599 		return 0;
1600 	}
1601 	e->gen = oe.gen;
1602 	entryPack(e, b->data, r->offset % r->epb);
1603 
1604 	/* BUG b should depend on the entry pointer */
1605 
1606 	markCopied(b);
1607 	blockDirty(b);
1608 	blockPut(b);
1609 	return 1;
1610 }
1611 
1612 /* assumes hold elk */
1613 int
1614 fileSnapshot(File *dst, File *src, u32int epoch, int doarchive)
1615 {
1616 	Entry e, ee;
1617 
1618 	/* add link to snapshot */
1619 	if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
1620 		return 0;
1621 
1622 	e.snap = epoch;
1623 	e.archive = doarchive;
1624 	ee.snap = epoch;
1625 	ee.archive = doarchive;
1626 
1627 	if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
1628 		return 0;
1629 	return 1;
1630 }
1631 
1632 int
1633 fileGetSources(File *f, Entry *e, Entry *ee, int mark)
1634 {
1635 	if(!getEntry(f->source, e, mark)
1636 	|| !getEntry(f->msource, ee, mark))
1637 		return 0;
1638 	return 1;
1639 }
1640 
1641 int
1642 fileWalkSources(File *f)
1643 {
1644 	if(f->mode == OReadOnly)
1645 		return 1;
1646 	if(!sourceLock2(f->source, f->msource, OReadWrite))
1647 		return 0;
1648 	sourceUnlock(f->source);
1649 	sourceUnlock(f->msource);
1650 	return 1;
1651 }
1652