xref: /inferno-os/liblogfs/read.c (revision 1aff7a0a7dab24c5871eb95737c86616c9fd848b)
1 #include "logfsos.h"
2 #include "logfs.h"
3 #include "local.h"
4 #include "fcall.h"
5 
6 struct DirReadState {
7 	u32int offset;
8 	u32int lastoffset;
9 	u32int limit;
10 	uchar *data;
11 };
12 
13 typedef struct ReaderState {
14 	uchar *buf;
15 	u32int maxoffset;
16 	LogfsServer *server;
17 	char *errmsg;
18 } ReaderState;
19 
20 static DirReadState *
21 drsinit(LogfsIdentityStore *is, Entry *list, uchar *buf, u32int buflen, u32int *rcount)
22 {
23 	Entry *p, *q;
24 	DirReadState *drs;
25 	u32int k;
26 	/*
27 	 * stash as many entries as will fit in the read buffer
28 	 */
29 	*rcount = 0;
30 	for(p = list; p; p = p->next) {
31 		uint len = logfsflattenentry(is, buf, buflen, p);
32 		if(len == 0)
33 			break;
34 		*rcount += len;
35 		buf += len;
36 		buflen -= len;
37 	}
38 	drs = logfsrealloc(nil, sizeof(*drs));
39 	if(drs == nil)
40 		return nil;
41 	drs->offset = *rcount;
42 	drs->lastoffset = drs->offset;
43 	k = 0;
44 	for(q = p; q; q = q->next)
45 		k += logfsflattenentry(is, nil, 0, q);
46 	if(k) {
47 		u32int k2;
48 //		print("drsinit: %ud bytes extra\n", k);
49 		drs->data = logfsrealloc(nil, k);
50 		if(drs->data == nil) {
51 			logfsfreemem(drs);
52 			return nil;
53 		}
54 		k2 = 0;
55 		for(q = p; q; q = q->next)
56 			k2 += logfsflattenentry(is, drs->data + k2, k - k2, q);
57 		drs->limit = drs->offset + k;
58 	}
59 //	print("drsinit: rcount %ud\n", *rcount);
60 	return drs;
61 }
62 
63 static void
64 drsread(DirReadState *drs, uchar *buf, u32int buflen, u32int *rcount)
65 {
66 	uchar *p;
67 	*rcount = 0;
68 	p = drs->data + drs->lastoffset - drs->offset;
69 	while(drs->lastoffset < drs->limit) {
70 		/*
71 		 * copy an entry, if it fits
72 		 */
73 		uint len = GBIT16(p) + BIT16SZ;
74 		if(len > buflen)
75 			break;
76 		memmove(buf, p, len);
77 		drs->lastoffset += len;
78 		*rcount += len;
79 		buf += len;
80 		buflen -= len;
81 		p += len;
82 	}
83 	if(drs->lastoffset >= drs->limit) {
84 		logfsfreemem(drs->data);
85 		drs->data = nil;
86 	}
87 }
88 
89 void
90 logfsdrsfree(DirReadState **drsp)
91 {
92 	DirReadState *drs = *drsp;
93 	if(drs) {
94 		logfsfreemem(drs->data);
95 		logfsfreemem(drs);
96 		*drsp = nil;
97 	}
98 }
99 
100 static int
101 reader(void *magic, u32int baseoffset, u32int limitoffset, Extent *e, u32int extentoffset)
102 {
103 	ReaderState *s = magic;
104 	LogfsServer *server;
105 	LogfsLowLevel *ll;
106 	LogfsLowLevelReadResult llrr;
107 	long seq;
108 	int page;
109 	int offset;
110 	long block;
111 	int pagesize;
112 	LogSegment *seg;
113 	int replace;
114 
115 	if(e == nil) {
116 //print("fill(%d, %d)\n", baseoffset, limitoffset);
117 		memset(s->buf + baseoffset, 0, limitoffset - baseoffset);
118 		if(limitoffset > s->maxoffset)
119 			s->maxoffset = limitoffset;
120 		return 1;
121 	}
122 	server = s->server;
123 	ll = server->ll;
124 	/*
125 	 * extentoffset is how much to trim off the front of the extent
126 	 */
127 	logfsflashaddr2spo(server, e->flashaddr + extentoffset, &seq, &page, &offset);
128 	/*
129 	 * offset is the offset within the page to where e->min is stored
130 	 */
131 //print("read(%d, %d, %c%ld/%ud/%ud)\n",
132 //	baseoffset, limitoffset, (e->flashaddr & LogAddr) ? 'L' :  'D', seq, page, offset);
133 	if(e->flashaddr & LogAddr) {
134 		if(seq >= server->activelog->unsweptblockindex && seq <= server->activelog->curblockindex)
135 			seg = server->activelog;
136 		else if(server->sweptlog && seq <= server->sweptlog->curblockindex)
137 			seg = server->sweptlog;
138 		else {
139 			print("logfsserverread: illegal log sequence number %ld (active=[%ld, %ld], swept=[%ld, %ld])\n",
140 				seq, server->activelog->unsweptblockindex, server->activelog->curblockindex,
141 				server->sweptlog ? 0L : -1L, server->sweptlog ? server->sweptlog->curblockindex : -1L);
142 			s->errmsg = logfseinternal;
143 			return -1;
144 		}
145 		if(seg->curpage == page && seg->curblockindex == seq) {
146 			/*
147 			 * it hasn't made it to disk yet
148 			 */
149 			memmove(s->buf + baseoffset, seg->pagebuf + offset, limitoffset - baseoffset);
150 			goto done;
151 		}
152 		if(seq < seg->unsweptblockindex) {
153 			/* data already swept */
154 			print("logfsserverread: log address has been swept\n");
155 			s->errmsg = logfseinternal;
156 			return -1;
157 		}
158 		block = seg->blockmap[seq];
159 	}
160 	else {
161 		seg = nil;
162 		if(seq >= server->ndatablocks)
163 			block = -1;
164 		else
165 			block = server->datablock[seq].block;
166 		if(block < 0) {
167 			print("logfsserveread: data address does not exist\n");
168 			s->errmsg = logfseinternal;
169 			return -1;
170 		}
171 	}
172 	/*
173 	 * read as many pages as necessary to get to the limitoffset
174 	 */
175 	pagesize = 1 << ll->l2pagesize;
176 	replace = 0;
177 	while(baseoffset < limitoffset) {
178 		u32int thistime;
179 		thistime = pagesize - offset;
180 		if(thistime > (limitoffset - baseoffset))
181 			thistime = limitoffset - baseoffset;
182 		s->errmsg = (*ll->readpagerange)(ll, s->buf + baseoffset, block, page,
183 			offset, thistime, &llrr);
184 		if(s->errmsg)
185 			return -1;
186 		if(llrr != LogfsLowLevelReadResultOk) {
187 			replace = 1;
188 		}
189 		baseoffset += thistime;
190 		page++;
191 		offset = 0;
192 	}
193 	if(replace) {
194 		s->errmsg = logfsserverreplaceblock(server, seg, seq);
195 		if(s->errmsg)
196 			return -1;
197 	}
198 done:
199 	if(limitoffset > s->maxoffset)
200 		s->maxoffset = limitoffset;
201 	return 1;
202 }
203 
204 char *
205 logfsserverread(LogfsServer *server, u32int fid, u32int offset, u32int count, uchar *buf, u32int buflen, u32int *rcount)
206 {
207 	Fid *f;
208 	Entry *e;
209 	ReaderState s;
210 	int rv;
211 
212 	if(server->trace > 1)
213 		print("logfsserverread(%ud, %ud, %ud)\n", fid, offset, count);
214 	f = logfsfidmapfindentry(server->fidmap, fid);
215 	if(f == nil)
216 		return logfsebadfid;
217 	if(f->openmode < 0)
218 		return logfsefidnotopen;
219 	if((f->openmode & 3) == OWRITE)
220 		return logfseaccess;
221 	if(count > buflen)
222 		return Etoobig;
223 	e = f->entry;
224 	if(e->deadandgone)
225 		return Eio;
226 	if(e->qid.type & QTDIR) {
227 		if(offset != 0) {
228 			if(f->drs == nil || f->drs->lastoffset != offset)
229 				return Eio;
230 			drsread(f->drs, buf, count, rcount);
231 		}
232 		else {
233 			logfsdrsfree(&f->drs);
234 			f->drs = drsinit(server->is, e->u.dir.list, buf, count, rcount);
235 			if(f->drs == nil)
236 				return Enomem;
237 		}
238 		return nil;
239 	}
240 	if(offset >= e->u.file.length) {
241 		*rcount = 0;
242 		return nil;
243 	}
244 	s.buf = buf;
245 	s.server = server;
246 	s.maxoffset = 0;
247 	rv = logfsextentlistwalkrange(e->u.file.extent, reader, &s, offset, offset + count);
248 	if(rv < 0)
249 		return s.errmsg;
250 	*rcount = s.maxoffset;
251 	return nil;
252 }
253 
254