예제 #1
0
파일: check.c 프로젝트: 99years/plan9
static void
scanSource(Fsck *chk, char *name, Source *r)
{
	u32int a, nb, o;
	Block *b;
	Entry e;

	if(!chk->useventi && globalToLocal(r->score)==NilBlock)
		return;
	if(!sourceGetEntry(r, &e)){
		error(chk, "could not get entry for %s", name);
		return;
	}
	a = globalToLocal(e.score);
	if(!chk->useventi && a==NilBlock)
		return;
	if(getBit(chk->smap, a))
		return;
	setBit(chk->smap, a);

	nb = (sourceGetSize(r) + r->dsize-1) / r->dsize;
	for(o = 0; o < nb; o++){
		b = sourceBlock(r, o, OReadOnly);
		if(b == nil){
			error(chk, "could not read block in data file %s", name);
			continue;
		}
		if(b->addr != NilBlock && getBit(chk->errmap, b->addr)){
			warn(chk, "previously reported error in block %ux is in file %s",
				b->addr, name);
		}
		blockPut(b);
	}
}
예제 #2
0
void ScrollBar::handleScrollTouch(ds::ui::Sprite* bs, const ds::ui::TouchInfo& ti){
	if(ti.mFingerIndex == 0){
		ci::Vec3f localPos = globalToLocal(ti.mCurrentGlobalPoint);
		
		float destPercent;
		if(mVertical){

			// This may not be right. Feel free to fix, but be sure you get it right and check multiple instances
			if(getPerspective()){
				localPos.y -= getHeight()/2.0f;
			}

			destPercent = localPos.y / getHeight();
		} else {
			destPercent = localPos.x / getWidth();
		}

		if(destPercent < 0.0f) destPercent = 0.0f;
		if(destPercent > 1.0f) destPercent = 1.0f;

		if(mScrollMoveCallback){
			mScrollMoveCallback(destPercent);
		}
	}
}
예제 #3
0
bool Slider::touchMoveHandler(ofTouchEventArgs &touch){
    if (touch.id == currentTouchId){
        setCursorPosition(globalToLocal(touch.x, touch.y));
        return true;
    }
    return false;
}
예제 #4
0
bool Slider::touchDownHandler(ofTouchEventArgs &touch){
    if (cursorPosition.distance(globalToLocal(touch.x, touch.y)) < cursorRadius * radiusTouchFactor){
        currentTouchId = touch.id;
        return true;
    }
    return false;
}
예제 #5
0
void TreeInspector::highlightSprite( const ds::ui::Sprite* sprite ) {
	auto showHighlight = [&]( const ds::ui::Sprite* s ) {
		if (!mHighlighter) {
			mHighlighter = new ds::ui::Sprite( mEngine );
			mHighlighter->setTransparent( false );
			mHighlighter->setColorA( ci::ColorA::hexA( 0xAA6fa8dc ) );
			addChildPtr( mHighlighter );

			auto crosshairColor = ci::Color::hex( (0xf6b26b) );

			auto hCrosshair = new ds::ui::Sprite( mEngine, 40.0f, 3.0f );
			hCrosshair->setTransparent( false );
			hCrosshair->setColor( crosshairColor );
			mHighlighter->addChildPtr( hCrosshair );
			auto vCrosshair = new ds::ui::Sprite( mEngine, 3.0f, 40.0f );
			vCrosshair->setTransparent( false );
			vCrosshair->setColor( crosshairColor );
			mHighlighter->addChildPtr( vCrosshair );
		}

		mHighlighter->show();
		mHighlighter->setSize( s->getWidth(), s->getHeight() );
		auto pos = s->getGlobalPosition();
		mHighlighter->setPosition( globalToLocal( pos ) );
	};

	if (sprite) {
		showHighlight(sprite);
	}
	else if (mHighlighter) {
		mHighlighter->hide();
	}

}
예제 #6
0
파일: pack.c 프로젝트: 99years/plan9
void
entryPack(Entry *e, uchar *p, int index)
{
	ulong t32;
	int flags;

	p += index * VtEntrySize;

	U32PUT(p, e->gen);
	U16PUT(p+4, e->psize);
	U16PUT(p+6, e->dsize);
	flags = e->flags | ((e->depth << VtEntryDepthShift) & VtEntryDepthMask);
	U8PUT(p+8, flags);
	memset(p+9, 0, 5);
	U48PUT(p+14, e->size, t32);

	if(flags & VtEntryLocal){
		if(globalToLocal(e->score) == NilBlock)
			abort();
		memset(p+20, 0, 7);
		U8PUT(p+27, e->archive);
		U32PUT(p+28, e->snap);
		U32PUT(p+32, e->tag);
		memmove(p+36, e->score+16, 4);
	}else
		memmove(p+20, e->score, VtScoreSize);
}
예제 #7
0
 void LocalMapLayer::updateRefereceRobotFrame(GeoObjectID id, MapAbstraction::MapRobotObjectPtr robot)
 {
     MapAbstraction::MapObjectPtr frozen(robot->Clone());
     QPointF local;
     globalToLocal(MapLibraryHelpers::transformCoords(robot->coords()), local);
     frozen->setCoords(MapAbstraction::GeoCoords(local.x(), local.y()));
     mRobotReferenceFrames.insert(id, frozen.staticCast<MapAbstraction::MapRobotObject>());
 }
