#include "logfsos.h" #include "logfs.h" #include "local.h" #include "fcall.h" static void maxpath(LogfsServer *server, ulong p) { if(p > server->path) server->path = p; } static char * recreate(LogfsServer *server, LogMessage *s, int *ok) { Entry *parent; char *errmsg; Entry *e; Path *p; parent = logfspathmapfinde(server->pathmap, s->path); if(parent == nil) return "can't find parent"; if(logfspathmapfindentry(server->pathmap, s->u.create.newpath) != nil){ Entry *d = logfspathmapfinde(server->pathmap, s->u.create.newpath); if(d == nil) print("existing was nil\n"); else{ print("existing: name=%q d=%d path=%8.8llux uid=%q gid=%q perm=%#uo\n", d->name, d->deadandgone, d->qid.path, d->uid, d->gid, d->perm); } return "duplicate path"; } if((parent->qid.type & QTDIR) == 0) return Enotdir; errmsg = logfsentrynew(server, 1, s->u.create.newpath, parent, s->u.create.name, s->u.create.uid, s->u.create.gid, s->u.create.mtime, s->u.create.uid, s->u.create.perm, s->u.create.cvers, 0, &e); if(errmsg) { *ok = 0; return errmsg; } /* p guaranteed to be non null */ errmsg = logfspathmapnewentry(server->pathmap, s->u.create.newpath, e, &p); if(errmsg) { logfsfreemem(e); *ok = 0; return errmsg; } e->next = parent->u.dir.list; parent->u.dir.list = e; return nil; } static char * reremove(LogfsServer *server, LogMessage *s, int *ok) { Entry *oe; Entry *parent; Entry **ep; Entry *e; char *ustmuid; USED(ok); oe = logfspathmapfinde(server->pathmap, s->path); if(oe == nil) return logfseunknownpath; parent = oe->parent; if(parent == oe) return "tried to remove root"; if((parent->qid.type & QTDIR) == 0) return Enotdir; if((oe->qid.type & QTDIR) != 0 && oe->u.dir.list) return logfsenotempty; for(ep = &parent->u.dir.list; e = *ep; ep = &e->next) if(e == oe) break; if(e == nil) return logfseinternal; ustmuid = logfsisustadd(server->is, s->u.remove.muid); if(ustmuid == nil) return Enomem; parent->mtime = s->u.remove.mtime; parent->muid = ustmuid; logfspathmapdeleteentry(server->pathmap, s->path); *ep = e->next; if(e->inuse > 1) { print("replay: entry inuse > 1\n"); e->inuse = 1; } logfsentryclunk(e); return nil; } static char * retrunc(LogfsServer *server, LogMessage *s, int *ok) { Entry *e; char *ustmuid; USED(ok); e = logfspathmapfinde(server->pathmap, s->path); if(e == nil) return logfseunknownpath; if((e->qid.type & QTDIR) != 0) return Eperm; if(e->u.file.cvers >= s->u.trunc.cvers) return "old news"; ustmuid = logfsisustadd(server->is, s->u.trunc.muid); if(ustmuid == nil) return Enomem; e->muid = ustmuid; e->mtime = s->u.trunc.mtime; e->qid.vers++; e->u.file.cvers = s->u.trunc.cvers; /* * zap all extents */ logfsextentlistreset(e->u.file.extent); e->u.file.length = 0; return nil; } static char * rewrite(LogfsServer *server, LogMessage *s, int *ok) { Entry *e; char *ustmuid; Extent extent; char *errmsg; USED(ok); e = logfspathmapfinde(server->pathmap, s->path); if(e == nil) return logfseunknownpath; if((e->qid.type & QTDIR) != 0) return Eperm; if(e->u.file.cvers != s->u.write.cvers) return nil; ustmuid = logfsisustadd(server->is, s->u.write.muid); if(ustmuid == nil) return Enomem; extent.min = s->u.write.offset; extent.max = s->u.write.offset + s->u.write.count; extent.flashaddr = s->u.write.flashaddr; errmsg = logfsextentlistinsert(e->u.file.extent, &extent, nil); if(errmsg) return errmsg; e->mtime = s->u.write.mtime; e->muid = ustmuid; if(extent.max > e->u.file.length) e->u.file.length = extent.max; /* TODO forsyth increments vers here; not sure whether necessary */ return nil; } static char * rewstat(LogfsServer *server, LogMessage *s, int *ok) { Entry *e; char *errmsg; char *cname, *ustgid, *ustmuid; char *ustuid; USED(ok); e = logfspathmapfinde(server->pathmap, s->path); if(e == nil) return logfseunknownpath; cname = nil; ustuid = nil; ustgid = nil; ustmuid = nil; if(s->u.wstat.name) { cname = logfsstrdup(s->u.wstat.name); if(cname == nil) { memerror: errmsg = Enomem; goto fail; } } if(s->u.wstat.uid) { ustuid = logfsisustadd(server->is, s->u.wstat.uid); if(ustuid == nil) goto memerror; } if(s->u.wstat.gid) { ustgid = logfsisustadd(server->is, s->u.wstat.gid); if(ustgid == nil) goto memerror; } if(s->u.wstat.muid) { ustmuid = logfsisustadd(server->is, s->u.wstat.muid); if(ustmuid == nil) goto memerror; } if(cname) { logfsfreemem(e->name); e->name = cname; cname = nil; } if(ustuid) e->uid = ustuid; if(ustgid) e->gid = ustgid; if(ustmuid) e->muid = ustmuid; if(s->u.wstat.perm != ~0) e->perm = (e->perm & DMDIR) | (s->u.wstat.perm & ~DMDIR); if(s->u.wstat.mtime != ~0) e->mtime = s->u.wstat.mtime; errmsg = nil; fail: logfsfreemem(cname); return errmsg; } static char * replayblock(LogfsServer *server, LogSegment *seg, uchar *buf, long i, int *pagep, int disableerrors) { int page; LogfsLowLevel *ll = server->ll; LogfsLowLevelReadResult llrr; ushort size; LogMessage s; int ppb = 1 << ll->l2pagesperblock; int pagesize = 1 << ll->l2pagesize; for(page = 0; page < ppb; page++) { uchar *p, *bufend; char *errmsg = (*ll->readpagerange)(ll, buf, seg->blockmap[i], page, 0, pagesize, &llrr); if(errmsg) return errmsg; if(llrr != LogfsLowLevelReadResultOk) logfsserverreplacelogblock(server, seg, i); /* ignore failure to replace block */ if(server->trace > 1) print("replaying seq %ld block %ld page %d\n", i, seg->blockmap[i], page); p = buf; if(*p == 0xff) break; bufend = p + pagesize; while(p < bufend) { int ok = 1; size = logfsconvM2S(p, bufend - p, &s); if(size == 0) return "parse failure"; if(server->trace > 1) { print(">> "); logfsdumpS(&s); print("\n"); } if(s.type == LogfsLogTend) break; switch(s.type) { case LogfsLogTstart: break; case LogfsLogTcreate: maxpath(server, s.path); maxpath(server, s.u.create.newpath); errmsg = recreate(server, &s, &ok); break; case LogfsLogTtrunc: maxpath(server, s.path); errmsg = retrunc(server, &s, &ok); break; case LogfsLogTremove: maxpath(server, s.path); errmsg = reremove(server, &s, &ok); break; case LogfsLogTwrite: maxpath(server, s.path); errmsg = rewrite(server, &s, &ok); break; case LogfsLogTwstat: maxpath(server, s.path); errmsg = rewstat(server, &s, &ok); break; default: return "bad tag in log page"; } if(!ok) return errmsg; if(errmsg && !disableerrors){ print("bad replay: %s\n", errmsg); print("on: "); logfsdumpS(&s); print("\n"); } p += size; } } *pagep = page; return nil; } static int map(void *magic, Extent *x, int hole) { LogfsServer *server; LogfsLowLevel *ll; long seq; int page; int offset; Pageset mask; DataBlock *db; if(hole || (x->flashaddr & LogAddr) != 0) return 1; server = magic; ll = server->ll; logfsflashaddr2spo(server, x->flashaddr, &seq, &page, &offset); if(seq >= server->ndatablocks || (db = server->datablock + seq)->block < 0) { print("huntfordata: seq %ld invalid\n", seq); return 1; } mask = logfsdatapagemask((x->max - x->min + offset + (1 << ll->l2pagesize) - 1) >> ll->l2pagesize, page); //print("mask 0x%.8ux free 0x%.8ux dirty 0x%.8ux\n", mask, db->free, db->dirty); if((db->free & mask) != mask) print("huntfordata: data referenced more than once: block %ld(%ld) free 0x%.8llux mask 0x%.8llux\n", seq, db->block, (u64int)db->free, (u64int)mask); db->free &= ~mask; db->dirty |= mask; return 1; } static void huntfordatainfile(LogfsServer *server, Entry *e) { logfsextentlistwalk(e->u.file.extent, map, server); } static void huntfordataindir(LogfsServer *server, Entry *pe) { Entry *e; for(e = pe->u.dir.list; e; e = e->next) if(e->qid.type & QTDIR) huntfordataindir(server, e); else huntfordatainfile(server, e); } char * logfsreplay(LogfsServer *server, LogSegment *seg, int disableerrorsforfirstblock) { uchar *buf; long i; int page; char *errmsg; if(seg == nil || seg->curblockindex < 0) return nil; buf = logfsrealloc(nil, 1 << server->ll->l2pagesize); if(buf == nil) return Enomem; for(i = 0; i <= seg->curblockindex; i++) { errmsg = replayblock(server, seg, buf, i, &page, disableerrorsforfirstblock); disableerrorsforfirstblock = 0; if(errmsg) { print("logfsreplay: error: %s\n", errmsg); goto fail; } } /* * if the last block ended early, restart at the first free page */ if(page < (1 << server->ll->l2pagesperblock)) seg->curpage = page; errmsg = nil; fail: logfsfreemem(buf); return errmsg; } void logfsreplayfinddata(LogfsServer *server) { huntfordataindir(server, &server->root); if(server->trace > 0) { long i; DataBlock *db; for(i = 0, db = server->datablock; i < server->ndatablocks; i++, db++) { logfsfreeanddirtydatablockcheck(server, i); if(db->block >= 0) print("%4ld: free 0x%.8llux dirty 0x%.8llux\n", i, (u64int)server->datablock[i].free, (u64int)server->datablock[i].dirty); } } }