implement Vac; include "sys.m";include "venti.m";include "vac.m"; sys: Sys;venti: Venti; werrstr, sprint, fprint, fildes: import sys;Roottype, Dirtype, Pointertype0, Datatype: import venti;Score, Session, Scoresize: import venti; dflag = 0; # from venti.bBIT8SZ: 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();} pstring(a: array of byte, o: int, s: string): int{ sa := array of byte s; # could do conversion ourselves n := len sa; a[o] = byte (n >> 8); a[o+1] = byte n; a[o+2:] = sa; return o+BIT16SZ+n;} gstring(a: array of byte, o: int): (string, int){ if(o < 0 || o+BIT16SZ > len a) return (nil, -1); l := (int a[o] << 8) | int a[o+1]; if(l > Maxstringsize) return (nil, -1); o += BIT16SZ; e := o+l; if(e > len a) return (nil, -1); return (string a[o:e], e);} gtstring(a: array of byte, o: int, n: int): string{ e := o + n; if(e > len a) return nil; for(i := o; i < e; i++) if(a[i] == byte 0) break; return string a[o:i];} Root.new(name, rtype: string, score: Score, blocksize: int, prev: ref Score): ref Root{ return ref Root(Rootversion, name, rtype, score, blocksize, prev);} Root.pack(r: self ref Root): array of byte{ d := array[Rootsize] of byte; i := 0; i = p16(d, i, r.version); i = ptstring(d, i, r.name, Rootnamelen); if(i < 0) return nil; i = ptstring(d, i, r.rtype, Rootnamelen); if(i < 0) return nil; i = pscore(d, i, r.score); i = p16(d, i, r.blocksize); if(r.prev == nil) { for(j := 0; j < Scoresize; j++) d[i+j] = byte 0; i += Scoresize; } else i = pscore(d, i, *r.prev); if(i != len d) { sys->werrstr("root pack, bad length: "+string i); return nil; } return d;} Root.unpack(d: array of byte): ref Root{ if(len d != Rootsize){ sys->werrstr("root entry is wrong length"); return nil; } r := ref blankroot; r.version = g16(d, 0); if(r.version != Rootversion){ sys->werrstr("unknown root version"); return nil; } o := BIT16SZ; r.name = gtstring(d, o, Rootnamelen); o += Rootnamelen; r.rtype = gtstring(d, o, Rootnamelen); o += Rootnamelen; r.score = gscore(d, o); o += Scoresize; r.blocksize = g16(d, o); o += BIT16SZ; r.prev = ref gscore(d, o); return r;} Entry.new(psize, dsize, flags: int, size: big, score: Venti->Score): ref Entry{ return ref Entry(0, psize, dsize, (flags&Entrydepthmask)>>Entrydepthshift, flags, size, score);} Entry.pack(e: self ref Entry): array of byte{ d := array[Entrysize] of byte; i := 0; i = p32(d, i, e.gen); i = p16(d, i, e.psize); i = p16(d, i, e.dsize); e.flags |= e.depth<<Entrydepthshift; d[i++] = byte e.flags; for(j := 0; j < 5; j++) d[i++] = byte 0; i = p48(d, i, e.size); i = pscore(d, i, e.score); if(i != len d) { werrstr(sprint("bad length, have %d, want %d", i, len d)); return nil; } return d;} Entry.unpack(d: array of byte): ref Entry{ if(len d != Entrysize){ sys->werrstr("entry is wrong length"); return nil; } e := ref blankentry; i := 0; e.gen = g32(d, i); i += BIT32SZ; e.psize = g16(d, i); i += BIT16SZ; e.dsize = g16(d, i); i += BIT16SZ; e.flags = int d[i]; e.depth = (e.flags & Entrydepthmask) >> Entrydepthshift; e.flags &= ~Entrydepthmask; i += BIT8SZ; i += 5; # skip something... e.size = g48(d, i); i += BIT48SZ; e.score = gscore(d, i); i += Scoresize; if((e.flags & Entryactive) == 0) return e; if(!checksize(e.psize) || !checksize(e.dsize)){ sys->werrstr(sys->sprint("bad blocksize (%d or %d)", e.psize, e.dsize)); return nil; } return e;} Direntry.new(): ref Direntry{ return ref Direntry(9, "", 0, 0, 0, 0, big 0, "", "", "", 0, 0, 0, 0, 0, 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);} 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{ # assume version 9 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; # + qidspace? 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(i != len d) { 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) { werrstr(sprint("bad magic (%x, want %x)", magic, Direntrymagic)); return nil; } (de.version, i) = eg16(d, i); if(de.version != 8 && de.version != 9) { 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; if(de.version == 9) ; # xxx handle qid space?, can be in here return de; } exception e { "too small:*" => 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) { 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) { 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) { 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) { 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) { 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 := Entryactive; if(f.dtype & Dirtype) flags |= Entrydir; flags |= i<<Entrydepthshift; score := Score(f.p[i].data()); if(len score.a == 0) score = Score.zero(); return (0, Entry.new(len f.p[i].d, f.dsize, 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); } 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;Lines 1–500 of 1094. Load more
implement Vac; include "sys.m"; include "venti.m"; include "vac.m"; sys: Sys; venti: Venti; werrstr, sprint, fprint, fildes: import sys; Roottype, Dirtype, Pointertype0, Datatype: import venti; Score, Session, Scoresize: import venti; dflag = 0; # from venti.b 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(); } pstring(a: array of byte, o: int, s: string): int { sa := array of byte s; # could do conversion ourselves n := len sa; a[o] = byte (n >> 8); a[o+1] = byte n; a[o+2:] = sa; return o+BIT16SZ+n; } gstring(a: array of byte, o: int): (string, int) { if(o < 0 || o+BIT16SZ > len a) return (nil, -1); l := (int a[o] << 8) | int a[o+1]; if(l > Maxstringsize) return (nil, -1); o += BIT16SZ; e := o+l; if(e > len a) return (nil, -1); return (string a[o:e], e); } gtstring(a: array of byte, o: int, n: int): string { e := o + n; if(e > len a) return nil; for(i := o; i < e; i++) if(a[i] == byte 0) break; return string a[o:i]; } Root.new(name, rtype: string, score: Score, blocksize: int, prev: ref Score): ref Root { return ref Root(Rootversion, name, rtype, score, blocksize, prev); } Root.pack(r: self ref Root): array of byte { d := array[Rootsize] of byte; i := 0; i = p16(d, i, r.version); i = ptstring(d, i, r.name, Rootnamelen); if(i < 0) return nil; i = ptstring(d, i, r.rtype, Rootnamelen); if(i < 0) return nil; i = pscore(d, i, r.score); i = p16(d, i, r.blocksize); if(r.prev == nil) { for(j := 0; j < Scoresize; j++) d[i+j] = byte 0; i += Scoresize; } else i = pscore(d, i, *r.prev); if(i != len d) { sys->werrstr("root pack, bad length: "+string i); return nil; } return d; } Root.unpack(d: array of byte): ref Root { if(len d != Rootsize){ sys->werrstr("root entry is wrong length"); return nil; } r := ref blankroot; r.version = g16(d, 0); if(r.version != Rootversion){ sys->werrstr("unknown root version"); return nil; } o := BIT16SZ; r.name = gtstring(d, o, Rootnamelen); o += Rootnamelen; r.rtype = gtstring(d, o, Rootnamelen); o += Rootnamelen; r.score = gscore(d, o); o += Scoresize; r.blocksize = g16(d, o); o += BIT16SZ; r.prev = ref gscore(d, o); return r; } Entry.new(psize, dsize, flags: int, size: big, score: Venti->Score): ref Entry { return ref Entry(0, psize, dsize, (flags&Entrydepthmask)>>Entrydepthshift, flags, size, score); } Entry.pack(e: self ref Entry): array of byte { d := array[Entrysize] of byte; i := 0; i = p32(d, i, e.gen); i = p16(d, i, e.psize); i = p16(d, i, e.dsize); e.flags |= e.depth<<Entrydepthshift; d[i++] = byte e.flags; for(j := 0; j < 5; j++) d[i++] = byte 0; i = p48(d, i, e.size); i = pscore(d, i, e.score); if(i != len d) { werrstr(sprint("bad length, have %d, want %d", i, len d)); return nil; } return d; } Entry.unpack(d: array of byte): ref Entry { if(len d != Entrysize){ sys->werrstr("entry is wrong length"); return nil; } e := ref blankentry; i := 0; e.gen = g32(d, i); i += BIT32SZ; e.psize = g16(d, i); i += BIT16SZ; e.dsize = g16(d, i); i += BIT16SZ; e.flags = int d[i]; e.depth = (e.flags & Entrydepthmask) >> Entrydepthshift; e.flags &= ~Entrydepthmask; i += BIT8SZ; i += 5; # skip something... e.size = g48(d, i); i += BIT48SZ; e.score = gscore(d, i); i += Scoresize; if((e.flags & Entryactive) == 0) return e; if(!checksize(e.psize) || !checksize(e.dsize)){ sys->werrstr(sys->sprint("bad blocksize (%d or %d)", e.psize, e.dsize)); return nil; } return e; } Direntry.new(): ref Direntry { return ref Direntry(9, "", 0, 0, 0, 0, big 0, "", "", "", 0, 0, 0, 0, 0, 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); } 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 { # assume version 9 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; # + qidspace? 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(i != len d) { 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) { werrstr(sprint("bad magic (%x, want %x)", magic, Direntrymagic)); return nil; } (de.version, i) = eg16(d, i); if(de.version != 8 && de.version != 9) { 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; if(de.version == 9) ; # xxx handle qid space?, can be in here return de; } exception e { "too small:*" => 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) { 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) { 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) { 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) { 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) { 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 := Entryactive; if(f.dtype & Dirtype) flags |= Entrydir; flags |= i<<Entrydepthshift; score := Score(f.p[i].data()); if(len score.a == 0) score = Score.zero(); return (0, Entry.new(len f.p[i].d, f.dsize, 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); } 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;