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