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 *
sweepcreate(LogfsServer * server,LogMessage * s,int * actionp)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 *
sweepwrite(LogfsServer * server,LogMessage * s,int readoffset,Entry ** ep,int * trimp,int * actionp)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
fixup(void * magic,Extent * e)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 *
sweepblock(LogfsServer * server,uchar * buf)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 *
logfsserverlogsweep(LogfsServer * server,int justone,int * didsomething)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