123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- #include "logfsos.h"
- #include "logfs.h"
- #include "local.h"
- struct LogfsBoot {
- LogfsLowLevel *ll;
- long bootblocks;
- long blocksize;
- long size;
- long *map;
- int trace;
- int printbad;
- // ulong bootpathmask;
- // int bootgenshift;
- };
- typedef struct LogfsBootPath LogfsBootPath;
- //#define LogfsBootGenBits 2
- //#define LogfsBootGenMask ((1 << LogfsBootGenBits) - 1)
- #define LogfsBootGenMask ((1 << L2BlockCopies) - 1)
- struct LogfsBootPath {
- ulong path;
- uchar gen;
- };
- #define LOGFSMKBOOTPATH(lb, p) mkdatapath((p)->path, (p)->gen)
- #define LOGFSSPLITBOOTPATHEX(bgs, bpm, p, v) ((p)->path = dataseqof(v), (p)->gen = copygenof(v))
- #define LOGFSSPLITBOOTPATH(lb, p, v) LOGFSSPLITBOOTPATHEX(0, 0, p, v)
- //#define LOGFSMKBOOTPATH(lb, p) (((p)->path & (lb)->bootpathmask) | (((p)->gen & LogfsBootGenMask) << (lb)->bootgenshift))
- //#define LOGFSSPLITBOOTPATHEX(bgs, bpm, p, v) ((p)->path = (v) & (bpm), (p)->gen = ((v) >> (bgs)) & LogfsBootGenMask)
- //#define LOGFSSPLITBOOTPATH(lb, p, v) LOGFSSPLITBOOTPATHEX((lb)->bootgenshift, (lb)->bootpathmask, p, v)
- extern LogfsBootPath logfsbooterasedpath;
- static char Ecorrupt[] = "filesystem corrupt";
- static char Enospc[] = "no free blocks";
- static char Eaddress[] = "address out of range";
- static char *
- logfsbootblockupdate(LogfsBoot *lb, void *buf, LogfsBootPath *path, uchar tag, ulong block)
- {
- LogfsLowLevel *ll = lb->ll;
- char *errmsg;
- ulong packedpath;
- if(lb->trace > 1)
- print("logfsbootblockupdate: path 0x%.8lux(%d) tag %s block %lud\n",
- path->path, path->gen, logfstagname(tag), block);
- packedpath = LOGFSMKBOOTPATH(lb, path);
- errmsg = (*ll->writeblock)(ll, buf, tag, packedpath, 1, &lb->bootblocks, block);
- if(errmsg) {
- /*
- * ensure block never used again until file system reinitialised
- * We have absolutely no idea what state it's in. This is most
- * likely if someone turns off the power (or at least threatens
- * the power supply), during a block update. This way the block
- * is protected until the file system in reinitialised. An alternative
- * would be check the file system after a power fail false alarm,
- * and erase any Tworse blocks
- */
- (*ll->setblocktag)(ll, block, LogfsTworse);
- return errmsg;
- }
-
- (*ll->setblocktag)(ll, block, tag);
- (*ll->setblockpath)(ll, block, packedpath);
- return nil;
- }
- char *
- logfsbootfettleblock(LogfsBoot *lb, long block, uchar tag, long path, int *markedbad)
- {
- LogfsLowLevel *ll = lb->ll;
- char *errmsg;
- void *llsave;
- errmsg = (*ll->eraseblock)(ll, block, &llsave, markedbad);
- if(errmsg || (markedbad && *markedbad)) {
- logfsfreemem(llsave);
- return errmsg;
- }
- errmsg = (*ll->reformatblock)(ll, block, tag, path, 1, &lb->bootblocks, llsave, markedbad);
- logfsfreemem(llsave);
- return errmsg;
- }
- /*
- * block transfer is the critical unit of update
- * we are going to assume that page writes and block erases are atomic
- * this can pretty much be assured by not starting a page write or block erase
- * if the device feels it is in power fail
- */
- static char *
- logfsbootblocktransfer(LogfsBoot *lb, void *buf, ulong oldblock, int markbad)
- {
- LogfsLowLevel *ll = lb->ll;
- long bestnewblock;
- ulong oldpackedpath;
- LogfsBootPath oldpath;
- short oldtag;
- char *errmsg;
- int markedbad;
- oldpackedpath = (*ll->getblockpath)(ll, oldblock);
- oldtag = (*ll->getblocktag)(ll, oldblock);
- LOGFSSPLITBOOTPATH(lb, &oldpath, oldpackedpath);
- for(;;) {
- LogfsBootPath newpath;
- bestnewblock = logfsfindfreeblock(ll, markbad ? AllocReasonReplace : AllocReasonTransfer);
- if(lb->trace > 0 && markbad)
- print("logfsbootblocktransfer: block %lud is bad, copying to %ld\n",
- oldblock, bestnewblock);
- if(lb->trace > 1 && !markbad)
- print("logfsbootblocktransfer: copying block %lud to %ld\n",
- oldblock, bestnewblock);
- if(bestnewblock == -1)
- return Enospc;
- newpath = oldpath;
- // newpath.gen = (newpath.gen + 1) & LogfsBootGenMask;
- newpath.gen = copygensucc(newpath.gen);
- errmsg = logfsbootblockupdate(lb, buf, &newpath, oldtag, bestnewblock);
- if(errmsg == nil)
- break;
- if(strcmp(errmsg, Eio) != 0)
- return errmsg;
- (*ll->markblockbad)(ll, bestnewblock);
- }
- #ifdef LOGFSTEST
- if(logfstest.partialupdate) {
- print("skipping erase\n");
- logfstest.partialupdate = 0;
- return nil;
- }
- if(logfstest.updatenoerase) {
- print("skipping erase\n");
- logfstest.updatenoerase = 0;
- return nil;
- }
- #endif
- if(oldtag == LogfsTboot)
- lb->map[oldpath.path] = bestnewblock;
- return logfsbootfettleblock(lb, oldblock, LogfsTnone, ~0, &markedbad);
- }
- static char *
- logfsbootblockread(LogfsBoot *lb, void *buf, long block, LogfsLowLevelReadResult *blocke)
- {
- LogfsLowLevel *ll = lb->ll;
- char *errmsg;
- *blocke = LogfsLowLevelReadResultOk;
- errmsg = (*ll->readblock)(ll, buf, block, blocke);
- if(errmsg)
- return errmsg;
- if(*blocke != LogfsLowLevelReadResultOk) {
- char *errmsg = logfsbootblocktransfer(lb, buf, block, 1);
- if(errmsg)
- return errmsg;
- }
- if(*blocke == LogfsLowLevelReadResultHardError)
- return Eio;
- return nil;
- }
- char *
- logfsbootread(LogfsBoot *lb, void *buf, long n, ulong offset)
- {
- int i;
- if(lb->trace > 0)
- print("logfsbootread(0x%.8lux, 0x%lx, 0x%lux)\n", (ulong)buf, n, offset);
- if(offset % lb->blocksize || n % lb->blocksize)
- return Eio;
- n /= lb->blocksize;
- offset /= lb->blocksize;
- if(offset + n > lb->bootblocks)
- return Eio;
- for(i = 0; i < n; i++) {
- LogfsLowLevelReadResult result;
- char *errmsg = logfsbootblockread(lb, buf, lb->map[offset + i], &result);
- if(errmsg)
- return errmsg;
- buf = (uchar *)buf + lb->blocksize;
- }
- return nil;
- }
- static char *
- logfsbootblockreplace(LogfsBoot *lb, void *buf, ulong logicalblock)
- {
- uchar *oldblockbuf;
- ulong oldblock;
- char *errmsg;
- LogfsLowLevelReadResult result;
- oldblock = lb->map[logicalblock];
- oldblockbuf = logfsrealloc(nil, lb->blocksize);
- if(oldblockbuf == nil)
- return Enomem;
- errmsg = logfsbootblockread(lb, oldblockbuf, oldblock, &result);
- if(errmsg == nil && memcmp(oldblockbuf, buf, lb->blocksize) != 0)
- errmsg = logfsbootblocktransfer(lb, buf, oldblock, 0);
- logfsfreemem(oldblockbuf);
- return errmsg;
- }
- char *
- logfsbootwrite(LogfsBoot *lb, void *buf, long n, ulong offset)
- {
- int i;
- if(lb->trace > 0)
- print("logfsbootwrite(0x%.8lux, 0x%lux, 0x%lux)\n", (ulong)buf, n, offset);
- /*
- * don't even get started on a write if the power has failed
- */
- if(offset % lb->blocksize || n % lb->blocksize)
- return Eio;
- n /= lb->blocksize;
- offset /= lb->blocksize;
- if(offset + n > lb->bootblocks)
- return Eio;
- for(i = 0; i < n; i++) {
- logfsbootblockreplace(lb, buf, offset + i);
- buf = (uchar *)buf + lb->blocksize;
- }
- return nil;
- }
- char *
- logfsbootio(LogfsBoot *lb, void *buf, long n, ulong offset, int write)
- {
- return (write ? logfsbootwrite : logfsbootread)(lb, buf, n, offset);
- }
- static char *
- eraseandformatblock(LogfsBoot *lb, long block, int trace)
- {
- char *errmsg;
- int markedbad;
- errmsg = logfsbootfettleblock(lb, block, LogfsTnone, ~0, &markedbad);
- if(errmsg)
- return errmsg;
- if(markedbad && trace > 1)
- print("erase/format failed - marked bad\n");
- return nil;
- }
- char *
- logfsbootopen(LogfsLowLevel *ll, long base, long limit, int trace, int printbad, LogfsBoot **lbp)
- {
- long *reversemap;
- ulong blocksize;
- ulong blocks;
- long i;
- long bootblockmax;
- LogfsBoot *lb = nil;
- ulong baseblock;
- char *errmsg;
- // int bootgenshift = ll->pathbits- LogfsBootGenBits;
- // ulong bootpathmask = (1 << (ll->pathbits - LogfsBootGenBits)) - 1;
- long expectedbootblocks;
- errmsg = (*ll->open)(ll, base, limit, trace, 1, &expectedbootblocks);
- if(errmsg)
- return errmsg;
- bootblockmax = -1;
- blocks = ll->blocks;
- baseblock = (*ll->getbaseblock)(ll);
- blocksize = (*ll->getblocksize)(ll);
- for(i = 0; i < blocks; i++) {
- if((*ll->getblocktag)(ll, i) == LogfsTboot) {
- long path = (*ll->getblockpath)(ll, i);
- LogfsBootPath lp;
- LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &lp, path);
- if((long)lp.path > bootblockmax)
- bootblockmax = lp.path;
- }
- }
- if(bootblockmax + 1 >= blocks) {
- if(printbad)
- print("logfsbootinit: bootblockmax %ld exceeds number of blocks\n", bootblockmax);
- return Ecorrupt;
- }
- if(bootblockmax < 0) {
- if(printbad)
- print("logfsbootopen: no boot area\n");
- return Ecorrupt;
- }
- if(bootblockmax + 1 != expectedbootblocks) {
- if(printbad)
- print("logfsbootopen: wrong number of bootblocks (found %lud, expected %lud)\n",
- bootblockmax + 1, expectedbootblocks);
- }
-
- reversemap = logfsrealloc(nil, sizeof(*reversemap) * (bootblockmax + 1));
- if(reversemap == nil)
- return Enomem;
- for(i = 0; i <= bootblockmax; i++)
- reversemap[i] = -1;
- for(i = 0; i < blocks; i++) {
- LogfsBootPath ipath;
- long rm;
- ulong ip;
- if((*ll->getblocktag)(ll, i) != LogfsTboot)
- continue;
- ip = (*ll->getblockpath)(ll, i);
- LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &ipath, ip);
- rm = reversemap[ipath.path];
- if(rm != -1) {
- if(printbad)
- print("logfsbootopen: blockaddr 0x%.8lux: path %ld(%d): duplicate\n",
- blocksize * (baseblock + i), ipath.path, ipath.gen);
- /*
- * resolve collision
- * if this one is partial, then erase it
- * if the existing one is partial, erase that
- * if both valid, give up
- */
- if((*ll->getblockpartialformatstatus)(ll, i)) {
- errmsg = eraseandformatblock(lb, i, trace);
- if(errmsg)
- goto error;
- }
- else if((*ll->getblockpartialformatstatus)(ll, rm)) {
- errmsg = eraseandformatblock(lb, rm, trace);
- if(errmsg)
- goto error;
- reversemap[ipath.path] = i;
- }
- else {
- int d;
- ulong rmp;
- LogfsBootPath rmpath;
- rmp = (*ll->getblockpath)(ll, rm);
- LOGFSSPLITBOOTPATHEX(bootgenshift, bootpathmask, &rmpath, rmp);
- d = (ipath.gen - rmpath.gen) & LogfsBootGenMask;
- if(printbad)
- print("i.gen = %d rm.gen = %d d = %d\n", ipath.gen, rmpath.gen, d);
- if(d == 1) {
- /* i is newer;
- * keep the OLDER one because
- * we might have had a write failure on the last page, but lost the
- * power before being able to mark the first page bad
- * if, worse, the auxiliary area's tag is the same for first and last page,
- * this looks like a successfully written page. so, we cannot believe the
- * data in the newer block unless we erased the old one, and then of
- * course, we wouldn't have a duplicate.
- */
- errmsg = eraseandformatblock(lb, i, trace);
- if(errmsg)
- goto error;
- }
- else if(d == LogfsBootGenMask) {
- /* rm is newer */
- errmsg = eraseandformatblock(lb, rm, trace);
- if(errmsg)
- goto error;
- reversemap[ipath.path] = i;
- }
- else {
- errmsg = Ecorrupt;
- goto error;
- }
- }
- }
- else
- reversemap[ipath.path] = i;
- }
- /*
- * final checks; not partial blocks, and no holes
- */
- for(i = 0; i <= bootblockmax; i++) {
- long rm;
- rm = reversemap[i];
- if(rm == -1) {
- if(printbad)
- print("logfsbootopen: missing boot block %ld\n", i);
- errmsg = Ecorrupt;
- goto error;
- }
- if((*ll->getblockpartialformatstatus)(ll, rm)) {
- if(printbad)
- print("logfsbootopen: boot block %ld partially written\n", rm);
- errmsg = Ecorrupt;
- goto error;
- }
- }
- /* the reverse map is consistent */
- lb = logfsrealloc(nil, sizeof(*lb));
- if(lb == nil) {
- errmsg = Enomem;
- goto error;
- }
- lb->blocksize = blocksize;
- lb->bootblocks = bootblockmax + 1;
- lb->map = reversemap;
- lb->trace = trace;
- lb->printbad = printbad;
- lb->ll = ll;
- lb->size = blocksize * lb->bootblocks;
- // lb->bootgenshift = bootgenshift;
- // lb->bootpathmask = bootpathmask;
- *lbp = lb;
- if(trace)
- print("logfsbootopen: success\n");
- return nil;
- error:
- logfsfreemem(reversemap);
- logfsfreemem(lb);
- return errmsg;
- }
- void
- logfsbootfree(LogfsBoot *lb)
- {
- if(lb) {
- logfsfreemem(lb->map);
- logfsfreemem(lb);
- }
- }
- char *
- logfsbootmap(LogfsBoot *lb, ulong laddress, ulong *lblockp, int *lboffsetp, int *lpagep, int *lpageoffsetp, ulong *pblockp, ulong *paddressp)
- {
- LogfsLowLevel *ll = lb->ll;
- ulong lblock;
- ulong lboffset, lpageoffset, lpage;
- ulong pblock;
- ulong paddress;
- lblock = laddress / lb->blocksize;
- if(lblock >= lb->bootblocks)
- return Eaddress;
- lboffset = laddress % lb->blocksize;
- pblock = lb->map[lblock];
- paddress = (*ll->calcrawaddress)(ll, pblock, lboffset);
- lpage = lboffset >> ll->l2pagesize;
- lpageoffset = lboffset & ((1 << ll->l2pagesize) - 1);
- if(lblockp)
- *lblockp = lblock;
- if(lboffsetp)
- *lboffsetp = lboffset;
- if(lpagep)
- *lpagep = lpage;
- if(lpageoffsetp)
- *lpageoffsetp = lpageoffset;
- if(pblockp)
- *pblockp = pblock;
- if(paddressp)
- *paddressp = paddress;
- return nil;
- }
- long
- logfsbootgetiosize(LogfsBoot *lb)
- {
- return lb->blocksize;
- }
- long
- logfsbootgetsize(LogfsBoot *lb)
- {
- return lb->size;
- }
- void
- logfsboottrace(LogfsBoot *lb, int level)
- {
- lb->trace = level;
- }
|