implement Vac; include "sys.m"; sys: Sys; sprint: import sys;include "venti.m"; venti: Venti; Entrysize, Scoresize, Roottype, Dirtype, Pointertype0, Datatype: import venti; Root, Entry, Score, Session: import venti;include "vac.m"; dflag = 0; BIT8SZ: con 1;BIT16SZ: con 2;BIT32SZ: con 4;BIT48SZ: con 6;BIT64SZ: con 8; Rootnamelen: con 128;Rootversion: con 2;Direntrymagic: con 16r1c4d9072;Metablockmagic: con 16r5656fc79;Maxstringsize: con 1000; blankroot: Root;blankentry: Entry;blankdirentry: Direntry;blankmetablock: Metablock;blankmetaentry: Metaentry; init(){ sys = load Sys Sys->PATH; venti = load Venti Venti->PATH; venti->init();} Direntry.new(): ref Direntry{ return ref Direntry(9, "", 0, 0, 0, 0, big 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, big 0, big 0);} Direntry.mk(d: Sys->Dir): ref Direntry{ atime := 0; # d.atime; mode := d.mode&Modeperm; if(d.mode&sys->DMAPPEND) mode |= Modeappend; if(d.mode&sys->DMEXCL) mode |= Modeexcl; if(d.mode&sys->DMDIR) mode |= Modedir; if(d.mode&sys->DMTMP) mode |= Modetemp; return ref Direntry(9, d.name, 0, 0, 0, 0, d.qid.path, d.uid, d.gid, d.muid, d.mtime, 0, 0, atime, mode, d.mode, 0, big 0, big 0);} Direntry.mkdir(de: self ref Direntry): ref Sys->Dir{ d := ref sys->nulldir; d.name = de.elem; d.uid = de.uid; d.gid = de.gid; d.muid = de.mid; d.qid.path = de.qid; d.qid.vers = 0; d.qid.qtype = de.emode>>24; d.mode = de.emode; d.atime = de.atime; d.mtime = de.mtime; d.length = big 0; return d;} strlen(s: string): int{ return 2+len array of byte s;} Direntry.pack(de: self ref Direntry): array of byte{ if(de.version != 9) { sys->werrstr("only version 9 supported"); return nil; } length := 4+2+strlen(de.elem)+4+4+4+4+8+strlen(de.uid)+strlen(de.gid)+strlen(de.mid)+4+4+4+4+4; if(de.qidspace) length += 1+2+8+8; d := array[length] of byte; i := 0; i = p32(d, i, Direntrymagic); i = p16(d, i, de.version); i = pstring(d, i, de.elem); i = p32(d, i, de.entry); if(de.version == 9) { i = p32(d, i, de.gen); i = p32(d, i, de.mentry); i = p32(d, i, de.mgen); } i = p64(d, i, de.qid); i = pstring(d, i, de.uid); i = pstring(d, i, de.gid); i = pstring(d, i, de.mid); i = p32(d, i, de.mtime); i = p32(d, i, de.mcount); i = p32(d, i, de.ctime); i = p32(d, i, de.atime); i = p32(d, i, de.mode); if(de.qidspace) { d[i++] = byte DirQidspace; i = p16(d, i, 16); i = p64(d, i, de.qidoff); i = p64(d, i, de.qidmax); } if(i != len d) { sys->werrstr(sprint("bad length for direntry (expected %d, have %d)", len d, i)); return nil; } return d;} Direntry.unpack(d: array of byte): ref Direntry{ { de := ref blankdirentry; i := 0; magic: int; (magic, i) = eg32(d, i); if(magic != Direntrymagic) { sys->werrstr(sprint("bad magic (%x, want %x)", magic, Direntrymagic)); return nil; } (de.version, i) = eg16(d, i); if(de.version != 8 && de.version != 9) { sys->werrstr(sprint("bad version (%d)", de.version)); return nil; } (de.elem, i) = egstring(d, i); (de.entry, i) = eg32(d, i); case de.version { 8 => de.gen = 0; de.mentry = de.entry+1; de.mgen = 0; 9 => (de.gen, i) = eg32(d, i); (de.mentry, i) = eg32(d, i); (de.mgen, i) = eg32(d, i); } (de.qid, i) = eg64(d, i); (de.uid, i) = egstring(d, i); (de.gid, i) = egstring(d, i); (de.mid, i) = egstring(d, i); (de.mtime, i) = eg32(d, i); (de.mcount, i) = eg32(d, i); (de.ctime, i) = eg32(d, i); (de.atime, i) = eg32(d, i); (de.mode, i) = eg32(d, i); de.emode = de.mode&Modeperm; if(de.mode&Modeappend) de.emode |= sys->DMAPPEND; if(de.mode&Modeexcl) de.emode |= sys->DMEXCL; if(de.mode&Modedir) de.emode |= sys->DMDIR; if(de.mode&Modetemp) de.emode |= sys->DMTMP; while(i < len d) { t := int d[i++]; n: int; (n, i) = eg16(d, i); case t { DirQidspace => if(n != 16) { sys->werrstr(sprint("invalid qidspace length %d", n)); return nil; } de.qidspace = 1; (de.qidoff, i) = eg64(d, i); (de.qidmax, i) = eg64(d, i); * => # ignore other optional fields i += n; } } return de; } exception e { "too small:*" => sys->werrstr("direntry "+e); return nil; * => raise e; }} Metablock.new(): ref Metablock{ return ref Metablock(0, 0, 0, 0);} Metablock.pack(mb: self ref Metablock, d: array of byte){ i := 0; i = p32(d, i, Metablockmagic); i = p16(d, i, mb.size); i = p16(d, i, mb.free); i = p16(d, i, mb.maxindex); i = p16(d, i, mb.nindex);} Metablock.unpack(d: array of byte): ref Metablock{ if(len d < Metablocksize) { sys->werrstr(sprint("bad length for metablock (%d, want %d)", len d, Metablocksize)); return nil; } i := 0; magic := g32(d, i); if(magic != Metablockmagic && magic != Metablockmagic+1) { sys->werrstr(sprint("bad magic for metablock (%x, need %x)", magic, Metablockmagic)); return nil; } i += BIT32SZ; mb := ref blankmetablock; mb.size = g16(d, i); i += BIT16SZ; mb.free = g16(d, i); i += BIT16SZ; mb.maxindex = g16(d, i); i += BIT16SZ; mb.nindex = g16(d, i); i += BIT16SZ; if(mb.nindex == 0) { sys->werrstr("bad metablock, nindex=0"); return nil; } return mb;} Metaentry.pack(me: self ref Metaentry, d: array of byte){ i := 0; i = p16(d, i, me.offset); i = p16(d, i, me.size);} Metaentry.unpack(d: array of byte, i: int): ref Metaentry{ o := Metablocksize+i*Metaentrysize; if(o+Metaentrysize > len d) { sys->werrstr(sprint("meta entry lies outside meta block, i=%d", i)); return nil; } me := ref blankmetaentry; me.offset = g16(d, o); o += BIT16SZ; me.size = g16(d, o); o += BIT16SZ; if(me.offset+me.size > len d) { sys->werrstr(sprint("meta entry points outside meta block, i=%d", i)); return nil; } return me;} Page.new(dsize: int): ref Page{ psize := (dsize/Scoresize)*Scoresize; return ref Page(array[psize] of byte, 0);} Page.add(p: self ref Page, s: Score){ for(i := 0; i < Scoresize; i++) p.d[p.o+i] = s.a[i]; p.o += Scoresize;} Page.full(p: self ref Page): int{ return p.o+Scoresize > len p.d;} Page.data(p: self ref Page): array of byte{ for(i := p.o; i >= Scoresize; i -= Scoresize) if(!Score(p.d[i-Scoresize:i]).eq(Score.zero())) break; return p.d[:i];} File.new(s: ref Session, dtype, dsize: int): ref File{ p := array[1] of ref Page; p[0] = Page.new(dsize); return ref File(p, dtype, dsize, big 0, s);} fflush(f: ref File, last: int): (int, ref Entry){ for(i := 0; i < len f.p; i++) { if(!last && !f.p[i].full()) return (0, nil); if(last && f.p[i].o == Scoresize) { flags := venti->Entryactive; if(f.dtype == Dirtype) flags |= venti->Entrydir; flags |= i<<venti->Entrydepthshift; score := Score(f.p[i].data()); if(len score.a == 0) score = Score.zero(); return (0, ref Entry(0, len f.p[i].d, f.dsize, i, flags, f.size, score)); } (ok, score) := f.s.write(Pointertype0+i, f.p[i].data()); if(ok < 0) return (-1, nil); f.p[i] = Page.new(f.dsize); if(i+1 == len f.p) { newp := array[len f.p+1] of ref Page; newp[:] = f.p; newp[len newp-1] = Page.new(f.dsize); f.p = newp; } f.p[i+1].add(score); } sys->werrstr("internal error in fflush"); return (-1, nil);} File.write(f: self ref File, d: array of byte): int{ (fok, nil) := fflush(f, 0); if(fok < 0) return -1; length := len d; for(i := len d; i > 0; i--) if(d[i-1] != byte 0) break; d = d[:i]; (ok, score) := f.s.write(f.dtype, d); if(ok < 0) return -1; f.size += big length; f.p[0].add(score); return 0;} File.finish(f: self ref File): ref Entry{ (ok, e) := fflush(f, 1); if(ok < 0) return nil; return e;} Sink.new(s: ref Venti->Session, dsize: int): ref Sink{ dirdsize := (dsize/Entrysize)*Entrysize; return ref Sink(File.new(s, Dirtype, dsize), array[dirdsize] of byte, 0, 0);} Sink.add(m: self ref Sink, e: ref Entry): int{ ed := e.pack(); if(ed == nil) return -1; n := len m.d - m.nd; if(n > len ed) n = len ed; m.d[m.nd:] = ed[:n]; m.nd += n; if(n < len ed) { if(m.f.write(m.d) < 0) return -1; m.nd = len ed - n; m.d[:] = ed[n:]; } return m.ne++;} Sink.finish(m: self ref Sink): ref Entry{ if(m.nd > 0) if(m.f.write(m.d[:m.nd]) < 0) return nil; e := m.f.finish(); e.dsize = len m.d; return e;} elemcmp(a, b: array of byte, fossil: int): int{ for(i := 0; i < len a && i < len b; i++) if(a[i] != b[i]) return (int a[i] - int b[i]); if(fossil) return len a - len b; return len b - len a;} Mentry.cmp(a, b: ref Mentry): int{ return elemcmp(array of byte a.elem, array of byte b.elem, 0);} MSink.new(s: ref Venti->Session, dsize: int): ref MSink{ return ref MSink(File.new(s, Datatype, dsize), array[dsize] of byte, 0, nil);} l2a[T](l: list of T): array of T{ a := array[len l] of T; i := 0; for(; l != nil; l = tl l) a[i++] = hd l; return a;} insertsort[T](a: array of T) for { T => cmp: fn(a, b: T): int; }{ for(i := 1; i < len a; i++) { tmp := a[i]; for(j := i; j > 0 && T.cmp(a[j-1], tmp) > 0; j--) a[j] = a[j-1]; a[j] = tmp; }} mflush(m: ref MSink, last: int): int{ d := array[len m.de] of byte; me := l2a(m.l); insertsort(me); o := Metablocksize; deo := o+len m.l*Metaentrysize; for(i := 0; i < len me; i++) { me[i].me.offset += deo; me[i].me.pack(d[o:]); o += Metaentrysize; } d[o:] = m.de[:m.nde]; o += m.nde; if(!last) while(o < len d) d[o++] = byte 0; mb := Metablock.new(); mb.nindex = len m.l; mb.maxindex = mb.nindex; mb.free = 0; mb.size = o; mb.pack(d); if(m.f.write(d[:o]) < 0) return -1; m.nde = 0; m.l = nil; return 0;} MSink.add(m: self ref MSink, de: ref Direntry): int{ d := de.pack(); if(d == nil) return -1;if(dflag) say(sprint("msink: adding direntry, length %d", len d)); if(Metablocksize+len m.l*Metaentrysize+m.nde + Metaentrysize+len d > len m.de) if(mflush(m, 0) < 0) return -1; m.de[m.nde:] = d; m.l = ref Mentry(de.elem, ref Metaentry(m.nde, len d))::m.l; m.nde += len d; return 0;} MSink.finish(m: self ref MSink): ref Entry{ if(m.nde > 0) mflush(m, 1); return m.f.finish();} Source.new(s: ref Session, e: ref Entry): ref Source{ dsize := e.dsize; if(e.flags&venti->Entrydir)Lines 1–500 of 1012. Load more
implement Vac; include "sys.m"; sys: Sys; sprint: import sys; include "venti.m"; venti: Venti; Entrysize, Scoresize, Roottype, Dirtype, Pointertype0, Datatype: import venti; Root, Entry, Score, Session: import venti; include "vac.m"; dflag = 0; BIT8SZ: con 1; BIT16SZ: con 2; BIT32SZ: con 4; BIT48SZ: con 6; BIT64SZ: con 8; Rootnamelen: con 128; Rootversion: con 2; Direntrymagic: con 16r1c4d9072; Metablockmagic: con 16r5656fc79; Maxstringsize: con 1000; blankroot: Root; blankentry: Entry; blankdirentry: Direntry; blankmetablock: Metablock; blankmetaentry: Metaentry; init() { sys = load Sys Sys->PATH; venti = load Venti Venti->PATH; venti->init(); } Direntry.new(): ref Direntry { return ref Direntry(9, "", 0, 0, 0, 0, big 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, big 0, big 0); } Direntry.mk(d: Sys->Dir): ref Direntry { atime := 0; # d.atime; mode := d.mode&Modeperm; if(d.mode&sys->DMAPPEND) mode |= Modeappend; if(d.mode&sys->DMEXCL) mode |= Modeexcl; if(d.mode&sys->DMDIR) mode |= Modedir; if(d.mode&sys->DMTMP) mode |= Modetemp; return ref Direntry(9, d.name, 0, 0, 0, 0, d.qid.path, d.uid, d.gid, d.muid, d.mtime, 0, 0, atime, mode, d.mode, 0, big 0, big 0); } Direntry.mkdir(de: self ref Direntry): ref Sys->Dir { d := ref sys->nulldir; d.name = de.elem; d.uid = de.uid; d.gid = de.gid; d.muid = de.mid; d.qid.path = de.qid; d.qid.vers = 0; d.qid.qtype = de.emode>>24; d.mode = de.emode; d.atime = de.atime; d.mtime = de.mtime; d.length = big 0; return d; } strlen(s: string): int { return 2+len array of byte s; } Direntry.pack(de: self ref Direntry): array of byte { if(de.version != 9) { sys->werrstr("only version 9 supported"); return nil; } length := 4+2+strlen(de.elem)+4+4+4+4+8+strlen(de.uid)+strlen(de.gid)+strlen(de.mid)+4+4+4+4+4; if(de.qidspace) length += 1+2+8+8; d := array[length] of byte; i := 0; i = p32(d, i, Direntrymagic); i = p16(d, i, de.version); i = pstring(d, i, de.elem); i = p32(d, i, de.entry); if(de.version == 9) { i = p32(d, i, de.gen); i = p32(d, i, de.mentry); i = p32(d, i, de.mgen); } i = p64(d, i, de.qid); i = pstring(d, i, de.uid); i = pstring(d, i, de.gid); i = pstring(d, i, de.mid); i = p32(d, i, de.mtime); i = p32(d, i, de.mcount); i = p32(d, i, de.ctime); i = p32(d, i, de.atime); i = p32(d, i, de.mode); if(de.qidspace) { d[i++] = byte DirQidspace; i = p16(d, i, 16); i = p64(d, i, de.qidoff); i = p64(d, i, de.qidmax); } if(i != len d) { sys->werrstr(sprint("bad length for direntry (expected %d, have %d)", len d, i)); return nil; } return d; } Direntry.unpack(d: array of byte): ref Direntry { { de := ref blankdirentry; i := 0; magic: int; (magic, i) = eg32(d, i); if(magic != Direntrymagic) { sys->werrstr(sprint("bad magic (%x, want %x)", magic, Direntrymagic)); return nil; } (de.version, i) = eg16(d, i); if(de.version != 8 && de.version != 9) { sys->werrstr(sprint("bad version (%d)", de.version)); return nil; } (de.elem, i) = egstring(d, i); (de.entry, i) = eg32(d, i); case de.version { 8 => de.gen = 0; de.mentry = de.entry+1; de.mgen = 0; 9 => (de.gen, i) = eg32(d, i); (de.mentry, i) = eg32(d, i); (de.mgen, i) = eg32(d, i); } (de.qid, i) = eg64(d, i); (de.uid, i) = egstring(d, i); (de.gid, i) = egstring(d, i); (de.mid, i) = egstring(d, i); (de.mtime, i) = eg32(d, i); (de.mcount, i) = eg32(d, i); (de.ctime, i) = eg32(d, i); (de.atime, i) = eg32(d, i); (de.mode, i) = eg32(d, i); de.emode = de.mode&Modeperm; if(de.mode&Modeappend) de.emode |= sys->DMAPPEND; if(de.mode&Modeexcl) de.emode |= sys->DMEXCL; if(de.mode&Modedir) de.emode |= sys->DMDIR; if(de.mode&Modetemp) de.emode |= sys->DMTMP; while(i < len d) { t := int d[i++]; n: int; (n, i) = eg16(d, i); case t { DirQidspace => if(n != 16) { sys->werrstr(sprint("invalid qidspace length %d", n)); return nil; } de.qidspace = 1; (de.qidoff, i) = eg64(d, i); (de.qidmax, i) = eg64(d, i); * => # ignore other optional fields i += n; } } return de; } exception e { "too small:*" => sys->werrstr("direntry "+e); return nil; * => raise e; } } Metablock.new(): ref Metablock { return ref Metablock(0, 0, 0, 0); } Metablock.pack(mb: self ref Metablock, d: array of byte) { i := 0; i = p32(d, i, Metablockmagic); i = p16(d, i, mb.size); i = p16(d, i, mb.free); i = p16(d, i, mb.maxindex); i = p16(d, i, mb.nindex); } Metablock.unpack(d: array of byte): ref Metablock { if(len d < Metablocksize) { sys->werrstr(sprint("bad length for metablock (%d, want %d)", len d, Metablocksize)); return nil; } i := 0; magic := g32(d, i); if(magic != Metablockmagic && magic != Metablockmagic+1) { sys->werrstr(sprint("bad magic for metablock (%x, need %x)", magic, Metablockmagic)); return nil; } i += BIT32SZ; mb := ref blankmetablock; mb.size = g16(d, i); i += BIT16SZ; mb.free = g16(d, i); i += BIT16SZ; mb.maxindex = g16(d, i); i += BIT16SZ; mb.nindex = g16(d, i); i += BIT16SZ; if(mb.nindex == 0) { sys->werrstr("bad metablock, nindex=0"); return nil; } return mb; } Metaentry.pack(me: self ref Metaentry, d: array of byte) { i := 0; i = p16(d, i, me.offset); i = p16(d, i, me.size); } Metaentry.unpack(d: array of byte, i: int): ref Metaentry { o := Metablocksize+i*Metaentrysize; if(o+Metaentrysize > len d) { sys->werrstr(sprint("meta entry lies outside meta block, i=%d", i)); return nil; } me := ref blankmetaentry; me.offset = g16(d, o); o += BIT16SZ; me.size = g16(d, o); o += BIT16SZ; if(me.offset+me.size > len d) { sys->werrstr(sprint("meta entry points outside meta block, i=%d", i)); return nil; } return me; } Page.new(dsize: int): ref Page { psize := (dsize/Scoresize)*Scoresize; return ref Page(array[psize] of byte, 0); } Page.add(p: self ref Page, s: Score) { for(i := 0; i < Scoresize; i++) p.d[p.o+i] = s.a[i]; p.o += Scoresize; } Page.full(p: self ref Page): int { return p.o+Scoresize > len p.d; } Page.data(p: self ref Page): array of byte { for(i := p.o; i >= Scoresize; i -= Scoresize) if(!Score(p.d[i-Scoresize:i]).eq(Score.zero())) break; return p.d[:i]; } File.new(s: ref Session, dtype, dsize: int): ref File { p := array[1] of ref Page; p[0] = Page.new(dsize); return ref File(p, dtype, dsize, big 0, s); } fflush(f: ref File, last: int): (int, ref Entry) { for(i := 0; i < len f.p; i++) { if(!last && !f.p[i].full()) return (0, nil); if(last && f.p[i].o == Scoresize) { flags := venti->Entryactive; if(f.dtype == Dirtype) flags |= venti->Entrydir; flags |= i<<venti->Entrydepthshift; score := Score(f.p[i].data()); if(len score.a == 0) score = Score.zero(); return (0, ref Entry(0, len f.p[i].d, f.dsize, i, flags, f.size, score)); } (ok, score) := f.s.write(Pointertype0+i, f.p[i].data()); if(ok < 0) return (-1, nil); f.p[i] = Page.new(f.dsize); if(i+1 == len f.p) { newp := array[len f.p+1] of ref Page; newp[:] = f.p; newp[len newp-1] = Page.new(f.dsize); f.p = newp; } f.p[i+1].add(score); } sys->werrstr("internal error in fflush"); return (-1, nil); } File.write(f: self ref File, d: array of byte): int { (fok, nil) := fflush(f, 0); if(fok < 0) return -1; length := len d; for(i := len d; i > 0; i--) if(d[i-1] != byte 0) break; d = d[:i]; (ok, score) := f.s.write(f.dtype, d); if(ok < 0) return -1; f.size += big length; f.p[0].add(score); return 0; } File.finish(f: self ref File): ref Entry { (ok, e) := fflush(f, 1); if(ok < 0) return nil; return e; } Sink.new(s: ref Venti->Session, dsize: int): ref Sink { dirdsize := (dsize/Entrysize)*Entrysize; return ref Sink(File.new(s, Dirtype, dsize), array[dirdsize] of byte, 0, 0); } Sink.add(m: self ref Sink, e: ref Entry): int { ed := e.pack(); if(ed == nil) return -1; n := len m.d - m.nd; if(n > len ed) n = len ed; m.d[m.nd:] = ed[:n]; m.nd += n; if(n < len ed) { if(m.f.write(m.d) < 0) return -1; m.nd = len ed - n; m.d[:] = ed[n:]; } return m.ne++; } Sink.finish(m: self ref Sink): ref Entry { if(m.nd > 0) if(m.f.write(m.d[:m.nd]) < 0) return nil; e := m.f.finish(); e.dsize = len m.d; return e; } elemcmp(a, b: array of byte, fossil: int): int { for(i := 0; i < len a && i < len b; i++) if(a[i] != b[i]) return (int a[i] - int b[i]); if(fossil) return len a - len b; return len b - len a; } Mentry.cmp(a, b: ref Mentry): int { return elemcmp(array of byte a.elem, array of byte b.elem, 0); } MSink.new(s: ref Venti->Session, dsize: int): ref MSink { return ref MSink(File.new(s, Datatype, dsize), array[dsize] of byte, 0, nil); } l2a[T](l: list of T): array of T { a := array[len l] of T; i := 0; for(; l != nil; l = tl l) a[i++] = hd l; return a; } insertsort[T](a: array of T) for { T => cmp: fn(a, b: T): int; } { for(i := 1; i < len a; i++) { tmp := a[i]; for(j := i; j > 0 && T.cmp(a[j-1], tmp) > 0; j--) a[j] = a[j-1]; a[j] = tmp; } } mflush(m: ref MSink, last: int): int { d := array[len m.de] of byte; me := l2a(m.l); insertsort(me); o := Metablocksize; deo := o+len m.l*Metaentrysize; for(i := 0; i < len me; i++) { me[i].me.offset += deo; me[i].me.pack(d[o:]); o += Metaentrysize; } d[o:] = m.de[:m.nde]; o += m.nde; if(!last) while(o < len d) d[o++] = byte 0; mb := Metablock.new(); mb.nindex = len m.l; mb.maxindex = mb.nindex; mb.free = 0; mb.size = o; mb.pack(d); if(m.f.write(d[:o]) < 0) return -1; m.nde = 0; m.l = nil; return 0; } MSink.add(m: self ref MSink, de: ref Direntry): int { d := de.pack(); if(d == nil) return -1; if(dflag) say(sprint("msink: adding direntry, length %d", len d)); if(Metablocksize+len m.l*Metaentrysize+m.nde + Metaentrysize+len d > len m.de) if(mflush(m, 0) < 0) return -1; m.de[m.nde:] = d; m.l = ref Mentry(de.elem, ref Metaentry(m.nde, len d))::m.l; m.nde += len d; return 0; } MSink.finish(m: self ref MSink): ref Entry { if(m.nde > 0) mflush(m, 1); return m.f.finish(); } Source.new(s: ref Session, e: ref Entry): ref Source { dsize := e.dsize; if(e.flags&venti->Entrydir)