예제 #8
0
파일: source.c 프로젝트: npe9/harvey
static int
sourceKill(Source *r, int doremove)
{
    Entry e;
    Block *b;
    uint32_t addr;
    uint32_t tag;
    int type;

    assert(sourceIsLocked(r));
    b = sourceLoad(r, &e);
    if(b == nil)
        return 0;

    assert(b->l.epoch == r->fs->ehi);

    if(doremove==0 && e.size == 0) {
        /* already truncated */
        blockPut(b);
        return 1;
    }

    /* remember info on link we are removing */
    addr = globalToLocal(e.score);
    type = entryType(&e);
    tag = e.tag;

    if(doremove) {
        if(e.gen != ~0)
            e.gen++;
        e.dsize = 0;
        e.psize = 0;
        e.flags = 0;
    } else {
        e.flags &= ~VtEntryLocal;
    }
    e.depth = 0;
    e.size = 0;
    e.tag = 0;
    memmove(e.score, vtZeroScore, VtScoreSize);
    entryPack(&e, b->data, r->offset % r->epb);
    blockDirty(b);
    if(addr != NilBlock)
        blockRemoveLink(b, addr, type, tag, 1);
    blockPut(b);

    if(doremove) {
        sourceUnlock(r);
        sourceClose(r);
    }

    return 1;
}
예제 #9
0
파일: Aspring.cpp 프로젝트: afrancois/Afw
bool Aspring::handleEvent(Aevent* evt)
{
	if(evt->Type == MOUSE_PRESS_EVENT)
	{		
		Vec3f localPoint = globalToLocal(evt->mousePos);
		if( pointInside( localPoint )){
			if(COLLIDER->mEditor != NULL)
				COLLIDER->mEditor->registerSpring(this);
			return true;
		}
	}
	return false;
}
예제 #10
0
파일: check.c 프로젝트: 99years/plan9
static void
checkEpoch(Fsck *chk, u32int epoch)
{
	u32int a;
	Block *b;
	Entry e;
	Label l;

	chk->print("checking epoch %ud...\n", epoch);

	for(a=0; a<chk->nblocks; a++){
		if(!readLabel(chk->cache, &l, (a+chk->hint)%chk->nblocks)){
			error(chk, "could not read label for addr 0x%.8#ux", a);
			continue;
		}
		if(l.tag == RootTag && l.epoch == epoch)
			break;
	}

	if(a == chk->nblocks){
		chk->print("could not find root block for epoch %ud", epoch);
		return;
	}

	a = (a+chk->hint)%chk->nblocks;
	b = cacheLocalData(chk->cache, a, BtDir, RootTag, OReadOnly, 0);
	if(b == nil){
		error(chk, "could not read root block 0x%.8#ux: %R", a);
		return;
	}

	/* no one should point at root blocks */
	setBit(chk->amap, a);
	setBit(chk->emap, a);
	setBit(chk->xmap, a);

	/*
	 * First entry is the rest of the file system.
	 * Second entry is link to previous epoch root,
	 * just a convenience to help the search.
	 */
	if(!entryUnpack(&e, b->data, 0)){
		error(chk, "could not unpack root block 0x%.8#ux: %R", a);
		blockPut(b);
		return;
	}
	walkEpoch(chk, b, e.score, BtDir, e.tag, epoch);
	if(entryUnpack(&e, b->data, 1))
		chk->hint = globalToLocal(e.score);
	blockPut(b);
}
void BasicInteractiveObject::addMultiTouch(mtRay ray, int touchId){
	MultiTouchPoint* mtp = new MultiTouchPoint();
	
	plane.pos	= getGlobalPosition();
	plane.norm	= ofVec3f(0,0,1)*getGlobalOrientation();
	
	float u		= plane.norm.dot(plane.pos-ray.pos)/plane.norm.dot(ray.dir);
	ofVec3f gintersection = ray.pos+ray.dir*u;
	
	mtp->screenpos.set(ray.screenpos);
	mtp->globalstartpos.set(gintersection);	
	mtp->localstartpos	= globalToLocal(mtp->globalstartpos);
	mtp->localpos		= mtp->localstartpos;
	mtp->localoffset	= mtp->localstartpos-getPosition();
	mtp->globalpos		= mtp->globalstartpos;
	mtp->globalmovedist	= 0;
	mtp->starttime		= ofGetElapsedTimeMillis();	
	
	activeMultiTouches[touchId] = mtp;
	mtcounter++;
    
    
    // Flipped order (was before)
    
	if(activeMultiTouches.size() == 1){
		// Start of Scope
		mtstarttime		= ofGetElapsedTimeMillis();
		mtscoperunning	= true;
		MultiTouchEvent params(this, mtp);
		ofNotifyEvent(firstTouchDownEvent,params,this);
	}
	
    
	if(activeMultiTouches.size()>=2){
		MultiTouchEvent params(this);
		ofNotifyEvent(startMultiTouchScopeEvent,params,this);
	}
    
    // Flipped order (was after)
    
    mttranslatedist	= 0;
	mtrotatedist	= 0;
	mtscaledist		= 0;
	
	mttranslatespeed.set(0,0,0);
	mtrotatespeed.set(0,0,0,1);
	mtscalespeed=0;
	
    resetMTStartValues();
}
예제 #12
0
bool poImageShape::pointInside(poPoint p, bool localize)
{	
    if(!visible || !tex->getSourceImage() || !tex)
		return false;
	
    // DO POINT INSIDE TEST FOR 2D
    if ( poCamera::getCurrentCameraType() == PO_CAMERA_2D )
    {
        if(localize) {
            p.y = getWindowHeight() - p.y;
            p = globalToLocal(p);
        }
        
        if(alphaTest) {
            // flip y value, since poImage y coordinates are reversed
            p.y = tex->getSourceImage()->getHeight() - p.y;
            poColor pix = tex->getSourceImagePixel(p);
            return pix.A > 0.f;
        }
        
        poRect r(0,0,tex->getWidth(),tex->getHeight());
        return r.contains(p.x, p.y);
    }
     
    // DO POINT INSIDE TEST FOR 3D
    if ( poCamera::getCurrentCameraType() == PO_CAMERA_3D )
    {
        if(localize) {
            p.y = getWindowHeight() - p.y;
        }
        
        /*if(alphaTest) {
            // flip y value, since poImage y coordinates are reversed
            p.y = tex->getSourceImage()->getHeight()*imageScale - p.y;
            p /= imageScale;
            poColor pix = tex->getSourceImagePixel(p);
            return pix.A > 0.f;
        }*/

        poRect r(0,0,tex->getWidth(),tex->getHeight());
        return pointInRect3D( p, getMatrixSet(), r );
    }
    
    return false;
}
예제 #13
0
int
scoreFmt(Fmt *f)
{
    uchar *v;
    int i;
    u32int addr;

    v = va_arg(f->args, uchar*);
    if(v == nil) {
        fmtprint(f, "*");
    } else if((addr = globalToLocal(v)) != NilBlock)
        fmtprint(f, "0x%.8ux", addr);
    else {
        for(i = 0; i < VtScoreSize; i++)
            fmtprint(f, "%2.2ux", v[i]);
    }

    return 0;
}
예제 #14
0
void MVScrollBar::changeValue(const MVPoint& global)
/****************************************************************************
*
* Function:		MVScrollBar::changeValue
* Parameters:	global	- New location of the mouse cursor
*
* Description:	Converts the mouse cursor location into a new value for
*				the scroll bar thumb, repositions the thumb and redraws
*				it.
*
****************************************************************************/
{
	MVPoint p(global);
	globalToLocal(p);

	int		left,right,start;
	int	oldValue = value;

	if (vertical) {
		start = p.y - 7;
		left = leftArrow.bottom();
		right = rightArrow.top();
		}
	else {
		start = p.x - 7;
		left = leftArrow.right();
		right = rightArrow.left();
		}

	if (start < left)		start = left;
	if (start > right-(_MV_sysScrollBarWidth+1))
		start = right-(_MV_sysScrollBarWidth+1);

	value = ((2*(maxVal-minVal)*(long)(start-left))
			/ (right-left-(_MV_sysScrollBarWidth+1))+1)/2;

	if (oldValue != value) {
		moveThumb();
		drawThumb(sbThumb);
		MV_message(owner,evBroadcast,cmScrollBarChanged,this);
		}
}
예제 #15
0
int MVScrollBar::getPartHit(const MVPoint& global)
/****************************************************************************
*
* Function:		MVScrollBar::getPartHit
* Parameters:	global	- Point to test for inclusion (global coords)
* Returns:		Code of part hit, -1 if none hit
*
* Description:	Determines which part of the scroll bar was hit.
*
****************************************************************************/
{
	MVPoint p(global);
	globalToLocal(p);

	if (bounds.includes(p)) {
		if (leftArrow.includes(p))
			return sbLeftArrow;
		if (rightArrow.includes(p))
			return sbRightArrow;

		if (thumb.includes(p))
			return sbThumb;

		if (vertical) {
			if (leftArrow.bottom() <= p.y && p.y < thumb.top())
				return sbPageLeft;
			if (thumb.bottom() <= p.y && p.y < rightArrow.top())
				return sbPageRight;
			}
		else {
			if (leftArrow.right() <= p.x && p.x < thumb.left())
				return sbPageLeft;
			if (thumb.right() <= p.x && p.x < rightArrow.left())
				return sbPageRight;
			}
		}

	return -1;
}
void BasicInteractiveObject::updateMultiTouch(mtRay ray, int touchId){
	if(isMultiTouchActive(touchId)){
	
		plane.pos	= getGlobalPosition();
		plane.norm	= ofVec3f(0,0,1)*getGlobalOrientation();
		
		float u		= plane.norm.dot(plane.pos-ray.pos)/plane.norm.dot(ray.dir);
		ofVec3f gintersection = ray.pos+ray.dir*u;
		
		MultiTouchPoint* mtp = activeMultiTouches[touchId];

		mtp->screenpos.set(ray.screenpos);
		mtp->globalposbef.set(mtp->globalpos);
		mtp->globalpos.set(gintersection);
		mtp->globalspeed.set(mtp->globalpos-mtp->globalposbef);
		mtp->localposbef.set(mtp->localpos);
		mtp->localpos.set(globalToLocal(mtp->globalpos));
		mtp->localspeeddamped.set((mtp->localspeed + (mtp->localpos-mtp->localposbef))/2);
		mtp->localspeed.set(mtp->localpos-mtp->localposbef);		
		mtp->globalmovedist += mtp->globalspeed.length();
	}
}
예제 #17
0
Block*
dataBlock(uchar score[VtScoreSize], uint type, uint tag)
{
    Block *b, *bl;
    int lpb;
    Label l;
    u32int addr;

    addr = globalToLocal(score);
    if(addr == NilBlock)
        return ventiBlock(score, type);

    lpb = h.blockSize/LabelSize;
    bl = readBlock(PartLabel, addr/lpb);
    if(bl == nil)
        return nil;
    if(!labelUnpack(&l, bl->data, addr%lpb)) {
        werrstr("%r");
        blockPut(bl);
        return nil;
    }
    blockPut(bl);
    if(l.type != type) {
        werrstr("type mismatch; got %d (%s) wanted %d (%s)",
                l.type, btStr(l.type), type, btStr(type));
        return nil;
    }
    if(tag && l.tag != tag) {
        werrstr("tag mismatch; got 0x%.8ux wanted 0x%.8ux",
                l.tag, tag);
        return nil;
    }
    b = readBlock(PartData, addr);
    if(b == nil)
        return nil;
    b->l = l;
    return b;
}
예제 #18
0
bool Afileexplorer::handleEvent(Aevent* evt)
{
	if(evt->Type == MOUSE_PRESS_EVENT )
	{
		Vec3f localPoint = globalToLocal(evt->mousePos);
		if( pointInside( localPoint ))
		{
			//! Not threaded...
			try {
				std::string p = getOpenFilePath( CMN->rootResourcePath );
				if( ! p.empty() ) { // an empty string means the user canceled
					FilePath->makeTexture( p );
				}
			}
			catch( ... ) {
				console() << "Afileexplorer experienced an event error" << std::endl;
				return false;
			}
			return true;
		}
	}
	return false;
}
예제 #19
0
uint MVListBase::findCellHit(const MVPoint& global,MVPoint& loc)
/****************************************************************************
*
* Function:     MVListBase::findCellHit
* Parameters:   global  - Global mouse location point
*               loc     - Place to store computed cell location
* Returns:      Flags representing where the mouse click occurred
*
* Description:  Determines where the mouse click occurred in reference to
*               the list box. If the mouse click was inside the list box,
*               we compute the location of the cell that was hit and return
*               it in loc.
*
****************************************************************************/
{
    MVPoint p(global);
    globalToLocal(p);

    if (bounds.includes(p)) {
        loc.x = (p.x - (bounds.left()+leftIndent)) / cellSize.x;
        loc.y = (p.y - (bounds.top()+topIndent)) / cellSize.y;

        loc += visible.topLeft;     // Make relative to visible cells
        return lsInside;            // The mouse click was inside the list
        }

    // The mouse click was outside of the list, so determine where it
    // occurred and set the relevant flags
    uint    flags = 0;

    if (p.x < bounds.left()+leftIndent)     flags |= lsLeft;
    if (p.x >= bounds.right()-rightIndent)  flags |= lsRight;
    if (p.y < bounds.top()+topIndent)       flags |= lsAbove;
    if (p.y >= bounds.bottom()-botIndent)   flags |= lsBelow;

    return flags;
}
예제 #20
0
파일: fs.c 프로젝트: bhanug/harvey
Fs *
fsOpen(char *file, VtSession *z, int32_t ncache, int mode)
{
	int fd, m;
	uint8_t oscore[VtScoreSize];
	Block *b, *bs;
	Disk *disk;
	Fs *fs;
	Super super;

	switch(mode){
	default:
		vtSetError(EBadMode);
		return nil;
	case OReadOnly:
		m = OREAD;
		break;
	case OReadWrite:
		m = ORDWR;
		break;
	}
	fd = open(file, m);
	if(fd < 0){
		vtSetError("open %s: %r", file);
		return nil;
	}

	bwatchInit();
	disk = diskAlloc(fd);
	if(disk == nil){
		vtSetError("diskAlloc: %R");
		close(fd);
		return nil;
	}

	fs = vtMemAllocZ(sizeof(Fs));
	fs->mode = mode;
	fs->name = vtStrDup(file);
	fs->blockSize = diskBlockSize(disk);
	fs->elk = vtLockAlloc();
	fs->cache = cacheAlloc(disk, z, ncache, mode);
	if(mode == OReadWrite && z)
		fs->arch = archInit(fs->cache, disk, fs, z);
	fs->z = z;

	b = cacheLocal(fs->cache, PartSuper, 0, mode);
	if(b == nil)
		goto Err;
	if(!superUnpack(&super, b->data)){
		blockPut(b);
		vtSetError("bad super block");
		goto Err;
	}
	blockPut(b);

	fs->ehi = super.epochHigh;
	fs->elo = super.epochLow;

//fprint(2, "%s: fs->ehi %d fs->elo %d active=%d\n", argv0, fs->ehi, fs->elo, super.active);

	fs->source = sourceRoot(fs, super.active, mode);
	if(fs->source == nil){
		/*
		 * Perhaps it failed because the block is copy-on-write.
		 * Do the copy and try again.
		 */
		if(mode == OReadOnly || strcmp(vtGetError(), EBadRoot) != 0)
			goto Err;
		b = cacheLocalData(fs->cache, super.active, BtDir, RootTag,
			OReadWrite, 0);
		if(b == nil){
			vtSetError("cacheLocalData: %R");
			goto Err;
		}
		if(b->l.epoch == fs->ehi){
			blockPut(b);
			vtSetError("bad root source block");
			goto Err;
		}
		b = blockCopy(b, RootTag, fs->ehi, fs->elo);
		if(b == nil)
			goto Err;
		localToGlobal(super.active, oscore);
		super.active = b->addr;
		bs = cacheLocal(fs->cache, PartSuper, 0, OReadWrite);
		if(bs == nil){
			blockPut(b);
			vtSetError("cacheLocal: %R");
			goto Err;
		}
		superPack(&super, bs->data);
		blockDependency(bs, b, 0, oscore, nil);
		blockPut(b);
		blockDirty(bs);
		blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
		blockPut(bs);
		fs->source = sourceRoot(fs, super.active, mode);
		if(fs->source == nil){
			vtSetError("sourceRoot: %R");
			goto Err;
		}
	}

//fprint(2, "%s: got fs source\n", argv0);

	vtRLock(fs->elk);
	fs->file = fileRoot(fs->source);
	fs->source->file = fs->file;		/* point back */
	vtRUnlock(fs->elk);
	if(fs->file == nil){
		vtSetError("fileRoot: %R");
		goto Err;
	}

//fprint(2, "%s: got file root\n", argv0);

	if(mode == OReadWrite){
		fs->metaFlush = periodicAlloc(fsMetaFlush, fs, 1000);
		fs->snap = snapInit(fs);
	}
	return fs;

Err:
fprint(2, "%s: fsOpen error\n", argv0);
	fsClose(fs);
	return nil;
}
예제 #21
0
Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPosition)
{
    return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
}
예제 #22
0
Point<int> ComponentPeer::globalToLocal (Point<int> p)   { return globalToLocal (p.toFloat()).roundToInt(); }
예제 #23
0
파일: check.c 프로젝트: 99years/plan9
/*
 * Walk the source tree making sure that the BtData
 * sources containing directory entries are okay.
 */
