xref: /inferno-os/liblogfs/sweep.c (revision 28942ead413418b56c5be78e8c4c400881fba72e)
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