1291 lines · 23.6 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Manage tree of VtFiles stored in the block cache. * * The single point of truth for the info about the VtFiles themselves * is the block data.  Because of this, there is no explicit locking of * VtFile structures, and indeed there may be more than one VtFile * structure for a given Venti file.  They synchronize through the  * block cache. * * This is a bit simpler than fossil because there are no epochs * or tags or anything else.  Just mutable local blocks and immutable * Venti blocks. */ #include <u.h>#include <libc.h>#include <venti.h> #define MaxBlock (1UL<<31) static char ENotDir[] = "walk in non-directory";static char ETooBig[] = "file too big";/* static char EBadAddr[] = "bad address"; */static char ELabelMismatch[] = "label mismatch"; static int	sizetodepth(uvlong s, int psize, int dsize);static VtBlock 	*fileload(VtFile *r, VtEntry *e);static int	shrinkdepth(VtFile*, VtBlock*, VtEntry*, int);static int	shrinksize(VtFile*, VtEntry*, uvlong);static int	growdepth(VtFile*, VtBlock*, VtEntry*, int); #define ISLOCKED(r)	((r)->b != nil)#define DEPTH(t)	((t)&VtTypeDepthMask) static VtFile *vtfilealloc(VtCache *c, VtBlock *b, VtFile *p, u32int offset, int mode){	int epb;	u32int size;	VtEntry e;	VtFile *r; 	assert(p==nil || ISLOCKED(p)); 	if(p == nil){		assert(offset == 0);		epb = 1;	}else		epb = p->dsize / VtEntrySize; 	if(b->type != VtDirType){		werrstr("bad block type %#uo", b->type);		return nil;	} 	/*	 * a non-active entry is the only thing that	 * can legitimately happen here. all the others	 * get prints.	 */	if(vtentryunpack(&e, b->data, offset % epb) < 0){		fprint(2, "vtentryunpack failed: %r (%.*H)\n", VtEntrySize, b->data+VtEntrySize*(offset%epb));		return nil;	}	if(!(e.flags & VtEntryActive)){		werrstr("entry not active");		return nil;	} 	if(DEPTH(e.type) < sizetodepth(e.size, e.psize, e.dsize)){		fprint(2, "depth %ud size %llud psize %ud dsize %ud\n",			DEPTH(e.type), e.size, e.psize, e.dsize);		werrstr("bad depth");		return nil;	} 	size = vtcacheblocksize(c);	if(e.dsize > size || e.psize > size){		werrstr("block sizes %ud, %ud bigger than cache block size %ud",			e.psize, e.dsize, size);		return nil;	} 	r = vtmallocz(sizeof(VtFile));	r->c = c;	r->mode = mode;	r->dsize = e.dsize;	r->psize = e.psize;	r->gen = e.gen;	r->dir = (e.type & VtTypeBaseMask) == VtDirType;	r->ref = 1;	r->parent = p;	if(p){		qlock(&p->lk);		assert(mode == VtOREAD || p->mode == VtORDWR);		p->ref++;		qunlock(&p->lk);	}else{		assert(b->addr != NilBlock);		r->local = 1;	}	memmove(r->score, b->score, VtScoreSize);	r->offset = offset;	r->epb = epb; 	return r;} VtFile *vtfileroot(VtCache *c, u32int addr, int mode){	VtFile *r;	VtBlock *b; 	b = vtcachelocal(c, addr, VtDirType);	if(b == nil)		return nil;	r = vtfilealloc(c, b, nil, 0, mode);	vtblockput(b);	return r;} VtFile*vtfileopenroot(VtCache *c, VtEntry *e){	VtBlock *b;	VtFile *f; 	b = vtcacheallocblock(c, VtDirType);	if(b == nil)		return nil; 	vtentrypack(e, b->data, 0);	f = vtfilealloc(c, b, nil, 0, VtORDWR);	vtblockput(b);	return f;} VtFile *vtfilecreateroot(VtCache *c, int psize, int dsize, int type){	VtEntry e; 	memset(&e, 0, sizeof e);	e.flags = VtEntryActive;	e.psize = psize;	e.dsize = dsize;	e.type = type;	memmove(e.score, vtzeroscore, VtScoreSize); 	return vtfileopenroot(c, &e);} VtFile *vtfileopen(VtFile *r, u32int offset, int mode){	ulong bn;	VtBlock *b; 	assert(ISLOCKED(r));	if(!r->dir){		werrstr(ENotDir);		return nil;	} 	bn = offset/(r->dsize/VtEntrySize); 	b = vtfileblock(r, bn, mode);	if(b == nil)		return nil;	r = vtfilealloc(r->c, b, r, offset, mode);	vtblockput(b);	return r;} VtFile*vtfilecreate(VtFile *r, int psize, int dsize, int type){	return _vtfilecreate(r, -1, psize, dsize, type);} VtFile*_vtfilecreate(VtFile *r, int o, int psize, int dsize, int type){	int i;	VtBlock *b;	u32int bn, size;	VtEntry e;	int epb;	VtFile *rr;	u32int offset;		assert(ISLOCKED(r));	assert(psize <= VtMaxLumpSize);	assert(dsize <= VtMaxLumpSize);	assert(type == VtDirType || type == VtDataType); 	if(!r->dir){		werrstr(ENotDir);		return nil;	} 	epb = r->dsize/VtEntrySize; 	size = vtfilegetdirsize(r);	/*	 * look at a random block to see if we can find an empty entry	 */	if(o == -1){		offset = lnrand(size+1);		offset -= offset % epb;	}else		offset = o; 	/* try the given block and then try the last block */	for(;;){		bn = offset/epb;		b = vtfileblock(r, bn, VtORDWR);		if(b == nil)			return nil;		for(i=offset%r->epb; i<epb; i++){			if(vtentryunpack(&e, b->data, i) < 0)				continue;			if((e.flags&VtEntryActive) == 0 && e.gen != ~0)				goto Found;		}		vtblockput(b);		if(offset == size){			fprint(2, "vtfilecreate: cannot happen\n");			werrstr("vtfilecreate: cannot happen");			return nil;		}		offset = size;	} Found:	/* found an entry - gen already set */	e.psize = psize;	e.dsize = dsize;	e.flags = VtEntryActive;	e.type = type;	e.size = 0;	memmove(e.score, vtzeroscore, VtScoreSize);	vtentrypack(&e, b->data, i); 	offset = bn*epb + i;	if(offset+1 > size){		if(vtfilesetdirsize(r, offset+1) < 0){			vtblockput(b);			return nil;		}	} 	rr = vtfilealloc(r->c, b, r, offset, VtORDWR);	vtblockput(b);	return rr;} static intvtfilekill(VtFile *r, int doremove){	VtEntry e;	VtBlock *b; 	assert(ISLOCKED(r));	b = fileload(r, &e);	if(b == nil)		return -1; 	if(doremove==0 && e.size == 0){		/* already truncated */		vtblockput(b);		return 0;	} 	if(doremove){		if(e.gen != ~0)			e.gen++;		e.dsize = 0;		e.psize = 0;		e.flags = 0;	}else		e.flags &= ~VtEntryLocal;	e.type = 0;	e.size = 0;	memmove(e.score, vtzeroscore, VtScoreSize);	vtentrypack(&e, b->data, r->offset % r->epb);	vtblockput(b); 	if(doremove){		vtfileunlock(r);		vtfileclose(r);	} 	return 0;} intvtfileremove(VtFile *r){	return vtfilekill(r, 1);} intvtfiletruncate(VtFile *r){	return vtfilekill(r, 0);} uvlongvtfilegetsize(VtFile *r){	VtEntry e;	VtBlock *b; 	assert(ISLOCKED(r));	b = fileload(r, &e);	if(b == nil)		return ~(uvlong)0;	vtblockput(b); 	return e.size;} static intshrinksize(VtFile *r, VtEntry *e, uvlong size){	int i, depth, type, isdir, ppb;	uvlong ptrsz;	uchar score[VtScoreSize];	VtBlock *b; 	b = vtcacheglobal(r->c, e->score, e->type);	if(b == nil)		return -1; 	ptrsz = e->dsize;	ppb = e->psize/VtScoreSize;	type = e->type;	depth = DEPTH(type);	for(i=0; i+1<depth; i++)		ptrsz *= ppb; 	isdir = r->dir;	while(depth > 0){		if(b->addr == NilBlock){			/* not worth copying the block just so we can zero some of it */			vtblockput(b);			return -1;		} 		/*		 * invariant: each pointer in the tree rooted at b accounts for ptrsz bytes		 */ 		/* zero the pointers to unnecessary blocks */		i = (size+ptrsz-1)/ptrsz;		for(; i<ppb; i++)			memmove(b->data+i*VtScoreSize, vtzeroscore, VtScoreSize); 		/* recurse (go around again) on the partially necessary block */		i = size/ptrsz;		size = size%ptrsz;		if(size == 0){			vtblockput(b);			return 0;		}		ptrsz /= ppb;		type--;		memmove(score, b->data+i*VtScoreSize, VtScoreSize);		vtblockput(b);		b = vtcacheglobal(r->c, score, type);		if(b == nil)			return -1;	} 	if(b->addr == NilBlock){		vtblockput(b);		return -1;	} 	/*	 * No one ever truncates BtDir blocks.	 */	if(depth==0 && !isdir && e->dsize > size)		memset(b->data+size, 0, e->dsize-size);	vtblockput(b);	return 0;} intvtfilesetsize(VtFile *r, u64int size){	int depth, edepth;	VtEntry e;	VtBlock *b; 	assert(ISLOCKED(r));	if(size == 0)		return vtfiletruncate(r); 	if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize){		werrstr(ETooBig);		return -1;	} 	b = fileload(r, &e);	if(b == nil)		return -1; 	/* quick out */	if(e.size == size){		vtblockput(b);		return 0;	} 	depth = sizetodepth(size, e.psize, e.dsize);	edepth = DEPTH(e.type);	if(depth < edepth){		if(shrinkdepth(r, b, &e, depth) < 0){			vtblockput(b);			return -1;		}	}else if(depth > edepth){		if(growdepth(r, b, &e, depth) < 0){			vtblockput(b);			return -1;		}	} 	if(size < e.size)		shrinksize(r, &e, size); 	e.size = size;	vtentrypack(&e, b->data, r->offset % r->epb);	vtblockput(b); 	return 0;} intvtfilesetdirsize(VtFile *r, u32int ds){	uvlong size;	int epb; 	assert(ISLOCKED(r));	epb = r->dsize/VtEntrySize; 	size = (uvlong)r->dsize*(ds/epb);	size += VtEntrySize*(ds%epb);	return vtfilesetsize(r, size);} u32intvtfilegetdirsize(VtFile *r){	ulong ds;	uvlong size;	int epb; 	assert(ISLOCKED(r));	epb = r->dsize/VtEntrySize; 	size = vtfilegetsize(r);	ds = epb*(size/r->dsize);	ds += (size%r->dsize)/VtEntrySize;	return ds;} intvtfilegetentry(VtFile *r, VtEntry *e){	VtBlock *b; 	assert(ISLOCKED(r));	b = fileload(r, e);	if(b == nil)		return -1;	vtblockput(b); 	return 0;} intvtfilesetentry(VtFile *r, VtEntry *e){	VtBlock *b;	VtEntry ee; 	assert(ISLOCKED(r));

Lines 1–500 of 1291.