static void
chkDir(Fsck *chk, char *name, Source *source, Source *meta)
{
	int i;
	u32int a1, a2, nb, o;
	char *s, *nn;
	uchar *bm;
	Block *b, *bb;
	DirEntry de;
	Entry e1, e2;
	MetaBlock mb;
	MetaEntry me;
	Source *r, *mr;

	if(!chk->useventi && globalToLocal(source->score)==NilBlock &&
	    globalToLocal(meta->score)==NilBlock)
		return;

	if(!sourceLock2(source, meta, OReadOnly)){
		warn(chk, "could not lock sources for %s: %R", name);
		return;
	}
	if(!sourceGetEntry(source, &e1) || !sourceGetEntry(meta, &e2)){
		warn(chk, "could not load entries for %s: %R", name);
		return;
	}
	a1 = globalToLocal(e1.score);
	a2 = globalToLocal(e2.score);
	if((!chk->useventi && a1==NilBlock && a2==NilBlock)
	|| (getBit(chk->smap, a1) && getBit(chk->smap, a2))){
		sourceUnlock(source);
		sourceUnlock(meta);
		return;
	}
	setBit(chk->smap, a1);
	setBit(chk->smap, a2);

	bm = vtMemAllocZ(sourceGetDirSize(source)/8 + 1);

	nb = (sourceGetSize(meta) + meta->dsize - 1)/meta->dsize;
	for(o = 0; o < nb; o++){
		b = sourceBlock(meta, o, OReadOnly);
		if(b == nil){
			error(chk, "could not read block in meta file: %s[%ud]: %R",
				name, o);
			continue;
		}
if(0)		fprint(2, "source %V:%d block %d addr %d\n", source->score,
			source->offset, o, b->addr);
		if(b->addr != NilBlock && getBit(chk->errmap, b->addr))
			warn(chk, "previously reported error in block %ux is in %s",
				b->addr, name);

		if(!mbUnpack(&mb, b->data, meta->dsize)){
			error(chk, "could not unpack meta block: %s[%ud]: %R",
				name, o);
			blockPut(b);
			continue;
		}
		if(!chkMetaBlock(&mb)){
			error(chk, "bad meta block: %s[%ud]: %R", name, o);
			blockPut(b);
			continue;
		}
		s = nil;
		for(i=mb.nindex-1; i>=0; i--){
			meUnpack(&me, &mb, i);
			if(!deUnpack(&de, &me)){
				error(chk,
				  "could not unpack dir entry: %s[%ud][%d]: %R",
					name, o, i);
				continue;
			}
			if(s && strcmp(s, de.elem) <= 0)
				error(chk,
			   "dir entry out of order: %s[%ud][%d] = %s last = %s",
					name, o, i, de.elem, s);
			vtMemFree(s);
			s = vtStrDup(de.elem);
			nn = smprint("%s/%s", name, de.elem);
			if(nn == nil){
				error(chk, "out of memory");
				continue;
			}
			if(chk->printdirs)
				if(de.mode&ModeDir)
					chk->print("%s/\n", nn);
			if(chk->printfiles)
				if(!(de.mode&ModeDir))
					chk->print("%s\n", nn);
			if(!(de.mode & ModeDir)){
				r = openSource(chk, source, nn, bm, de.entry,
					de.gen, 0, &mb, i, b);
				if(r != nil){
					if(sourceLock(r, OReadOnly)){
						scanSource(chk, nn, r);
						sourceUnlock(r);
					}
					sourceClose(r);
				}
				deCleanup(&de);
				free(nn);
				continue;
			}

			r = openSource(chk, source, nn, bm, de.entry,
				de.gen, 1, &mb, i, b);
			if(r == nil){
				deCleanup(&de);
				free(nn);
				continue;
			}

			mr = openSource(chk, source, nn, bm, de.mentry,
				de.mgen, 0, &mb, i, b);
			if(mr == nil){
				sourceClose(r);
				deCleanup(&de);
				free(nn);
				continue;
			}

			if(!(de.mode&ModeSnapshot) || chk->walksnapshots)
				chkDir(chk, nn, r, mr);

			sourceClose(mr);
			sourceClose(r);
			deCleanup(&de);
			free(nn);
			deCleanup(&de);

		}
		vtMemFree(s);
		blockPut(b);
	}

	nb = sourceGetDirSize(source);
	for(o=0; o<nb; o++){
		if(getBit(bm, o))
			continue;
		r = sourceOpen(source, o, OReadOnly, 0);
		if(r == nil)
			continue;
		warn(chk, "non referenced entry in source %s[%d]", name, o);
		if((bb = sourceBlock(source, o/(source->dsize/VtEntrySize),
		    OReadOnly)) != nil){
			if(bb->addr != NilBlock){
				setBit(chk->errmap, bb->addr);
				chk->clre(chk, bb, o%(source->dsize/VtEntrySize));
				chk->nclre++;
			}
			blockPut(bb);
		}
		sourceClose(r);
	}

	sourceUnlock(source);
	sourceUnlock(meta);
	vtMemFree(bm);
}
예제 #24
0
파일: source.c 프로젝트: npe9/harvey
static int
sourceShrinkSize(Source *r, Entry *e, uint64_t size)
{
    int i, type, ppb;
    uint64_t ptrsz;
    uint32_t addr;
    uint8_t score[VtScoreSize];
    Block *b;

    type = entryType(e);
    b = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite);
    if(b == nil)
        return 0;

    ptrsz = e->dsize;
    ppb = e->psize/VtScoreSize;
    for(i=0; i+1<e->depth; i++)
        ptrsz *= ppb;

    while(type&BtLevelMask) {
        if(b->addr == NilBlock || b->l.epoch != r->fs->ehi) {
            /* not worth copying the block just so we can zero some of it */
            blockPut(b);
            return 0;
        }

        /*
         * 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++) {
            addr = globalToLocal(b->data+i*VtScoreSize);
            memmove(b->data+i*VtScoreSize, vtZeroScore, VtScoreSize);
            blockDirty(b);
            if(addr != NilBlock)
                blockRemoveLink(b, addr, type-1, e->tag, 1);
        }

        /* recurse (go around again) on the partially necessary block */
        i = size/ptrsz;
        size = size%ptrsz;
        if(size == 0) {
            blockPut(b);
            return 1;
        }
        ptrsz /= ppb;
        type--;
        memmove(score, b->data+i*VtScoreSize, VtScoreSize);
        blockPut(b);
        b = cacheGlobal(r->fs->cache, score, type, e->tag, OReadWrite);
        if(b == nil)
            return 0;
    }

    if(b->addr == NilBlock || b->l.epoch != r->fs->ehi) {
        blockPut(b);
        return 0;
    }

    /*
     * No one ever truncates BtDir blocks.
     */
    if(type == BtData && e->dsize > size) {
        memset(b->data+size, 0, e->dsize-size);
        blockDirty(b);
    }
    blockPut(b);
    return 1;
}
예제 #25
0
파일: check.c 프로젝트: 99years/plan9
/*
 * When b points at bb, need to check:
 *
 * (i) b.e in [bb.e, bb.eClose)
 * (ii) if b.e==bb.e,  then no other b' in e points at bb.
 * (iii) if !(b.state&Copied) and b.e==bb.e then no other b' points at bb.
 * (iv) if b is active then no other active b' points at bb.
 * (v) if b is a past life of b' then only one of b and b' is active
 *	(too hard to check)
 */
