xref: /inferno-os/liblogfs/replay.c (revision 5d0c4cf3fc288434c41cba52dd998ab1d7375a7b)
1 #include "logfsos.h"
2 #include "logfs.h"
3 #include "local.h"
4 #include "fcall.h"
5 
6 static void
7 maxpath(LogfsServer *server, ulong p)
8 {
9 	if(p > server->path)
10 		server->path = p;
11 }
12 
13 static char *
14 recreate(LogfsServer *server, LogMessage *s, int *ok)
15 {
16 	Entry *parent;
17 	char *errmsg;
18 	Entry *e;
19 	Path *p;
20 
21 	parent = logfspathmapfinde(server->pathmap, s->path);
22 	if(parent == nil)
23 		return "can't find parent";
24 	if(logfspathmapfindentry(server->pathmap, s->u.create.newpath) != nil){
25 		Entry *d = logfspathmapfinde(server->pathmap, s->u.create.newpath);
26 		if(d == nil)
27 			print("existing was nil\n");
28 		else{
29 			print("existing: name=%q d=%d path=%8.8llux uid=%q gid=%q perm=%#uo\n",
30 				d->name, d->deadandgone, d->qid.path, d->uid, d->gid, d->perm);
31 		}
32 		return "duplicate path";
33 	}
34 	if((parent->qid.type & QTDIR) == 0)
35 		return Enotdir;
36 	errmsg = logfsentrynew(server, 1, s->u.create.newpath, parent,
37 		s->u.create.name, s->u.create.uid, s->u.create.gid, s->u.create.mtime, s->u.create.uid,
38 		s->u.create.perm, s->u.create.cvers, 0, &e);
39 	if(errmsg) {
40 		*ok = 0;
41 		return errmsg;
42 	}
43 	/* p guaranteed to be non null */
44 	errmsg = logfspathmapnewentry(server->pathmap, s->u.create.newpath, e, &p);
45 	if(errmsg) {
46 		logfsfreemem(e);
47 		*ok = 0;
48 		return errmsg;
49 	}
50 	e->next = parent->u.dir.list;
51 	parent->u.dir.list = e;
52 	return nil;
53 }
54 
55 static char *
56 reremove(LogfsServer *server, LogMessage *s, int *ok)
57 {
58 	Entry *oe;
59 	Entry *parent;
60 	Entry **ep;
61 	Entry *e;
62 	char *ustmuid;
63 
64 	USED(ok);
65 	oe = logfspathmapfinde(server->pathmap, s->path);
66 	if(oe == nil)
67 		return logfseunknownpath;
68 	parent = oe->parent;
69 	if(parent == oe)
70 		return "tried to remove root";
71 	if((parent->qid.type & QTDIR) == 0)
72 		return Enotdir;
73 	if((oe->qid.type & QTDIR) != 0 && oe->u.dir.list)
74 		return logfsenotempty;
75 	for(ep = &parent->u.dir.list; e = *ep; ep = &e->next)
76 		if(e == oe)
77 			break;
78 	if(e == nil)
79 		return logfseinternal;
80 	ustmuid = logfsisustadd(server->is, s->u.remove.muid);
81 	if(ustmuid == nil)
82 		return Enomem;
83 	parent->mtime = s->u.remove.mtime;
84 	parent->muid = ustmuid;
85 	logfspathmapdeleteentry(server->pathmap, s->path);
86 	*ep = e->next;
87 	if(e->inuse > 1) {
88 		print("replay: entry inuse > 1\n");
89 		e->inuse = 1;
90 	}
91 	logfsentryclunk(e);
92 	return nil;
93 }
94 
95 static char *
96 retrunc(LogfsServer *server, LogMessage *s, int *ok)
97 {
98 	Entry *e;
99 	char *ustmuid;
100 
101 	USED(ok);
102 	e = logfspathmapfinde(server->pathmap, s->path);
103 	if(e == nil)
104 		return logfseunknownpath;
105 	if((e->qid.type & QTDIR) != 0)
106 		return Eperm;
107 	if(e->u.file.cvers >= s->u.trunc.cvers)
108 		return "old news";
109 	ustmuid = logfsisustadd(server->is, s->u.trunc.muid);
110 	if(ustmuid == nil)
111 		return Enomem;
112 	e->muid = ustmuid;
113 	e->mtime = s->u.trunc.mtime;
114 	e->qid.vers++;
115 	e->u.file.cvers = s->u.trunc.cvers;
116 	/*
117 	 * zap all extents
118 	 */
119 	logfsextentlistreset(e->u.file.extent);
120 	e->u.file.length = 0;
121 	return nil;
122 }
123 
124 static char *
125 rewrite(LogfsServer *server, LogMessage *s, int *ok)
126 {
127 	Entry *e;
128 	char *ustmuid;
129 	Extent extent;
130 	char *errmsg;
131 
132 	USED(ok);
133 	e = logfspathmapfinde(server->pathmap, s->path);
134 	if(e == nil)
135 		return logfseunknownpath;
136 	if((e->qid.type & QTDIR) != 0)
137 		return Eperm;
138 	if(e->u.file.cvers != s->u.write.cvers)
139 		return nil;
140 	ustmuid = logfsisustadd(server->is, s->u.write.muid);
141 	if(ustmuid == nil)
142 		return Enomem;
143 	extent.min = s->u.write.offset;
144 	extent.max = s->u.write.offset + s->u.write.count;
145 	extent.flashaddr = s->u.write.flashaddr;
146 	errmsg = logfsextentlistinsert(e->u.file.extent, &extent, nil);
147 	if(errmsg)
148 		return errmsg;
149 	e->mtime = s->u.write.mtime;
150 	e->muid = ustmuid;
151 	if(extent.max > e->u.file.length)
152 		e->u.file.length = extent.max;
153 	/* TODO forsyth increments vers here; not sure whether necessary */
154 	return nil;
155 }
156 
157 static char *
158 rewstat(LogfsServer *server, LogMessage *s, int *ok)
159 {
160 	Entry *e;
161 	char *errmsg;
162 	char *cname, *ustgid, *ustmuid;
163 	char *ustuid;
164 
165 	USED(ok);
166 	e = logfspathmapfinde(server->pathmap, s->path);
167 	if(e == nil)
168 		return logfseunknownpath;
169 	cname = nil;
170 	ustuid = nil;
171 	ustgid = nil;
172 	ustmuid = nil;
173 	if(s->u.wstat.name) {
174 		cname = logfsstrdup(s->u.wstat.name);
175 		if(cname == nil) {
176 		memerror:
177 			errmsg = Enomem;
178 			goto fail;
179 		}
180 	}
181 	if(s->u.wstat.uid) {
182 		ustuid = logfsisustadd(server->is, s->u.wstat.uid);
183 		if(ustuid == nil)
184 			goto memerror;
185 	}
186 	if(s->u.wstat.gid) {
187 		ustgid = logfsisustadd(server->is, s->u.wstat.gid);
188 		if(ustgid == nil)
189 			goto memerror;
190 	}
191 	if(s->u.wstat.muid) {
192 		ustmuid = logfsisustadd(server->is, s->u.wstat.muid);
193 		if(ustmuid == nil)
194 			goto memerror;
195 	}
196 	if(cname) {
197 		logfsfreemem(e->name);
198 		e->name = cname;
199 		cname = nil;
200 	}
201 	if(ustuid)
202 		e->uid = ustuid;
203 	if(ustgid)
204 		e->gid = ustgid;
205 	if(ustmuid)
206 		e->muid = ustmuid;
207 	if(s->u.wstat.perm != ~0)
208 		e->perm = (e->perm & DMDIR) | (s->u.wstat.perm & ~DMDIR);
209 	if(s->u.wstat.mtime != ~0)
210 		e->mtime = s->u.wstat.mtime;
211 	errmsg = nil;
212 fail:
213 	logfsfreemem(cname);
214 	return errmsg;
215 }
216 
217 static char *
218 replayblock(LogfsServer *server, LogSegment *seg, uchar *buf, long i, int *pagep, int disableerrors)
219 {
220 	int page;
221 	LogfsLowLevel *ll = server->ll;
222 	LogfsLowLevelReadResult llrr;
223 	ushort size;
224 	LogMessage s;
225 	int ppb = 1 << ll->l2pagesperblock;
226 	int pagesize = 1 << ll->l2pagesize;
227 
228 	for(page = 0; page < ppb; page++) {
229 		uchar *p, *bufend;
230 		char *errmsg = (*ll->readpagerange)(ll, buf, seg->blockmap[i], page, 0,  pagesize, &llrr);
231 		if(errmsg)
232 			return errmsg;
233 		if(llrr != LogfsLowLevelReadResultOk)
234 			logfsserverreplacelogblock(server, seg, i);
235 			/* ignore failure to replace block */
236 		if(server->trace > 1)
237 			print("replaying seq %ld block %ld page %d\n", i, seg->blockmap[i], page);
238 		p = buf;
239 		if(*p == 0xff)
240 			break;
241 		bufend = p + pagesize;
242 		while(p < bufend) {
243 			int ok = 1;
244 			size = logfsconvM2S(p, bufend - p, &s);
245 			if(size == 0)
246 				return "parse failure";
247 			if(server->trace > 1) {
248 				print(">> ");
249 				logfsdumpS(&s);
250 				print("\n");
251 			}
252 			if(s.type == LogfsLogTend)
253 				break;
254 			switch(s.type) {
255 			case LogfsLogTstart:
256 				break;
257 			case LogfsLogTcreate:
258 				maxpath(server, s.path);
259 				maxpath(server, s.u.create.newpath);
260 				errmsg = recreate(server, &s, &ok);
261 				break;
262 			case LogfsLogTtrunc:
263 				maxpath(server, s.path);
264 				errmsg = retrunc(server, &s, &ok);
265 				break;
266 			case LogfsLogTremove:
267 				maxpath(server, s.path);
268 				errmsg = reremove(server, &s, &ok);
269 				break;
270 			case LogfsLogTwrite:
271 				maxpath(server, s.path);
272 				errmsg = rewrite(server, &s, &ok);
273 				break;
274 			case LogfsLogTwstat:
275 				maxpath(server, s.path);
276 				errmsg = rewstat(server, &s, &ok);
277 				break;
278 			default:
279 				return "bad tag in log page";
280 			}
281 			if(!ok)
282 				return errmsg;
283 			if(errmsg && !disableerrors){
284 				print("bad replay: %s\n", errmsg);
285 				print("on: "); logfsdumpS(&s); print("\n");
286 			}
287 			p += size;
288 		}
289 	}
290 	*pagep = page;
291 	return nil;
292 }
293 
294 static int
295 map(void *magic, Extent *x, int hole)
296 {
297 	LogfsServer *server;
298 	LogfsLowLevel *ll;
299 	long seq;
300 	int page;
301 	int offset;
302 	Pageset mask;
303 	DataBlock *db;
304 
305 	if(hole || (x->flashaddr & LogAddr) != 0)
306 		return 1;
307 	server = magic;
308 	ll = server->ll;
309 	logfsflashaddr2spo(server, x->flashaddr, &seq, &page, &offset);
310 	if(seq >= server->ndatablocks || (db = server->datablock + seq)->block < 0) {
311 		print("huntfordata: seq %ld invalid\n", seq);
312 		return 1;
313 	}
314 	mask = logfsdatapagemask((x->max - x->min + offset + (1 << ll->l2pagesize) - 1) >> ll->l2pagesize, page);
315 //print("mask 0x%.8ux free 0x%.8ux dirty 0x%.8ux\n", mask, db->free, db->dirty);
316 	if((db->free & mask) != mask)
317 		print("huntfordata: data referenced more than once: block %ld(%ld) free 0x%.8llux mask 0x%.8llux\n",
318 			seq, db->block, (u64int)db->free, (u64int)mask);
319 	db->free &= ~mask;
320 	db->dirty |= mask;
321 	return 1;
322 }
323 
324 static void
325 huntfordatainfile(LogfsServer *server, Entry *e)
326 {
327 	logfsextentlistwalk(e->u.file.extent, map, server);
328 }
329 
330 static void
331 huntfordataindir(LogfsServer *server, Entry *pe)
332 {
333 	Entry *e;
334 	for(e = pe->u.dir.list; e; e = e->next)
335 		if(e->qid.type & QTDIR)
336 			huntfordataindir(server, e);
337 		else
338 			huntfordatainfile(server, e);
339 }
340 
341 char *
342 logfsreplay(LogfsServer *server, LogSegment *seg, int disableerrorsforfirstblock)
343 {
344 	uchar *buf;
345 	long i;
346 	int page;
347 	char *errmsg;
348 
349 	if(seg == nil || seg->curblockindex < 0)
350 		return nil;
351 	buf = logfsrealloc(nil, 1 << server->ll->l2pagesize);
352 	if(buf == nil)
353 		return Enomem;
354 	for(i = 0; i <= seg->curblockindex; i++) {
355 		errmsg = replayblock(server, seg, buf,  i, &page, disableerrorsforfirstblock);
356 		disableerrorsforfirstblock = 0;
357 		if(errmsg) {
358 			print("logfsreplay: error: %s\n", errmsg);
359 			goto fail;
360 		}
361 	}
362 	/*
363 	 * if the last block ended early, restart at the first free page
364 	 */
365 	if(page < (1 << server->ll->l2pagesperblock))
366 		seg->curpage = page;
367 	errmsg = nil;
368 fail:
369 	logfsfreemem(buf);
370 	return errmsg;
371 }
372 
373 void
374 logfsreplayfinddata(LogfsServer *server)
375 {
376 	huntfordataindir(server, &server->root);
377 	if(server->trace > 0) {
378 		long i;
379 		DataBlock *db;
380 		for(i = 0, db = server->datablock; i < server->ndatablocks; i++, db++) {
381 			logfsfreeanddirtydatablockcheck(server, i);
382 			if(db->block >= 0)
383 				print("%4ld: free 0x%.8llux dirty 0x%.8llux\n", i, (u64int)server->datablock[i].free, (u64int)server->datablock[i].dirty);
384 		}
385 	}
386 }
387