summaryrefslogtreecommitdiff
path: root/liblogfs/read.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /liblogfs/read.c
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'liblogfs/read.c')
-rw-r--r--liblogfs/read.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/liblogfs/read.c b/liblogfs/read.c
new file mode 100644
index 00000000..e97a934f
--- /dev/null
+++ b/liblogfs/read.c
@@ -0,0 +1,253 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "local.h"
+#include "fcall.h"
+
+struct DirReadState {
+ u32int offset;
+ u32int lastoffset;
+ u32int limit;
+ uchar *data;
+};
+
+typedef struct ReaderState {
+ uchar *buf;
+ u32int maxoffset;
+ LogfsServer *server;
+ char *errmsg;
+} ReaderState;
+
+static DirReadState *
+drsinit(LogfsIdentityStore *is, Entry *list, uchar *buf, u32int buflen, u32int *rcount)
+{
+ Entry *p, *q;
+ DirReadState *drs;
+ u32int k;
+ /*
+ * stash as many entries as will fit in the read buffer
+ */
+ *rcount = 0;
+ for(p = list; p; p = p->next) {
+ uint len = logfsflattenentry(is, buf, buflen, p);
+ if(len == 0)
+ break;
+ *rcount += len;
+ buf += len;
+ buflen -= len;
+ }
+ drs = logfsrealloc(nil, sizeof(*drs));
+ if(drs == nil)
+ return nil;
+ drs->offset = *rcount;
+ drs->lastoffset = drs->offset;
+ k = 0;
+ for(q = p; q; q = q->next)
+ k += logfsflattenentry(is, nil, 0, q);
+ if(k) {
+ u32int k2;
+// print("drsinit: %ud bytes extra\n", k);
+ drs->data = logfsrealloc(nil, k);
+ if(drs->data == nil) {
+ logfsfreemem(drs);
+ return nil;
+ }
+ k2 = 0;
+ for(q = p; q; q = q->next)
+ k2 += logfsflattenentry(is, drs->data + k2, k - k2, q);
+ drs->limit = drs->offset + k;
+ }
+// print("drsinit: rcount %ud\n", *rcount);
+ return drs;
+}
+
+static void
+drsread(DirReadState *drs, uchar *buf, u32int buflen, u32int *rcount)
+{
+ uchar *p;
+ *rcount = 0;
+ p = drs->data + drs->lastoffset - drs->offset;
+ while(drs->lastoffset < drs->limit) {
+ /*
+ * copy an entry, if it fits
+ */
+ uint len = GBIT16(p) + BIT16SZ;
+ if(len > buflen)
+ break;
+ memcpy(buf, p, len);
+ drs->lastoffset += len;
+ *rcount += len;
+ buf += len;
+ buflen -= len;
+ p += len;
+ }
+ if(drs->lastoffset >= drs->limit) {
+ logfsfreemem(drs->data);
+ drs->data = nil;
+ }
+}
+
+void
+logfsdrsfree(DirReadState **drsp)
+{
+ DirReadState *drs = *drsp;
+ if(drs) {
+ logfsfreemem(drs->data);
+ logfsfreemem(drs);
+ *drsp = nil;
+ }
+}
+
+static int
+reader(void *magic, u32int baseoffset, u32int limitoffset, Extent *e, u32int extentoffset)
+{
+ ReaderState *s = magic;
+ LogfsServer *server;
+ LogfsLowLevel *ll;
+ LogfsLowLevelReadResult llrr;
+ long seq;
+ int page;
+ int offset;
+ long block;
+ int pagesize;
+ LogSegment *seg;
+ int replace;
+
+ if(e == nil) {
+//print("fill(%d, %d)\n", baseoffset, limitoffset);
+ memset(s->buf + baseoffset, 0, limitoffset - baseoffset);
+ if(limitoffset > s->maxoffset)
+ s->maxoffset = limitoffset;
+ return 1;
+ }
+ server = s->server;
+ ll = server->ll;
+ /*
+ * extentoffset is how much to trim off the front of the extent
+ */
+ logfsflashaddr2spo(server, e->flashaddr + extentoffset, &seq, &page, &offset);
+ /*
+ * offset is the offset within the page to where e->min is stored
+ */
+//print("read(%d, %d, %c%ld/%ud/%ud)\n",
+// baseoffset, limitoffset, (e->flashaddr & LogAddr) ? 'L' : 'D', seq, page, offset);
+ if(e->flashaddr & LogAddr) {
+ if(seq >= server->activelog->unsweptblockindex && seq <= server->activelog->curblockindex)
+ seg = server->activelog;
+ else if(server->sweptlog && seq <= server->sweptlog->curblockindex)
+ seg = server->sweptlog;
+ else {
+ print("logfsserverread: illegal log sequence number %ld (active=[%ld, %ld], swept=[%ld, %ld])\n",
+ seq, server->activelog->unsweptblockindex, server->activelog->curblockindex,
+ server->sweptlog ? 0L : -1L, server->sweptlog ? server->sweptlog->curblockindex : -1L);
+ s->errmsg = logfseinternal;
+ return -1;
+ }
+ if(seg->curpage == page && seg->curblockindex == seq) {
+ /*
+ * it hasn't made it to disk yet
+ */
+ memcpy(s->buf + baseoffset, seg->pagebuf + offset, limitoffset - baseoffset);
+ goto done;
+ }
+ if(seq < seg->unsweptblockindex) {
+ /* data already swept */
+ print("logfsserverread: log address has been swept\n");
+ s->errmsg = logfseinternal;
+ return -1;
+ }
+ block = seg->blockmap[seq];
+ }
+ else {
+ seg = nil;
+ if(seq >= server->ndatablocks)
+ block = -1;
+ else
+ block = server->datablock[seq].block;
+ if(block < 0) {
+ print("logfsserveread: data address does not exist\n");
+ s->errmsg = logfseinternal;
+ return -1;
+ }
+ }
+ /*
+ * read as many pages as necessary to get to the limitoffset
+ */
+ pagesize = 1 << ll->l2pagesize;
+ replace = 0;
+ while(baseoffset < limitoffset) {
+ u32int thistime;
+ thistime = pagesize - offset;
+ if(thistime > (limitoffset - baseoffset))
+ thistime = limitoffset - baseoffset;
+ s->errmsg = (*ll->readpagerange)(ll, s->buf + baseoffset, block, page,
+ offset, thistime, &llrr);
+ if(s->errmsg)
+ return -1;
+ if(llrr != LogfsLowLevelReadResultOk) {
+ replace = 1;
+ }
+ baseoffset += thistime;
+ page++;
+ offset = 0;
+ }
+ if(replace) {
+ s->errmsg = logfsserverreplaceblock(server, seg, seq);
+ if(s->errmsg)
+ return -1;
+ }
+done:
+ if(limitoffset > s->maxoffset)
+ s->maxoffset = limitoffset;
+ return 1;
+}
+
+char *
+logfsserverread(LogfsServer *server, u32int fid, u32int offset, u32int count, uchar *buf, u32int buflen, u32int *rcount)
+{
+ Fid *f;
+ Entry *e;
+ ReaderState s;
+ int rv;
+
+ if(server->trace > 1)
+ print("logfsserverread(%ud, %ud, %ud)\n", fid, offset, count);
+ f = logfsfidmapfindentry(server->fidmap, fid);
+ if(f == nil)
+ return logfsebadfid;
+ if(f->openmode < 0)
+ return logfsefidnotopen;
+ if((f->openmode & 3) == OWRITE)
+ return logfseaccess;
+ if(count > buflen)
+ return Etoobig;
+ e = f->entry;
+ if(e->deadandgone)
+ return Eio;
+ if(e->qid.type & QTDIR) {
+ if(offset != 0) {
+ if(f->drs == nil || f->drs->lastoffset != offset)
+ return Eio;
+ drsread(f->drs, buf, count, rcount);
+ }
+ else {
+ logfsdrsfree(&f->drs);
+ f->drs = drsinit(server->is, e->u.dir.list, buf, count, rcount);
+ if(f->drs == nil)
+ return Enomem;
+ }
+ return nil;
+ }
+ if(offset >= e->u.file.length) {
+ *rcount = 0;
+ return nil;
+ }
+ s.buf = buf;
+ s.server = server;
+ s.maxoffset = 0;
+ rv = logfsextentlistwalkrange(e->u.file.extent, reader, &s, offset, offset + count);
+ if(rv < 0)
+ return s.errmsg;
+ *rcount = s.maxoffset;
+ return nil;
+}
+