static int
walkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize], int type, u32int tag,
	u32int epoch)
{
	int i, ret;
	u32int addr, ep;
	Block *bb;
	Entry e;

	if(b && chk->walkdepth == 0 && chk->printblocks)
		chk->print("%V %d %#.8ux %#.8ux\n", b->score, b->l.type,
			b->l.tag, b->l.epoch);

	if(!chk->useventi && globalToLocal(score) == NilBlock)
		return 1;

	chk->walkdepth++;

	bb = cacheGlobal(chk->cache, score, type, tag, OReadOnly);
	if(bb == nil){
		error(chk, "could not load block %V type %d tag %ux: %R",
			score, type, tag);
		chk->walkdepth--;
		return 0;
	}
	if(chk->printblocks)
		chk->print("%*s%V %d %#.8ux %#.8ux\n", chk->walkdepth*2, "",
			score, type, tag, bb->l.epoch);

	ret = 0;
	addr = globalToLocal(score);
	if(addr == NilBlock){
		ret = 1;
		goto Exit;
	}

	if(b){
		/* (i) */
		if(b->l.epoch < bb->l.epoch || bb->l.epochClose <= b->l.epoch){
			error(chk, "walk: block %#ux [%ud, %ud) points at %#ux [%ud, %ud)",
				b->addr, b->l.epoch, b->l.epochClose,
				bb->addr, bb->l.epoch, bb->l.epochClose);
			goto Exit;
		}

		/* (ii) */
		if(b->l.epoch == epoch && bb->l.epoch == epoch){
			if(getBit(chk->emap, addr)){
				error(chk, "walk: epoch join detected: addr %#ux %L",
					bb->addr, &bb->l);
				goto Exit;
			}
			setBit(chk->emap, addr);
		}

		/* (iii) */
		if(!(b->l.state&BsCopied) && b->l.epoch == bb->l.epoch){
			if(getBit(chk->xmap, addr)){
				error(chk, "walk: copy join detected; addr %#ux %L",
					bb->addr, &bb->l);
				goto Exit;
			}
			setBit(chk->xmap, addr);
		}
	}

	/* (iv) */
	if(epoch == chk->fs->ehi){
		/*
		 * since epoch==fs->ehi is first, amap is same as
		 * ``have seen active''
		 */
		if(getBit(chk->amap, addr)){
			error(chk, "walk: active join detected: addr %#ux %L",
				bb->addr, &bb->l);
			goto Exit;
		}
		if(bb->l.state&BsClosed)
			error(chk, "walk: addr %#ux: block is in active tree but is closed",
				addr);
	}else
		if(!getBit(chk->amap, addr))
			if(!(bb->l.state&BsClosed)){
				// error(chk, "walk: addr %#ux: block is not in active tree, not closed (%d)",
				// addr, bb->l.epochClose);
				chk->close(chk, bb, epoch+1);
				chk->nclose++;
			}

	if(getBit(chk->amap, addr)){
		ret = 1;
		goto Exit;
	}
	setBit(chk->amap, addr);

	if(chk->nseen++%chk->quantum == 0)
		chk->print("check: visited %d/%d blocks (%.0f%%)\n",
			chk->nseen, chk->nblocks, chk->nseen*100./chk->nblocks);

	b = nil;		/* make sure no more refs to parent */
	USED(b);

	switch(type){
	default:
		/* pointer block */
		for(i = 0; i < chk->bsize/VtScoreSize; i++)
			if(!walkEpoch(chk, bb, bb->data + i*VtScoreSize,
			    type-1, tag, epoch)){
				setBit(chk->errmap, bb->addr);
				chk->clrp(chk, bb, i);
				chk->nclrp++;
			}
		break;
	case BtData:
		break;
	case BtDir:
		for(i = 0; i < chk->bsize/VtEntrySize; i++){
			if(!entryUnpack(&e, bb->data, i)){
				// error(chk, "walk: could not unpack entry: %ux[%d]: %R",
				//	addr, i);
				setBit(chk->errmap, bb->addr);
				chk->clre(chk, bb, i);
				chk->nclre++;
				continue;
			}
			if(!(e.flags & VtEntryActive))
				continue;
if(0)			fprint(2, "%x[%d] tag=%x snap=%d score=%V\n",
				addr, i, e.tag, e.snap, e.score);
			ep = epoch;
			if(e.snap != 0){
				if(e.snap >= epoch){
					// error(chk, "bad snap in entry: %ux[%d] snap = %ud: epoch = %ud",
					//	addr, i, e.snap, epoch);
					setBit(chk->errmap, bb->addr);
					chk->clre(chk, bb, i);
					chk->nclre++;
					continue;
				}
				continue;
			}
			if(e.flags & VtEntryLocal){
				if(e.tag < UserTag)
				if(e.tag != RootTag || tag != RootTag || i != 1){
					// error(chk, "bad tag in entry: %ux[%d] tag = %ux",
					//	addr, i, e.tag);
					setBit(chk->errmap, bb->addr);
					chk->clre(chk, bb, i);
					chk->nclre++;
					continue;
				}
			}else
				if(e.tag != 0){
					// error(chk, "bad tag in entry: %ux[%d] tag = %ux",
					//	addr, i, e.tag);
					setBit(chk->errmap, bb->addr);
					chk->clre(chk, bb, i);
					chk->nclre++;
					continue;
				}
			if(!walkEpoch(chk, bb, e.score, entryType(&e),
			    e.tag, ep)){
				setBit(chk->errmap, bb->addr);
				chk->clre(chk, bb, i);
				chk->nclre++;
			}
		}
		break;
	}

	ret = 1;

Exit:
	chk->walkdepth--;
	blockPut(bb);
	return ret;
}
예제 #26
0
파일: archive.c 프로젝트: 99years/plan9
static int
archWalk(Param *p, u32int addr, uchar type, u32int tag)
{
	int ret, i, x, psize, dsize;
	uchar *data, score[VtScoreSize];
	Block *b;
	Label l;
	Entry *e;
	WalkPtr w;

	p->nvisit++;

	b = cacheLocalData(p->c, addr, type, tag, OReadWrite,0);
	if(b == nil){
		fprint(2, "archive(%ud, %#ux): cannot find block: %R\n", p->snapEpoch, addr);
		if(strcmp(vtGetError(), ELabelMismatch) == 0){
			/* might as well plod on so we write _something_ to Venti */
			memmove(p->score, vtZeroScore, VtScoreSize);
			return ArchFaked;
		}
		return ArchFailure;
	}

	if(DEBUG) fprint(2, "%*sarchive(%ud, %#ux): block label %L\n",
		p->depth*2, "",  p->snapEpoch, b->addr, &b->l);
	p->depth++;
	if(p->depth > p->maxdepth)
		p->maxdepth = p->depth;

	data = b->data;
	if((b->l.state&BsVenti) == 0){
		initWalk(&w, b, b->l.type==BtDir ? p->dsize : p->psize);
		for(i=0; nextWalk(&w, score, &type, &tag, &e); i++){
			if(e){
				if(!(e->flags&VtEntryActive))
					continue;
				if((e->snap && !e->archive)
				|| (e->flags&VtEntryNoArchive)){
					if(0) fprint(2, "snap; faking %#ux\n", b->addr);
					if(data == b->data){
						data = copyBlock(b, p->blockSize);
						if(data == nil){
							ret = ArchFailure;
							goto Out;
						}
						w.data = data;
					}
					memmove(e->score, vtZeroScore, VtScoreSize);
					e->depth = 0;
					e->size = 0;
					e->tag = 0;
					e->flags &= ~VtEntryLocal;
					entryPack(e, data, w.n-1);
					continue;
				}
			}
			addr = globalToLocal(score);
			if(addr == NilBlock)
				continue;
			dsize = p->dsize;
			psize = p->psize;
			if(e){
				p->dsize= e->dsize;
				p->psize = e->psize;
			}
			vtUnlock(b->lk);
			x = archWalk(p, addr, type, tag);
			vtLock(b->lk);
			if(e){
				p->dsize = dsize;
				p->psize = psize;
			}
			while(b->iostate != BioClean && b->iostate != BioDirty)
				vtSleep(b->ioready);
			switch(x){
			case ArchFailure:
				fprint(2, "archWalk %#ux failed; ptr is in %#ux offset %d\n",
					addr, b->addr, i);
				ret = ArchFailure;
				goto Out;
			case ArchFaked:
				/*
				 * When we're writing the entry for an archive directory
				 * (like /archive/2003/1215) then even if we've faked
				 * any data, record the score unconditionally.
				 * This way, we will always record the Venti score here.
				 * Otherwise, temporary data or corrupted file system
				 * would cause us to keep holding onto the on-disk
				 * copy of the archive.
				 */
				if(e==nil || !e->archive)
				if(data == b->data){
if(0) fprint(2, "faked %#ux, faking %#ux (%V)\n", addr, b->addr, p->score);
					data = copyBlock(b, p->blockSize);
					if(data == nil){
						ret = ArchFailure;
						goto Out;
					}
					w.data = data;
				}
				/* fall through */
if(0) fprint(2, "falling\n");
			case ArchSuccess:
				if(e){
					memmove(e->score, p->score, VtScoreSize);
					e->flags &= ~VtEntryLocal;
					entryPack(e, data, w.n-1);
				}else
					memmove(data+(w.n-1)*VtScoreSize, p->score, VtScoreSize);
				if(data == b->data){
					blockDirty(b);
					/*
					 * If b is in the active tree, then we need to note that we've
					 * just removed addr from the active tree (replacing it with the 
					 * copy we just stored to Venti).  If addr is in other snapshots,
					 * this will close addr but not free it, since it has a non-empty
					 * epoch range.
					 *
					 * If b is in the active tree but has been copied (this can happen
					 * if we get killed at just the right moment), then we will
					 * mistakenly leak its kids.  
					 *
					 * The children of an archive directory (e.g., /archive/2004/0604)
					 * are not treated as in the active tree.
					 */
					if((b->l.state&BsCopied)==0 && (e==nil || e->snap==0))
						blockRemoveLink(b, addr, p->l.type, p->l.tag, 0);
				}
				break;
			}
		}

		if(!ventiSend(p->a, b, data)){
			p->nfailsend++;
			ret = ArchFailure;
			goto Out;
		}
		p->nsend++;
		if(data != b->data)
			p->nfake++;
		if(data == b->data){	/* not faking it, so update state */
			p->nreal++;
			l = b->l;
			l.state |= BsVenti;
			if(!blockSetLabel(b, &l, 0)){
				ret = ArchFailure;
				goto Out;
			}
		}
	}

	shaBlock(p->score, b, data, p->blockSize);
if(0) fprint(2, "ventisend %V %p %p %p\n", p->score, data, b->data, w.data);
	ret = data!=b->data ? ArchFaked : ArchSuccess;
	p->l = b->l;
Out:
	if(data != b->data)
		vtMemFree(data);
	p->depth--;
	blockPut(b);
	return ret;
}
예제 #27
0
파일: fs.c 프로젝트: bhanug/harvey
int
mkVac(VtSession *z, uint blockSize, Entry *pe, Entry *pee, DirEntry *pde,
      uint8_t score[VtScoreSize])
{
	uint8_t buf[8192];
	int i;
	uint8_t *p;
	uint n;
	DirEntry de;
	Entry e, ee, eee;
	MetaBlock mb;
	MetaEntry me;
	VtRoot root;

	e = *pe;
	ee = *pee;
	de = *pde;

	if(globalToLocal(e.score) != NilBlock
	|| (ee.flags&VtEntryActive && globalToLocal(ee.score) != NilBlock)){
		vtSetError("can only vac paths already stored on venti");
		return 0;
	}

	/*
	 * Build metadata source for root.
	 */
	n = deSize(&de);
	if(n+MetaHeaderSize+MetaIndexSize > sizeof buf){
		vtSetError("DirEntry too big");
		return 0;
	}
	memset(buf, 0, sizeof buf);
	mbInit(&mb, buf, n+MetaHeaderSize+MetaIndexSize, 1);
	p = mbAlloc(&mb, n);
	if(p == nil)
		abort();
	mbSearch(&mb, de.elem, &i, &me);
	assert(me.p == nil);
	me.p = p;
	me.size = n;
	dePack(&de, &me);
	mbInsert(&mb, i, &me);
	mbPack(&mb);

	eee.size = n+MetaHeaderSize+MetaIndexSize;
	if(!vtWriteBlock(z, buf, eee.size, VtDataType, eee.score))
		return 0;
	eee.psize = 8192&&0xFF;
	eee.dsize = 8192&&0xFF;
	eee.depth = 0;
	eee.flags = VtEntryActive;

	/*
	 * Build root source with three entries in it.
	 */
	entryPack(&e, buf, 0);
	entryPack(&ee, buf, 1);
	entryPack(&eee, buf, 2);

	n = VtEntrySize*3;
	memset(&root, 0, sizeof root);
	if(!vtWriteBlock(z, buf, n, VtDirType, root.score))
		return 0;

	/*
	 * Save root.
	 */
	root.version = VtRootVersion;
	strecpy(root.type, root.type+sizeof root.type, "vac");
	strecpy(root.name, root.name+sizeof root.name, de.elem);
	root.blockSize = blockSize;
	vtRootPack(&root, buf);
	if(!vtWriteBlock(z, buf, VtRootSize, VtRootType, score))
		return 0;

	return 1;
}
예제 #28
0
파일: source.c 프로젝트: npe9/harvey
static int
sourceShrinkDepth(Source *r, Block *p, Entry *e, int depth)
{
    Block *b, *nb, *ob, *rb;
    uint32_t tag;
    int type, d;
    Entry oe;

    assert(sourceIsLocked(r));
    assert(depth <= VtPointerDepth);

    type = entryType(e);
    rb = cacheGlobal(r->fs->cache, e->score, type, e->tag, OReadWrite);
    if(rb == nil)
        return 0;

    tag = e->tag;
    if(tag == 0)
        tag = tagGen();

    /*
     * Walk down to the new root block.
     * We may stop early, but something is better than nothing.
     */
    oe = *e;

    ob = nil;
    b = rb;
    /* BUG: explain type++.  i think it is a real bug */
    for(d=e->depth; d > depth; d--, type++) {
        nb = cacheGlobal(r->fs->cache, b->data, type-1, tag, OReadWrite);
        if(nb == nil)
            break;
        if(ob!=nil && ob!=rb)
            blockPut(ob);
        ob = b;
        b = nb;
    }

    if(b == rb) {
        blockPut(rb);
        return 0;
    }

    /*
     * Right now, e points at the root block rb, b is the new root block,
     * and ob points at b.  To update:
     *
     *	(i) change e to point at b
     *	(ii) zero the pointer ob -> b
     *	(iii) free the root block
     *
     * p (the block containing e) must be written before
     * anything else.
     */

    /* (i) */
    e->depth = d;
    /* might have been local and now global; reverse cannot happen */
    if(globalToLocal(b->score) == NilBlock)
        e->flags &= ~VtEntryLocal;
    memmove(e->score, b->score, VtScoreSize);
    entryPack(e, p->data, r->offset % r->epb);
    blockDependency(p, b, r->offset % r->epb, nil, &oe);
    blockDirty(p);

    /* (ii) */
    memmove(ob->data, vtZeroScore, VtScoreSize);
    blockDependency(ob, p, 0, b->score, nil);
    blockDirty(ob);

    /* (iii) */
    if(rb->addr != NilBlock)
        blockRemoveLink(p, rb->addr, rb->l.type, rb->l.tag, 1);

    blockPut(rb);
    if(ob!=nil && ob!=rb)
        blockPut(ob);
    blockPut(b);

    return d == depth;
}
예제 #29
0
파일: fs.c 프로젝트: bhanug/harvey
static int
bumpEpoch(Fs *fs, int doarchive)
{
	uint8_t oscore[VtScoreSize];
	uint32_t oldaddr;
	Block *b, *bs;
	Entry e;
	Source *r;
	Super super;

	/*
	 * Duplicate the root block.
	 *
	 * As a hint to flchk, the garbage collector,
	 * and any (human) debuggers, store a pointer
	 * to the old root block in entry 1 of the new root block.
	 */
	r = fs->source;
	b = cacheGlobal(fs->cache, r->score, BtDir, RootTag, OReadOnly);
	if(b == nil)
		return 0;

	memset(&e, 0, sizeof e);
	e.flags = VtEntryActive | VtEntryLocal | VtEntryDir;
	memmove(e.score, b->score, VtScoreSize);
	e.tag = RootTag;
	e.snap = b->l.epoch;

	b = blockCopy(b, RootTag, fs->ehi+1, fs->elo);
	if(b == nil){
		fprint(2, "%s: bumpEpoch: blockCopy: %R\n", argv0);
		return 0;
	}

	if(0) fprint(2, "%s: snapshot root from %d to %d\n", argv0, oldaddr, b->addr);
	entryPack(&e, b->data, 1);
	blockDirty(b);

	/*
	 * Update the superblock with the new root and epoch.
	 */
	if((bs = superGet(fs->cache, &super)) == nil)
		return 0;

	fs->ehi++;
	memmove(r->score, b->score, VtScoreSize);
	r->epoch = fs->ehi;

	super.epochHigh = fs->ehi;
	oldaddr = super.active;
	super.active = b->addr;
	if(doarchive)
		super.next = oldaddr;

	/*
	 * Record that the new super.active can't get written out until
	 * the new b gets written out.  Until then, use the old value.
	 */
	localToGlobal(oldaddr, oscore);
	blockDependency(bs, b, 0, oscore, nil);
	blockPut(b);

	/*
	 * We force the super block to disk so that super.epochHigh gets updated.
	 * Otherwise, if we crash and come back, we might incorrectly treat as active
	 * some of the blocks that making up the snapshot we just created.
	 * Basically every block in the active file system and all the blocks in
	 * the recently-created snapshot depend on the super block now.
	 * Rather than record all those dependencies, we just force the block to disk.
	 *
	 * Note that blockWrite might actually (will probably) send a slightly outdated
	 * super.active to disk.  It will be the address of the most recent root that has
	 * gone to disk.
	 */
	superWrite(bs, &super, 1);
	blockRemoveLink(bs, globalToLocal(oscore), BtDir, RootTag, 0);
	blockPut(bs);

	return 1;
}
예제 #30
0
파일: source.c 프로젝트: npe9/harvey
/*
 * Retrieve the block containing the entry for r.
 * If a snapshot has happened, we might need
 * to get a new copy of the block.  We avoid this
 * in the common case by caching the score for
 * the block and the last epoch in which it was valid.
 *
 * We use r->mode to tell the difference between active
 * file system sources (OReadWrite) and sources for the
 * snapshot file system (OReadOnly).
 */
