xref: /plan9/sys/src/cmd/venti/srv/fixarenas.c (revision 4fd2ae8bdcc31de12a96e54a2d1e38444a2887ed)
1368c31abSDavid du Colombier /*
2368c31abSDavid du Colombier  * Check and fix an arena partition.
3368c31abSDavid du Colombier  *
4368c31abSDavid du Colombier  * This is a lot grittier than the rest of Venti because
5368c31abSDavid du Colombier  * it can't just give up if a byte here or there is wrong.
6368c31abSDavid du Colombier  *
7368c31abSDavid du Colombier  * The rule here (hopefully followed!) is that block corruption
8368c31abSDavid du Colombier  * only ever has a local effect -- there are no blocks that you
9368c31abSDavid du Colombier  * can wipe out that will cause large portions of
10368c31abSDavid du Colombier  * uncorrupted data blocks to be useless.
11368c31abSDavid du Colombier  */
12368c31abSDavid du Colombier 
13368c31abSDavid du Colombier #include "stdinc.h"
14368c31abSDavid du Colombier #include "dat.h"
15368c31abSDavid du Colombier #include "fns.h"
16368c31abSDavid du Colombier #include "whack.h"
17368c31abSDavid du Colombier 
18a6b1725cSDavid du Colombier #define ROUNDUP(x,n)		(((x)+(n)-1)&~((n)-1))
19a6b1725cSDavid du Colombier 
20368c31abSDavid du Colombier #pragma varargck type "z" uvlong
21368c31abSDavid du Colombier #pragma varargck type "z" vlong
22368c31abSDavid du Colombier #pragma varargck type "t" uint
23368c31abSDavid du Colombier 
24368c31abSDavid du Colombier enum
25368c31abSDavid du Colombier {
26368c31abSDavid du Colombier 	K = 1024,
27368c31abSDavid du Colombier 	M = 1024*1024,
28368c31abSDavid du Colombier 	G = 1024*1024*1024,
29368c31abSDavid du Colombier 
30368c31abSDavid du Colombier 	Block = 4096,
31368c31abSDavid du Colombier };
32368c31abSDavid du Colombier 
33368c31abSDavid du Colombier int debugsha1;
34368c31abSDavid du Colombier 
35368c31abSDavid du Colombier int verbose;
36368c31abSDavid du Colombier Part *part;
37368c31abSDavid du Colombier char *file;
38368c31abSDavid du Colombier char *basename;
39368c31abSDavid du Colombier char *dumpbase;
40368c31abSDavid du Colombier int fix;
41368c31abSDavid du Colombier int badreads;
42368c31abSDavid du Colombier int unseal;
43368c31abSDavid du Colombier uchar zero[MaxDiskBlock];
44368c31abSDavid du Colombier 
45368c31abSDavid du Colombier Arena lastarena;
46368c31abSDavid du Colombier ArenaPart ap;
47368c31abSDavid du Colombier uvlong arenasize;
48368c31abSDavid du Colombier int nbadread;
49368c31abSDavid du Colombier int nbad;
50368c31abSDavid du Colombier uvlong partend;
51368c31abSDavid du Colombier void checkarena(vlong, int);
52368c31abSDavid du Colombier 
53368c31abSDavid du Colombier void
usage(void)54368c31abSDavid du Colombier usage(void)
55368c31abSDavid du Colombier {
56368c31abSDavid du Colombier 	fprint(2, "usage: fixarenas [-fv] [-a arenasize] [-b blocksize] file [ranges]\n");
57368c31abSDavid du Colombier 	threadexitsall(0);
58368c31abSDavid du Colombier }
59368c31abSDavid du Colombier 
60368c31abSDavid du Colombier /*
61368c31abSDavid du Colombier  * Format number in simplest way that is okay with unittoull.
62368c31abSDavid du Colombier  */
63368c31abSDavid du Colombier static int
zfmt(Fmt * fmt)64368c31abSDavid du Colombier zfmt(Fmt *fmt)
65368c31abSDavid du Colombier {
66368c31abSDavid du Colombier 	vlong x;
67368c31abSDavid du Colombier 
68368c31abSDavid du Colombier 	x = va_arg(fmt->args, vlong);
69368c31abSDavid du Colombier 	if(x == 0)
70368c31abSDavid du Colombier 		return fmtstrcpy(fmt, "0");
71368c31abSDavid du Colombier 	if(x%G == 0)
72368c31abSDavid du Colombier 		return fmtprint(fmt, "%lldG", x/G);
73368c31abSDavid du Colombier 	if(x%M == 0)
74368c31abSDavid du Colombier 		return fmtprint(fmt, "%lldM", x/M);
75368c31abSDavid du Colombier 	if(x%K == 0)
76368c31abSDavid du Colombier 		return fmtprint(fmt, "%lldK", x/K);
77368c31abSDavid du Colombier 	return fmtprint(fmt, "%lld", x);
78368c31abSDavid du Colombier }
79368c31abSDavid du Colombier 
80368c31abSDavid du Colombier /*
81368c31abSDavid du Colombier  * Format time like ctime without newline.
82368c31abSDavid du Colombier  */
83368c31abSDavid du Colombier static int
tfmt(Fmt * fmt)84368c31abSDavid du Colombier tfmt(Fmt *fmt)
85368c31abSDavid du Colombier {
86368c31abSDavid du Colombier 	uint t;
87368c31abSDavid du Colombier 	char buf[30];
88368c31abSDavid du Colombier 
89368c31abSDavid du Colombier 	t = va_arg(fmt->args, uint);
90368c31abSDavid du Colombier 	strcpy(buf, ctime(t));
91368c31abSDavid du Colombier 	buf[28] = 0;
92368c31abSDavid du Colombier 	return fmtstrcpy(fmt, buf);
93368c31abSDavid du Colombier }
94368c31abSDavid du Colombier 
95368c31abSDavid du Colombier /*
96368c31abSDavid du Colombier  * Coalesce messages about unreadable sectors into larger ranges.
97368c31abSDavid du Colombier  * bad(0, 0) flushes the buffer.
98368c31abSDavid du Colombier  */
99368c31abSDavid du Colombier static void
bad(char * msg,vlong o,int len)100368c31abSDavid du Colombier bad(char *msg, vlong o, int len)
101368c31abSDavid du Colombier {
102368c31abSDavid du Colombier 	static vlong lb0, lb1;
103368c31abSDavid du Colombier 	static char *lmsg;
104368c31abSDavid du Colombier 
105368c31abSDavid du Colombier 	if(msg == nil)
106368c31abSDavid du Colombier 		msg = lmsg;
107368c31abSDavid du Colombier 	if(o == -1){
108368c31abSDavid du Colombier 		lmsg = nil;
109368c31abSDavid du Colombier 		lb0 = 0;
110368c31abSDavid du Colombier 		lb1 = 0;
111368c31abSDavid du Colombier 		return;
112368c31abSDavid du Colombier 	}
113368c31abSDavid du Colombier 	if(lb1 != o || (msg && lmsg && strcmp(msg, lmsg) != 0)){
114368c31abSDavid du Colombier 		if(lb0 != lb1)
115368c31abSDavid du Colombier 			print("%s %#llux+%#llux (%,lld+%,lld)\n",
116368c31abSDavid du Colombier 				lmsg, lb0, lb1-lb0, lb0, lb1-lb0);
117368c31abSDavid du Colombier 		lb0 = o;
118368c31abSDavid du Colombier 	}
119368c31abSDavid du Colombier 	lmsg = msg;
120368c31abSDavid du Colombier 	lb1 = o+len;
121368c31abSDavid du Colombier }
122368c31abSDavid du Colombier 
123368c31abSDavid du Colombier /*
124368c31abSDavid du Colombier  * Read in the len bytes of data at the offset.  If can't for whatever reason,
125368c31abSDavid du Colombier  * fill it with garbage but print an error.
126368c31abSDavid du Colombier  */
127368c31abSDavid du Colombier static uchar*
readdisk(uchar * buf,vlong offset,int len)128368c31abSDavid du Colombier readdisk(uchar *buf, vlong offset, int len)
129368c31abSDavid du Colombier {
130368c31abSDavid du Colombier 	int i, j, k, n;
131368c31abSDavid du Colombier 
132368c31abSDavid du Colombier 	if(offset >= partend){
133*4fd2ae8bSDavid du Colombier 		memset(buf, 0xFB, len);
134368c31abSDavid du Colombier 		return buf;
135368c31abSDavid du Colombier 	}
136368c31abSDavid du Colombier 
137368c31abSDavid du Colombier 	if(offset+len > partend){
138*4fd2ae8bSDavid du Colombier 		memset(buf, 0xFB, len);
139368c31abSDavid du Colombier 		len = partend - offset;
140368c31abSDavid du Colombier 	}
141368c31abSDavid du Colombier 
142368c31abSDavid du Colombier 	if(readpart(part, offset, buf, len) >= 0)
143368c31abSDavid du Colombier 		return buf;
144368c31abSDavid du Colombier 
145368c31abSDavid du Colombier 	/*
146368c31abSDavid du Colombier 	 * The read failed.  Clear the buffer to nonsense, and
147368c31abSDavid du Colombier 	 * then try reading in smaller pieces.  If that fails,
148368c31abSDavid du Colombier 	 * read in even smaller pieces.  And so on down to sectors.
149368c31abSDavid du Colombier 	 */
150368c31abSDavid du Colombier 	memset(buf, 0xFD, len);
151368c31abSDavid du Colombier 	for(i=0; i<len; i+=64*K){
152368c31abSDavid du Colombier 		n = 64*K;
153368c31abSDavid du Colombier 		if(i+n > len)
154368c31abSDavid du Colombier 			n = len-i;
155368c31abSDavid du Colombier 		if(readpart(part, offset+i, buf+i, n) >= 0)
156368c31abSDavid du Colombier 			continue;
157368c31abSDavid du Colombier 		for(j=i; j<len && j<i+64*K; j+=4*K){
158368c31abSDavid du Colombier 			n = 4*K;
159368c31abSDavid du Colombier 			if(j+n > len)
160368c31abSDavid du Colombier 				n = len-j;
161368c31abSDavid du Colombier 			if(readpart(part, offset+j, buf+j, n) >= 0)
162368c31abSDavid du Colombier 				continue;
163368c31abSDavid du Colombier 			for(k=j; k<len && k<j+4*K; k+=512){
164368c31abSDavid du Colombier 				if(readpart(part, offset+k, buf+k, 512) >= 0)
165368c31abSDavid du Colombier 					continue;
166368c31abSDavid du Colombier 				bad("disk read failed at", k, 512);
167368c31abSDavid du Colombier 				badreads++;
168368c31abSDavid du Colombier 			}
169368c31abSDavid du Colombier 		}
170368c31abSDavid du Colombier 	}
171368c31abSDavid du Colombier 	bad(nil, 0, 0);
172368c31abSDavid du Colombier 	return buf;
173368c31abSDavid du Colombier }
174368c31abSDavid du Colombier 
175368c31abSDavid du Colombier /*
176368c31abSDavid du Colombier  * Buffer to support running SHA1 hash of the disk.
177368c31abSDavid du Colombier  */
178368c31abSDavid du Colombier typedef struct Shabuf Shabuf;
179368c31abSDavid du Colombier struct Shabuf
180368c31abSDavid du Colombier {
181368c31abSDavid du Colombier 	int fd;
182368c31abSDavid du Colombier 	vlong offset;
183368c31abSDavid du Colombier 	DigestState state;
184368c31abSDavid du Colombier 	int rollback;
185368c31abSDavid du Colombier 	vlong r0;
186368c31abSDavid du Colombier 	DigestState *hist;
187368c31abSDavid du Colombier 	int nhist;
188368c31abSDavid du Colombier };
189368c31abSDavid du Colombier 
190368c31abSDavid du Colombier void
sbdebug(Shabuf * sb,char * file)191368c31abSDavid du Colombier sbdebug(Shabuf *sb, char *file)
192368c31abSDavid du Colombier {
193368c31abSDavid du Colombier 	int fd;
194368c31abSDavid du Colombier 
195368c31abSDavid du Colombier 	if(sb->fd > 0){
196368c31abSDavid du Colombier 		close(sb->fd);
197368c31abSDavid du Colombier 		sb->fd = 0;
198368c31abSDavid du Colombier 	}
199368c31abSDavid du Colombier 	if((fd = create(file, OWRITE, 0666)) < 0)
200368c31abSDavid du Colombier 		return;
201368c31abSDavid du Colombier 	if(fd == 0){
202368c31abSDavid du Colombier 		fd = dup(fd, -1);
203368c31abSDavid du Colombier 		close(0);
204368c31abSDavid du Colombier 	}
205368c31abSDavid du Colombier 	sb->fd = fd;
206368c31abSDavid du Colombier }
207368c31abSDavid du Colombier 
208368c31abSDavid du Colombier void
sbupdate(Shabuf * sb,uchar * p,vlong offset,int len)209368c31abSDavid du Colombier sbupdate(Shabuf *sb, uchar *p, vlong offset, int len)
210368c31abSDavid du Colombier {
211368c31abSDavid du Colombier 	int n, x;
212368c31abSDavid du Colombier 	vlong o;
213368c31abSDavid du Colombier 
214368c31abSDavid du Colombier 	if(sb->rollback && !sb->hist){
215368c31abSDavid du Colombier 		sb->r0 = offset;
216368c31abSDavid du Colombier 		sb->nhist = 1;
217368c31abSDavid du Colombier 		sb->hist = vtmalloc(sb->nhist*sizeof *sb->hist);
218368c31abSDavid du Colombier 		memset(sb->hist, 0, sizeof sb->hist[0]);
219368c31abSDavid du Colombier 	}
220368c31abSDavid du Colombier 	if(sb->r0 == 0)
221368c31abSDavid du Colombier 		sb->r0 = offset;
222368c31abSDavid du Colombier 
223368c31abSDavid du Colombier 	if(sb->offset < offset || sb->offset >= offset+len){
224368c31abSDavid du Colombier 		if(0) print("sbupdate %p %#llux+%d but offset=%#llux\n",
225368c31abSDavid du Colombier 			p, offset, len, sb->offset);
226368c31abSDavid du Colombier 		return;
227368c31abSDavid du Colombier 	}
228368c31abSDavid du Colombier 	x = sb->offset - offset;
229368c31abSDavid du Colombier 	if(0) print("sbupdate %p %#llux+%d skip %d\n",
230368c31abSDavid du Colombier 		sb, offset, len, x);
231368c31abSDavid du Colombier 	if(x){
232368c31abSDavid du Colombier 		p += x;
233368c31abSDavid du Colombier 		offset += x;
234368c31abSDavid du Colombier 		len -= x;
235368c31abSDavid du Colombier 	}
236368c31abSDavid du Colombier 	assert(sb->offset == offset);
237368c31abSDavid du Colombier 
238368c31abSDavid du Colombier 	if(sb->fd > 0)
239368c31abSDavid du Colombier 		pwrite(sb->fd, p, len, offset - sb->r0);
240368c31abSDavid du Colombier 
241368c31abSDavid du Colombier 	if(!sb->rollback){
242368c31abSDavid du Colombier 		sha1(p, len, nil, &sb->state);
243368c31abSDavid du Colombier 		sb->offset += len;
244368c31abSDavid du Colombier 		return;
245368c31abSDavid du Colombier 	}
246368c31abSDavid du Colombier 
247368c31abSDavid du Colombier 	/* save state every 4M so we can roll back quickly */
248368c31abSDavid du Colombier 	o = offset - sb->r0;
249368c31abSDavid du Colombier 	while(len > 0){
250368c31abSDavid du Colombier 		n = 4*M - o%(4*M);
251368c31abSDavid du Colombier 		if(n > len)
252368c31abSDavid du Colombier 			n = len;
253368c31abSDavid du Colombier 		sha1(p, n, nil, &sb->state);
254368c31abSDavid du Colombier 		sb->offset += n;
255368c31abSDavid du Colombier 		o += n;
256368c31abSDavid du Colombier 		p += n;
257368c31abSDavid du Colombier 		len -= n;
258368c31abSDavid du Colombier 		if(o%(4*M) == 0){
259368c31abSDavid du Colombier 			x = o/(4*M);
260368c31abSDavid du Colombier 			if(x >= sb->nhist){
261368c31abSDavid du Colombier 				if(x != sb->nhist)
262368c31abSDavid du Colombier 					print("oops! x=%d nhist=%d\n", x, sb->nhist);
263368c31abSDavid du Colombier 				sb->nhist += 32;
264368c31abSDavid du Colombier 				sb->hist = vtrealloc(sb->hist, sb->nhist*sizeof *sb->hist);
265368c31abSDavid du Colombier 			}
266368c31abSDavid du Colombier 			sb->hist[x] = sb->state;
267368c31abSDavid du Colombier 		}
268368c31abSDavid du Colombier 	}
269368c31abSDavid du Colombier }
270368c31abSDavid du Colombier 
271368c31abSDavid du Colombier void
sbdiskhash(Shabuf * sb,vlong eoffset)272368c31abSDavid du Colombier sbdiskhash(Shabuf *sb, vlong eoffset)
273368c31abSDavid du Colombier {
274368c31abSDavid du Colombier 	static uchar dbuf[4*M];
275368c31abSDavid du Colombier 	int n;
276368c31abSDavid du Colombier 
277368c31abSDavid du Colombier 	while(sb->offset < eoffset){
278368c31abSDavid du Colombier 		n = sizeof dbuf;
279368c31abSDavid du Colombier 		if(sb->offset+n > eoffset)
280368c31abSDavid du Colombier 			n = eoffset - sb->offset;
281368c31abSDavid du Colombier 		readdisk(dbuf, sb->offset, n);
282368c31abSDavid du Colombier 		sbupdate(sb, dbuf, sb->offset, n);
283368c31abSDavid du Colombier 	}
284368c31abSDavid du Colombier }
285368c31abSDavid du Colombier 
286368c31abSDavid du Colombier void
sbrollback(Shabuf * sb,vlong offset)287368c31abSDavid du Colombier sbrollback(Shabuf *sb, vlong offset)
288368c31abSDavid du Colombier {
289368c31abSDavid du Colombier 	int x;
290368c31abSDavid du Colombier 	vlong o;
291368c31abSDavid du Colombier 	Dir d;
292368c31abSDavid du Colombier 
293368c31abSDavid du Colombier 	if(!sb->rollback || !sb->r0){
294368c31abSDavid du Colombier 		print("cannot rollback sha\n");
295368c31abSDavid du Colombier 		return;
296368c31abSDavid du Colombier 	}
297368c31abSDavid du Colombier 	if(offset >= sb->offset)
298368c31abSDavid du Colombier 		return;
299368c31abSDavid du Colombier 	o = offset - sb->r0;
300368c31abSDavid du Colombier 	x = o/(4*M);
301368c31abSDavid du Colombier 	if(x >= sb->nhist){
302368c31abSDavid du Colombier 		print("cannot rollback sha\n");
303368c31abSDavid du Colombier 		return;
304368c31abSDavid du Colombier 	}
305368c31abSDavid du Colombier 	sb->state = sb->hist[x];
306368c31abSDavid du Colombier 	sb->offset = sb->r0 + x*4*M;
307368c31abSDavid du Colombier 	assert(sb->offset <= offset);
308368c31abSDavid du Colombier 
309368c31abSDavid du Colombier 	if(sb->fd > 0){
310368c31abSDavid du Colombier 		nulldir(&d);
311368c31abSDavid du Colombier 		d.length = sb->offset - sb->r0;
312368c31abSDavid du Colombier 		dirfwstat(sb->fd, &d);
313368c31abSDavid du Colombier 	}
314368c31abSDavid du Colombier }
315368c31abSDavid du Colombier 
316368c31abSDavid du Colombier void
sbscore(Shabuf * sb,uchar * score)317368c31abSDavid du Colombier sbscore(Shabuf *sb, uchar *score)
318368c31abSDavid du Colombier {
319368c31abSDavid du Colombier 	if(sb->hist){
320368c31abSDavid du Colombier 		free(sb->hist);
321368c31abSDavid du Colombier 		sb->hist = nil;
322368c31abSDavid du Colombier 	}
323368c31abSDavid du Colombier 	sha1(nil, 0, score, &sb->state);
324368c31abSDavid du Colombier }
325368c31abSDavid du Colombier 
326368c31abSDavid du Colombier /*
327368c31abSDavid du Colombier  * If we're fixing arenas, then editing this memory edits the disk!
328368c31abSDavid du Colombier  * It will be written back out as new data is paged in.
329368c31abSDavid du Colombier  */
330368c31abSDavid du Colombier uchar buf[4*M];
331368c31abSDavid du Colombier uchar sbuf[4*M];
332368c31abSDavid du Colombier vlong bufoffset;
333368c31abSDavid du Colombier int buflen;
334368c31abSDavid du Colombier 
335368c31abSDavid du Colombier static void pageout(void);
336368c31abSDavid du Colombier static uchar*
pagein(vlong offset,int len)337368c31abSDavid du Colombier pagein(vlong offset, int len)
338368c31abSDavid du Colombier {
339368c31abSDavid du Colombier 	pageout();
340368c31abSDavid du Colombier 	if(offset >= partend){
341368c31abSDavid du Colombier 		memset(buf, 0xFB, sizeof buf);
342368c31abSDavid du Colombier 		return buf;
343368c31abSDavid du Colombier 	}
344368c31abSDavid du Colombier 
345368c31abSDavid du Colombier 	if(offset+len > partend){
346368c31abSDavid du Colombier 		memset(buf, 0xFB, sizeof buf);
347368c31abSDavid du Colombier 		len = partend - offset;
348368c31abSDavid du Colombier 	}
349368c31abSDavid du Colombier 	bufoffset = offset;
350368c31abSDavid du Colombier 	buflen = len;
351368c31abSDavid du Colombier 	readdisk(buf, offset, len);
352368c31abSDavid du Colombier 	memmove(sbuf, buf, len);
353368c31abSDavid du Colombier 	return buf;
354368c31abSDavid du Colombier }
355368c31abSDavid du Colombier 
356368c31abSDavid du Colombier static void
pageout(void)357368c31abSDavid du Colombier pageout(void)
358368c31abSDavid du Colombier {
359368c31abSDavid du Colombier 	if(buflen==0 || !fix || memcmp(buf, sbuf, buflen) == 0){
360368c31abSDavid du Colombier 		buflen = 0;
361368c31abSDavid du Colombier 		return;
362368c31abSDavid du Colombier 	}
363368c31abSDavid du Colombier 	if(writepart(part, bufoffset, buf, buflen) < 0)
364368c31abSDavid du Colombier 		print("disk write failed at %#llux+%#ux (%,lld+%,d)\n",
365368c31abSDavid du Colombier 			bufoffset, buflen, bufoffset, buflen);
366368c31abSDavid du Colombier 	buflen = 0;
367368c31abSDavid du Colombier }
368368c31abSDavid du Colombier 
369368c31abSDavid du Colombier static void
zerorange(vlong offset,int len)370368c31abSDavid du Colombier zerorange(vlong offset, int len)
371368c31abSDavid du Colombier {
372368c31abSDavid du Colombier 	int i;
373368c31abSDavid du Colombier 	vlong ooff;
374368c31abSDavid du Colombier 	int olen;
375368c31abSDavid du Colombier 	enum { MinBlock = 4*K, MaxBlock = 8*K };
376368c31abSDavid du Colombier 
377368c31abSDavid du Colombier 	if(0)
378368c31abSDavid du Colombier 	if(bufoffset <= offset && offset+len <= bufoffset+buflen){
379368c31abSDavid du Colombier 		memset(buf+(offset-bufoffset), 0, len);
380368c31abSDavid du Colombier 		return;
381368c31abSDavid du Colombier 	}
382368c31abSDavid du Colombier 
383368c31abSDavid du Colombier 	ooff = bufoffset;
384368c31abSDavid du Colombier 	olen = buflen;
385368c31abSDavid du Colombier 
386368c31abSDavid du Colombier 	i = offset%MinBlock;
387368c31abSDavid du Colombier 	if(i+len < MaxBlock){
388368c31abSDavid du Colombier 		pagein(offset-i, (len+MinBlock-1)&~(MinBlock-1));
389368c31abSDavid du Colombier 		memset(buf+i, 0, len);
390368c31abSDavid du Colombier 	}else{
391368c31abSDavid du Colombier 		pagein(offset-i, MaxBlock);
392368c31abSDavid du Colombier 		memset(buf+i, 0, MaxBlock-i);
393368c31abSDavid du Colombier 		offset += MaxBlock-i;
394368c31abSDavid du Colombier 		len -= MaxBlock-i;
395368c31abSDavid du Colombier 		while(len >= MaxBlock){
396368c31abSDavid du Colombier 			pagein(offset, MaxBlock);
397368c31abSDavid du Colombier 			memset(buf, 0, MaxBlock);
398368c31abSDavid du Colombier 			offset += MaxBlock;
399368c31abSDavid du Colombier 			len -= MaxBlock;
400368c31abSDavid du Colombier 		}
401368c31abSDavid du Colombier 		pagein(offset, (len+MinBlock-1)&~(MinBlock-1));
402368c31abSDavid du Colombier 		memset(buf, 0, len);
403368c31abSDavid du Colombier 	}
404368c31abSDavid du Colombier 	pagein(ooff, olen);
405368c31abSDavid du Colombier }
406368c31abSDavid du Colombier 
407368c31abSDavid du Colombier /*
408368c31abSDavid du Colombier  * read/write integers
409368c31abSDavid du Colombier  *
410368c31abSDavid du Colombier static void
411368c31abSDavid du Colombier p16(uchar *p, u16int u)
412368c31abSDavid du Colombier {
413368c31abSDavid du Colombier 	p[0] = (u>>8) & 0xFF;
414368c31abSDavid du Colombier 	p[1] = u & 0xFF;
415368c31abSDavid du Colombier }
416368c31abSDavid du Colombier */
417368c31abSDavid du Colombier 
418368c31abSDavid du Colombier static u16int
u16(uchar * p)419368c31abSDavid du Colombier u16(uchar *p)
420368c31abSDavid du Colombier {
421368c31abSDavid du Colombier 	return (p[0]<<8)|p[1];
422368c31abSDavid du Colombier }
423368c31abSDavid du Colombier 
424368c31abSDavid du Colombier static void
p32(uchar * p,u32int u)425368c31abSDavid du Colombier p32(uchar *p, u32int u)
426368c31abSDavid du Colombier {
427368c31abSDavid du Colombier 	p[0] = (u>>24) & 0xFF;
428368c31abSDavid du Colombier 	p[1] = (u>>16) & 0xFF;
429368c31abSDavid du Colombier 	p[2] = (u>>8) & 0xFF;
430368c31abSDavid du Colombier 	p[3] = u & 0xFF;
431368c31abSDavid du Colombier }
432368c31abSDavid du Colombier 
433368c31abSDavid du Colombier static u32int
u32(uchar * p)434368c31abSDavid du Colombier u32(uchar *p)
435368c31abSDavid du Colombier {
436368c31abSDavid du Colombier 	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
437368c31abSDavid du Colombier }
438368c31abSDavid du Colombier 
439368c31abSDavid du Colombier /*
440368c31abSDavid du Colombier static void
441368c31abSDavid du Colombier p64(uchar *p, u64int u)
442368c31abSDavid du Colombier {
443368c31abSDavid du Colombier 	p32(p, u>>32);
444368c31abSDavid du Colombier 	p32(p, u);
445368c31abSDavid du Colombier }
446368c31abSDavid du Colombier */
447368c31abSDavid du Colombier 
448368c31abSDavid du Colombier static u64int
u64(uchar * p)449368c31abSDavid du Colombier u64(uchar *p)
450368c31abSDavid du Colombier {
451368c31abSDavid du Colombier 	return ((u64int)u32(p)<<32) | u32(p+4);
452368c31abSDavid du Colombier }
453368c31abSDavid du Colombier 
454368c31abSDavid du Colombier static int
vlongcmp(const void * va,const void * vb)455368c31abSDavid du Colombier vlongcmp(const void *va, const void *vb)
456368c31abSDavid du Colombier {
457368c31abSDavid du Colombier 	vlong a, b;
458368c31abSDavid du Colombier 
459368c31abSDavid du Colombier 	a = *(vlong*)va;
460368c31abSDavid du Colombier 	b = *(vlong*)vb;
461368c31abSDavid du Colombier 	if(a < b)
462368c31abSDavid du Colombier 		return -1;
463368c31abSDavid du Colombier 	if(b > a)
464368c31abSDavid du Colombier 		return 1;
465368c31abSDavid du Colombier 	return 0;
466368c31abSDavid du Colombier }
467368c31abSDavid du Colombier 
468368c31abSDavid du Colombier /* D and S are in draw.h */
469368c31abSDavid du Colombier #define D VD
470368c31abSDavid du Colombier #define S VS
471368c31abSDavid du Colombier 
472368c31abSDavid du Colombier enum
473368c31abSDavid du Colombier {
474368c31abSDavid du Colombier 	D = 0x10000,
475368c31abSDavid du Colombier 	Z = 0x20000,
476368c31abSDavid du Colombier 	S = 0x30000,
477368c31abSDavid du Colombier 	T = 0x40000,
478368c31abSDavid du Colombier 	N = 0xFFFF
479368c31abSDavid du Colombier };
480368c31abSDavid du Colombier typedef struct Info Info;
481368c31abSDavid du Colombier struct Info
482368c31abSDavid du Colombier {
483368c31abSDavid du Colombier 	int len;
484368c31abSDavid du Colombier 	char *name;
485368c31abSDavid du Colombier };
486368c31abSDavid du Colombier 
487368c31abSDavid du Colombier Info partinfo[] = {
488368c31abSDavid du Colombier 	4,	"magic",
489368c31abSDavid du Colombier 	D|4,	"version",
490368c31abSDavid du Colombier 	Z|4,	"blocksize",
491368c31abSDavid du Colombier 	4,	"arenabase",
492368c31abSDavid du Colombier 	0
493368c31abSDavid du Colombier };
494368c31abSDavid du Colombier 
495368c31abSDavid du Colombier Info headinfo4[] = {
496368c31abSDavid du Colombier 	4,	"magic",
497368c31abSDavid du Colombier 	D|4,	"version",
498368c31abSDavid du Colombier 	S|ANameSize,	"name",
499368c31abSDavid du Colombier 	Z|4,	"blocksize",
500368c31abSDavid du Colombier 	Z|8,	"size",
501368c31abSDavid du Colombier 	0
502368c31abSDavid du Colombier };
503368c31abSDavid du Colombier 
504368c31abSDavid du Colombier Info headinfo5[] = {
505368c31abSDavid du Colombier 	4,	"magic",
506368c31abSDavid du Colombier 	D|4,	"version",
507368c31abSDavid du Colombier 	S|ANameSize,	"name",
508368c31abSDavid du Colombier 	Z|4,	"blocksize",
509368c31abSDavid du Colombier 	Z|8,	"size",
510368c31abSDavid du Colombier 	4,	"clumpmagic",
511368c31abSDavid du Colombier 	0
512368c31abSDavid du Colombier };
513368c31abSDavid du Colombier 
514368c31abSDavid du Colombier Info tailinfo4[] = {
515368c31abSDavid du Colombier 	4,	"magic",
516368c31abSDavid du Colombier 	D|4,	"version",
517368c31abSDavid du Colombier 	S|ANameSize,	"name",
518368c31abSDavid du Colombier 	D|4,	"clumps",
519368c31abSDavid du Colombier 	D|4,	"cclumps",
520368c31abSDavid du Colombier 	T|4,	"ctime",
521368c31abSDavid du Colombier 	T|4,	"wtime",
522368c31abSDavid du Colombier 	D|8,	"used",
523368c31abSDavid du Colombier 	D|8,	"uncsize",
524368c31abSDavid du Colombier 	1,	"sealed",
525368c31abSDavid du Colombier 	0
526368c31abSDavid du Colombier };
527368c31abSDavid du Colombier 
528368c31abSDavid du Colombier Info tailinfo4a[] = {
529368c31abSDavid du Colombier 	/* tailinfo 4 */
530368c31abSDavid du Colombier 	4,	"magic",
531368c31abSDavid du Colombier 	D|4,	"version",
532368c31abSDavid du Colombier 	S|ANameSize,	"name",
533368c31abSDavid du Colombier 	D|4,	"clumps",
534368c31abSDavid du Colombier 	D|4,	"cclumps",
535368c31abSDavid du Colombier 	T|4,	"ctime",
536368c31abSDavid du Colombier 	T|4,	"wtime",
537368c31abSDavid du Colombier 	D|8,	"used",
538368c31abSDavid du Colombier 	D|8,	"uncsize",
539368c31abSDavid du Colombier 	1,	"sealed",
540368c31abSDavid du Colombier 
541368c31abSDavid du Colombier 	/* mem stats */
542368c31abSDavid du Colombier 	1,	"extension",
543368c31abSDavid du Colombier 	D|4,	"mem.clumps",
544368c31abSDavid du Colombier 	D|4,	"mem.cclumps",
545368c31abSDavid du Colombier 	D|8,	"mem.used",
546368c31abSDavid du Colombier 	D|8,	"mem.uncsize",
547368c31abSDavid du Colombier 	1,	"mem.sealed",
548368c31abSDavid du Colombier 	0
549368c31abSDavid du Colombier };
550368c31abSDavid du Colombier 
551368c31abSDavid du Colombier Info tailinfo5[] = {
552368c31abSDavid du Colombier 	4,	"magic",
553368c31abSDavid du Colombier 	D|4,	"version",
554368c31abSDavid du Colombier 	S|ANameSize,	"name",
555368c31abSDavid du Colombier 	D|4,	"clumps",
556368c31abSDavid du Colombier 	D|4,	"cclumps",
557368c31abSDavid du Colombier 	T|4,	"ctime",
558368c31abSDavid du Colombier 	T|4,	"wtime",
559368c31abSDavid du Colombier 	4,	"clumpmagic",
560368c31abSDavid du Colombier 	D|8,	"used",
561368c31abSDavid du Colombier 	D|8,	"uncsize",
562368c31abSDavid du Colombier 	1,	"sealed",
563368c31abSDavid du Colombier 	0
564368c31abSDavid du Colombier };
565368c31abSDavid du Colombier 
566368c31abSDavid du Colombier Info tailinfo5a[] = {
567368c31abSDavid du Colombier 	/* tailinfo 5 */
568368c31abSDavid du Colombier 	4,	"magic",
569368c31abSDavid du Colombier 	D|4,	"version",
570368c31abSDavid du Colombier 	S|ANameSize,	"name",
571368c31abSDavid du Colombier 	D|4,	"clumps",
572368c31abSDavid du Colombier 	D|4,	"cclumps",
573368c31abSDavid du Colombier 	T|4,	"ctime",
574368c31abSDavid du Colombier 	T|4,	"wtime",
575368c31abSDavid du Colombier 	4,	"clumpmagic",
576368c31abSDavid du Colombier 	D|8,	"used",
577368c31abSDavid du Colombier 	D|8,	"uncsize",
578368c31abSDavid du Colombier 	1,	"sealed",
579368c31abSDavid du Colombier 
580368c31abSDavid du Colombier 	/* mem stats */
581368c31abSDavid du Colombier 	1,	"extension",
582368c31abSDavid du Colombier 	D|4,	"mem.clumps",
583368c31abSDavid du Colombier 	D|4,	"mem.cclumps",
584368c31abSDavid du Colombier 	D|8,	"mem.used",
585368c31abSDavid du Colombier 	D|8,	"mem.uncsize",
586368c31abSDavid du Colombier 	1,	"mem.sealed",
587368c31abSDavid du Colombier 	0
588368c31abSDavid du Colombier };
589368c31abSDavid du Colombier 
590368c31abSDavid du Colombier void
showdiffs(uchar * want,uchar * have,int len,Info * info)591368c31abSDavid du Colombier showdiffs(uchar *want, uchar *have, int len, Info *info)
592368c31abSDavid du Colombier {
593368c31abSDavid du Colombier 	int n;
594368c31abSDavid du Colombier 
595368c31abSDavid du Colombier 	while(len > 0 && (n=info->len&N) > 0){
596368c31abSDavid du Colombier 		if(memcmp(have, want, n) != 0){
597368c31abSDavid du Colombier 			switch(info->len){
598368c31abSDavid du Colombier 			case 1:
599368c31abSDavid du Colombier 				print("\t%s: correct=%d disk=%d\n",
600368c31abSDavid du Colombier 					info->name, *want, *have);
601368c31abSDavid du Colombier 				break;
602368c31abSDavid du Colombier 			case 4:
603368c31abSDavid du Colombier 				print("\t%s: correct=%#ux disk=%#ux\n",
604368c31abSDavid du Colombier 					info->name, u32(want), u32(have));
605368c31abSDavid du Colombier 				break;
606368c31abSDavid du Colombier 			case D|4:
607368c31abSDavid du Colombier 				print("\t%s: correct=%,ud disk=%,ud\n",
608368c31abSDavid du Colombier 					info->name, u32(want), u32(have));
609368c31abSDavid du Colombier 				break;
610368c31abSDavid du Colombier 			case T|4:
611368c31abSDavid du Colombier 				print("\t%s: correct=%t\n\t\tdisk=%t\n",
612368c31abSDavid du Colombier 					info->name, u32(want), u32(have));
613368c31abSDavid du Colombier 				break;
614368c31abSDavid du Colombier 			case Z|4:
615368c31abSDavid du Colombier 				print("\t%s: correct=%z disk=%z\n",
616368c31abSDavid du Colombier 					info->name, (uvlong)u32(want), (uvlong)u32(have));
617368c31abSDavid du Colombier 				break;
618368c31abSDavid du Colombier 			case D|8:
619368c31abSDavid du Colombier 				print("\t%s: correct=%,lld disk=%,lld\n",
620368c31abSDavid du Colombier 					info->name, u64(want), u64(have));
621368c31abSDavid du Colombier 				break;
622368c31abSDavid du Colombier 			case Z|8:
623368c31abSDavid du Colombier 				print("\t%s: correct=%z disk=%z\n",
624368c31abSDavid du Colombier 					info->name, u64(want), u64(have));
625368c31abSDavid du Colombier 				break;
626368c31abSDavid du Colombier 			case S|ANameSize:
627368c31abSDavid du Colombier 				print("\t%s: correct=%s disk=%.*s\n",
628368c31abSDavid du Colombier 					info->name, (char*)want,
629368c31abSDavid du Colombier 					utfnlen((char*)have, ANameSize-1),
630368c31abSDavid du Colombier 					(char*)have);
631368c31abSDavid du Colombier 				break;
632368c31abSDavid du Colombier 			default:
633368c31abSDavid du Colombier 				print("\t%s: correct=%.*H disk=%.*H\n",
634368c31abSDavid du Colombier 					info->name, n, want, n, have);
635368c31abSDavid du Colombier 				break;
636368c31abSDavid du Colombier 			}
637368c31abSDavid du Colombier 		}
638368c31abSDavid du Colombier 		have += n;
639368c31abSDavid du Colombier 		want += n;
640368c31abSDavid du Colombier 		len -= n;
641368c31abSDavid du Colombier 		info++;
642368c31abSDavid du Colombier 	}
643368c31abSDavid du Colombier 	if(len > 0 && memcmp(have, want, len) != 0){
644368c31abSDavid du Colombier 		if(memcmp(want, zero, len) != 0)
645368c31abSDavid du Colombier 			print("!!\textra want data in showdiffs (bug in fixarenas)\n");
646368c31abSDavid du Colombier 		else
647368c31abSDavid du Colombier 			print("\tnon-zero data on disk after structure\n");
648368c31abSDavid du Colombier 		if(verbose > 1){
649368c31abSDavid du Colombier 			print("want: %.*H\n", len, want);
650368c31abSDavid du Colombier 			print("have: %.*H\n", len, have);
651368c31abSDavid du Colombier 		}
652368c31abSDavid du Colombier 	}
653368c31abSDavid du Colombier }
654368c31abSDavid du Colombier 
655368c31abSDavid du Colombier /*
656368c31abSDavid du Colombier  * Does part begin with an arena?
657368c31abSDavid du Colombier  */
658368c31abSDavid du Colombier int
isonearena(void)659368c31abSDavid du Colombier isonearena(void)
660368c31abSDavid du Colombier {
661368c31abSDavid du Colombier 	return u32(pagein(0, Block)) == ArenaHeadMagic;
662368c31abSDavid du Colombier }
663368c31abSDavid du Colombier 
66423566e0cSDavid du Colombier static int tabsizes[] = { 16*1024, 64*1024, 512*1024, 768*1024, };
665368c31abSDavid du Colombier /*
666368c31abSDavid du Colombier  * Poke around on the disk to guess what the ArenaPart numbers are.
667368c31abSDavid du Colombier  */
668368c31abSDavid du Colombier void
guessgeometry(void)669368c31abSDavid du Colombier guessgeometry(void)
670368c31abSDavid du Colombier {
671368c31abSDavid du Colombier 	int i, j, n, bestn, ndiff, nhead, ntail;
672368c31abSDavid du Colombier 	uchar *p, *ep, *sp;
673368c31abSDavid du Colombier 	u64int diff[100], head[20], tail[20];
674368c31abSDavid du Colombier 	u64int offset, bestdiff;
675368c31abSDavid du Colombier 
676368c31abSDavid du Colombier 	ap.version = ArenaPartVersion;
677368c31abSDavid du Colombier 
678368c31abSDavid du Colombier 	if(arenasize == 0 || ap.blocksize == 0){
679368c31abSDavid du Colombier 		/*
680368c31abSDavid du Colombier 		 * The ArenaPart block at offset PartBlank may be corrupt or just wrong.
681368c31abSDavid du Colombier 		 * Instead, look for the individual arena headers and tails, which there
682368c31abSDavid du Colombier 		 * are many of, and once we've seen enough, infer the spacing.
683368c31abSDavid du Colombier 		 *
684368c31abSDavid du Colombier 		 * Of course, nothing in the file format requires that arenas be evenly
685368c31abSDavid du Colombier 		 * spaced, but fmtarenas always does that for us.
686368c31abSDavid du Colombier 		 */
687368c31abSDavid du Colombier 		nhead = 0;
688368c31abSDavid du Colombier 		ntail = 0;
689368c31abSDavid du Colombier 		for(offset=PartBlank; offset<partend; offset+=4*M){
690368c31abSDavid du Colombier 			p = pagein(offset, 4*M);
691368c31abSDavid du Colombier 			for(sp=p, ep=p+4*M; p<ep; p+=K){
692368c31abSDavid du Colombier 				if(u32(p) == ArenaHeadMagic && nhead < nelem(head)){
693368c31abSDavid du Colombier 					if(verbose)
694368c31abSDavid du Colombier 						print("arena head at %#llx\n", offset+(p-sp));
695368c31abSDavid du Colombier 					head[nhead++] = offset+(p-sp);
696368c31abSDavid du Colombier 				}
697368c31abSDavid du Colombier 				if(u32(p) == ArenaMagic && ntail < nelem(tail)){
698368c31abSDavid du Colombier 					tail[ntail++] = offset+(p-sp);
699368c31abSDavid du Colombier 					if(verbose)
700368c31abSDavid du Colombier 						print("arena tail at %#llx\n", offset+(p-sp));
701368c31abSDavid du Colombier 				}
702368c31abSDavid du Colombier 			}
703368c31abSDavid du Colombier 			if(nhead == nelem(head) && ntail == nelem(tail))
704368c31abSDavid du Colombier 				break;
705368c31abSDavid du Colombier 		}
706368c31abSDavid du Colombier 		if(nhead < 3 && ntail < 3)
707368c31abSDavid du Colombier 			sysfatal("too few intact arenas: %d heads, %d tails", nhead, ntail);
708368c31abSDavid du Colombier 
709368c31abSDavid du Colombier 		/*
710368c31abSDavid du Colombier 		 * Arena size is likely the most common
711368c31abSDavid du Colombier 		 * inter-head or inter-tail spacing.
712368c31abSDavid du Colombier 		 */
713368c31abSDavid du Colombier 		ndiff = 0;
714368c31abSDavid du Colombier 		for(i=1; i<nhead; i++)
715368c31abSDavid du Colombier 			diff[ndiff++] = head[i] - head[i-1];
716368c31abSDavid du Colombier 		for(i=1; i<ntail; i++)
717368c31abSDavid du Colombier 			diff[ndiff++] = tail[i] - tail[i-1];
718368c31abSDavid du Colombier 		qsort(diff, ndiff, sizeof diff[0], vlongcmp);
719368c31abSDavid du Colombier 		bestn = 0;
720368c31abSDavid du Colombier 		bestdiff = 0;
721368c31abSDavid du Colombier 		for(i=1, n=1; i<=ndiff; i++, n++){
722368c31abSDavid du Colombier 			if(i==ndiff || diff[i] != diff[i-1]){
723368c31abSDavid du Colombier 				if(n > bestn){
724368c31abSDavid du Colombier 					bestn = n;
725368c31abSDavid du Colombier 					bestdiff = diff[i-1];
726368c31abSDavid du Colombier 				}
727368c31abSDavid du Colombier 				n = 0;
728368c31abSDavid du Colombier 			}
729368c31abSDavid du Colombier 		}
730368c31abSDavid du Colombier 		print("arena size likely %z (%d of %d)\n", bestdiff, bestn, ndiff);
731368c31abSDavid du Colombier 		if(arenasize != 0 && arenasize != bestdiff)
732368c31abSDavid du Colombier 			print("using user-specified size %z instead\n", arenasize);
733368c31abSDavid du Colombier 		else
734368c31abSDavid du Colombier 			arenasize = bestdiff;
735368c31abSDavid du Colombier 
736368c31abSDavid du Colombier 		/*
737368c31abSDavid du Colombier 		 * The arena tail for an arena is arenasize-blocksize from the head.
738368c31abSDavid du Colombier 		 */
739368c31abSDavid du Colombier 		ndiff = 0;
740368c31abSDavid du Colombier 		for(i=j=0; i<nhead && j<ntail; ){
741368c31abSDavid du Colombier 			if(tail[j] < head[i]){
742368c31abSDavid du Colombier 				j++;
743368c31abSDavid du Colombier 				continue;
744368c31abSDavid du Colombier 			}
745368c31abSDavid du Colombier 			if(tail[j] < head[i]+arenasize){
746368c31abSDavid du Colombier 				diff[ndiff++] = head[i]+arenasize - tail[j];
747368c31abSDavid du Colombier 				j++;
748368c31abSDavid du Colombier 				continue;
749368c31abSDavid du Colombier 			}
750368c31abSDavid du Colombier 			i++;
751368c31abSDavid du Colombier 		}
752368c31abSDavid du Colombier 		if(ndiff < 3)
753368c31abSDavid du Colombier 			sysfatal("too few intact arenas: %d head, tail pairs", ndiff);
754368c31abSDavid du Colombier 		qsort(diff, ndiff, sizeof diff[0], vlongcmp);
755368c31abSDavid du Colombier 		bestn = 0;
756368c31abSDavid du Colombier 		bestdiff = 0;
757368c31abSDavid du Colombier 		for(i=1, n=1; i<=ndiff; i++, n++){
758368c31abSDavid du Colombier 			if(i==ndiff || diff[i] != diff[i-1]){
759368c31abSDavid du Colombier 				if(n > bestn){
760368c31abSDavid du Colombier 					bestn = n;
761368c31abSDavid du Colombier 					bestdiff = diff[i-1];
762368c31abSDavid du Colombier 				}
763368c31abSDavid du Colombier 				n = 0;
764368c31abSDavid du Colombier 			}
765368c31abSDavid du Colombier 		}
766368c31abSDavid du Colombier 		print("block size likely %z (%d of %d)\n", bestdiff, bestn, ndiff);
767368c31abSDavid du Colombier 		if(ap.blocksize != 0 && ap.blocksize != bestdiff)
768368c31abSDavid du Colombier 			print("using user-specified size %z instead\n", (vlong)ap.blocksize);
769368c31abSDavid du Colombier 		else
770368c31abSDavid du Colombier 			ap.blocksize = bestdiff;
771368c31abSDavid du Colombier 		if(ap.blocksize == 0 || ap.blocksize&(ap.blocksize-1))
772368c31abSDavid du Colombier 			sysfatal("block size not a power of two");
773368c31abSDavid du Colombier 		if(ap.blocksize > MaxDiskBlock)
774368c31abSDavid du Colombier 			sysfatal("block size too big (max=%d)", MaxDiskBlock);
775368c31abSDavid du Colombier 
776368c31abSDavid du Colombier 		/*
777368c31abSDavid du Colombier 		 * Use head/tail information to deduce arena base.
778368c31abSDavid du Colombier 		 */
779368c31abSDavid du Colombier 		ndiff = 0;
780368c31abSDavid du Colombier 		for(i=0; i<nhead; i++)
781368c31abSDavid du Colombier 			diff[ndiff++] = head[i]%arenasize;
782368c31abSDavid du Colombier 		for(i=0; i<ntail; i++)
783368c31abSDavid du Colombier 			diff[ndiff++] = (tail[i]+ap.blocksize)%arenasize;
784368c31abSDavid du Colombier 		qsort(diff, ndiff, sizeof diff[0], vlongcmp);
785368c31abSDavid du Colombier 		bestn = 0;
786368c31abSDavid du Colombier 		bestdiff = 0;
787368c31abSDavid du Colombier 		for(i=1, n=1; i<=ndiff; i++, n++){
788368c31abSDavid du Colombier 			if(i==ndiff || diff[i] != diff[i-1]){
789368c31abSDavid du Colombier 				if(n > bestn){
790368c31abSDavid du Colombier 					bestn = n;
791368c31abSDavid du Colombier 					bestdiff = diff[i-1];
792368c31abSDavid du Colombier 				}
793368c31abSDavid du Colombier 				n = 0;
794368c31abSDavid du Colombier 			}
795368c31abSDavid du Colombier 		}
796368c31abSDavid du Colombier 		ap.arenabase = bestdiff;
797368c31abSDavid du Colombier 	}
798368c31abSDavid du Colombier 
799a6b1725cSDavid du Colombier 	ap.tabbase = ROUNDUP(PartBlank+HeadSize, ap.blocksize);
800368c31abSDavid du Colombier 	/*
801368c31abSDavid du Colombier 	 * XXX pick up table, check arenabase.
802368c31abSDavid du Colombier 	 * XXX pick up table, record base name.
803368c31abSDavid du Colombier 	 */
804368c31abSDavid du Colombier 
805368c31abSDavid du Colombier 	/*
806368c31abSDavid du Colombier 	 * Somewhat standard computation.
807368c31abSDavid du Colombier 	 * Fmtarenas used to use 64k tab, now uses 512k tab.
808368c31abSDavid du Colombier 	 */
809368c31abSDavid du Colombier 	if(ap.arenabase == 0){
81023566e0cSDavid du Colombier 		print("trying standard arena bases...\n");
811368c31abSDavid du Colombier 		for(i=0; i<nelem(tabsizes); i++){
81223566e0cSDavid du Colombier 			ap.arenabase = ROUNDUP(PartBlank+HeadSize+tabsizes[i], ap.blocksize);
813368c31abSDavid du Colombier 			p = pagein(ap.arenabase, Block);
814368c31abSDavid du Colombier 			if(u32(p) == ArenaHeadMagic)
815368c31abSDavid du Colombier 				break;
816368c31abSDavid du Colombier 		}
817368c31abSDavid du Colombier 	}
818368c31abSDavid du Colombier 	p = pagein(ap.arenabase, Block);
819368c31abSDavid du Colombier 	print("arena base likely %z%s\n", (vlong)ap.arenabase,
820368c31abSDavid du Colombier 		u32(p)!=ArenaHeadMagic ? " (but no arena head there)" : "");
821368c31abSDavid du Colombier 
822368c31abSDavid du Colombier 	ap.tabsize = ap.arenabase - ap.tabbase;
823368c31abSDavid du Colombier }
824368c31abSDavid du Colombier 
825368c31abSDavid du Colombier /*
826368c31abSDavid du Colombier  * Check the arena partition blocks and then the arenas listed in range.
827368c31abSDavid du Colombier  */
828368c31abSDavid du Colombier void
checkarenas(char * range)829368c31abSDavid du Colombier checkarenas(char *range)
830368c31abSDavid du Colombier {
831368c31abSDavid du Colombier 	char *s, *t;
832368c31abSDavid du Colombier 	int i, lo, hi, narena;
833368c31abSDavid du Colombier 	uchar dbuf[HeadSize];
834368c31abSDavid du Colombier 	uchar *p;
835368c31abSDavid du Colombier 
836368c31abSDavid du Colombier 	guessgeometry();
837368c31abSDavid du Colombier 
838368c31abSDavid du Colombier 	partend -= partend%ap.blocksize;
839368c31abSDavid du Colombier 
840368c31abSDavid du Colombier 	memset(dbuf, 0, sizeof dbuf);
841368c31abSDavid du Colombier 	packarenapart(&ap, dbuf);
842368c31abSDavid du Colombier 	p = pagein(PartBlank, Block);
843368c31abSDavid du Colombier 	if(memcmp(p, dbuf, HeadSize) != 0){
844368c31abSDavid du Colombier 		print("on-disk arena part superblock incorrect\n");
845368c31abSDavid du Colombier 		showdiffs(dbuf, p, HeadSize, partinfo);
846368c31abSDavid du Colombier 	}
847368c31abSDavid du Colombier 	memmove(p, dbuf, HeadSize);
848368c31abSDavid du Colombier 
849368c31abSDavid du Colombier 	narena = (partend-ap.arenabase + arenasize-1)/arenasize;
850368c31abSDavid du Colombier 	if(range == nil){
851368c31abSDavid du Colombier 		for(i=0; i<narena; i++)
852368c31abSDavid du Colombier 			checkarena(ap.arenabase+(vlong)i*arenasize, i);
853368c31abSDavid du Colombier 	}else if(strcmp(range, "none") == 0){
854368c31abSDavid du Colombier 		/* nothing */
855368c31abSDavid du Colombier 	}else{
856368c31abSDavid du Colombier 		/* parse, e.g., -4,8-9,10- */
857368c31abSDavid du Colombier 		for(s=range; *s; s=t){
858368c31abSDavid du Colombier 			t = strchr(s, ',');
859368c31abSDavid du Colombier 			if(t)
860368c31abSDavid du Colombier 				*t++ = 0;
861368c31abSDavid du Colombier 			else
862368c31abSDavid du Colombier 				t = s+strlen(s);
863368c31abSDavid du Colombier 			if(*s == '-')
864368c31abSDavid du Colombier 				lo = 0;
865368c31abSDavid du Colombier 			else
866368c31abSDavid du Colombier 				lo = strtol(s, &s, 0);
867368c31abSDavid du Colombier 			hi = lo;
868368c31abSDavid du Colombier 			if(*s == '-'){
869368c31abSDavid du Colombier 				s++;
870368c31abSDavid du Colombier 				if(*s == 0)
871368c31abSDavid du Colombier 					hi = narena-1;
872368c31abSDavid du Colombier 				else
873368c31abSDavid du Colombier 					hi = strtol(s, &s, 0);
874368c31abSDavid du Colombier 			}
875368c31abSDavid du Colombier 			if(*s != 0){
876368c31abSDavid du Colombier 				print("bad arena range: %s\n", s);
877368c31abSDavid du Colombier 				continue;
878368c31abSDavid du Colombier 			}
879368c31abSDavid du Colombier 			for(i=lo; i<=hi; i++)
880368c31abSDavid du Colombier 				checkarena(ap.arenabase+(vlong)i*arenasize, i);
881368c31abSDavid du Colombier 		}
882368c31abSDavid du Colombier 	}
883368c31abSDavid du Colombier }
884368c31abSDavid du Colombier 
885368c31abSDavid du Colombier /*
886368c31abSDavid du Colombier  * Is there a clump here at p?
887368c31abSDavid du Colombier  */
888368c31abSDavid du Colombier static int
isclump(uchar * p,Clump * cl,u32int * pmagic)889368c31abSDavid du Colombier isclump(uchar *p, Clump *cl, u32int *pmagic)
890368c31abSDavid du Colombier {
891368c31abSDavid du Colombier 	int n;
892368c31abSDavid du Colombier 	u32int magic;
893368c31abSDavid du Colombier 	uchar score[VtScoreSize], *bp;
894368c31abSDavid du Colombier 	Unwhack uw;
895368c31abSDavid du Colombier 	uchar ubuf[70*1024];
896368c31abSDavid du Colombier 
897368c31abSDavid du Colombier 	bp = p;
898368c31abSDavid du Colombier 	magic = u32(p);
899368c31abSDavid du Colombier 	if(magic == 0)
900368c31abSDavid du Colombier 		return 0;
901368c31abSDavid du Colombier 	p += U32Size;
902368c31abSDavid du Colombier 
903368c31abSDavid du Colombier 	cl->info.type = vtfromdisktype(*p);
904368c31abSDavid du Colombier 	if(cl->info.type == 0xFF)
905368c31abSDavid du Colombier 		return 0;
906368c31abSDavid du Colombier 	p++;
907368c31abSDavid du Colombier 	cl->info.size = u16(p);
908368c31abSDavid du Colombier 	p += U16Size;
909368c31abSDavid du Colombier 	cl->info.uncsize = u16(p);
910368c31abSDavid du Colombier 	if(cl->info.size > cl->info.uncsize)
911368c31abSDavid du Colombier 		return 0;
912368c31abSDavid du Colombier 	p += U16Size;
913368c31abSDavid du Colombier 	scorecp(cl->info.score, p);
914368c31abSDavid du Colombier 	p += VtScoreSize;
915368c31abSDavid du Colombier 	cl->encoding = *p;
916368c31abSDavid du Colombier 	p++;
917368c31abSDavid du Colombier 	cl->creator = u32(p);
918368c31abSDavid du Colombier 	p += U32Size;
919368c31abSDavid du Colombier 	cl->time = u32(p);
920368c31abSDavid du Colombier 	p += U32Size;
921368c31abSDavid du Colombier 
922368c31abSDavid du Colombier 	switch(cl->encoding){
923368c31abSDavid du Colombier 	case ClumpENone:
924368c31abSDavid du Colombier 		if(cl->info.size != cl->info.uncsize)
925368c31abSDavid du Colombier 			return 0;
926368c31abSDavid du Colombier 		scoremem(score, p, cl->info.size);
927368c31abSDavid du Colombier 		if(scorecmp(score, cl->info.score) != 0)
928368c31abSDavid du Colombier 			return 0;
929368c31abSDavid du Colombier 		break;
930368c31abSDavid du Colombier 	case ClumpECompress:
931368c31abSDavid du Colombier 		if(cl->info.size >= cl->info.uncsize)
932368c31abSDavid du Colombier 			return 0;
933368c31abSDavid du Colombier 		unwhackinit(&uw);
934368c31abSDavid du Colombier 		n = unwhack(&uw, ubuf, cl->info.uncsize, p, cl->info.size);
935368c31abSDavid du Colombier 		if(n != cl->info.uncsize)
936368c31abSDavid du Colombier 			return 0;
937368c31abSDavid du Colombier 		scoremem(score, ubuf, cl->info.uncsize);
938368c31abSDavid du Colombier 		if(scorecmp(score, cl->info.score) != 0)
939368c31abSDavid du Colombier 			return 0;
940368c31abSDavid du Colombier 		break;
941368c31abSDavid du Colombier 	default:
942368c31abSDavid du Colombier 		return 0;
943368c31abSDavid du Colombier 	}
944368c31abSDavid du Colombier 	p += cl->info.size;
945368c31abSDavid du Colombier 
946368c31abSDavid du Colombier 	/* it all worked out in the end */
947368c31abSDavid du Colombier 	*pmagic = magic;
948368c31abSDavid du Colombier 	return p - bp;
949368c31abSDavid du Colombier }
950368c31abSDavid du Colombier 
951368c31abSDavid du Colombier /*
952368c31abSDavid du Colombier  * All ClumpInfos seen in this arena.
953368c31abSDavid du Colombier  * Kept in binary tree so we can look up by score.
954368c31abSDavid du Colombier  */
955368c31abSDavid du Colombier typedef struct Cit Cit;
956368c31abSDavid du Colombier struct Cit
957368c31abSDavid du Colombier {
958368c31abSDavid du Colombier 	int left;
959368c31abSDavid du Colombier 	int right;
960368c31abSDavid du Colombier 	vlong corrupt;
961368c31abSDavid du Colombier 	ClumpInfo ci;
962368c31abSDavid du Colombier };
963368c31abSDavid du Colombier Cit *cibuf;
964368c31abSDavid du Colombier int ciroot;
965368c31abSDavid du Colombier int ncibuf, mcibuf;
966368c31abSDavid du Colombier 
967368c31abSDavid du Colombier void
resetcibuf(void)968368c31abSDavid du Colombier resetcibuf(void)
969368c31abSDavid du Colombier {
970368c31abSDavid du Colombier 	ncibuf = 0;
971368c31abSDavid du Colombier 	ciroot = -1;
972368c31abSDavid du Colombier }
973368c31abSDavid du Colombier 
974368c31abSDavid du Colombier int*
ltreewalk(int * p,uchar * score)975368c31abSDavid du Colombier ltreewalk(int *p, uchar *score)
976368c31abSDavid du Colombier {
977368c31abSDavid du Colombier 	int i;
978368c31abSDavid du Colombier 
979368c31abSDavid du Colombier 	for(;;){
980368c31abSDavid du Colombier 		if(*p == -1)
981368c31abSDavid du Colombier 			return p;
982368c31abSDavid du Colombier 		i = scorecmp(cibuf[*p].ci.score, score);
983368c31abSDavid du Colombier 		if(i == 0)
984368c31abSDavid du Colombier 			return p;
985368c31abSDavid du Colombier 		if(i < 0)
986368c31abSDavid du Colombier 			p = &cibuf[*p].right;
987368c31abSDavid du Colombier 		else
988368c31abSDavid du Colombier 			p = &cibuf[*p].left;
989368c31abSDavid du Colombier 	}
990368c31abSDavid du Colombier }
991368c31abSDavid du Colombier 
992368c31abSDavid du Colombier void
addcibuf(ClumpInfo * ci,vlong corrupt)993368c31abSDavid du Colombier addcibuf(ClumpInfo *ci, vlong corrupt)
994368c31abSDavid du Colombier {
995368c31abSDavid du Colombier 	Cit *cit;
996368c31abSDavid du Colombier 
997368c31abSDavid du Colombier 	if(ncibuf == mcibuf){
998368c31abSDavid du Colombier 		mcibuf += 131072;
999368c31abSDavid du Colombier 		cibuf = vtrealloc(cibuf, mcibuf*sizeof cibuf[0]);
1000368c31abSDavid du Colombier 	}
1001368c31abSDavid du Colombier 	cit = &cibuf[ncibuf];
1002368c31abSDavid du Colombier 	cit->ci = *ci;
1003368c31abSDavid du Colombier 	cit->left = -1;
1004368c31abSDavid du Colombier 	cit->right = -1;
1005368c31abSDavid du Colombier 	cit->corrupt = corrupt;
1006368c31abSDavid du Colombier 	if(!corrupt)
1007368c31abSDavid du Colombier 		*ltreewalk(&ciroot, ci->score) = ncibuf;
1008368c31abSDavid du Colombier 	ncibuf++;
1009368c31abSDavid du Colombier }
1010368c31abSDavid du Colombier 
1011368c31abSDavid du Colombier void
addcicorrupt(vlong len)1012368c31abSDavid du Colombier addcicorrupt(vlong len)
1013368c31abSDavid du Colombier {
1014368c31abSDavid du Colombier 	static ClumpInfo zci;
1015368c31abSDavid du Colombier 
1016368c31abSDavid du Colombier 	addcibuf(&zci, len);
1017368c31abSDavid du Colombier }
1018368c31abSDavid du Colombier 
1019368c31abSDavid du Colombier int
haveclump(uchar * score)1020368c31abSDavid du Colombier haveclump(uchar *score)
1021368c31abSDavid du Colombier {
1022368c31abSDavid du Colombier 	int i;
1023368c31abSDavid du Colombier 	int p;
1024368c31abSDavid du Colombier 
1025368c31abSDavid du Colombier 	p = ciroot;
1026368c31abSDavid du Colombier 	for(;;){
1027368c31abSDavid du Colombier 		if(p == -1)
1028368c31abSDavid du Colombier 			return 0;
1029368c31abSDavid du Colombier 		i = scorecmp(cibuf[p].ci.score, score);
1030368c31abSDavid du Colombier 		if(i == 0)
1031368c31abSDavid du Colombier 			return 1;
1032368c31abSDavid du Colombier 		if(i < 0)
1033368c31abSDavid du Colombier 			p = cibuf[p].right;
1034368c31abSDavid du Colombier 		else
1035368c31abSDavid du Colombier 			p = cibuf[p].left;
1036368c31abSDavid du Colombier 	}
1037368c31abSDavid du Colombier }
1038368c31abSDavid du Colombier 
1039368c31abSDavid du Colombier int
matchci(ClumpInfo * ci,uchar * p)1040368c31abSDavid du Colombier matchci(ClumpInfo *ci, uchar *p)
1041368c31abSDavid du Colombier {
1042368c31abSDavid du Colombier 	if(ci->type != vtfromdisktype(p[0]))
1043368c31abSDavid du Colombier 		return 0;
1044368c31abSDavid du Colombier 	if(ci->size != u16(p+1))
1045368c31abSDavid du Colombier 		return 0;
1046368c31abSDavid du Colombier 	if(ci->uncsize != u16(p+3))
1047368c31abSDavid du Colombier 		return 0;
1048368c31abSDavid du Colombier 	if(scorecmp(ci->score, p+5) != 0)
1049368c31abSDavid du Colombier 		return 0;
1050368c31abSDavid du Colombier 	return 1;
1051368c31abSDavid du Colombier }
1052368c31abSDavid du Colombier 
1053368c31abSDavid du Colombier int
sealedarena(uchar * p,int blocksize)1054368c31abSDavid du Colombier sealedarena(uchar *p, int blocksize)
1055368c31abSDavid du Colombier {
1056368c31abSDavid du Colombier 	int v, n;
1057368c31abSDavid du Colombier 
1058368c31abSDavid du Colombier 	v = u32(p+4);
1059368c31abSDavid du Colombier 	switch(v){
1060368c31abSDavid du Colombier 	default:
1061368c31abSDavid du Colombier 		return 0;
1062368c31abSDavid du Colombier 	case ArenaVersion4:
1063368c31abSDavid du Colombier 		n = ArenaSize4;
1064368c31abSDavid du Colombier 		break;
1065368c31abSDavid du Colombier 	case ArenaVersion5:
1066368c31abSDavid du Colombier 		n = ArenaSize5;
1067368c31abSDavid du Colombier 		break;
1068368c31abSDavid du Colombier 	}
1069368c31abSDavid du Colombier 	if(p[n-1] != 1){
1070368c31abSDavid du Colombier 		print("arena tail says not sealed\n");
1071368c31abSDavid du Colombier 		return 0;
1072368c31abSDavid du Colombier 	}
1073368c31abSDavid du Colombier 	if(memcmp(p+n, zero, blocksize-VtScoreSize-n) != 0){
1074368c31abSDavid du Colombier 		print("arena tail followed by non-zero data\n");
1075368c31abSDavid du Colombier 		return 0;
1076368c31abSDavid du Colombier 	}
1077368c31abSDavid du Colombier 	if(memcmp(p+blocksize-VtScoreSize, zero, VtScoreSize) == 0){
1078368c31abSDavid du Colombier 		print("arena score zero\n");
1079368c31abSDavid du Colombier 		return 0;
1080368c31abSDavid du Colombier 	}
1081368c31abSDavid du Colombier 	return 1;
1082368c31abSDavid du Colombier }
1083368c31abSDavid du Colombier 
1084368c31abSDavid du Colombier int
okayname(char * name,int n)1085368c31abSDavid du Colombier okayname(char *name, int n)
1086368c31abSDavid du Colombier {
1087368c31abSDavid du Colombier 	char buf[20];
1088368c31abSDavid du Colombier 
1089368c31abSDavid du Colombier 	if(nameok(name) < 0)
1090368c31abSDavid du Colombier 		return 0;
1091368c31abSDavid du Colombier 	sprint(buf, "%d", n);
1092368c31abSDavid du Colombier 	if(n == 0)
1093368c31abSDavid du Colombier 		buf[0] = 0;
1094368c31abSDavid du Colombier 	if(strlen(name) < strlen(buf)
1095368c31abSDavid du Colombier 	|| strcmp(name+strlen(name)-strlen(buf), buf) != 0)
1096368c31abSDavid du Colombier 		return 0;
1097368c31abSDavid du Colombier 	return 1;
1098368c31abSDavid du Colombier }
1099368c31abSDavid du Colombier 
1100368c31abSDavid du Colombier int
clumpinfocmp(ClumpInfo * a,ClumpInfo * b)1101368c31abSDavid du Colombier clumpinfocmp(ClumpInfo *a, ClumpInfo *b)
1102368c31abSDavid du Colombier {
1103368c31abSDavid du Colombier 	if(a->type != b->type)
1104368c31abSDavid du Colombier 		return a->type - b->type;
1105368c31abSDavid du Colombier 	if(a->size != b->size)
1106368c31abSDavid du Colombier 		return a->size - b->size;
1107368c31abSDavid du Colombier 	if(a->uncsize != b->uncsize)
1108368c31abSDavid du Colombier 		return a->uncsize - b->uncsize;
1109368c31abSDavid du Colombier 	return scorecmp(a->score, b->score);
1110368c31abSDavid du Colombier }
1111368c31abSDavid du Colombier 
1112368c31abSDavid du Colombier ClumpInfo*
loadci(vlong offset,Arena * arena,int nci)1113368c31abSDavid du Colombier loadci(vlong offset, Arena *arena, int nci)
1114368c31abSDavid du Colombier {
1115368c31abSDavid du Colombier 	int i, j, per;
1116368c31abSDavid du Colombier 	uchar *p, *sp;
1117368c31abSDavid du Colombier 	ClumpInfo *bci, *ci;
1118368c31abSDavid du Colombier 
1119368c31abSDavid du Colombier 	per = arena->blocksize/ClumpInfoSize;
1120368c31abSDavid du Colombier 	bci = vtmalloc(nci*sizeof bci[0]);
1121368c31abSDavid du Colombier 	ci = bci;
1122368c31abSDavid du Colombier 	offset += arena->size - arena->blocksize;
1123368c31abSDavid du Colombier 	p = sp = nil;
1124368c31abSDavid du Colombier 	for(i=0; i<nci; i+=per){
1125368c31abSDavid du Colombier 		if(p == sp){
1126368c31abSDavid du Colombier 			sp = pagein(offset-4*M, 4*M);
1127368c31abSDavid du Colombier 			p = sp+4*M;
1128368c31abSDavid du Colombier 		}
1129368c31abSDavid du Colombier 		p -= arena->blocksize;
1130368c31abSDavid du Colombier 		offset -= arena->blocksize;
1131368c31abSDavid du Colombier 		for(j=0; j<per && i+j<nci; j++)
1132368c31abSDavid du Colombier 			unpackclumpinfo(ci++, p+j*ClumpInfoSize);
1133368c31abSDavid du Colombier 	}
1134368c31abSDavid du Colombier 	return bci;
1135368c31abSDavid du Colombier }
1136368c31abSDavid du Colombier 
1137368c31abSDavid du Colombier vlong
writeci(vlong offset,Arena * arena,ClumpInfo * ci,int nci)1138368c31abSDavid du Colombier writeci(vlong offset, Arena *arena, ClumpInfo *ci, int nci)
1139368c31abSDavid du Colombier {
1140368c31abSDavid du Colombier 	int i, j, per;
1141368c31abSDavid du Colombier 	uchar *p, *sp;
1142368c31abSDavid du Colombier 
1143368c31abSDavid du Colombier 	per = arena->blocksize/ClumpInfoSize;
1144368c31abSDavid du Colombier 	offset += arena->size - arena->blocksize;
1145368c31abSDavid du Colombier 	p = sp = nil;
1146368c31abSDavid du Colombier 	for(i=0; i<nci; i+=per){
1147368c31abSDavid du Colombier 		if(p == sp){
1148368c31abSDavid du Colombier 			sp = pagein(offset-4*M, 4*M);
1149368c31abSDavid du Colombier 			p = sp+4*M;
1150368c31abSDavid du Colombier 		}
1151368c31abSDavid du Colombier 		p -= arena->blocksize;
1152368c31abSDavid du Colombier 		offset -= arena->blocksize;
1153368c31abSDavid du Colombier 		memset(p, 0, arena->blocksize);
1154368c31abSDavid du Colombier 		for(j=0; j<per && i+j<nci; j++)
1155368c31abSDavid du Colombier 			packclumpinfo(ci++, p+j*ClumpInfoSize);
1156368c31abSDavid du Colombier 	}
1157368c31abSDavid du Colombier 	pageout();
1158368c31abSDavid du Colombier 	return offset;
1159368c31abSDavid du Colombier }
1160368c31abSDavid du Colombier 
1161368c31abSDavid du Colombier void
loadarenabasics(vlong offset0,int anum,ArenaHead * head,Arena * arena)1162368c31abSDavid du Colombier loadarenabasics(vlong offset0, int anum, ArenaHead *head, Arena *arena)
1163368c31abSDavid du Colombier {
1164368c31abSDavid du Colombier 	char dname[ANameSize];
1165368c31abSDavid du Colombier 	static char lastbase[ANameSize];
1166368c31abSDavid du Colombier 	uchar *p;
1167368c31abSDavid du Colombier 	Arena oarena;
1168368c31abSDavid du Colombier 	ArenaHead ohead;
1169368c31abSDavid du Colombier 
1170368c31abSDavid du Colombier 	/*
1171368c31abSDavid du Colombier 	 * Fmtarenas makes all arenas the same size
1172368c31abSDavid du Colombier 	 * except the last, which may be smaller.
1173368c31abSDavid du Colombier 	 * It uses the same block size for arenas as for
1174368c31abSDavid du Colombier 	 * the arena partition blocks.
1175368c31abSDavid du Colombier 	 */
1176368c31abSDavid du Colombier 	arena->size = arenasize;
1177368c31abSDavid du Colombier 	if(offset0+arena->size > partend)
1178368c31abSDavid du Colombier 		arena->size = partend - offset0;
1179368c31abSDavid du Colombier 	head->size = arena->size;
1180368c31abSDavid du Colombier 
1181368c31abSDavid du Colombier 	arena->blocksize = ap.blocksize;
1182368c31abSDavid du Colombier 	head->blocksize = arena->blocksize;
1183368c31abSDavid du Colombier 
1184368c31abSDavid du Colombier 	/*
1185368c31abSDavid du Colombier 	 * Look for clump magic and name in head/tail blocks.
1186368c31abSDavid du Colombier 	 * All the other info we will reconstruct just in case.
1187368c31abSDavid du Colombier 	 */
1188368c31abSDavid du Colombier 	p = pagein(offset0, arena->blocksize);
1189368c31abSDavid du Colombier 	memset(&ohead, 0, sizeof ohead);
1190368c31abSDavid du Colombier 	if(unpackarenahead(&ohead, p) >= 0){
1191368c31abSDavid du Colombier 		head->version = ohead.version;
1192368c31abSDavid du Colombier 		head->clumpmagic = ohead.clumpmagic;
1193368c31abSDavid du Colombier 		if(okayname(ohead.name, anum))
1194368c31abSDavid du Colombier 			strcpy(head->name, ohead.name);
1195368c31abSDavid du Colombier 	}
1196368c31abSDavid du Colombier 
1197368c31abSDavid du Colombier 	p = pagein(offset0+arena->size-arena->blocksize,
1198368c31abSDavid du Colombier 		arena->blocksize);
1199368c31abSDavid du Colombier 	memset(&oarena, 0, sizeof oarena);
1200368c31abSDavid du Colombier 	if(unpackarena(&oarena, p) >= 0){
1201368c31abSDavid du Colombier 		arena->version = oarena.version;
1202368c31abSDavid du Colombier 		arena->clumpmagic = oarena.clumpmagic;
1203368c31abSDavid du Colombier 		if(okayname(oarena.name, anum))
1204368c31abSDavid du Colombier 			strcpy(arena->name, oarena.name);
1205368c31abSDavid du Colombier 		arena->diskstats.clumps = oarena.diskstats.clumps;
1206368c31abSDavid du Colombier print("old arena: sealed=%d\n", oarena.diskstats.sealed);
1207368c31abSDavid du Colombier 		arena->diskstats.sealed = oarena.diskstats.sealed;
1208368c31abSDavid du Colombier 	}
1209368c31abSDavid du Colombier 
1210368c31abSDavid du Colombier 	/* Head trumps arena. */
1211368c31abSDavid du Colombier 	if(head->version){
1212368c31abSDavid du Colombier 		arena->version = head->version;
1213368c31abSDavid du Colombier 		arena->clumpmagic = head->clumpmagic;
1214368c31abSDavid du Colombier 	}
1215368c31abSDavid du Colombier 	if(arena->version == 0)
1216368c31abSDavid du Colombier 		arena->version = ArenaVersion5;
1217368c31abSDavid du Colombier 	if(basename){
1218368c31abSDavid du Colombier 		if(anum == -1)
1219368c31abSDavid du Colombier 			snprint(arena->name, ANameSize, "%s", basename);
1220368c31abSDavid du Colombier 		else
1221368c31abSDavid du Colombier 			snprint(arena->name, ANameSize, "%s%d", basename, anum);
1222368c31abSDavid du Colombier 	}else if(lastbase[0])
1223368c31abSDavid du Colombier 		snprint(arena->name, ANameSize, "%s%d", lastbase, anum);
1224368c31abSDavid du Colombier 	else if(head->name[0])
1225368c31abSDavid du Colombier 		strcpy(arena->name, head->name);
1226368c31abSDavid du Colombier 	else if(arena->name[0] == 0)
1227368c31abSDavid du Colombier 		sysfatal("cannot determine base name for arena; use -n");
1228368c31abSDavid du Colombier 	strcpy(lastbase, arena->name);
1229368c31abSDavid du Colombier 	sprint(dname, "%d", anum);
1230368c31abSDavid du Colombier 	lastbase[strlen(lastbase)-strlen(dname)] = 0;
1231368c31abSDavid du Colombier 
1232368c31abSDavid du Colombier 	/* Was working in arena, now copy to head. */
1233368c31abSDavid du Colombier 	head->version = arena->version;
1234368c31abSDavid du Colombier 	memmove(head->name, arena->name, sizeof head->name);
1235368c31abSDavid du Colombier 	head->blocksize = arena->blocksize;
1236368c31abSDavid du Colombier 	head->size = arena->size;
1237368c31abSDavid du Colombier }
1238368c31abSDavid du Colombier 
1239368c31abSDavid du Colombier void
shahead(Shabuf * sb,vlong offset0,ArenaHead * head)1240368c31abSDavid du Colombier shahead(Shabuf *sb, vlong offset0, ArenaHead *head)
1241368c31abSDavid du Colombier {
1242368c31abSDavid du Colombier 	uchar headbuf[MaxDiskBlock];
1243368c31abSDavid du Colombier 
1244368c31abSDavid du Colombier 	sb->offset = offset0;
1245368c31abSDavid du Colombier 	memset(headbuf, 0, sizeof headbuf);
1246368c31abSDavid du Colombier 	packarenahead(head, headbuf);
1247368c31abSDavid du Colombier 	sbupdate(sb, headbuf, offset0, head->blocksize);
1248368c31abSDavid du Colombier }
1249368c31abSDavid du Colombier 
1250368c31abSDavid du Colombier u32int
newclumpmagic(int version)1251368c31abSDavid du Colombier newclumpmagic(int version)
1252368c31abSDavid du Colombier {
1253368c31abSDavid du Colombier 	u32int m;
1254368c31abSDavid du Colombier 
1255368c31abSDavid du Colombier 	if(version == ArenaVersion4)
1256368c31abSDavid du Colombier 		return _ClumpMagic;
1257368c31abSDavid du Colombier 	do{
1258368c31abSDavid du Colombier 		m = fastrand();
1259368c31abSDavid du Colombier 	}while(m==0 || m == _ClumpMagic);
1260368c31abSDavid du Colombier 	return m;
1261368c31abSDavid du Colombier }
1262368c31abSDavid du Colombier 
1263368c31abSDavid du Colombier /*
1264368c31abSDavid du Colombier  * Poke around in the arena to find the clump data
1265368c31abSDavid du Colombier  * and compute the relevant statistics.
1266368c31abSDavid du Colombier  */
1267368c31abSDavid du Colombier void
guessarena(vlong offset0,int anum,ArenaHead * head,Arena * arena,uchar * oldscore,uchar * score)1268368c31abSDavid du Colombier guessarena(vlong offset0, int anum, ArenaHead *head, Arena *arena,
1269368c31abSDavid du Colombier 	uchar *oldscore, uchar *score)
1270368c31abSDavid du Colombier {
1271368c31abSDavid du Colombier 	uchar dbuf[MaxDiskBlock];
1272368c31abSDavid du Colombier 	int needtozero, clumps, nb1, nb2, minclumps;
1273368c31abSDavid du Colombier 	int inbad, n, ncib, printed, sealing, smart;
1274368c31abSDavid du Colombier 	u32int magic;
1275368c31abSDavid du Colombier 	uchar *sp, *ep, *p;
1276368c31abSDavid du Colombier 	vlong boffset, eoffset, lastclumpend, leaked;
1277368c31abSDavid du Colombier 	vlong offset, toffset, totalcorrupt, v;
1278368c31abSDavid du Colombier 	Clump cl;
1279368c31abSDavid du Colombier 	ClumpInfo *bci, *ci, *eci, *xci;
1280368c31abSDavid du Colombier 	Cit *bcit, *cit, *ecit;
1281368c31abSDavid du Colombier 	Shabuf oldsha, newsha;
1282368c31abSDavid du Colombier 
1283368c31abSDavid du Colombier 	/*
1284368c31abSDavid du Colombier 	 * We expect to find an arena, with data, between offset
1285368c31abSDavid du Colombier 	 * and offset+arenasize.  With any luck, the data starts at
1286368c31abSDavid du Colombier 	 * offset+ap.blocksize.  The blocks have variable size and
1287368c31abSDavid du Colombier 	 * aren't padded at all, which doesn't give us any alignment
1288368c31abSDavid du Colombier 	 * constraints.  The blocks are compressed or high entropy,
1289368c31abSDavid du Colombier 	 * but the headers are pretty low entropy (except the score):
1290368c31abSDavid du Colombier 	 *
1291368c31abSDavid du Colombier 	 *	type[1] (range 0 thru 9, 13)
1292368c31abSDavid du Colombier 	 *	size[2]
1293368c31abSDavid du Colombier 	 *	uncsize[2] (<= size)
1294368c31abSDavid du Colombier 	 *
1295368c31abSDavid du Colombier 	 * so we can look for these.  We check the scores as we go,
1296368c31abSDavid du Colombier 	 * so we can't make any wrong turns.  If we find ourselves
1297368c31abSDavid du Colombier 	 * in a dead end, scan forward looking for a new start.
1298368c31abSDavid du Colombier 	 */
1299368c31abSDavid du Colombier 
1300368c31abSDavid du Colombier 	resetcibuf();
1301368c31abSDavid du Colombier 	memset(head, 0, sizeof *head);
1302368c31abSDavid du Colombier 	memset(arena, 0, sizeof *arena);
1303368c31abSDavid du Colombier 	memset(oldscore, 0, VtScoreSize);
1304368c31abSDavid du Colombier 	memset(score, 0, VtScoreSize);
1305368c31abSDavid du Colombier 	memset(&oldsha, 0, sizeof oldsha);
1306368c31abSDavid du Colombier 	memset(&newsha, 0, sizeof newsha);
1307368c31abSDavid du Colombier 	newsha.rollback = 1;
1308368c31abSDavid du Colombier 
1309368c31abSDavid du Colombier 	if(0){
1310368c31abSDavid du Colombier 		sbdebug(&oldsha, "old.sha");
1311368c31abSDavid du Colombier 		sbdebug(&newsha, "new.sha");
1312368c31abSDavid du Colombier 	}
1313368c31abSDavid du Colombier 
1314368c31abSDavid du Colombier 	loadarenabasics(offset0, anum, head, arena);
1315368c31abSDavid du Colombier 
1316368c31abSDavid du Colombier 	/* start the clump hunt */
1317368c31abSDavid du Colombier 
1318368c31abSDavid du Colombier 	clumps = 0;
1319368c31abSDavid du Colombier 	totalcorrupt = 0;
1320368c31abSDavid du Colombier 	sealing = 1;
1321368c31abSDavid du Colombier 	boffset = offset0 + arena->blocksize;
1322368c31abSDavid du Colombier 	offset = boffset;
1323368c31abSDavid du Colombier 	eoffset = offset0+arena->size - arena->blocksize;
1324368c31abSDavid du Colombier 	toffset = eoffset;
1325368c31abSDavid du Colombier 	sp = pagein(offset0, 4*M);
1326368c31abSDavid du Colombier 
1327368c31abSDavid du Colombier 	if(arena->diskstats.sealed){
1328368c31abSDavid du Colombier 		oldsha.offset = offset0;
1329368c31abSDavid du Colombier 		sbupdate(&oldsha, sp, offset0, 4*M);
1330368c31abSDavid du Colombier 	}
1331368c31abSDavid du Colombier 	ep = sp+4*M;
1332368c31abSDavid du Colombier 	p = sp + (boffset - offset0);
1333368c31abSDavid du Colombier 	ncib = arena->blocksize / ClumpInfoSize;	/* ci per block in index */
1334368c31abSDavid du Colombier 	lastclumpend = offset;
1335368c31abSDavid du Colombier 	nbad = 0;
1336368c31abSDavid du Colombier 	inbad = 0;
1337368c31abSDavid du Colombier 	needtozero = 0;
1338368c31abSDavid du Colombier 	minclumps = 0;
1339368c31abSDavid du Colombier 	while(offset < eoffset){
1340368c31abSDavid du Colombier 		/*
1341368c31abSDavid du Colombier 		 * Shift buffer if we're running out of room.
1342368c31abSDavid du Colombier 		 */
1343368c31abSDavid du Colombier 		if(p+70*K >= ep){
1344368c31abSDavid du Colombier 			/*
1345368c31abSDavid du Colombier 			 * Start the post SHA1 buffer.   By now we should know the
1346368c31abSDavid du Colombier 			 * clumpmagic and arena version, so we can create a
1347368c31abSDavid du Colombier 			 * correct head block to get things going.
1348368c31abSDavid du Colombier 			 */
1349368c31abSDavid du Colombier 			if(sealing && fix && newsha.offset == 0){
1350368c31abSDavid du Colombier 				newsha.offset = offset0;
1351368c31abSDavid du Colombier 				if(arena->clumpmagic == 0){
1352368c31abSDavid du Colombier 					if(arena->version == 0)
1353368c31abSDavid du Colombier 						arena->version = ArenaVersion5;
1354368c31abSDavid du Colombier 					arena->clumpmagic = newclumpmagic(arena->version);
1355368c31abSDavid du Colombier 				}
1356368c31abSDavid du Colombier 				head->clumpmagic = arena->clumpmagic;
1357368c31abSDavid du Colombier 				shahead(&newsha, offset0, head);
1358368c31abSDavid du Colombier 			}
1359368c31abSDavid du Colombier 			n = 4*M-256*K;
1360368c31abSDavid du Colombier 			if(sealing && fix){
1361368c31abSDavid du Colombier 				sbdiskhash(&newsha, bufoffset);
1362368c31abSDavid du Colombier 				sbupdate(&newsha, buf, bufoffset, 4*M-256*K);
1363368c31abSDavid du Colombier 			}
1364368c31abSDavid du Colombier 			pagein(bufoffset+n, 4*M);
1365368c31abSDavid du Colombier 			p -= n;
1366368c31abSDavid du Colombier 			if(arena->diskstats.sealed)
1367368c31abSDavid du Colombier 				sbupdate(&oldsha, buf, bufoffset, 4*M);
1368368c31abSDavid du Colombier 		}
1369368c31abSDavid du Colombier 
1370368c31abSDavid du Colombier 		/*
1371368c31abSDavid du Colombier 		 * Check for a clump at p, which is at offset in the disk.
1372368c31abSDavid du Colombier 		 * Duplicate clumps happen in corrupted disks
1373368c31abSDavid du Colombier 		 * (the same pattern gets written many times in a row)
1374368c31abSDavid du Colombier 		 * and should never happen during regular use.
1375368c31abSDavid du Colombier 		 */
1376368c31abSDavid du Colombier 		magic = 0;
1377368c31abSDavid du Colombier 		if((n = isclump(p, &cl, &magic)) > 0){
1378368c31abSDavid du Colombier 			/*
1379368c31abSDavid du Colombier 			 * If we were in the middle of some corrupted data,
1380368c31abSDavid du Colombier 			 * flush a warning about it and then add any clump
1381368c31abSDavid du Colombier 			 * info blocks as necessary.
1382368c31abSDavid du Colombier 			 */
1383368c31abSDavid du Colombier 			if(inbad){
1384368c31abSDavid du Colombier 				inbad = 0;
1385368c31abSDavid du Colombier 				v = offset-lastclumpend;
1386368c31abSDavid du Colombier 				if(needtozero){
1387368c31abSDavid du Colombier 					zerorange(lastclumpend, v);
1388368c31abSDavid du Colombier 					sbrollback(&newsha, lastclumpend);
1389368c31abSDavid du Colombier 					print("corrupt clump data - %#llux+%#llux (%,llud bytes)\n",
1390368c31abSDavid du Colombier 						lastclumpend, v, v);
1391368c31abSDavid du Colombier 				}
1392368c31abSDavid du Colombier 				addcicorrupt(v);
1393368c31abSDavid du Colombier 				totalcorrupt += v;
1394368c31abSDavid du Colombier 				nb1 = (minclumps+ncib-1)/ncib;
1395368c31abSDavid du Colombier 				minclumps += (v+ClumpSize+VtMaxLumpSize-1)/(ClumpSize+VtMaxLumpSize);
1396368c31abSDavid du Colombier 				nb2 = (minclumps+ncib-1)/ncib;
1397368c31abSDavid du Colombier 				eoffset -= (nb2-nb1)*arena->blocksize;
1398368c31abSDavid du Colombier 			}
1399368c31abSDavid du Colombier 
1400368c31abSDavid du Colombier 			if(haveclump(cl.info.score))
1401368c31abSDavid du Colombier 				print("warning: duplicate clump %d %V at %#llux+%#d\n", cl.info.type, cl.info.score, offset, n);
1402368c31abSDavid du Colombier 
1403368c31abSDavid du Colombier 			/*
1404368c31abSDavid du Colombier 			 * If clumps use different magic numbers, we don't care.
1405368c31abSDavid du Colombier 			 * We'll just use the first one we find and make the others
1406368c31abSDavid du Colombier 			 * follow suit.
1407368c31abSDavid du Colombier 			 */
1408368c31abSDavid du Colombier 			if(arena->clumpmagic == 0){
1409368c31abSDavid du Colombier 				print("clump type %d size %d score %V magic %x\n",
1410368c31abSDavid du Colombier 					cl.info.type, cl.info.size, cl.info.score, magic);
1411368c31abSDavid du Colombier 				arena->clumpmagic = magic;
1412368c31abSDavid du Colombier 				if(magic == _ClumpMagic)
1413368c31abSDavid du Colombier 					arena->version = ArenaVersion4;
1414368c31abSDavid du Colombier 				else
1415368c31abSDavid du Colombier 					arena->version = ArenaVersion5;
1416368c31abSDavid du Colombier 			}
1417368c31abSDavid du Colombier 			if(magic != arena->clumpmagic)
1418368c31abSDavid du Colombier 				p32(p, arena->clumpmagic);
1419368c31abSDavid du Colombier 			if(clumps == 0)
1420368c31abSDavid du Colombier 				arena->ctime = cl.time;
1421368c31abSDavid du Colombier 
1422368c31abSDavid du Colombier 			/*
1423368c31abSDavid du Colombier 			 * Record the clump, update arena stats,
1424368c31abSDavid du Colombier 			 * grow clump info blocks if needed.
1425368c31abSDavid du Colombier 			 */
1426368c31abSDavid du Colombier 			if(verbose > 1)
1427368c31abSDavid du Colombier 				print("\tclump %d: %d %V at %#llux+%#ux (%d)\n",
1428368c31abSDavid du Colombier 					clumps, cl.info.type, cl.info.score, offset, n, n);
1429368c31abSDavid du Colombier 			addcibuf(&cl.info, 0);
1430368c31abSDavid du Colombier 			if(minclumps%ncib == 0)
1431368c31abSDavid du Colombier 				eoffset -= arena->blocksize;
1432368c31abSDavid du Colombier 			minclumps++;
1433368c31abSDavid du Colombier 			clumps++;
1434368c31abSDavid du Colombier 			if(cl.encoding != ClumpENone)
1435368c31abSDavid du Colombier 				arena->diskstats.cclumps++;
1436368c31abSDavid du Colombier 			arena->diskstats.uncsize += cl.info.uncsize;
1437368c31abSDavid du Colombier 			arena->wtime = cl.time;
1438368c31abSDavid du Colombier 
1439368c31abSDavid du Colombier 			/*
1440368c31abSDavid du Colombier 			 * Move to next clump.
1441368c31abSDavid du Colombier 			 */
1442368c31abSDavid du Colombier 			offset += n;
1443368c31abSDavid du Colombier 			p += n;
1444368c31abSDavid du Colombier 			lastclumpend = offset;
1445368c31abSDavid du Colombier 		}else{
1446368c31abSDavid du Colombier 			/*
1447368c31abSDavid du Colombier 			 * Overwrite malformed clump data with zeros later.
1448368c31abSDavid du Colombier 			 * For now, just record whether it needs to be overwritten.
1449368c31abSDavid du Colombier 			 * Bad regions must be of size at least ClumpSize.
1450368c31abSDavid du Colombier 			 * Postponing the overwriting keeps us from writing past
1451368c31abSDavid du Colombier 			 * the end of the arena data (which might be directory data)
1452368c31abSDavid du Colombier 			 * with zeros.
1453368c31abSDavid du Colombier 			 */
1454368c31abSDavid du Colombier 			if(!inbad){
1455368c31abSDavid du Colombier 				inbad = 1;
1456368c31abSDavid du Colombier 				needtozero = 0;
1457368c31abSDavid du Colombier 				if(memcmp(p, zero, ClumpSize) != 0)
1458368c31abSDavid du Colombier 					needtozero = 1;
1459368c31abSDavid du Colombier 				p += ClumpSize;
1460368c31abSDavid du Colombier 				offset += ClumpSize;
1461368c31abSDavid du Colombier 				nbad++;
1462368c31abSDavid du Colombier 			}else{
1463368c31abSDavid du Colombier 				if(*p != 0)
1464368c31abSDavid du Colombier 					needtozero = 1;
1465368c31abSDavid du Colombier 				p++;
1466368c31abSDavid du Colombier 				offset++;
1467368c31abSDavid du Colombier 			}
1468368c31abSDavid du Colombier 		}
1469368c31abSDavid du Colombier 	}
1470368c31abSDavid du Colombier 	pageout();
1471368c31abSDavid du Colombier 
1472368c31abSDavid du Colombier 	if(verbose)
1473368c31abSDavid du Colombier 		print("readable clumps: %d; min. directory entries: %d\n",
1474368c31abSDavid du Colombier 			clumps, minclumps);
1475368c31abSDavid du Colombier 	arena->diskstats.used = lastclumpend - boffset;
1476368c31abSDavid du Colombier 	leaked = eoffset - lastclumpend;
1477368c31abSDavid du Colombier 	if(verbose)
1478368c31abSDavid du Colombier 		print("used from %#llux to %#llux = %,lld (%,lld unused)\n",
1479368c31abSDavid du Colombier 			boffset, lastclumpend, arena->diskstats.used, leaked);
1480368c31abSDavid du Colombier 
1481368c31abSDavid du Colombier 	/*
1482368c31abSDavid du Colombier 	 * Finish the SHA1 of the old data.
1483368c31abSDavid du Colombier 	 */
1484368c31abSDavid du Colombier 	if(arena->diskstats.sealed){
1485368c31abSDavid du Colombier 		sbdiskhash(&oldsha, toffset);
1486368c31abSDavid du Colombier 		readdisk(dbuf, toffset, arena->blocksize);
1487368c31abSDavid du Colombier 		scorecp(dbuf+arena->blocksize-VtScoreSize, zero);
1488368c31abSDavid du Colombier 		sbupdate(&oldsha, dbuf, toffset, arena->blocksize);
1489368c31abSDavid du Colombier 		sbscore(&oldsha, oldscore);
1490368c31abSDavid du Colombier 	}
1491368c31abSDavid du Colombier 
1492368c31abSDavid du Colombier 	/*
1493368c31abSDavid du Colombier 	 * If we still don't know the clump magic, the arena
1494368c31abSDavid du Colombier 	 * must be empty.  It still needs a value, so make
1495368c31abSDavid du Colombier 	 * something up.
1496368c31abSDavid du Colombier 	 */
1497368c31abSDavid du Colombier 	if(arena->version == 0)
1498368c31abSDavid du Colombier 		arena->version = ArenaVersion5;
1499368c31abSDavid du Colombier 	if(arena->clumpmagic == 0){
1500368c31abSDavid du Colombier 		if(arena->version == ArenaVersion4)
1501368c31abSDavid du Colombier 			arena->clumpmagic = _ClumpMagic;
1502368c31abSDavid du Colombier 		else{
1503368c31abSDavid du Colombier 			do
1504368c31abSDavid du Colombier 				arena->clumpmagic = fastrand();
1505368c31abSDavid du Colombier 			while(arena->clumpmagic==_ClumpMagic
1506368c31abSDavid du Colombier 				||arena->clumpmagic==0);
1507368c31abSDavid du Colombier 		}
1508368c31abSDavid du Colombier 		head->clumpmagic = arena->clumpmagic;
1509368c31abSDavid du Colombier 	}
1510368c31abSDavid du Colombier 
1511368c31abSDavid du Colombier 	/*
1512368c31abSDavid du Colombier 	 * Guess at number of clumpinfo blocks to load.
1513368c31abSDavid du Colombier 	 * If we guess high, it's no big deal.  If we guess low,
1514368c31abSDavid du Colombier 	 * we'll be forced into rewriting the whole directory.
1515368c31abSDavid du Colombier 	 * Still not such a big deal.
1516368c31abSDavid du Colombier 	 */
1517368c31abSDavid du Colombier 	if(clumps == 0 || arena->diskstats.used == totalcorrupt)
1518368c31abSDavid du Colombier 		goto Nocib;
1519368c31abSDavid du Colombier 	if(clumps < arena->diskstats.clumps)
1520368c31abSDavid du Colombier 		clumps = arena->diskstats.clumps;
1521368c31abSDavid du Colombier 	if(clumps < ncibuf)
1522368c31abSDavid du Colombier 		clumps = ncibuf;
1523368c31abSDavid du Colombier 	clumps += totalcorrupt/
1524368c31abSDavid du Colombier 		((arena->diskstats.used - totalcorrupt)/clumps);
1525368c31abSDavid du Colombier 	clumps += totalcorrupt/2000;
1526368c31abSDavid du Colombier 	if(clumps < minclumps)
1527368c31abSDavid du Colombier 		clumps = minclumps;
1528368c31abSDavid du Colombier 	clumps += ncib-1;
1529368c31abSDavid du Colombier 	clumps -= clumps%ncib;
1530368c31abSDavid du Colombier 
1531368c31abSDavid du Colombier 	/*
1532368c31abSDavid du Colombier 	 * Can't write into the actual data.
1533368c31abSDavid du Colombier 	 */
1534368c31abSDavid du Colombier 	v = offset0 + arena->size - arena->blocksize;
1535368c31abSDavid du Colombier 	v -= (clumps+ncib-1)/ncib * arena->blocksize;
1536368c31abSDavid du Colombier 	if(v < lastclumpend){
1537368c31abSDavid du Colombier 		v = offset0 + arena->size - arena->blocksize;
1538368c31abSDavid du Colombier 		clumps = (v-lastclumpend)/arena->blocksize * ncib;
1539368c31abSDavid du Colombier 	}
1540368c31abSDavid du Colombier 
1541368c31abSDavid du Colombier 	if(clumps < minclumps)
1542368c31abSDavid du Colombier 		print("cannot happen?\n");
1543368c31abSDavid du Colombier 
1544368c31abSDavid du Colombier 	/*
1545368c31abSDavid du Colombier 	 * Check clumpinfo blocks against directory we created.
1546368c31abSDavid du Colombier 	 * The tricky part is handling the corrupt sections of arena.
1547368c31abSDavid du Colombier 	 * If possible, we remark just the affected directory entries
1548368c31abSDavid du Colombier 	 * rather than slide everything down.
1549368c31abSDavid du Colombier 	 *
1550368c31abSDavid du Colombier 	 * Allocate clumps+1 blocks and check that we don't need
1551368c31abSDavid du Colombier 	 * the last one at the end.
1552368c31abSDavid du Colombier 	 */
1553368c31abSDavid du Colombier 	bci = loadci(offset0, arena, clumps+1);
1554368c31abSDavid du Colombier 	eci = bci+clumps+1;
1555368c31abSDavid du Colombier 	bcit = cibuf;
1556368c31abSDavid du Colombier 	ecit = cibuf+ncibuf;
1557368c31abSDavid du Colombier 
155823566e0cSDavid du Colombier 	smart = 0;	/* Somehow the smart code doesn't do corrupt clumps right. */
1559368c31abSDavid du Colombier Again:
1560368c31abSDavid du Colombier 	nbad = 0;
1561368c31abSDavid du Colombier 	ci = bci;
1562368c31abSDavid du Colombier 	for(cit=bcit; cit<ecit && ci<eci; cit++){
1563368c31abSDavid du Colombier 		if(cit->corrupt){
1564368c31abSDavid du Colombier 			vlong n, m;
1565368c31abSDavid du Colombier 			if(smart){
1566368c31abSDavid du Colombier 				/*
1567368c31abSDavid du Colombier 				 * If we can, just mark existing entries as corrupt.
1568368c31abSDavid du Colombier 				 */
1569368c31abSDavid du Colombier 				n = cit->corrupt;
1570368c31abSDavid du Colombier 				for(xci=ci; n>0 && xci<eci; xci++)
1571368c31abSDavid du Colombier 					n -= ClumpSize+xci->size;
1572368c31abSDavid du Colombier 				if(n > 0 || xci >= eci)
1573368c31abSDavid du Colombier 					goto Dumb;
1574368c31abSDavid du Colombier 				printed = 0;
1575368c31abSDavid du Colombier 				for(; ci<xci; ci++){
1576368c31abSDavid du Colombier 					if(verbose && ci->type != VtCorruptType){
1577368c31abSDavid du Colombier 						if(!printed){
1578368c31abSDavid du Colombier 							print("marking directory %d-%d as corrupt\n",
1579368c31abSDavid du Colombier 								(int)(ci-bci), (int)(xci-bci));
1580368c31abSDavid du Colombier 							printed = 1;
1581368c31abSDavid du Colombier 						}
1582368c31abSDavid du Colombier 						print("\ttype=%d size=%d uncsize=%d score=%V\n",
1583368c31abSDavid du Colombier 							ci->type, ci->size, ci->uncsize, ci->score);
1584368c31abSDavid du Colombier 					}
1585368c31abSDavid du Colombier 					ci->type = VtCorruptType;
1586368c31abSDavid du Colombier 				}
1587368c31abSDavid du Colombier 			}else{
1588368c31abSDavid du Colombier 			Dumb:
1589368c31abSDavid du Colombier 				print("\trewriting clump directory\n");
1590368c31abSDavid du Colombier 				/*
1591368c31abSDavid du Colombier 				 * Otherwise, blaze a new trail.
1592368c31abSDavid du Colombier 				 */
1593368c31abSDavid du Colombier 				n = cit->corrupt;
1594368c31abSDavid du Colombier 				while(n > 0 && ci < eci){
1595368c31abSDavid du Colombier 					if(n < ClumpSize)
1596368c31abSDavid du Colombier 						sysfatal("bad math in clump corrupt");
1597368c31abSDavid du Colombier 					if(n <= VtMaxLumpSize+ClumpSize)
1598368c31abSDavid du Colombier 						m = n;
1599368c31abSDavid du Colombier 					else{
1600368c31abSDavid du Colombier 						m = VtMaxLumpSize+ClumpSize;
1601368c31abSDavid du Colombier 						if(n-m < ClumpSize)
1602368c31abSDavid du Colombier 							m -= ClumpSize;
1603368c31abSDavid du Colombier 					}
1604368c31abSDavid du Colombier 					ci->type = VtCorruptType;
1605368c31abSDavid du Colombier 					ci->size = m-ClumpSize;
1606368c31abSDavid du Colombier 					ci->uncsize = m-ClumpSize;
1607368c31abSDavid du Colombier 					memset(ci->score, 0, VtScoreSize);
1608368c31abSDavid du Colombier 					ci++;
1609368c31abSDavid du Colombier 					n -= m;
1610368c31abSDavid du Colombier 				}
1611368c31abSDavid du Colombier 			}
1612368c31abSDavid du Colombier 			continue;
1613368c31abSDavid du Colombier 		}
1614368c31abSDavid du Colombier 		if(clumpinfocmp(&cit->ci, ci) != 0){
1615368c31abSDavid du Colombier 			if(verbose && (smart || verbose>1)){
1616368c31abSDavid du Colombier 				print("clumpinfo %d\n", (int)(ci-bci));
1617368c31abSDavid du Colombier 				print("\twant: %d %d %d %V\n",
1618368c31abSDavid du Colombier 					cit->ci.type, cit->ci.size,
1619368c31abSDavid du Colombier 					cit->ci.uncsize, cit->ci.score);
1620368c31abSDavid du Colombier 				print("\thave: %d %d %d %V\n",
1621368c31abSDavid du Colombier 					ci->type, ci->size,
1622368c31abSDavid du Colombier 					ci->uncsize, ci->score);
1623368c31abSDavid du Colombier 			}
1624368c31abSDavid du Colombier 			*ci = cit->ci;
1625368c31abSDavid du Colombier 			nbad++;
1626368c31abSDavid du Colombier 		}
1627368c31abSDavid du Colombier 		ci++;
1628368c31abSDavid du Colombier 	}
1629368c31abSDavid du Colombier 	if(ci >= eci || cit < ecit){
1630368c31abSDavid du Colombier 		print("ran out of space editing existing directory; rewriting\n");
1631368c31abSDavid du Colombier 		print("# eci %ld ci %ld ecit %ld cit %ld\n", eci-bci, ci-bci, ecit-bcit, cit-bcit);
1632368c31abSDavid du Colombier 		assert(smart);	/* can't happen second time thru */
1633368c31abSDavid du Colombier 		smart = 0;
1634368c31abSDavid du Colombier 		goto Again;
1635368c31abSDavid du Colombier 	}
1636368c31abSDavid du Colombier 
1637368c31abSDavid du Colombier 	assert(ci <= eci);
1638368c31abSDavid du Colombier 	arena->diskstats.clumps = ci-bci;
1639368c31abSDavid du Colombier 	eoffset = writeci(offset0, arena, bci, ci-bci);
1640368c31abSDavid du Colombier 	if(sealing && fix)
1641368c31abSDavid du Colombier 		sbrollback(&newsha, v);
1642368c31abSDavid du Colombier print("eoffset=%lld lastclumpend=%lld diff=%lld unseal=%d\n", eoffset, lastclumpend, eoffset-lastclumpend, unseal);
1643368c31abSDavid du Colombier 	if(lastclumpend > eoffset)
1644368c31abSDavid du Colombier 		print("arena directory overwrote blocks!  cannot happen!\n");
1645368c31abSDavid du Colombier 	free(bci);
1646368c31abSDavid du Colombier 	if(smart && nbad)
1647368c31abSDavid du Colombier 		print("arena directory has %d bad or missing entries\n", nbad);
1648368c31abSDavid du Colombier Nocib:
1649368c31abSDavid du Colombier 	if(eoffset - lastclumpend > 64*1024 && (!arena->diskstats.sealed || unseal)){
1650368c31abSDavid du Colombier 		if(arena->diskstats.sealed)
1651368c31abSDavid du Colombier 			print("unsealing arena\n");
1652368c31abSDavid du Colombier 		sealing = 0;
1653368c31abSDavid du Colombier 		memset(oldscore, 0, VtScoreSize);
1654368c31abSDavid du Colombier 	}
1655368c31abSDavid du Colombier 
1656368c31abSDavid du Colombier 	/*
1657368c31abSDavid du Colombier 	 * Finish the SHA1 of the new data - only meaningful
1658368c31abSDavid du Colombier 	 * if we've been writing to disk (`fix').
1659368c31abSDavid du Colombier 	 */
1660368c31abSDavid du Colombier 	arena->diskstats.sealed = sealing;
1661368c31abSDavid du Colombier 	arena->memstats = arena->diskstats;
1662368c31abSDavid du Colombier 	if(sealing && fix){
1663368c31abSDavid du Colombier 		uchar tbuf[MaxDiskBlock];
1664368c31abSDavid du Colombier 
1665368c31abSDavid du Colombier 		sbdiskhash(&newsha, toffset);
1666368c31abSDavid du Colombier 		memset(tbuf, 0, sizeof tbuf);
1667368c31abSDavid du Colombier 		packarena(arena, tbuf);
1668368c31abSDavid du Colombier 		sbupdate(&newsha, tbuf, toffset, arena->blocksize);
1669368c31abSDavid du Colombier 		sbscore(&newsha, score);
1670368c31abSDavid du Colombier 	}
1671368c31abSDavid du Colombier }
1672368c31abSDavid du Colombier 
1673368c31abSDavid du Colombier void
dumparena(vlong offset,int anum,Arena * arena)1674368c31abSDavid du Colombier dumparena(vlong offset, int anum, Arena *arena)
1675368c31abSDavid du Colombier {
1676368c31abSDavid du Colombier 	char buf[1000];
1677368c31abSDavid du Colombier 	vlong o, e;
1678368c31abSDavid du Colombier 	int fd, n;
1679368c31abSDavid du Colombier 
1680368c31abSDavid du Colombier 	snprint(buf, sizeof buf, "%s.%d", dumpbase, anum);
1681368c31abSDavid du Colombier 	if((fd = create(buf, OWRITE, 0666)) < 0){
1682368c31abSDavid du Colombier 		fprint(2, "create %s: %r\n", buf);
1683368c31abSDavid du Colombier 		return;
1684368c31abSDavid du Colombier 	}
1685368c31abSDavid du Colombier 	e = offset+arena->size;
1686368c31abSDavid du Colombier 	for(o=offset; o<e; o+=n){
1687368c31abSDavid du Colombier 		n = 4*M;
1688368c31abSDavid du Colombier 		if(o+n > e)
1689368c31abSDavid du Colombier 			n = e-o;
1690368c31abSDavid du Colombier 		if(pwrite(fd, pagein(o, n), n, o-offset) != n){
1691368c31abSDavid du Colombier 			fprint(2, "write %s at %#llux: %r\n", buf, o-offset);
1692368c31abSDavid du Colombier 			return;
1693368c31abSDavid du Colombier 		}
1694368c31abSDavid du Colombier 	}
1695368c31abSDavid du Colombier }
1696368c31abSDavid du Colombier 
1697368c31abSDavid du Colombier void
checkarena(vlong offset,int anum)1698368c31abSDavid du Colombier checkarena(vlong offset, int anum)
1699368c31abSDavid du Colombier {
1700368c31abSDavid du Colombier 	uchar dbuf[MaxDiskBlock];
1701368c31abSDavid du Colombier 	uchar *p, oldscore[VtScoreSize], score[VtScoreSize];
1702368c31abSDavid du Colombier 	Arena arena, oarena;
1703368c31abSDavid du Colombier 	ArenaHead head;
1704368c31abSDavid du Colombier 	Info *fmt, *fmta;
1705368c31abSDavid du Colombier 	int sz;
1706368c31abSDavid du Colombier 
1707368c31abSDavid du Colombier 	print("# arena %d: offset %#llux\n", anum, offset);
1708368c31abSDavid du Colombier 
1709368c31abSDavid du Colombier 	if(offset >= partend){
1710368c31abSDavid du Colombier 		print("arena offset out of bounds\n");
1711368c31abSDavid du Colombier 		return;
1712368c31abSDavid du Colombier 	}
1713368c31abSDavid du Colombier 
1714368c31abSDavid du Colombier 	guessarena(offset, anum, &head, &arena, oldscore, score);
1715368c31abSDavid du Colombier 
1716368c31abSDavid du Colombier 	if(verbose){
1717368c31abSDavid du Colombier 		print("#\tversion=%d name=%s blocksize=%d size=%z",
1718368c31abSDavid du Colombier 			head.version, head.name, head.blocksize, head.size);
1719368c31abSDavid du Colombier 		if(head.clumpmagic)
1720368c31abSDavid du Colombier 			print(" clumpmagic=%#.8ux", head.clumpmagic);
1721368c31abSDavid du Colombier 		print("\n#\tclumps=%d cclumps=%d used=%,lld uncsize=%,lld\n",
1722368c31abSDavid du Colombier 			arena.diskstats.clumps, arena.diskstats.cclumps,
1723368c31abSDavid du Colombier 			arena.diskstats.used, arena.diskstats.uncsize);
1724368c31abSDavid du Colombier 		print("#\tctime=%t\n", arena.ctime);
1725368c31abSDavid du Colombier 		print("#\twtime=%t\n", arena.wtime);
1726368c31abSDavid du Colombier 		if(arena.diskstats.sealed)
1727368c31abSDavid du Colombier 			print("#\tsealed score=%V\n", score);
1728368c31abSDavid du Colombier 	}
1729368c31abSDavid du Colombier 
1730368c31abSDavid du Colombier 	if(dumpbase){
1731368c31abSDavid du Colombier 		dumparena(offset, anum, &arena);
1732368c31abSDavid du Colombier 		return;
1733368c31abSDavid du Colombier 	}
1734368c31abSDavid du Colombier 
1735368c31abSDavid du Colombier 	memset(dbuf, 0, sizeof dbuf);
1736368c31abSDavid du Colombier 	packarenahead(&head, dbuf);
1737368c31abSDavid du Colombier 	p = pagein(offset, arena.blocksize);
1738368c31abSDavid du Colombier 	if(memcmp(dbuf, p, arena.blocksize) != 0){
1739368c31abSDavid du Colombier 		print("on-disk arena header incorrect\n");
1740368c31abSDavid du Colombier 		showdiffs(dbuf, p, arena.blocksize,
1741368c31abSDavid du Colombier 			arena.version==ArenaVersion4 ? headinfo4 : headinfo5);
1742368c31abSDavid du Colombier 	}
1743368c31abSDavid du Colombier 	memmove(p, dbuf, arena.blocksize);
1744368c31abSDavid du Colombier 
1745368c31abSDavid du Colombier 	memset(dbuf, 0, sizeof dbuf);
1746368c31abSDavid du Colombier 	packarena(&arena, dbuf);
1747368c31abSDavid du Colombier 	if(arena.diskstats.sealed)
1748368c31abSDavid du Colombier 		scorecp(dbuf+arena.blocksize-VtScoreSize, score);
1749368c31abSDavid du Colombier 	p = pagein(offset+arena.size-arena.blocksize, arena.blocksize);
1750368c31abSDavid du Colombier 	memset(&oarena, 0, sizeof oarena);
1751368c31abSDavid du Colombier 	unpackarena(&oarena, p);
1752368c31abSDavid du Colombier 	if(arena.version == ArenaVersion4){
1753368c31abSDavid du Colombier 		sz = ArenaSize4;
1754368c31abSDavid du Colombier 		fmt = tailinfo4;
1755368c31abSDavid du Colombier 		fmta = tailinfo4a;
1756368c31abSDavid du Colombier 	}else{
1757368c31abSDavid du Colombier 		sz = ArenaSize5;
1758368c31abSDavid du Colombier 		fmt = tailinfo5;
1759368c31abSDavid du Colombier 		fmta = tailinfo5a;
1760368c31abSDavid du Colombier 	}
1761368c31abSDavid du Colombier 	if(p[sz] == 1){
1762368c31abSDavid du Colombier 		fmt = fmta;
1763368c31abSDavid du Colombier 		if(oarena.diskstats.sealed){
1764368c31abSDavid du Colombier 			/*
1765368c31abSDavid du Colombier 			 * some arenas were sealed with the extension
1766368c31abSDavid du Colombier 			 * before we adopted the convention that if it didn't
1767368c31abSDavid du Colombier 			 * add new information it gets dropped.
1768368c31abSDavid du Colombier 			 */
1769368c31abSDavid du Colombier 			_packarena(&arena, dbuf, 1);
1770368c31abSDavid du Colombier 		}
1771368c31abSDavid du Colombier 	}
1772368c31abSDavid du Colombier 	if(memcmp(dbuf, p, arena.blocksize-VtScoreSize) != 0){
1773368c31abSDavid du Colombier 		print("on-disk arena tail incorrect\n");
1774368c31abSDavid du Colombier 		showdiffs(dbuf, p, arena.blocksize-VtScoreSize, fmt);
1775368c31abSDavid du Colombier 	}
1776368c31abSDavid du Colombier 	if(arena.diskstats.sealed){
1777368c31abSDavid du Colombier 		if(oarena.diskstats.sealed)
1778368c31abSDavid du Colombier 		if(scorecmp(p+arena.blocksize-VtScoreSize, oldscore) != 0){
1779368c31abSDavid du Colombier 			print("on-disk arena seal score incorrect\n");
1780368c31abSDavid du Colombier 			print("\tcorrect=%V\n", oldscore);
1781368c31abSDavid du Colombier 			print("\t   disk=%V\n", p+arena.blocksize-VtScoreSize);
1782368c31abSDavid du Colombier 		}
1783368c31abSDavid du Colombier 		if(fix && scorecmp(p+arena.blocksize-VtScoreSize, score) != 0){
1784368c31abSDavid du Colombier 			print("%ssealing arena%s: %V\n",
1785368c31abSDavid du Colombier 				oarena.diskstats.sealed ? "re" : "",
1786368c31abSDavid du Colombier 				scorecmp(oldscore, score) == 0 ?
1787368c31abSDavid du Colombier 					"" : " after changes", score);
1788368c31abSDavid du Colombier 		}
1789368c31abSDavid du Colombier 	}
1790368c31abSDavid du Colombier 	memmove(p, dbuf, arena.blocksize);
1791368c31abSDavid du Colombier 
1792368c31abSDavid du Colombier 	pageout();
1793368c31abSDavid du Colombier }
1794368c31abSDavid du Colombier 
1795368c31abSDavid du Colombier AMapN*
buildamap(void)1796368c31abSDavid du Colombier buildamap(void)
1797368c31abSDavid du Colombier {
1798368c31abSDavid du Colombier 	uchar *p;
1799368c31abSDavid du Colombier 	vlong o;
1800368c31abSDavid du Colombier 	ArenaHead h;
1801368c31abSDavid du Colombier 	AMapN *an;
1802368c31abSDavid du Colombier 	AMap *m;
1803368c31abSDavid du Colombier 
1804368c31abSDavid du Colombier 	an = vtmallocz(sizeof *an);
1805368c31abSDavid du Colombier 	for(o=ap.arenabase; o<partend; o+=arenasize){
1806368c31abSDavid du Colombier 		p = pagein(o, Block);
1807368c31abSDavid du Colombier 		if(unpackarenahead(&h, p) >= 0){
1808368c31abSDavid du Colombier 			an->map = vtrealloc(an->map, (an->n+1)*sizeof an->map[0]);
1809368c31abSDavid du Colombier 			m = &an->map[an->n++];
1810368c31abSDavid du Colombier 			m->start = o;
1811368c31abSDavid du Colombier 			m->stop = o+h.size;
1812368c31abSDavid du Colombier 			strcpy(m->name, h.name);
1813368c31abSDavid du Colombier 		}
1814368c31abSDavid du Colombier 	}
1815368c31abSDavid du Colombier 	return an;
1816368c31abSDavid du Colombier }
1817368c31abSDavid du Colombier 
1818368c31abSDavid du Colombier void
checkmap(void)1819368c31abSDavid du Colombier checkmap(void)
1820368c31abSDavid du Colombier {
1821368c31abSDavid du Colombier 	char *s;
1822368c31abSDavid du Colombier 	uchar *p;
1823368c31abSDavid du Colombier 	int i, len;
1824368c31abSDavid du Colombier 	AMapN *an;
1825368c31abSDavid du Colombier 	Fmt fmt;
1826368c31abSDavid du Colombier 
1827368c31abSDavid du Colombier 	an = buildamap();
1828368c31abSDavid du Colombier 	fmtstrinit(&fmt);
1829368c31abSDavid du Colombier 	fmtprint(&fmt, "%ud\n", an->n);
1830368c31abSDavid du Colombier 	for(i=0; i<an->n; i++)
1831368c31abSDavid du Colombier 		fmtprint(&fmt, "%s\t%lld\t%lld\n",
1832368c31abSDavid du Colombier 			an->map[i].name, an->map[i].start, an->map[i].stop);
1833368c31abSDavid du Colombier 	s = fmtstrflush(&fmt);
1834368c31abSDavid du Colombier 	len = strlen(s);
1835368c31abSDavid du Colombier 	if(len > ap.tabsize){
1836368c31abSDavid du Colombier 		print("arena partition map too long: need %z bytes have %z\n",
1837368c31abSDavid du Colombier 			(vlong)len, (vlong)ap.tabsize);
1838368c31abSDavid du Colombier 		len = ap.tabsize;
1839368c31abSDavid du Colombier 	}
1840368c31abSDavid du Colombier 
1841368c31abSDavid du Colombier 	if(ap.tabsize >= 4*M){	/* can't happen - max arenas is 2000 */
1842368c31abSDavid du Colombier 		print("arena partition map *way* too long\n");
1843368c31abSDavid du Colombier 		return;
1844368c31abSDavid du Colombier 	}
1845368c31abSDavid du Colombier 
1846368c31abSDavid du Colombier 	p = pagein(ap.tabbase, ap.tabsize);
1847368c31abSDavid du Colombier 	if(memcmp(p, s, len) != 0){
1848368c31abSDavid du Colombier 		print("arena partition map incorrect; rewriting.\n");
1849368c31abSDavid du Colombier 		memmove(p, s, len);
1850368c31abSDavid du Colombier 	}
1851368c31abSDavid du Colombier 	pageout();
1852368c31abSDavid du Colombier }
1853368c31abSDavid du Colombier 
1854368c31abSDavid du Colombier int mainstacksize = 512*1024;
1855368c31abSDavid du Colombier 
1856368c31abSDavid du Colombier void
threadmain(int argc,char ** argv)1857368c31abSDavid du Colombier threadmain(int argc, char **argv)
1858368c31abSDavid du Colombier {
1859368c31abSDavid du Colombier 	int mode;
1860368c31abSDavid du Colombier 
1861368c31abSDavid du Colombier 	mode = OREAD;
1862368c31abSDavid du Colombier 	readonly = 1;
1863368c31abSDavid du Colombier 	ARGBEGIN{
1864368c31abSDavid du Colombier 	case 'U':
1865368c31abSDavid du Colombier 		unseal = 1;
1866368c31abSDavid du Colombier 		break;
1867368c31abSDavid du Colombier 	case 'a':
1868368c31abSDavid du Colombier 		arenasize = unittoull(EARGF(usage()));
1869368c31abSDavid du Colombier 		break;
1870368c31abSDavid du Colombier 	case 'b':
1871368c31abSDavid du Colombier 		ap.blocksize = unittoull(EARGF(usage()));
1872368c31abSDavid du Colombier 		break;
1873368c31abSDavid du Colombier 	case 'f':
1874368c31abSDavid du Colombier 		fix = 1;
1875368c31abSDavid du Colombier 		mode = ORDWR;
1876368c31abSDavid du Colombier 		readonly = 0;
1877368c31abSDavid du Colombier 		break;
1878368c31abSDavid du Colombier 	case 'n':
1879368c31abSDavid du Colombier 		basename = EARGF(usage());
1880368c31abSDavid du Colombier 		break;
1881368c31abSDavid du Colombier 	case 'v':
1882368c31abSDavid du Colombier 		verbose++;
1883368c31abSDavid du Colombier 		break;
1884368c31abSDavid du Colombier 	case 'x':
1885368c31abSDavid du Colombier 		dumpbase = EARGF(usage());
1886368c31abSDavid du Colombier 		break;
1887368c31abSDavid du Colombier 	default:
1888368c31abSDavid du Colombier 		usage();
1889368c31abSDavid du Colombier 	}ARGEND
1890368c31abSDavid du Colombier 
1891368c31abSDavid du Colombier 	if(argc != 1 && argc != 2)
1892368c31abSDavid du Colombier 		usage();
1893368c31abSDavid du Colombier 
1894368c31abSDavid du Colombier 	file = argv[0];
1895368c31abSDavid du Colombier 
1896368c31abSDavid du Colombier 	ventifmtinstall();
1897368c31abSDavid du Colombier 	fmtinstall('z', zfmt);
1898368c31abSDavid du Colombier 	fmtinstall('t', tfmt);
1899368c31abSDavid du Colombier 	quotefmtinstall();
1900368c31abSDavid du Colombier 
1901368c31abSDavid du Colombier 	part = initpart(file, mode|ODIRECT);
1902368c31abSDavid du Colombier 	if(part == nil)
1903368c31abSDavid du Colombier 		sysfatal("can't open %s: %r", file);
1904368c31abSDavid du Colombier 	partend = part->size;
1905368c31abSDavid du Colombier 
1906368c31abSDavid du Colombier 	if(isonearena()){
1907368c31abSDavid du Colombier 		checkarena(0, -1);
1908368c31abSDavid du Colombier 		threadexitsall(nil);
1909368c31abSDavid du Colombier 	}
1910368c31abSDavid du Colombier 	checkarenas(argc > 1 ? argv[1] : nil);
1911368c31abSDavid du Colombier 	checkmap();
1912368c31abSDavid du Colombier 	threadexitsall(nil);
1913368c31abSDavid du Colombier }
1914368c31abSDavid du Colombier 
1915