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