plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

file.c (31246B)


      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 *
     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
     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 *
     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 *
    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 *
    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 *
    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 *
    327 fileWalk(File *f, char *elem)
    328 {
    329 	return _fileWalk(f, elem, 0);
    330 }
    331 
    332 File *
    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*
    373 fileOpen(Fs *fs, char *path)
    374 {
    375 	return _fileOpen(fs, path, 0);
    376 }
    377 
    378 static void
    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 *
    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
    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
    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
    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
    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
    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
    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
    804 fileSetDir(File *f, DirEntry *dir, char *uid)
    805 {
    806 	File *ff;
    807 	char *oelem;
    808 	u32int mask;
    809 	u64int size;
    810 
    811 	/* can not set permissions for the root */
    812 	if(fileIsRoot(f)){
    813 		werrstr(ERoot);
    814 		return 0;
    815 	}
    816 
    817 	if(!fileLock(f))
    818 		return 0;
    819 
    820 	if(f->source->mode != OReadWrite){
    821 		werrstr(EReadOnly);
    822 		fileUnlock(f);
    823 		return 0;
    824 	}
    825 
    826 	fileMetaLock(f);
    827 
    828 	/* check new name does not already exist */
    829 	if(strcmp(f->dir.elem, dir->elem) != 0){
    830 		for(ff = f->up->down; ff; ff=ff->next){
    831 			if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
    832 				werrstr(EExists);
    833 				goto Err;
    834 			}
    835 		}
    836 
    837 		ff = dirLookup(f->up, dir->elem);
    838 		if(ff != nil){
    839 			fileDecRef(ff);
    840 			werrstr(EExists);
    841 			goto Err;
    842 		}
    843 	}
    844 
    845 	if(!sourceLock2(f->source, f->msource, -1))
    846 		goto Err;
    847 	if(!fileIsDir(f)){
    848 		size = sourceGetSize(f->source);
    849 		if(size != dir->size){
    850 			if(!sourceSetSize(f->source, dir->size)){
    851 				sourceUnlock(f->source);
    852 				if(f->msource)
    853 					sourceUnlock(f->msource);
    854 				goto Err;
    855 			}
    856 			/* commited to changing it now */
    857 		}
    858 	}
    859 	/* commited to changing it now */
    860 	if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
    861 		fileSetTmp(f, dir->mode&ModeTemporary);
    862 	sourceUnlock(f->source);
    863 	if(f->msource)
    864 		sourceUnlock(f->msource);
    865 
    866 	oelem = nil;
    867 	if(strcmp(f->dir.elem, dir->elem) != 0){
    868 		oelem = f->dir.elem;
    869 		f->dir.elem = vtstrdup(dir->elem);
    870 	}
    871 
    872 	if(strcmp(f->dir.uid, dir->uid) != 0){
    873 		vtfree(f->dir.uid);
    874 		f->dir.uid = vtstrdup(dir->uid);
    875 	}
    876 
    877 	if(strcmp(f->dir.gid, dir->gid) != 0){
    878 		vtfree(f->dir.gid);
    879 		f->dir.gid = vtstrdup(dir->gid);
    880 	}
    881 
    882 	f->dir.mtime = dir->mtime;
    883 	f->dir.atime = dir->atime;
    884 
    885 //fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
    886 	mask = ~(ModeDir|ModeSnapshot);
    887 	f->dir.mode &= ~mask;
    888 	f->dir.mode |= mask & dir->mode;
    889 	f->dirty = 1;
    890 //fprint(2, "->%x\n", f->dir.mode);
    891 
    892 	fileMetaFlush2(f, oelem);
    893 	vtfree(oelem);
    894 
    895 	fileMetaUnlock(f);
    896 	fileUnlock(f);
    897 
    898 	fileWAccess(f->up, uid);
    899 
    900 	return 1;
    901 Err:
    902 	fileMetaUnlock(f);
    903 	fileUnlock(f);
    904 	return 0;
    905 }
    906 
    907 int
    908 fileSetQidSpace(File *f, u64int offset, u64int max)
    909 {
    910 	int ret;
    911 
    912 	if(!fileLock(f))
    913 		return 0;
    914 	fileMetaLock(f);
    915 	f->dir.qidSpace = 1;
    916 	f->dir.qidOffset = offset;
    917 	f->dir.qidMax = max;
    918 	ret = fileMetaFlush2(f, nil)>=0;
    919 	fileMetaUnlock(f);
    920 	fileUnlock(f);
    921 	return ret;
    922 }
    923 
    924 
    925 uvlong
    926 fileGetId(File *f)
    927 {
    928 	/* immutable */
    929 	return f->dir.qid;
    930 }
    931 
    932 ulong
    933 fileGetMcount(File *f)
    934 {
    935 	ulong mcount;
    936 
    937 	fileMetaLock(f);
    938 	mcount = f->dir.mcount;
    939 	fileMetaUnlock(f);
    940 	return mcount;
    941 }
    942 
    943 ulong
    944 fileGetMode(File *f)
    945 {
    946 	ulong mode;
    947 
    948 	fileMetaLock(f);
    949 	mode = f->dir.mode;
    950 	fileMetaUnlock(f);
    951 	return mode;
    952 }
    953 
    954 int
    955 fileIsDir(File *f)
    956 {
    957 	/* immutable */
    958 	return (f->dir.mode & ModeDir) != 0;
    959 }
    960 
    961 int
    962 fileIsAppend(File *f)
    963 {
    964 	return (f->dir.mode & ModeAppend) != 0;
    965 }
    966 
    967 int
    968 fileIsExclusive(File *f)
    969 {
    970 	return (f->dir.mode & ModeExclusive) != 0;
    971 }
    972 
    973 int
    974 fileIsTemporary(File *f)
    975 {
    976 	return (f->dir.mode & ModeTemporary) != 0;
    977 }
    978 
    979 int
    980 fileIsRoot(File *f)
    981 {
    982 	return f == f->fs->file;
    983 }
    984 
    985 int
    986 fileIsRoFs(File *f)
    987 {
    988 	return f->fs->mode == OReadOnly;
    989 }
    990 
    991 int
    992 fileGetSize(File *f, uvlong *size)
    993 {
    994 	if(!fileRLock(f))
    995 		return 0;
    996 	if(!sourceLock(f->source, OReadOnly)){
    997 		fileRUnlock(f);
    998 		return 0;
    999 	}
   1000 	*size = sourceGetSize(f->source);
   1001 	sourceUnlock(f->source);
   1002 	fileRUnlock(f);
   1003 
   1004 	return 1;
   1005 }
   1006 
   1007 int
   1008 fileMetaFlush(File *f, int rec)
   1009 {
   1010 	File **kids, *p;
   1011 	int nkids;
   1012 	int i, rv;
   1013 
   1014 	fileMetaLock(f);
   1015 	rv = fileMetaFlush2(f, nil);
   1016 	fileMetaUnlock(f);
   1017 
   1018 	if(!rec || !fileIsDir(f))
   1019 		return rv;
   1020 
   1021 	if(!fileLock(f))
   1022 		return rv;
   1023 	nkids = 0;
   1024 	for(p=f->down; p; p=p->next)
   1025 		nkids++;
   1026 	kids = vtmalloc(nkids*sizeof(File*));
   1027 	i = 0;
   1028 	for(p=f->down; p; p=p->next){
   1029 		kids[i++] = p;
   1030 		p->ref++;
   1031 	}
   1032 	fileUnlock(f);
   1033 
   1034 	for(i=0; i<nkids; i++){
   1035 		rv |= fileMetaFlush(kids[i], 1);
   1036 		fileDecRef(kids[i]);
   1037 	}
   1038 	vtfree(kids);
   1039 	return rv;
   1040 }
   1041 
   1042 /* assumes metaLock is held */
   1043 static int
   1044 fileMetaFlush2(File *f, char *oelem)
   1045 {
   1046 	File *fp;
   1047 	Block *b, *bb;
   1048 	MetaBlock mb;
   1049 	MetaEntry me, me2;
   1050 	int i, n;
   1051 	u32int boff;
   1052 
   1053 	if(!f->dirty)
   1054 		return 0;
   1055 
   1056 	if(oelem == nil)
   1057 		oelem = f->dir.elem;
   1058 
   1059 //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem);
   1060 
   1061 	fp = f->up;
   1062 
   1063 	if(!sourceLock(fp->msource, -1))
   1064 		return -1;
   1065 	/* can happen if source is clri'ed out from under us */
   1066 	if(f->boff == NilBlock)
   1067 		goto Err1;
   1068 	b = sourceBlock(fp->msource, f->boff, OReadWrite);
   1069 	if(b == nil)
   1070 		goto Err1;
   1071 
   1072 	if(!mbUnpack(&mb, b->data, fp->msource->dsize))
   1073 		goto Err;
   1074 	if(!mbSearch(&mb, oelem, &i, &me))
   1075 		goto Err;
   1076 
   1077 	n = deSize(&f->dir);
   1078 if(0)fprint(2, "old size %d new size %d\n", me.size, n);
   1079 
   1080 	if(mbResize(&mb, &me, n)){
   1081 		/* fits in the block */
   1082 		mbDelete(&mb, i);
   1083 		if(strcmp(f->dir.elem, oelem) != 0)
   1084 			mbSearch(&mb, f->dir.elem, &i, &me2);
   1085 		dePack(&f->dir, &me);
   1086 		mbInsert(&mb, i, &me);
   1087 		mbPack(&mb);
   1088 		blockDirty(b);
   1089 		blockPut(b);
   1090 		sourceUnlock(fp->msource);
   1091 		f->dirty = 0;
   1092 
   1093 		return 1;
   1094 	}
   1095 
   1096 	/*
   1097 	 * moving entry to another block
   1098 	 * it is feasible for the fs to crash leaving two copies
   1099 	 * of the directory entry.  This is just too much work to
   1100 	 * fix.  Given that entries are only allocated in a block that
   1101 	 * is less than PercentageFull, most modifications of meta data
   1102 	 * will fit within the block.  i.e. this code should almost
   1103 	 * never be executed.
   1104 	 */
   1105 	boff = fileMetaAlloc(fp, &f->dir, f->boff+1);
   1106 	if(boff == NilBlock){
   1107 		/* mbResize might have modified block */
   1108 		mbPack(&mb);
   1109 		blockDirty(b);
   1110 		goto Err;
   1111 	}
   1112 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
   1113 	f->boff = boff;
   1114 
   1115 	/* make sure deletion goes to disk after new entry */
   1116 	bb = sourceBlock(fp->msource, f->boff, OReadWrite);
   1117 	mbDelete(&mb, i);
   1118 	mbPack(&mb);
   1119 	blockDependency(b, bb, -1, nil, nil);
   1120 	blockPut(bb);
   1121 	blockDirty(b);
   1122 	blockPut(b);
   1123 	sourceUnlock(fp->msource);
   1124 
   1125 	f->dirty = 0;
   1126 
   1127 	return 1;
   1128 
   1129 Err:
   1130 	blockPut(b);
   1131 Err1:
   1132 	sourceUnlock(fp->msource);
   1133 	return -1;
   1134 }
   1135 
   1136 static int
   1137 fileMetaRemove(File *f, char *uid)
   1138 {
   1139 	Block *b;
   1140 	MetaBlock mb;
   1141 	MetaEntry me;
   1142 	int i;
   1143 	File *up;
   1144 
   1145 	up = f->up;
   1146 
   1147 	fileWAccess(up, uid);
   1148 
   1149 	fileMetaLock(f);
   1150 
   1151 	sourceLock(up->msource, OReadWrite);
   1152 	b = sourceBlock(up->msource, f->boff, OReadWrite);
   1153 	if(b == nil)
   1154 		goto Err;
   1155 
   1156 	if(!mbUnpack(&mb, b->data, up->msource->dsize))
   1157 {
   1158 fprint(2, "U\n");
   1159 		goto Err;
   1160 }
   1161 	if(!mbSearch(&mb, f->dir.elem, &i, &me))
   1162 {
   1163 fprint(2, "S\n");
   1164 		goto Err;
   1165 }
   1166 	mbDelete(&mb, i);
   1167 	mbPack(&mb);
   1168 	sourceUnlock(up->msource);
   1169 
   1170 	blockDirty(b);
   1171 	blockPut(b);
   1172 
   1173 	f->removed = 1;
   1174 	f->boff = NilBlock;
   1175 	f->dirty = 0;
   1176 
   1177 	fileMetaUnlock(f);
   1178 	return 1;
   1179 
   1180 Err:
   1181 	sourceUnlock(up->msource);
   1182 	blockPut(b);
   1183 	fileMetaUnlock(f);
   1184 	return 0;
   1185 }
   1186 
   1187 /* assume file is locked, assume f->msource is locked */
   1188 static int
   1189 fileCheckEmpty(File *f)
   1190 {
   1191 	u32int i, n;
   1192 	Block *b;
   1193 	MetaBlock mb;
   1194 	Source *r;
   1195 
   1196 	r = f->msource;
   1197 	n = (sourceGetSize(r)+r->dsize-1)/r->dsize;
   1198 	for(i=0; i<n; i++){
   1199 		b = sourceBlock(r, i, OReadOnly);
   1200 		if(b == nil)
   1201 			goto Err;
   1202 		if(!mbUnpack(&mb, b->data, r->dsize))
   1203 			goto Err;
   1204 		if(mb.nindex > 0){
   1205 			werrstr(ENotEmpty);
   1206 			goto Err;
   1207 		}
   1208 		blockPut(b);
   1209 	}
   1210 	return 1;
   1211 Err:
   1212 	blockPut(b);
   1213 	return 0;
   1214 }
   1215 
   1216 int
   1217 fileRemove(File *f, char *uid)
   1218 {
   1219 	File *ff;
   1220 
   1221 	/* can not remove the root */
   1222 	if(fileIsRoot(f)){
   1223 		werrstr(ERoot);
   1224 		return 0;
   1225 	}
   1226 
   1227 	if(!fileLock(f))
   1228 		return 0;
   1229 
   1230 	if(f->source->mode != OReadWrite){
   1231 		werrstr(EReadOnly);
   1232 		goto Err1;
   1233 	}
   1234 	if(!sourceLock2(f->source, f->msource, -1))
   1235 		goto Err1;
   1236 	if(fileIsDir(f) && !fileCheckEmpty(f))
   1237 		goto Err;
   1238 
   1239 	for(ff=f->down; ff; ff=ff->next)
   1240 		assert(ff->removed);
   1241 
   1242 	sourceRemove(f->source);
   1243 	f->source->file = nil;		/* erase back pointer */
   1244 	f->source = nil;
   1245 	if(f->msource){
   1246 		sourceRemove(f->msource);
   1247 		f->msource = nil;
   1248 	}
   1249 
   1250 	fileUnlock(f);
   1251 
   1252 	if(!fileMetaRemove(f, uid))
   1253 		return 0;
   1254 
   1255 	return 1;
   1256 
   1257 Err:
   1258 	sourceUnlock(f->source);
   1259 	if(f->msource)
   1260 		sourceUnlock(f->msource);
   1261 Err1:
   1262 	fileUnlock(f);
   1263 	return 0;
   1264 }
   1265 
   1266 static int
   1267 clri(File *f, char *uid)
   1268 {
   1269 	int r;
   1270 
   1271 	if(f == nil)
   1272 		return 0;
   1273 	if(f->up->source->mode != OReadWrite){
   1274 		werrstr(EReadOnly);
   1275 		fileDecRef(f);
   1276 		return 0;
   1277 	}
   1278 	r = fileMetaRemove(f, uid);
   1279 	fileDecRef(f);
   1280 	return r;
   1281 }
   1282 
   1283 int
   1284 fileClriPath(Fs *fs, char *path, char *uid)
   1285 {
   1286 	return clri(_fileOpen(fs, path, 1), uid);
   1287 }
   1288 
   1289 int
   1290 fileClri(File *dir, char *elem, char *uid)
   1291 {
   1292 	return clri(_fileWalk(dir, elem, 1), uid);
   1293 }
   1294 
   1295 File *
   1296 fileIncRef(File *vf)
   1297 {
   1298 	fileMetaLock(vf);
   1299 	assert(vf->ref > 0);
   1300 	vf->ref++;
   1301 	fileMetaUnlock(vf);
   1302 	return vf;
   1303 }
   1304 
   1305 int
   1306 fileDecRef(File *f)
   1307 {
   1308 	File *p, *q, **qq;
   1309 
   1310 	if(f->up == nil){
   1311 		/* never linked in */
   1312 		assert(f->ref == 1);
   1313 		fileFree(f);
   1314 		return 1;
   1315 	}
   1316 
   1317 	fileMetaLock(f);
   1318 	f->ref--;
   1319 	if(f->ref > 0){
   1320 		fileMetaUnlock(f);
   1321 		return 0;
   1322 	}
   1323 	assert(f->ref == 0);
   1324 	assert(f->down == nil);
   1325 
   1326 	fileMetaFlush2(f, nil);
   1327 
   1328 	p = f->up;
   1329 	qq = &p->down;
   1330 	for(q = *qq; q; q = *qq){
   1331 		if(q == f)
   1332 			break;
   1333 		qq = &q->next;
   1334 	}
   1335 	assert(q != nil);
   1336 	*qq = f->next;
   1337 
   1338 	fileMetaUnlock(f);
   1339 	fileFree(f);
   1340 
   1341 	fileDecRef(p);
   1342 	return 1;
   1343 }
   1344 
   1345 File *
   1346 fileGetParent(File *f)
   1347 {
   1348 	if(fileIsRoot(f))
   1349 		return fileIncRef(f);
   1350 	return fileIncRef(f->up);
   1351 }
   1352 
   1353 DirEntryEnum *
   1354 deeOpen(File *f)
   1355 {
   1356 	DirEntryEnum *dee;
   1357 	File *p;
   1358 
   1359 	if(!fileIsDir(f)){
   1360 		werrstr(ENotDir);
   1361 		fileDecRef(f);
   1362 		return nil;
   1363 	}
   1364 
   1365 	/* flush out meta data */
   1366 	if(!fileLock(f))
   1367 		return nil;
   1368 	for(p=f->down; p; p=p->next)
   1369 		fileMetaFlush2(p, nil);
   1370 	fileUnlock(f);
   1371 
   1372 	dee = vtmallocz(sizeof(DirEntryEnum));
   1373 	dee->file = fileIncRef(f);
   1374 
   1375 	return dee;
   1376 }
   1377 
   1378 static int
   1379 dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
   1380 {
   1381 	Block *b;
   1382 	ulong bn;
   1383 	Entry e;
   1384 	int epb;
   1385 
   1386 	epb = s->dsize/VtEntrySize;
   1387 	bn = elem/epb;
   1388 	elem -= bn*epb;
   1389 
   1390 	b = sourceBlock(s, bn, OReadOnly);
   1391 	if(b == nil)
   1392 		goto Err;
   1393 	if(!entryUnpack(&e, b->data, elem))
   1394 		goto Err;
   1395 
   1396 	/* hanging entries are returned as zero size */
   1397 	if(!(e.flags & VtEntryActive) || e.gen != gen)
   1398 		*size = 0;
   1399 	else
   1400 		*size = e.size;
   1401 	blockPut(b);
   1402 	return 1;
   1403 
   1404 Err:
   1405 	blockPut(b);
   1406 	return 0;
   1407 }
   1408 
   1409 static int
   1410 deeFill(DirEntryEnum *dee)
   1411 {
   1412 	int i, n;
   1413 	Source *meta, *source;
   1414 	MetaBlock mb;
   1415 	MetaEntry me;
   1416 	File *f;
   1417 	Block *b;
   1418 	DirEntry *de;
   1419 
   1420 	/* clean up first */
   1421 	for(i=dee->i; i<dee->n; i++)
   1422 		deCleanup(dee->buf+i);
   1423 	vtfree(dee->buf);
   1424 	dee->buf = nil;
   1425 	dee->i = 0;
   1426 	dee->n = 0;
   1427 
   1428 	f = dee->file;
   1429 
   1430 	source = f->source;
   1431 	meta = f->msource;
   1432 
   1433 	b = sourceBlock(meta, dee->boff, OReadOnly);
   1434 	if(b == nil)
   1435 		goto Err;
   1436 	if(!mbUnpack(&mb, b->data, meta->dsize))
   1437 		goto Err;
   1438 
   1439 	n = mb.nindex;
   1440 	dee->buf = vtmalloc(n * sizeof(DirEntry));
   1441 
   1442 	for(i=0; i<n; i++){
   1443 		de = dee->buf + i;
   1444 		meUnpack(&me, &mb, i);
   1445 		if(!deUnpack(de, &me))
   1446 			goto Err;
   1447 		dee->n++;
   1448 		if(!(de->mode & ModeDir))
   1449 		if(!dirEntrySize(source, de->entry, de->gen, &de->size))
   1450 			goto Err;
   1451 	}
   1452 	dee->boff++;
   1453 	blockPut(b);
   1454 	return 1;
   1455 Err:
   1456 	blockPut(b);
   1457 	return 0;
   1458 }
   1459 
   1460 int
   1461 deeRead(DirEntryEnum *dee, DirEntry *de)
   1462 {
   1463 	int ret, didread;
   1464 	File *f;
   1465 	u32int nb;
   1466 
   1467 	if(dee == nil){
   1468 		werrstr("cannot happen in deeRead");
   1469 		return -1;
   1470 	}
   1471 
   1472 	f = dee->file;
   1473 	if(!fileRLock(f))
   1474 		return -1;
   1475 
   1476 	if(!sourceLock2(f->source, f->msource, OReadOnly)){
   1477 		fileRUnlock(f);
   1478 		return -1;
   1479 	}
   1480 
   1481 	nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
   1482 
   1483 	didread = 0;
   1484 	while(dee->i >= dee->n){
   1485 		if(dee->boff >= nb){
   1486 			ret = 0;
   1487 			goto Return;
   1488 		}
   1489 		didread = 1;
   1490 		if(!deeFill(dee)){
   1491 			ret = -1;
   1492 			goto Return;
   1493 		}
   1494 	}
   1495 
   1496 	memmove(de, dee->buf + dee->i, sizeof(DirEntry));
   1497 	dee->i++;
   1498 	ret = 1;
   1499 
   1500 Return:
   1501 	sourceUnlock(f->source);
   1502 	sourceUnlock(f->msource);
   1503 	fileRUnlock(f);
   1504 
   1505 	if(didread)
   1506 		fileRAccess(f);
   1507 	return ret;
   1508 }
   1509 
   1510 void
   1511 deeClose(DirEntryEnum *dee)
   1512 {
   1513 	int i;
   1514 	if(dee == nil)
   1515 		return;
   1516 	for(i=dee->i; i<dee->n; i++)
   1517 		deCleanup(dee->buf+i);
   1518 	vtfree(dee->buf);
   1519 	fileDecRef(dee->file);
   1520 	vtfree(dee);
   1521 }
   1522 
   1523 /*
   1524  * caller must lock f->source and f->msource
   1525  * caller must NOT lock the source and msource
   1526  * referenced by dir.
   1527  */
   1528 static u32int
   1529 fileMetaAlloc(File *f, DirEntry *dir, u32int start)
   1530 {
   1531 	u32int nb, bo;
   1532 	Block *b, *bb;
   1533 	MetaBlock mb;
   1534 	int nn;
   1535 	uchar *p;
   1536 	int i, n, epb;
   1537 	MetaEntry me;
   1538 	Source *s, *ms;
   1539 
   1540 	s = f->source;
   1541 	ms = f->msource;
   1542 
   1543 	n = deSize(dir);
   1544 	nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize;
   1545 	b = nil;
   1546 	if(start > nb)
   1547 		start = nb;
   1548 	for(bo=start; bo<nb; bo++){
   1549 		b = sourceBlock(ms, bo, OReadWrite);
   1550 		if(b == nil)
   1551 			goto Err;
   1552 		if(!mbUnpack(&mb, b->data, ms->dsize))
   1553 			goto Err;
   1554 		nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
   1555 		if(n <= nn && mb.nindex < mb.maxindex)
   1556 			break;
   1557 		blockPut(b);
   1558 		b = nil;
   1559 	}
   1560 
   1561 	/* add block to meta file */
   1562 	if(b == nil){
   1563 		b = sourceBlock(ms, bo, OReadWrite);
   1564 		if(b == nil)
   1565 			goto Err;
   1566 		sourceSetSize(ms, (nb+1)*ms->dsize);
   1567 		mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
   1568 	}
   1569 
   1570 	p = mbAlloc(&mb, n);
   1571 	if(p == nil){
   1572 		/* mbAlloc might have changed block */
   1573 		mbPack(&mb);
   1574 		blockDirty(b);
   1575 		werrstr(EBadMeta);
   1576 		goto Err;
   1577 	}
   1578 
   1579 	mbSearch(&mb, dir->elem, &i, &me);
   1580 	assert(me.p == nil);
   1581 	me.p = p;
   1582 	me.size = n;
   1583 	dePack(dir, &me);
   1584 	mbInsert(&mb, i, &me);
   1585 	mbPack(&mb);
   1586 
   1587 	/* meta block depends on super block for qid ... */
   1588 	bb = cacheLocal(b->c, PartSuper, 0, OReadOnly);
   1589 	blockDependency(b, bb, -1, nil, nil);
   1590 	blockPut(bb);
   1591 
   1592 	/* ... and one or two dir entries */
   1593 	epb = s->dsize/VtEntrySize;
   1594 	bb = sourceBlock(s, dir->entry/epb, OReadOnly);
   1595 	blockDependency(b, bb, -1, nil, nil);
   1596 	blockPut(bb);
   1597 	if(dir->mode & ModeDir){
   1598 		bb = sourceBlock(s, dir->mentry/epb, OReadOnly);
   1599 		blockDependency(b, bb, -1, nil, nil);
   1600 		blockPut(bb);
   1601 	}
   1602 
   1603 	blockDirty(b);
   1604 	blockPut(b);
   1605 	return bo;
   1606 Err:
   1607 	blockPut(b);
   1608 	return NilBlock;
   1609 }
   1610 
   1611 static int
   1612 chkSource(File *f)
   1613 {
   1614 	if(f->partial)
   1615 		return 1;
   1616 
   1617 	if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
   1618 		werrstr(ERemoved);
   1619 		return 0;
   1620 	}
   1621 	return 1;
   1622 }
   1623 
   1624 static int
   1625 fileRLock(File *f)
   1626 {
   1627 	assert(!canwlock(&f->fs->elk));
   1628 	rlock(&f->lk);
   1629 	if(!chkSource(f)){
   1630 		fileRUnlock(f);
   1631 		return 0;
   1632 	}
   1633 	return 1;
   1634 }
   1635 
   1636 static void
   1637 fileRUnlock(File *f)
   1638 {
   1639 	runlock(&f->lk);
   1640 }
   1641 
   1642 static int
   1643 fileLock(File *f)
   1644 {
   1645 	assert(!canwlock(&f->fs->elk));
   1646 	wlock(&f->lk);
   1647 	if(!chkSource(f)){
   1648 		fileUnlock(f);
   1649 		return 0;
   1650 	}
   1651 	return 1;
   1652 }
   1653 
   1654 static void
   1655 fileUnlock(File *f)
   1656 {
   1657 	wunlock(&f->lk);
   1658 }
   1659 
   1660 /*
   1661  * f->source and f->msource must NOT be locked.
   1662  * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
   1663  * We have to respect that ordering.
   1664  */
   1665 static void
   1666 fileMetaLock(File *f)
   1667 {
   1668 if(f->up == nil)
   1669 fprint(2, "f->elem = %s\n", f->dir.elem);
   1670 	assert(f->up != nil);
   1671 	assert(!canwlock(&f->fs->elk));
   1672 	wlock(&f->up->lk);
   1673 }
   1674 
   1675 static void
   1676 fileMetaUnlock(File *f)
   1677 {
   1678 	wunlock(&f->up->lk);
   1679 }
   1680 
   1681 /*
   1682  * f->source and f->msource must NOT be locked.
   1683  * see fileMetaLock.
   1684  */
   1685 static void
   1686 fileRAccess(File* f)
   1687 {
   1688 	if(f->mode == OReadOnly || f->fs->noatimeupd)
   1689 		return;
   1690 
   1691 	fileMetaLock(f);
   1692 	f->dir.atime = time(0L);
   1693 	f->dirty = 1;
   1694 	fileMetaUnlock(f);
   1695 }
   1696 
   1697 /*
   1698  * f->source and f->msource must NOT be locked.
   1699  * see fileMetaLock.
   1700  */
   1701 static void
   1702 fileWAccess(File* f, char *mid)
   1703 {
   1704 	if(f->mode == OReadOnly)
   1705 		return;
   1706 
   1707 	fileMetaLock(f);
   1708 	f->dir.atime = f->dir.mtime = time(0L);
   1709 	if(strcmp(f->dir.mid, mid) != 0){
   1710 		vtfree(f->dir.mid);
   1711 		f->dir.mid = vtstrdup(mid);
   1712 	}
   1713 	f->dir.mcount++;
   1714 	f->dirty = 1;
   1715 	fileMetaUnlock(f);
   1716 
   1717 /*RSC: let's try this */
   1718 /*presotto - lets not
   1719 	if(f->up)
   1720 		fileWAccess(f->up, mid);
   1721 */
   1722 }
   1723 
   1724 static int
   1725 getEntry(Source *r, Entry *e, int checkepoch)
   1726 {
   1727 	u32int epoch;
   1728 	Block *b;
   1729 
   1730 	if(r == nil){
   1731 		memset(&e, 0, sizeof e);
   1732 		return 1;
   1733 	}
   1734 
   1735 	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly);
   1736 	if(b == nil)
   1737 		return 0;
   1738 	if(!entryUnpack(e, b->data, r->offset % r->epb)){
   1739 		blockPut(b);
   1740 		return 0;
   1741 	}
   1742 	epoch = b->l.epoch;
   1743 	blockPut(b);
   1744 
   1745 	if(checkepoch){
   1746 		b = cacheGlobal(r->fs->cache, e->score, entryType(e), e->tag, OReadOnly);
   1747 		if(b){
   1748 			if(b->l.epoch >= epoch)
   1749 				fprint(2, "warning: entry %p epoch not older %#.8ux/%d %V/%d in getEntry\n",
   1750 					r, b->addr, b->l.epoch, r->score, epoch);
   1751 			blockPut(b);
   1752 		}
   1753 	}
   1754 
   1755 	return 1;
   1756 }
   1757 
   1758 static int
   1759 setEntry(Source *r, Entry *e)
   1760 {
   1761 	Block *b;
   1762 	Entry oe;
   1763 
   1764 	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
   1765 	if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
   1766 	if(b == nil)
   1767 		return 0;
   1768 	if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
   1769 		blockPut(b);
   1770 		return 0;
   1771 	}
   1772 	e->gen = oe.gen;
   1773 	entryPack(e, b->data, r->offset % r->epb);
   1774 
   1775 	/* BUG b should depend on the entry pointer */
   1776 
   1777 	blockDirty(b);
   1778 	blockPut(b);
   1779 	return 1;
   1780 }
   1781 
   1782 /* assumes hold elk */
   1783 int
   1784 fileSnapshot(File *dst, File *src, u32int epoch, int doarchive)
   1785 {
   1786 	Entry e, ee;
   1787 
   1788 	/* add link to snapshot */
   1789 	if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
   1790 		return 0;
   1791 
   1792 	e.snap = epoch;
   1793 	e.archive = doarchive;
   1794 	ee.snap = epoch;
   1795 	ee.archive = doarchive;
   1796 
   1797 	if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
   1798 		return 0;
   1799 	return 1;
   1800 }
   1801 
   1802 int
   1803 fileGetSources(File *f, Entry *e, Entry *ee)
   1804 {
   1805 	if(!getEntry(f->source, e, 0)
   1806 	|| !getEntry(f->msource, ee, 0))
   1807 		return 0;
   1808 	return 1;
   1809 }
   1810 
   1811 /*
   1812  * Walk down to the block(s) containing the Entries
   1813  * for f->source and f->msource, copying as we go.
   1814  */
   1815 int
   1816 fileWalkSources(File *f)
   1817 {
   1818 	if(f->mode == OReadOnly){
   1819 		fprint(2, "readonly in fileWalkSources\n");
   1820 		return 1;
   1821 	}
   1822 	if(!sourceLock2(f->source, f->msource, OReadWrite)){
   1823 		fprint(2, "sourceLock2 failed in fileWalkSources\n");
   1824 		return 0;
   1825 	}
   1826 	sourceUnlock(f->source);
   1827 	sourceUnlock(f->msource);
   1828 	return 1;
   1829 }
   1830 
   1831 /*
   1832  * convert File* to full path name in malloced string.
   1833  * this hasn't been as useful as we hoped it would be.
   1834  */
   1835 char *
   1836 fileName(File *f)
   1837 {
   1838 	char *name, *pname;
   1839 	File *p;
   1840 	static char root[] = "/";
   1841 
   1842 	if (f == nil)
   1843 		return vtstrdup("/**GOK**");
   1844 
   1845 	p = fileGetParent(f);
   1846 	if (p == f)
   1847 		name = vtstrdup(root);
   1848 	else {
   1849 		pname = fileName(p);
   1850 		if (strcmp(pname, root) == 0)
   1851 			name = smprint("/%s", f->dir.elem);
   1852 		else
   1853 			name = smprint("%s/%s", pname, f->dir.elem);
   1854 		free(pname);
   1855 	}
   1856 	fileDecRef(p);
   1857 	return name;
   1858 }