1 #include "stdinc.h"
2 #include "dat.h"
3 #include "fns.h"
4 #include "whack.h"
5 
6 /*
7  * Write a lump to disk.  Updates ia with an index address
8  * for the newly-written lump.  Upon return, the lump will
9  * have been placed in the disk cache but will likely not be on disk yet.
10  */
11 int
storeclump(Index * ix,ZBlock * zb,u8int * sc,int type,u32int creator,IAddr * ia)12 storeclump(Index *ix, ZBlock *zb, u8int *sc, int type, u32int creator, IAddr *ia)
13 {
14 	ZBlock *cb;
15 	Clump cl;
16 	u64int a;
17 	u8int bh[VtScoreSize];
18 	int size, dsize;
19 
20 	trace(TraceLump, "storeclump enter", sc, type);
21 	size = zb->len;
22 	if(size > VtMaxLumpSize){
23 		seterr(EStrange, "lump too large");
24 		return -1;
25 	}
26 	if(vttypevalid(type) < 0){
27 		seterr(EStrange, "invalid lump type");
28 		return -1;
29 	}
30 
31 	if(0){
32 		scoremem(bh, zb->data, size);
33 		if(scorecmp(sc, bh) != 0){
34 			seterr(ECorrupt, "storing clump: corrupted; expected=%V got=%V, size=%d", sc, bh, size);
35 			return -1;
36 		}
37 	}
38 
39 	cb = alloczblock(size + ClumpSize + U32Size, 0, 0);
40 	if(cb == nil)
41 		return -1;
42 
43 	cl.info.type = type;
44 	cl.info.uncsize = size;
45 	cl.creator = creator;
46 	cl.time = now();
47 	scorecp(cl.info.score, sc);
48 
49 	trace(TraceLump, "storeclump whackblock");
50 	dsize = whackblock(&cb->data[ClumpSize], zb->data, size);
51 	if(dsize > 0 && dsize < size){
52 		cl.encoding = ClumpECompress;
53 	}else{
54 		if(dsize > size){
55 			fprint(2, "whack error: dsize=%d size=%d\n", dsize, size);
56 			abort();
57 		}
58 		cl.encoding = ClumpENone;
59 		dsize = size;
60 		memmove(&cb->data[ClumpSize], zb->data, size);
61 	}
62 	memset(cb->data+ClumpSize+dsize, 0, 4);
63 	cl.info.size = dsize;
64 
65 	a = writeiclump(ix, &cl, cb->data);
66 	trace(TraceLump, "storeclump exit %lld", a);
67 	freezblock(cb);
68 	if(a == TWID64)
69 		return -1;
70 
71 	ia->addr = a;
72 	ia->type = type;
73 	ia->size = size;
74 	ia->blocks = (dsize + ClumpSize + (1 << ABlockLog) - 1) >> ABlockLog;
75 
76 /*
77 	qlock(&stats.lock);
78 	stats.clumpwrites++;
79 	stats.clumpbwrites += size;
80 	stats.clumpbcomp += dsize;
81 	qunlock(&stats.lock);
82 */
83 
84 	return 0;
85 }
86 
87 u32int
clumpmagic(Arena * arena,u64int aa)88 clumpmagic(Arena *arena, u64int aa)
89 {
90 	u8int buf[U32Size];
91 
92 	if(readarena(arena, aa, buf, U32Size) == TWID32)
93 		return TWID32;
94 	return unpackmagic(buf);
95 }
96 
97 /*
98  * fetch a block based at addr.
99  * score is filled in with the block's score.
100  * blocks is roughly the length of the clump on disk;
101  * if zero, the length is unknown.
102  */
103 ZBlock*
loadclump(Arena * arena,u64int aa,int blocks,Clump * cl,u8int * score,int verify)104 loadclump(Arena *arena, u64int aa, int blocks, Clump *cl, u8int *score, int verify)
105 {
106 	Unwhack uw;
107 	ZBlock *zb, *cb;
108 	u8int bh[VtScoreSize], *buf;
109 	u32int n;
110 	int nunc;
111 
112 /*
113 	qlock(&stats.lock);
114 	stats.clumpreads++;
115 	qunlock(&stats.lock);
116 */
117 
118 	if(blocks <= 0)
119 		blocks = 1;
120 
121 	trace(TraceLump, "loadclump enter");
122 
123 	cb = alloczblock(blocks << ABlockLog, 0, 0);
124 	if(cb == nil)
125 		return nil;
126 	n = readarena(arena, aa, cb->data, blocks << ABlockLog);
127 	if(n < ClumpSize){
128 		if(n != 0)
129 			seterr(ECorrupt, "loadclump read less than a header");
130 		freezblock(cb);
131 		return nil;
132 	}
133 	trace(TraceLump, "loadclump unpack");
134 	if(unpackclump(cl, cb->data, arena->clumpmagic) < 0){
135 		seterr(ECorrupt, "loadclump %s %llud: %r", arena->name, aa);
136 		freezblock(cb);
137 		return nil;
138 	}
139 	if(cl->info.type == VtCorruptType){
140 		seterr(EOk, "clump is marked corrupt");
141 		freezblock(cb);
142 		return nil;
143 	}
144 	n -= ClumpSize;
145 	if(n < cl->info.size){
146 		freezblock(cb);
147 		n = cl->info.size;
148 		cb = alloczblock(n, 0, 0);
149 		if(cb == nil)
150 			return nil;
151 		if(readarena(arena, aa + ClumpSize, cb->data, n) != n){
152 			seterr(ECorrupt, "loadclump read too little data");
153 			freezblock(cb);
154 			return nil;
155 		}
156 		buf = cb->data;
157 	}else
158 		buf = cb->data + ClumpSize;
159 
160 	scorecp(score, cl->info.score);
161 
162 	zb = alloczblock(cl->info.uncsize, 0, 0);
163 	if(zb == nil){
164 		freezblock(cb);
165 		return nil;
166 	}
167 	switch(cl->encoding){
168 	case ClumpECompress:
169 		trace(TraceLump, "loadclump decompress");
170 		unwhackinit(&uw);
171 		nunc = unwhack(&uw, zb->data, cl->info.uncsize, buf, cl->info.size);
172 		if(nunc != cl->info.uncsize){
173 			if(nunc < 0)
174 				seterr(ECorrupt, "decompression of %llud failed: %s", aa, uw.err);
175 			else
176 				seterr(ECorrupt, "decompression of %llud gave partial block: %d/%d\n", aa, nunc, cl->info.uncsize);
177 			freezblock(cb);
178 			freezblock(zb);
179 			return nil;
180 		}
181 		break;
182 	case ClumpENone:
183 		if(cl->info.size != cl->info.uncsize){
184 			seterr(ECorrupt, "loading clump: bad uncompressed size for uncompressed block %llud", aa);
185 			freezblock(cb);
186 			freezblock(zb);
187 			return nil;
188 		}
189 		scoremem(bh, buf, cl->info.uncsize);
190 		if(scorecmp(cl->info.score, bh) != 0)
191 			seterr(ECorrupt, "pre-copy sha1 wrong at %s %llud: expected=%V got=%V", arena->name, aa, cl->info.score, bh);
192 		memmove(zb->data, buf, cl->info.uncsize);
193 		break;
194 	default:
195 		seterr(ECorrupt, "unknown encoding in loadlump %llud", aa);
196 		freezblock(cb);
197 		freezblock(zb);
198 		return nil;
199 	}
200 	freezblock(cb);
201 
202 	if(verify){
203 		trace(TraceLump, "loadclump verify");
204 		scoremem(bh, zb->data, cl->info.uncsize);
205 		if(scorecmp(cl->info.score, bh) != 0){
206 			seterr(ECorrupt, "loading clump: corrupted at %s %llud; expected=%V got=%V", arena->name, aa, cl->info.score, bh);
207 			freezblock(zb);
208 			return nil;
209 		}
210 		if(vttypevalid(cl->info.type) < 0){
211 			seterr(ECorrupt, "loading lump at %s %llud: invalid lump type %d", arena->name, aa, cl->info.type);
212 			freezblock(zb);
213 			return nil;
214 		}
215 	}
216 
217 	trace(TraceLump, "loadclump exit");
218 /*
219 	qlock(&stats.lock);
220 	stats.clumpbreads += cl->info.size;
221 	stats.clumpbuncomp += cl->info.uncsize;
222 	qunlock(&stats.lock);
223 */
224 	return zb;
225 }
226