diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /liblogfs/sweep.c | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'liblogfs/sweep.c')
| -rw-r--r-- | liblogfs/sweep.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/liblogfs/sweep.c b/liblogfs/sweep.c new file mode 100644 index 00000000..f52509fd --- /dev/null +++ b/liblogfs/sweep.c @@ -0,0 +1,272 @@ +#include "lib9.h" +#include "logfs.h" +#include "local.h" + +enum { + ThrowAway, + Keep, + Repack, + Error, +}; + +#define setaction(a) if(*actionp < (a)) *actionp = a +#define REPACK setaction(Repack) +#define KEEP setaction(Keep) +#define OPTCOPYEX(name, etag, stag) \ + if(e->etag != s->stag) { \ + s->stag = e->etag; \ + REPACK; \ + } +#define OPTSTRCOPYEX(name, etag, stag) \ + if(strcmp(e->etag, s->stag) != 0) { \ + s->stag = e->etag; \ + REPACK; \ + } + +#define OPTCOPY(name, tag, sunion) OPTCOPYEX(name, tag, u.sunion.tag) +#define OPTSTRCOPY(name, tag, sunion) OPTSTRCOPYEX(name, tag, u.sunion.tag) + +static char * +sweepcreate(LogfsServer *server, LogMessage *s, int *actionp) +{ + Entry *pe, *e; + e = logfspathmapfinde(server->pathmap, s->u.create.newpath); + if(e == nil) + /* file no longer exists */ + return nil; + pe = logfspathmapfinde(server->pathmap, s->path); + if(pe == nil) + /* file exists but parent doesn't - not good, but can continue */ + return "parent missing"; + if((pe->perm & DMDIR) == 0 || (e->perm & DMDIR) != (s->u.create.perm & DMDIR)) + /* parent must be a directory, and + * the directory mode cannot change + */ + return logfseinternal; + if((e->perm & DMDIR) == 0) { + OPTCOPYEX("cvers", u.file.cvers, u.create.cvers); + } + OPTSTRCOPY("name", name, create); + OPTCOPY("mtime", mtime, create); + OPTCOPY("perm", perm, create); + OPTSTRCOPY("uid", uid, create); + OPTSTRCOPY("gid", gid, create); + KEEP; + return nil; +} + +static char * +sweepwrite(LogfsServer *server, LogMessage *s, int readoffset, Entry **ep, int *trimp, int *actionp) +{ + Entry *e; + Extent extent; + Extent *ext; + *ep = nil; + e = logfspathmapfinde(server->pathmap, s->path); + if(e == nil) + /* gone, gone */ + return nil; + if(e->perm & DMDIR) + return logfseinternal; + if(e->u.file.cvers != s->u.write.cvers) + /* trunced more recently */ + return nil; + extent.min = s->u.write.offset; + extent.max = extent.min + s->u.write.count; + extent.flashaddr = s->u.write.flashaddr; + ext = logfsextentlistmatch(e->u.file.extent, &extent); + if(ext == nil) + return nil; + if(s->u.write.data) { + /* + * trim the front of the data so that when fixing up extents, + * flashaddr refers to the first byte + */ + int offset; + logfsflashaddr2o(server, ext->flashaddr, &offset); + *trimp = offset - readoffset; + *ep = e; + } + KEEP; + return nil; +} + +typedef struct FixupState { + LogfsServer *server; + int oldoffset; + u32int newflashaddr; +} FixupState; + +static int +fixup(void *magic, Extent *e) +{ + FixupState *state = magic; + int offset; + logfsflashaddr2o(state->server, e->flashaddr, &offset); + e->flashaddr = state->newflashaddr + (offset - state->oldoffset); + return 1; +} + +static char * +sweepblock(LogfsServer *server, uchar *buf) +{ + char *errmsg; + LogSegment *active = server->activelog; + LogSegment *swept = server->sweptlog; + int pagesize, ppb, page; + LogfsLowLevel *ll = server->ll; + LogfsLowLevelReadResult llrr; + int markedbad; + long oblock; + + if(active == nil) + return nil; + if(swept == nil) { + errmsg = logfslogsegmentnew(server, loggensucc(active->gen), &server->sweptlog); + if(errmsg) + return errmsg; + swept = server->sweptlog; + } + /* + * if this is last block in the active log, flush it, so that the read of the last page works + */ + if(active->unsweptblockindex == active->curblockindex) + logfslogsegmentflush(server, 1); + ppb = (1 << ll->l2pagesperblock); + pagesize = (1 << ll->l2pagesize); + for(page = 0; page < ppb; page++) { + uchar *p, *bufend; + errmsg = (*ll->readpagerange)(ll, buf, active->blockmap[active->unsweptblockindex], page, 0, pagesize, &llrr); + if(errmsg) + goto fail; + if(llrr != LogfsLowLevelReadResultOk) + logfsserverreplacelogblock(server, active, active->unsweptblockindex); + p = buf; + if(*p == 0xff) + break; + bufend = p + pagesize; + while(p < bufend) { + int action; + uint size; + LogMessage s; + Entry *e; + int trim; + + size = logfsconvM2S(p, bufend - p, &s); + if(size == 0) + return "parse failure"; + if(server->trace > 1) { + print("A>> "); + logfsdumpS(&s); + print("\n"); + } + if(s.type == LogfsLogTend) + break; + action = ThrowAway; + switch(s.type) { + case LogfsLogTstart: + break; + case LogfsLogTcreate: + errmsg = sweepcreate(server, &s, &action); + break; + case LogfsLogTremove: + /* always obsolete; might check that path really doesn't exist */ + break; + case LogfsLogTtrunc: + /* always obsolete, unless collecting out of order */ + break; + case LogfsLogTwrite: + errmsg = sweepwrite(server, &s, s.u.write.data ? s.u.write.data - buf : 0, &e, &trim, &action); + break; + case LogfsLogTwstat: + /* always obsolete, unless collecting out of order */ + break; + default: + return "bad tag in log page"; + } + if(action == Error) + return errmsg; + if(errmsg) + print("bad sweep: %s\n", errmsg); + if(action == Keep) + action = Repack; /* input buffer has been wrecked, so can't just copy it */ + if(action == Keep) { + /* write 'size' bytes to log */ + errmsg = logfslogbytes(server, 0, p, size); + if(errmsg) + goto fail; + } + else if(action == Repack) { + /* TODO - handle writes */ + if(s.type == LogfsLogTwrite && s.u.write.data) { + FixupState state; + errmsg = logfslogwrite(server, 0, s.path, s.u.write.offset + trim, s.u.write.count - trim, + s.u.write.mtime, s.u.write.cvers, + s.u.write.muid, s.u.write.data + trim, &state.newflashaddr); + if(errmsg == nil && s.u.write.data != nil) { + Extent extent; + /* TODO - deal with a failure to write the changes */ + state.oldoffset = s.u.write.data - buf + trim; + state.server = server; + extent.min = s.u.write.offset; + extent.max = extent.min + s.u.write.count; + extent.flashaddr = s.u.write.flashaddr; + logfsextentlistmatchall(e->u.file.extent, fixup, &state, &extent); + } + } + else + errmsg = logfslog(server, 0, &s); + if(errmsg) + goto fail; + } + p += size; + } + } + /* + * this log block is no longer needed + */ + oblock = active->blockmap[active->unsweptblockindex++]; + errmsg = logfsbootfettleblock(server->lb, oblock, LogfsTnone, ~0, &markedbad); + if(errmsg) + goto fail; + if(active->unsweptblockindex > active->curblockindex) { + /* + * the activelog is now empty, so make the sweptlog the active one + */ + logfslogsegmentfree(&active); + server->activelog = swept; + server->sweptlog = nil; + swept->dirty = 0; + } + return nil; +fail: + return errmsg; +} + +char * +logfsserverlogsweep(LogfsServer *server, int justone, int *didsomething) +{ + uchar *buf; + char *errmsg; + + /* + * TODO - is it even worth doing? + */ + *didsomething = 0; + if(!server->activelog->dirty) + return nil; + buf = logfsrealloc(nil, (1 << server->ll->l2pagesize)); + if(buf == nil) + return Enomem; + errmsg = nil; + while(server->activelog->unsweptblockindex <= server->activelog->curblockindex) { + errmsg = sweepblock(server, buf); + if(errmsg) + break; + if(server->sweptlog == nil || justone) + break; + } + logfsfreemem(buf); + *didsomething = 1; + return errmsg; +} |