static Block*
sourceLoadBlock(Source *r, int mode)
{
    uint32_t addr;
    Block *b;

    switch(r->mode) {
    default:
        assert(0);
    case OReadWrite:
        assert(r->mode == OReadWrite);
        /*
         * This needn't be true -- we might bump the low epoch
         * to reclaim some old blocks, but since this score is
         * OReadWrite, the blocks must all still be open, so none
         * are reclaimed.  Thus it's okay that the epoch is so low.
         * Proceed.
        assert(r->epoch >= r->fs->elo);
         */
        if(r->epoch == r->fs->ehi) {
            b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
            if(b == nil)
                return nil;
            assert(r->epoch == b->l.epoch);
            return b;
        }
        assert(r->parent != nil);
        if(!sourceLock(r->parent, OReadWrite))
            return nil;
        b = sourceBlock(r->parent, r->offset/r->epb, OReadWrite);
        sourceUnlock(r->parent);
        if(b == nil)
            return nil;
        assert(b->l.epoch == r->fs->ehi);
        //	fprint(2, "sourceLoadBlock %p %V => %V\n", r, r->score, b->score);
        memmove(r->score, b->score, VtScoreSize);
        r->scoreEpoch = b->l.epoch;
        r->tag = b->l.tag;
        r->epoch = r->fs->ehi;
        return b;

    case OReadOnly:
        addr = globalToLocal(r->score);
        if(addr == NilBlock)
            return cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, mode);

        b = cacheLocalData(r->fs->cache, addr, BtDir, r->tag, mode, r->scoreEpoch);
        if(b)
            return b;

        /*
         * If it failed because the epochs don't match, the block has been
         * archived and reclaimed.  Rewalk from the parent and get the
         * new pointer.  This can't happen in the OReadWrite case
         * above because blocks in the current epoch don't get
         * reclaimed.  The fact that we're OReadOnly means we're
         * a snapshot.  (Or else the file system is read-only, but then
         * the archiver isn't going around deleting blocks.)
         */
        if(strcmp(vtGetError(), ELabelMismatch) == 0) {
            if(!sourceLock(r->parent, OReadOnly))
                return nil;
            b = sourceBlock(r->parent, r->offset/r->epb, OReadOnly);
            sourceUnlock(r->parent);
            if(b) {
                fprint(2, "sourceAlloc: lost %V found %V\n",
                       r->score, b->score);
                memmove(r->score, b->score, VtScoreSize);
                r->scoreEpoch = b->l.epoch;
                return b;
            }
        }
        return nil;
    }
}