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