xref: /inferno-os/liblogfs/boot.c (revision 28942ead413418b56c5be78e8c4c400881fba72e)
1 #include "logfsos.h"
2 #include "logfs.h"
3 #include "local.h"
4 
5 struct LogfsBoot {
6 	LogfsLowLevel *ll;
7 	long bootblocks;
8 	long blocksize;
9 	long size;
10 	long *map;
11 	int trace;
12 	int printbad;
13 //	ulong bootpathmask;
14 //	int bootgenshift;
15 };
16 
17 typedef struct LogfsBootPath LogfsBootPath;
18 
19 //#define LogfsBootGenBits 2
20 //#define LogfsBootGenMask ((1 << LogfsBootGenBits) - 1)
21 #define LogfsBootGenMask ((1 << L2BlockCopies) - 1)
22 
23 struct LogfsBootPath {
24 	ulong path;
25 	uchar gen;
26 };
27 
28 #define LOGFSMKBOOTPATH(lb, p) mkdatapath((p)->path, (p)->gen)
29 #define LOGFSSPLITBOOTPATHEX(bgs, bpm, p, v) ((p)->path = dataseqof(v), (p)->gen = copygenof(v))
30 #define LOGFSSPLITBOOTPATH(lb, p, v) LOGFSSPLITBOOTPATHEX(0, 0, p, v)
31 
32 //#define LOGFSMKBOOTPATH(lb, p) (((p)->path & (lb)->bootpathmask) | (((p)->gen & LogfsBootGenMask) << (lb)->bootgenshift))
33 //#define LOGFSSPLITBOOTPATHEX(bgs, bpm, p, v) ((p)->path = (v) & (bpm), (p)->gen = ((v) >> (bgs)) & LogfsBootGenMask)
34 //#define LOGFSSPLITBOOTPATH(lb, p, v) LOGFSSPLITBOOTPATHEX((lb)->bootgenshift, (lb)->bootpathmask, p, v)
35 
36 extern LogfsBootPath logfsbooterasedpath;
37 
38 static char Ecorrupt[] = "filesystem corrupt";
39 static char Enospc[] = "no free blocks";
40 static char Eaddress[] = "address out of range";
41 
42 static char *
logfsbootblockupdate(LogfsBoot * lb,void * buf,LogfsBootPath * path,uchar tag,ulong block)43 logfsbootblockupdate(LogfsBoot *lb, void *buf, LogfsBootPath *path, uchar tag, ulong block)
44 {
45 	LogfsLowLevel *ll =  lb->ll;
46 	char *errmsg;
47 	ulong packedpath;
48 
49 	if(lb->trace > 1)
50 		print("logfsbootblockupdate: path 0x%.8lux(%d) tag %s block %lud\n",
51 		    path->path, path->gen, logfstagname(tag), block);
52 
53 	packedpath = LOGFSMKBOOTPATH(lb, path);
54 	errmsg = (*ll->writeblock)(ll, buf, tag, packedpath, 1, &lb->bootblocks, block);
55 
56 	if(errmsg) {
57 		/*
58 		 * ensure block never used again until file system reinitialised
59 		 * We have absolutely no idea what state it's in. This is most
60 		 * likely if someone turns off the power (or at least threatens
61 		 * the power supply), during a block update. This way the block
62 		 * is protected until the file system in reinitialised. An alternative
63 		 * would be check the file system after a power fail false alarm,
64 		 * and erase any Tworse blocks
65 		 */
66 		(*ll->setblocktag)(ll, block, LogfsTworse);
67 		return errmsg;
68 	}
69 
70 	(*ll->setblocktag)(ll, block, tag);
71 	(*ll->setblockpath)(ll, block, packedpath);
72 
73 	return nil;
74 }
75 
76 char *
logfsbootfettleblock(LogfsBoot * lb,long block,uchar tag,long path,int * markedbad)77 logfsbootfettleblock(LogfsBoot *lb, long block, uchar tag, long path, int *markedbad)
78 {
79 	LogfsLowLevel *ll = lb->ll;
80 	char *errmsg;
81 	void *llsave;
82 
83 	errmsg = (*ll->eraseblock)(ll, block, &llsave, markedbad);
84 	if(errmsg || (markedbad && *markedbad)) {
85 		logfsfreemem(llsave);
86 		return errmsg;
87 	}
88 	errmsg = (*ll->reformatblock)(ll, block, tag, path, 1, &lb->bootblocks, llsave, markedbad);
89 	logfsfreemem(llsave);
90 	return errmsg;
91 }
92 
93 /*
94  * block transfer is the critical unit of update
95  * we are going to assume that page writes and block erases are atomic
96  * this can pretty much be assured by not starting a page write or block erase
97  * if the device feels it is in power fail
98  */
99 
100 static char *
logfsbootblocktransfer(LogfsBoot * lb,void * buf,ulong oldblock,int markbad)101 logfsbootblocktransfer(LogfsBoot *lb, void *buf, ulong oldblock, int markbad)
102 {
103 	LogfsLowLevel *ll = lb->ll;
104 	long bestnewblock;
105 	ulong oldpackedpath;
106 	LogfsBootPath oldpath;
107 	short oldtag;
108 	char *errmsg;
109 	int markedbad;
110 
111 	oldpackedpath = (*ll->getblockpath)(ll, oldblock);
112 	oldtag = (*ll->getblocktag)(ll, oldblock);
113 
114 	LOGFSSPLITBOOTPATH(lb, &oldpath, oldpackedpath);
115 
116 	for(;;) {
117 		LogfsBootPath newpath;
118 
119 		bestnewblock = logfsfindfreeblock(ll, markbad ? AllocReasonReplace : AllocReasonTransfer);
120 		if(lb->trace > 0 && markbad)
121 			print("logfsbootblocktransfer: block %lud is bad, copying to %ld\n",
122 				oldblock, bestnewblock);
123 		if(lb->trace > 1 && !markbad)
124 			print("logfsbootblocktransfer: copying block %lud to %ld\n",
125 				oldblock, bestnewblock);
126 		if(bestnewblock == -1)
127 			return Enospc;
128 		newpath = oldpath;
129 //		newpath.gen = (newpath.gen + 1) & LogfsBootGenMask;
130 		newpath.gen = copygensucc(newpath.gen);
131 		errmsg = logfsbootblockupdate(lb, buf, &newpath, oldtag, bestnewblock);
132 		if(errmsg == nil)
133 			break;
134 		if(strcmp(errmsg, Eio) != 0)
135 			return errmsg;
136 		(*ll->markblockbad)(ll, bestnewblock);
137 	}
138 
139 #ifdef LOGFSTEST
140 	if(logfstest.partialupdate) {
141 		print("skipping erase\n");
142 		logfstest.partialupdate = 0;
143 		return nil;
144 	}
145 	if(logfstest.updatenoerase) {
146 		print("skipping erase\n");
147 		logfstest.updatenoerase = 0;
148 		return nil;
149 	}
150 #endif
151 
152 	if(oldtag == LogfsTboot)
153 		lb->map[oldpath.path] = bestnewblock;
154 
155 	return logfsbootfettleblock(lb, oldblock, LogfsTnone, ~0, &markedbad);
156 }
157 
158 static char *
logfsbootblockread(LogfsBoot * lb,void * buf,long block,LogfsLowLevelReadResult * blocke)159 logfsbootblockread(LogfsBoot *lb, void *buf, long block, LogfsLowLevelReadResult *blocke)
160 {
161 	LogfsLowLevel *ll = lb->ll;
162 	char *errmsg;
163 
164 	*blocke = LogfsLowLevelReadResultOk;
165 	errmsg = (*ll->readblock)(ll, buf, block, blocke);
166 	if(errmsg)
167 		return errmsg;
168 
169 	if(*blocke != LogfsLowLevelReadResultOk) {
170 		char *errmsg = logfsbootblocktransfer(lb, buf, block, 1);
171 		if(errmsg)
172 			return errmsg;
173 	}
174 
175 	if(*blocke == LogfsLowLevelReadResultHardError)
176 		return Eio;
177 
178 	return nil;
179 }
180 
181 char *
logfsbootread(LogfsBoot * lb,void * buf,long n,ulong offset)182 logfsbootread(LogfsBoot *lb, void *buf, long n, ulong offset)
183 {
184 	int i;
185 
186 	if(lb->trace > 0)
187 		print("logfsbootread(0x%.8lux, 0x%lx, 0x%lux)\n", (ulong)buf, n, offset);
188 	if(offset % lb->blocksize || n % lb->blocksize)
189 		return Eio;
190 	n /= lb->blocksize;
191 	offset /= lb->blocksize;
192 	if(offset + n > lb->bootblocks)
193 		return Eio;
194 	for(i = 0; i < n; i++) {
195 		LogfsLowLevelReadResult result;
196 		char *errmsg = logfsbootblockread(lb, buf, lb->map[offset + i], &result);
197 		if(errmsg)
198 			return errmsg;
199 		buf = (uchar *)buf + lb->blocksize;
200 	}
201 	return nil;
202 }
203 
204 static char *
logfsbootblockreplace(LogfsBoot * lb,void * buf,ulong logicalblock)205 logfsbootblockreplace(LogfsBoot *lb, void *buf, ulong logicalblock)
206 {
207 	uchar *oldblockbuf;
208 	ulong oldblock;
209 	char *errmsg;
210 	LogfsLowLevelReadResult result;
211 
212 	oldblock = lb->map[logicalblock];
213 	oldblockbuf = logfsrealloc(nil, lb->blocksize);
214 	if(oldblockbuf == nil)
215 		return Enomem;
216 
217 	errmsg = logfsbootblockread(lb, oldblockbuf, oldblock, &result);
218 	if(errmsg == nil && memcmp(oldblockbuf, buf, lb->blocksize) != 0)
219 		errmsg = logfsbootblocktransfer(lb, buf, oldblock, 0);
220 
221 	logfsfreemem(oldblockbuf);
222 	return errmsg;
223 }
224 
225 char *
logfsbootwrite(LogfsBoot * lb,void * buf,long n,ulong offset)226 logfsbootwrite(LogfsBoot *lb, void *buf, long n, ulong offset)
227 {
228 	int i;
229 
230 	if(lb->trace > 0)
231 		print("logfsbootwrite(0x%.8lux, 0x%lux, 0x%lux)\n", (ulong)buf, n, offset);
232 	/*
233 	 * don't even get started on a write if the power has failed
234 	 */
235 	if(offset % lb->blocksize || n % lb->blocksize)
236 		return Eio;
237 	n /= lb->blocksize;
238 	offset /= lb->blocksize;
239 	if(offset + n > lb->bootblocks)
240 		return Eio;
241 	for(i = 0; i < n; i++) {
242 		logfsbootblockreplace(lb, buf, offset + i);
243 		buf = (uchar *)buf + lb->blocksize;
244 	}
245 	return nil;
246 }
247 
248 char *
logfsbootio(LogfsBoot * lb,void * buf,long n,ulong offset,int write)249 logfsbootio(LogfsBoot *lb, void *buf, long n, ulong offset, int write)
250 {
251 	return (write ? logfsbootwrite : logfsbootread)(lb, buf, n, offset);
252 }
253 
254 static char *
eraseandformatblock(LogfsBoot * lb,long block,int trace)255 eraseandformatblock(LogfsBoot *lb, long block, int trace)
256 {
257 	char *errmsg;
258 	int markedbad;
259 
260 	errmsg = logfsbootfettleblock(lb, block, LogfsTnone, ~0, &markedbad);
261 	if(errmsg)
262 		return errmsg;
263 	if(markedbad && trace > 1)
264 		print("erase/format failed - marked bad\n");
265 	return nil;
266 }
267 
268 char *
logfsbootopen(LogfsLowLevel * ll,long base,long limit,int trace,int printbad,LogfsBoot ** lbp)269 logfsbootopen(LogfsLowLevel *ll, long base, long limit, int trace, int printbad, LogfsBoot **lbp)
270 {
271 	long *reversemap;
272 	ulong blocksize;
273 	ulong blocks;
274 	long i;
275 	long bootblockmax;
276 	LogfsBoot *lb = nil;
277 	ulong baseblock;
278 	char *errmsg;
279 //	int bootgenshift = ll->pathbits- LogfsBootGenBits;
280 //	ulong bootpathmask = (1 << (ll->pathbits - LogfsBootGenBits)) - 1;
281 	long expectedbootblocks;
282 
283 	errmsg = (*ll->open)(ll, base, limit, trace, 1, &expectedbootblocks);
284 	if(errmsg)
285 		return errmsg;
286 
287 	bootblockmax = -1;
288 	blocks = ll->blocks;
289 	baseblock = (*ll->getbaseblock)(ll);
290 	blocksize = (*ll->getblocksize)(ll);
291 
292 	for(i = 0; i < blocks; i++) {
293 		if((*ll->getblocktag)(ll, i) == LogfsTboot) {
294 			long path = (*ll->getblockpath)(ll, i);
295 			LogfsBootPath lp;
296 			LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &lp, path);
297 			if((long)lp.path > bootblockmax)
298 				bootblockmax = lp.path;
299 		}
300 	}
301 	if(bootblockmax + 1 >= blocks) {
302 		if(printbad)
303 			print("logfsbootinit: bootblockmax %ld exceeds number of blocks\n", bootblockmax);
304 		return Ecorrupt;
305 	}
306 	if(bootblockmax < 0) {
307 		if(printbad)
308 			print("logfsbootopen: no boot area\n");
309 		return Ecorrupt;
310 	}
311 	if(bootblockmax + 1 != expectedbootblocks) {
312 		if(printbad)
313 			print("logfsbootopen: wrong number of bootblocks (found %lud, expected %lud)\n",
314 				bootblockmax + 1, expectedbootblocks);
315 	}
316 
317 	reversemap = logfsrealloc(nil, sizeof(*reversemap) * (bootblockmax + 1));
318 	if(reversemap == nil)
319 		return Enomem;
320 
321 	for(i = 0; i <= bootblockmax; i++)
322 		reversemap[i] = -1;
323 	for(i = 0; i < blocks; i++) {
324 		LogfsBootPath ipath;
325 		long rm;
326 		ulong ip;
327 
328 		if((*ll->getblocktag)(ll, i) != LogfsTboot)
329 			continue;
330 		ip = (*ll->getblockpath)(ll, i);
331 		LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &ipath, ip);
332 		rm = reversemap[ipath.path];
333 		if(rm != -1) {
334 			if(printbad)
335 				print("logfsbootopen: blockaddr 0x%.8lux: path %ld(%d): duplicate\n",
336 					blocksize * (baseblock + i), ipath.path, ipath.gen);
337 			/*
338 			 * resolve collision
339 			 * if this one is partial, then erase it
340 			 * if the existing one is partial, erase that
341 			 * if both valid, give up
342 			 */
343 			if((*ll->getblockpartialformatstatus)(ll, i)) {
344 				errmsg = eraseandformatblock(lb, i, trace);
345 				if(errmsg)
346 					goto error;
347 			}
348 			else if((*ll->getblockpartialformatstatus)(ll, rm)) {
349 				errmsg = eraseandformatblock(lb, rm, trace);
350 				if(errmsg)
351 					goto error;
352 				reversemap[ipath.path] = i;
353 			}
354 			else {
355 				int d;
356 				ulong rmp;
357 				LogfsBootPath rmpath;
358 				rmp = (*ll->getblockpath)(ll, rm);
359 				LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &rmpath, rmp);
360 				d = (ipath.gen - rmpath.gen) & LogfsBootGenMask;
361 				if(printbad)
362 					print("i.gen = %d rm.gen = %d d = %d\n", ipath.gen, rmpath.gen, d);
363 				if(d == 1) {
364 					/* i is newer;
365 					 * keep the OLDER one because
366 					 * we might have had a write failure on the last page, but lost the
367 					 * power before being able to mark the first page bad
368 					 * if, worse, the auxiliary area's tag is the same for first and last page,
369 					 * this looks like a successfully written page. so, we cannot believe the
370 					 * data in the newer block unless we erased the old one, and then of
371 					 * course, we wouldn't have a duplicate.
372 					 */
373 					errmsg = eraseandformatblock(lb, i, trace);
374 					if(errmsg)
375 						goto error;
376 				}
377 				else if(d == LogfsBootGenMask) {
378 					/* rm is newer */
379 					errmsg = eraseandformatblock(lb, rm, trace);
380 					if(errmsg)
381 						goto error;
382 					reversemap[ipath.path] = i;
383 				}
384 				else {
385 					errmsg = Ecorrupt;
386 					goto error;
387 				}
388 			}
389 		}
390 		else
391 			reversemap[ipath.path] = i;
392 	}
393 	/*
394 	 * final checks; not partial blocks, and no holes
395 	 */
396 	for(i = 0; i <= bootblockmax; i++) {
397 		long rm;
398 		rm = reversemap[i];
399 		if(rm == -1) {
400 			if(printbad)
401 				print("logfsbootopen: missing boot block %ld\n", i);
402 			errmsg = Ecorrupt;
403 			goto error;
404 		}
405 		if((*ll->getblockpartialformatstatus)(ll, rm)) {
406 			if(printbad)
407 				print("logfsbootopen: boot block %ld partially written\n", rm);
408 			errmsg = Ecorrupt;
409 			goto error;
410 		}
411 	}
412 	/* the reverse map is consistent */
413 	lb = logfsrealloc(nil, sizeof(*lb));
414 	if(lb == nil) {
415 		errmsg = Enomem;
416 		goto error;
417 	}
418 
419 	lb->blocksize = blocksize;
420 	lb->bootblocks = bootblockmax + 1;
421 	lb->map = reversemap;
422 	lb->trace = trace;
423 	lb->printbad = printbad;
424 	lb->ll = ll;
425 	lb->size = blocksize * lb->bootblocks;
426 //	lb->bootgenshift = bootgenshift;
427 //	lb->bootpathmask = bootpathmask;
428 	*lbp = lb;
429 	if(trace)
430 		print("logfsbootopen: success\n");
431 	return nil;
432 
433 error:
434 	logfsfreemem(reversemap);
435 	logfsfreemem(lb);
436 	return errmsg;
437 }
438 
439 void
logfsbootfree(LogfsBoot * lb)440 logfsbootfree(LogfsBoot *lb)
441 {
442 	if(lb) {
443 		logfsfreemem(lb->map);
444 		logfsfreemem(lb);
445 	}
446 }
447 
448 char *
logfsbootmap(LogfsBoot * lb,ulong laddress,ulong * lblockp,int * lboffsetp,int * lpagep,int * lpageoffsetp,ulong * pblockp,ulong * paddressp)449 logfsbootmap(LogfsBoot *lb, ulong laddress, ulong *lblockp, int *lboffsetp, int *lpagep, int *lpageoffsetp, ulong *pblockp, ulong *paddressp)
450 {
451 	LogfsLowLevel *ll = lb->ll;
452 	ulong lblock;
453 	ulong lboffset, lpageoffset, lpage;
454 	ulong pblock;
455 	ulong paddress;
456 
457 	lblock = laddress / lb->blocksize;
458 	if(lblock >= lb->bootblocks)
459 		return Eaddress;
460 	lboffset = laddress % lb->blocksize;
461 	pblock = lb->map[lblock];
462 	paddress = (*ll->calcrawaddress)(ll, pblock, lboffset);
463 	lpage = lboffset >>  ll->l2pagesize;
464 	lpageoffset = lboffset & ((1 << ll->l2pagesize) - 1);
465 	if(lblockp)
466 		*lblockp = lblock;
467 	if(lboffsetp)
468 		*lboffsetp = lboffset;
469 	if(lpagep)
470 		*lpagep = lpage;
471 	if(lpageoffsetp)
472 		*lpageoffsetp = lpageoffset;
473 	if(pblockp)
474 		*pblockp = pblock;
475 	if(paddressp)
476 		*paddressp = paddress;
477 	return nil;
478 }
479 
480 long
logfsbootgetiosize(LogfsBoot * lb)481 logfsbootgetiosize(LogfsBoot *lb)
482 {
483 	return lb->blocksize;
484 }
485 
486 long
logfsbootgetsize(LogfsBoot * lb)487 logfsbootgetsize(LogfsBoot *lb)
488 {
489 	return lb->size;
490 }
491 
492 void
logfsboottrace(LogfsBoot * lb,int level)493 logfsboottrace(LogfsBoot *lb, int level)
494 {
495 	lb->trace = level;
496 }
497