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