1 #include "lib9.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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 440 logfsbootfree(LogfsBoot *lb) 441 { 442 if(lb) { 443 logfsfreemem(lb->map); 444 logfsfreemem(lb); 445 } 446 } 447 448 char * 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 481 logfsbootgetiosize(LogfsBoot *lb) 482 { 483 return lb->blocksize; 484 } 485 486 long 487 logfsbootgetsize(LogfsBoot *lb) 488 { 489 return lb->size; 490 } 491 492 void 493 logfsboottrace(LogfsBoot *lb, int level) 494 { 495 lb->trace = level; 496 } 497