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