1 #include "logfsos.h" 2 #include "logfs.h" 3 #include "local.h" 4 5 enum { 6 ThrowAway, 7 Keep, 8 Repack, 9 Error, 10 }; 11 12 #define setaction(a) if(*actionp < (a)) *actionp = a 13 #define REPACK setaction(Repack) 14 #define KEEP setaction(Keep) 15 #define OPTCOPYEX(name, etag, stag) \ 16 if(e->etag != s->stag) { \ 17 s->stag = e->etag; \ 18 REPACK; \ 19 } 20 #define OPTSTRCOPYEX(name, etag, stag) \ 21 if(strcmp(e->etag, s->stag) != 0) { \ 22 s->stag = e->etag; \ 23 REPACK; \ 24 } 25 26 #define OPTCOPY(name, tag, sunion) OPTCOPYEX(name, tag, u.sunion.tag) 27 #define OPTSTRCOPY(name, tag, sunion) OPTSTRCOPYEX(name, tag, u.sunion.tag) 28 29 static char * 30 sweepcreate(LogfsServer *server, LogMessage *s, int *actionp) 31 { 32 Entry *pe, *e; 33 e = logfspathmapfinde(server->pathmap, s->u.create.newpath); 34 if(e == nil) 35 /* file no longer exists */ 36 return nil; 37 pe = logfspathmapfinde(server->pathmap, s->path); 38 if(pe == nil) 39 /* file exists but parent doesn't - not good, but can continue */ 40 return "parent missing"; 41 if((pe->perm & DMDIR) == 0 || (e->perm & DMDIR) != (s->u.create.perm & DMDIR)) 42 /* parent must be a directory, and 43 * the directory mode cannot change 44 */ 45 return logfseinternal; 46 if((e->perm & DMDIR) == 0) { 47 OPTCOPYEX("cvers", u.file.cvers, u.create.cvers); 48 } 49 OPTSTRCOPY("name", name, create); 50 OPTCOPY("mtime", mtime, create); 51 OPTCOPY("perm", perm, create); 52 OPTSTRCOPY("uid", uid, create); 53 OPTSTRCOPY("gid", gid, create); 54 KEEP; 55 return nil; 56 } 57 58 static char * 59 sweepwrite(LogfsServer *server, LogMessage *s, int readoffset, Entry **ep, int *trimp, int *actionp) 60 { 61 Entry *e; 62 Extent extent; 63 Extent *ext; 64 *ep = nil; 65 e = logfspathmapfinde(server->pathmap, s->path); 66 if(e == nil) 67 /* gone, gone */ 68 return nil; 69 if(e->perm & DMDIR) 70 return logfseinternal; 71 if(e->u.file.cvers != s->u.write.cvers) 72 /* trunced more recently */ 73 return nil; 74 extent.min = s->u.write.offset; 75 extent.max = extent.min + s->u.write.count; 76 extent.flashaddr = s->u.write.flashaddr; 77 ext = logfsextentlistmatch(e->u.file.extent, &extent); 78 if(ext == nil) 79 return nil; 80 if(s->u.write.data) { 81 /* 82 * trim the front of the data so that when fixing up extents, 83 * flashaddr refers to the first byte 84 */ 85 int offset; 86 logfsflashaddr2o(server, ext->flashaddr, &offset); 87 *trimp = offset - readoffset; 88 *ep = e; 89 } 90 KEEP; 91 return nil; 92 } 93 94 typedef struct FixupState { 95 LogfsServer *server; 96 int oldoffset; 97 u32int newflashaddr; 98 } FixupState; 99 100 static int 101 fixup(void *magic, Extent *e) 102 { 103 FixupState *state = magic; 104 int offset; 105 logfsflashaddr2o(state->server, e->flashaddr, &offset); 106 e->flashaddr = state->newflashaddr + (offset - state->oldoffset); 107 return 1; 108 } 109 110 static char * 111 sweepblock(LogfsServer *server, uchar *buf) 112 { 113 char *errmsg; 114 LogSegment *active = server->activelog; 115 LogSegment *swept = server->sweptlog; 116 int pagesize, ppb, page; 117 LogfsLowLevel *ll = server->ll; 118 LogfsLowLevelReadResult llrr; 119 int markedbad; 120 long oblock; 121 122 if(active == nil) 123 return nil; 124 if(swept == nil) { 125 errmsg = logfslogsegmentnew(server, loggensucc(active->gen), &server->sweptlog); 126 if(errmsg) 127 return errmsg; 128 swept = server->sweptlog; 129 } 130 /* 131 * if this is last block in the active log, flush it, so that the read of the last page works 132 */ 133 if(active->unsweptblockindex == active->curblockindex) 134 logfslogsegmentflush(server, 1); 135 ppb = (1 << ll->l2pagesperblock); 136 pagesize = (1 << ll->l2pagesize); 137 for(page = 0; page < ppb; page++) { 138 uchar *p, *bufend; 139 errmsg = (*ll->readpagerange)(ll, buf, active->blockmap[active->unsweptblockindex], page, 0, pagesize, &llrr); 140 if(errmsg) 141 goto fail; 142 if(llrr != LogfsLowLevelReadResultOk) 143 logfsserverreplacelogblock(server, active, active->unsweptblockindex); 144 p = buf; 145 if(*p == 0xff) 146 break; 147 bufend = p + pagesize; 148 while(p < bufend) { 149 int action; 150 uint size; 151 LogMessage s; 152 Entry *e; 153 int trim; 154 155 size = logfsconvM2S(p, bufend - p, &s); 156 if(size == 0) 157 return "parse failure"; 158 if(server->trace > 1) { 159 print("A>> "); 160 logfsdumpS(&s); 161 print("\n"); 162 } 163 if(s.type == LogfsLogTend) 164 break; 165 action = ThrowAway; 166 switch(s.type) { 167 case LogfsLogTstart: 168 break; 169 case LogfsLogTcreate: 170 errmsg = sweepcreate(server, &s, &action); 171 break; 172 case LogfsLogTremove: 173 /* always obsolete; might check that path really doesn't exist */ 174 break; 175 case LogfsLogTtrunc: 176 /* always obsolete, unless collecting out of order */ 177 break; 178 case LogfsLogTwrite: 179 errmsg = sweepwrite(server, &s, s.u.write.data ? s.u.write.data - buf : 0, &e, &trim, &action); 180 break; 181 case LogfsLogTwstat: 182 /* always obsolete, unless collecting out of order */ 183 break; 184 default: 185 return "bad tag in log page"; 186 } 187 if(action == Error) 188 return errmsg; 189 if(errmsg) 190 print("bad sweep: %s\n", errmsg); 191 if(action == Keep) 192 action = Repack; /* input buffer has been wrecked, so can't just copy it */ 193 if(action == Keep) { 194 /* write 'size' bytes to log */ 195 errmsg = logfslogbytes(server, 0, p, size); 196 if(errmsg) 197 goto fail; 198 } 199 else if(action == Repack) { 200 /* TODO - handle writes */ 201 if(s.type == LogfsLogTwrite && s.u.write.data) { 202 FixupState state; 203 errmsg = logfslogwrite(server, 0, s.path, s.u.write.offset + trim, s.u.write.count - trim, 204 s.u.write.mtime, s.u.write.cvers, 205 s.u.write.muid, s.u.write.data + trim, &state.newflashaddr); 206 if(errmsg == nil && s.u.write.data != nil) { 207 Extent extent; 208 /* TODO - deal with a failure to write the changes */ 209 state.oldoffset = s.u.write.data - buf + trim; 210 state.server = server; 211 extent.min = s.u.write.offset; 212 extent.max = extent.min + s.u.write.count; 213 extent.flashaddr = s.u.write.flashaddr; 214 logfsextentlistmatchall(e->u.file.extent, fixup, &state, &extent); 215 } 216 } 217 else 218 errmsg = logfslog(server, 0, &s); 219 if(errmsg) 220 goto fail; 221 } 222 p += size; 223 } 224 } 225 /* 226 * this log block is no longer needed 227 */ 228 oblock = active->blockmap[active->unsweptblockindex++]; 229 errmsg = logfsbootfettleblock(server->lb, oblock, LogfsTnone, ~0, &markedbad); 230 if(errmsg) 231 goto fail; 232 if(active->unsweptblockindex > active->curblockindex) { 233 /* 234 * the activelog is now empty, so make the sweptlog the active one 235 */ 236 logfslogsegmentfree(&active); 237 server->activelog = swept; 238 server->sweptlog = nil; 239 swept->dirty = 0; 240 } 241 return nil; 242 fail: 243 return errmsg; 244 } 245 246 char * 247 logfsserverlogsweep(LogfsServer *server, int justone, int *didsomething) 248 { 249 uchar *buf; 250 char *errmsg; 251 252 /* 253 * TODO - is it even worth doing? 254 */ 255 *didsomething = 0; 256 if(!server->activelog->dirty) 257 return nil; 258 buf = logfsrealloc(nil, (1 << server->ll->l2pagesize)); 259 if(buf == nil) 260 return Enomem; 261 errmsg = nil; 262 while(server->activelog->unsweptblockindex <= server->activelog->curblockindex) { 263 errmsg = sweepblock(server, buf); 264 if(errmsg) 265 break; 266 if(server->sweptlog == nil || justone) 267 break; 268 } 269 logfsfreemem(buf); 270 *didsomething = 1; 271 return errmsg; 272 } 273