| #include "stdinc.h" | 
 | #include "dat.h" | 
 | #include "fns.h" | 
 | #include "whack.h" | 
 |  | 
 | /* | 
 |  * Write a lump to disk.  Updates ia with an index address | 
 |  * for the newly-written lump.  Upon return, the lump will | 
 |  * have been placed in the disk cache but will likely not be on disk yet. | 
 |  */ | 
 | int | 
 | storeclump(Index *ix, ZBlock *zb, u8int *sc, int type, u32int creator, IAddr *ia) | 
 | { | 
 | 	ZBlock *cb; | 
 | 	Clump cl; | 
 | 	u64int a; | 
 | 	u8int bh[VtScoreSize]; | 
 | 	int size, dsize; | 
 |  | 
 | 	trace(TraceLump, "storeclump enter", sc, type); | 
 | 	size = zb->len; | 
 | 	if(size > VtMaxLumpSize){ | 
 | 		seterr(EStrange, "lump too large"); | 
 | 		return -1; | 
 | 	} | 
 | 	if(vttypevalid(type) < 0){ | 
 | 		seterr(EStrange, "invalid lump type"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	if(0){ | 
 | 		scoremem(bh, zb->data, size); | 
 | 		if(scorecmp(sc, bh) != 0){ | 
 | 			seterr(ECorrupt, "storing clump: corrupted; expected=%V got=%V, size=%d", sc, bh, size); | 
 | 			return -1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	cb = alloczblock(size + ClumpSize + U32Size, 0, 0); | 
 | 	if(cb == nil) | 
 | 		return -1; | 
 |  | 
 | 	cl.info.type = type; | 
 | 	cl.info.uncsize = size; | 
 | 	cl.creator = creator; | 
 | 	cl.time = now(); | 
 | 	scorecp(cl.info.score, sc); | 
 |  | 
 | 	trace(TraceLump, "storeclump whackblock"); | 
 | 	dsize = whackblock(&cb->data[ClumpSize], zb->data, size); | 
 | 	if(dsize > 0 && dsize < size){ | 
 | 		cl.encoding = ClumpECompress; | 
 | 	}else{ | 
 | 		if(dsize > size){ | 
 | 			fprint(2, "whack error: dsize=%d size=%d\n", dsize, size); | 
 | 			abort(); | 
 | 		} | 
 | 		cl.encoding = ClumpENone; | 
 | 		dsize = size; | 
 | 		memmove(&cb->data[ClumpSize], zb->data, size); | 
 | 	} | 
 | 	memset(cb->data+ClumpSize+dsize, 0, 4); | 
 | 	cl.info.size = dsize; | 
 |  | 
 | 	a = writeiclump(ix, &cl, cb->data); | 
 | 	trace(TraceLump, "storeclump exit %lld", a); | 
 | 	freezblock(cb); | 
 | 	if(a == TWID64) | 
 | 		return -1; | 
 |  | 
 | 	ia->addr = a; | 
 | 	ia->type = type; | 
 | 	ia->size = size; | 
 | 	ia->blocks = (dsize + ClumpSize + (1 << ABlockLog) - 1) >> ABlockLog; | 
 |  | 
 | /* | 
 | 	qlock(&stats.lock); | 
 | 	stats.clumpwrites++; | 
 | 	stats.clumpbwrites += size; | 
 | 	stats.clumpbcomp += dsize; | 
 | 	qunlock(&stats.lock); | 
 | */ | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | u32int | 
 | clumpmagic(Arena *arena, u64int aa) | 
 | { | 
 | 	u8int buf[U32Size]; | 
 |  | 
 | 	if(readarena(arena, aa, buf, U32Size) == TWID32) | 
 | 		return TWID32; | 
 | 	return unpackmagic(buf); | 
 | } | 
 |  | 
 | /* | 
 |  * fetch a block based at addr. | 
 |  * score is filled in with the block's score. | 
 |  * blocks is roughly the length of the clump on disk; | 
 |  * if zero, the length is unknown. | 
 |  */ | 
 | ZBlock* | 
 | loadclump(Arena *arena, u64int aa, int blocks, Clump *cl, u8int *score, int verify) | 
 | { | 
 | 	Unwhack uw; | 
 | 	ZBlock *zb, *cb; | 
 | 	u8int bh[VtScoreSize], *buf; | 
 | 	u32int n; | 
 | 	int nunc; | 
 |  | 
 | /* | 
 | 	qlock(&stats.lock); | 
 | 	stats.clumpreads++; | 
 | 	qunlock(&stats.lock); | 
 | */ | 
 |  | 
 | 	if(blocks <= 0) | 
 | 		blocks = 1; | 
 |  | 
 | 	trace(TraceLump, "loadclump enter"); | 
 |  | 
 | 	cb = alloczblock(blocks << ABlockLog, 0, 0); | 
 | 	if(cb == nil) | 
 | 		return nil; | 
 | 	n = readarena(arena, aa, cb->data, blocks << ABlockLog); | 
 | 	if(n < ClumpSize){ | 
 | 		if(n != 0) | 
 | 			seterr(ECorrupt, "loadclump read less than a header"); | 
 | 		freezblock(cb); | 
 | 		return nil; | 
 | 	} | 
 | 	trace(TraceLump, "loadclump unpack"); | 
 | 	if(unpackclump(cl, cb->data, arena->clumpmagic) < 0){ | 
 | 		seterr(ECorrupt, "loadclump %s %llud: %r", arena->name, aa); | 
 | 		freezblock(cb); | 
 | 		return nil; | 
 | 	} | 
 | 	if(cl->info.type == VtCorruptType){ | 
 | 		seterr(EOk, "clump is marked corrupt"); | 
 | 		freezblock(cb); | 
 | 		return nil; | 
 | 	} | 
 | 	n -= ClumpSize; | 
 | 	if(n < cl->info.size){ | 
 | 		freezblock(cb); | 
 | 		n = cl->info.size; | 
 | 		cb = alloczblock(n, 0, 0); | 
 | 		if(cb == nil) | 
 | 			return nil; | 
 | 		if(readarena(arena, aa + ClumpSize, cb->data, n) != n){ | 
 | 			seterr(ECorrupt, "loadclump read too little data"); | 
 | 			freezblock(cb); | 
 | 			return nil; | 
 | 		} | 
 | 		buf = cb->data; | 
 | 	}else | 
 | 		buf = cb->data + ClumpSize; | 
 |  | 
 | 	scorecp(score, cl->info.score); | 
 |  | 
 | 	zb = alloczblock(cl->info.uncsize, 0, 0); | 
 | 	if(zb == nil){ | 
 | 		freezblock(cb); | 
 | 		return nil; | 
 | 	} | 
 | 	switch(cl->encoding){ | 
 | 	case ClumpECompress: | 
 | 		trace(TraceLump, "loadclump decompress"); | 
 | 		unwhackinit(&uw); | 
 | 		nunc = unwhack(&uw, zb->data, cl->info.uncsize, buf, cl->info.size); | 
 | 		if(nunc != cl->info.uncsize){ | 
 | 			if(nunc < 0) | 
 | 				seterr(ECorrupt, "decompression of %llud failed: %s", aa, uw.err); | 
 | 			else | 
 | 				seterr(ECorrupt, "decompression of %llud gave partial block: %d/%d\n", aa, nunc, cl->info.uncsize); | 
 | 			freezblock(cb); | 
 | 			freezblock(zb); | 
 | 			return nil; | 
 | 		} | 
 | 		break; | 
 | 	case ClumpENone: | 
 | 		if(cl->info.size != cl->info.uncsize){ | 
 | 			seterr(ECorrupt, "loading clump: bad uncompressed size for uncompressed block %llud", aa); | 
 | 			freezblock(cb); | 
 | 			freezblock(zb); | 
 | 			return nil; | 
 | 		} | 
 | 		scoremem(bh, buf, cl->info.uncsize); | 
 | 		if(scorecmp(cl->info.score, bh) != 0) | 
 | 			seterr(ECorrupt, "pre-copy sha1 wrong at %s %llud: expected=%V got=%V", arena->name, aa, cl->info.score, bh); | 
 | 		memmove(zb->data, buf, cl->info.uncsize); | 
 | 		break; | 
 | 	default: | 
 | 		seterr(ECorrupt, "unknown encoding in loadlump %llud", aa); | 
 | 		freezblock(cb); | 
 | 		freezblock(zb); | 
 | 		return nil; | 
 | 	} | 
 | 	freezblock(cb); | 
 |  | 
 | 	if(verify){ | 
 | 		trace(TraceLump, "loadclump verify"); | 
 | 		scoremem(bh, zb->data, cl->info.uncsize); | 
 | 		if(scorecmp(cl->info.score, bh) != 0){ | 
 | 			seterr(ECorrupt, "loading clump: corrupted at %s %llud; expected=%V got=%V", arena->name, aa, cl->info.score, bh); | 
 | 			freezblock(zb); | 
 | 			return nil; | 
 | 		} | 
 | 		if(vttypevalid(cl->info.type) < 0){ | 
 | 			seterr(ECorrupt, "loading lump at %s %llud: invalid lump type %d", arena->name, aa, cl->info.type); | 
 | 			freezblock(zb); | 
 | 			return nil; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	trace(TraceLump, "loadclump exit"); | 
 | /* | 
 | 	qlock(&stats.lock); | 
 | 	stats.clumpbreads += cl->info.size; | 
 | 	stats.clumpbuncomp += cl->info.uncsize; | 
 | 	qunlock(&stats.lock); | 
 | */ | 
 | 	return zb; | 
 | } |