changelog shortlog tags branches changeset files revisions annotate raw help

Mercurial > hg > ventivac / appl/lib/vac.b

changeset 137: b59aee5994b9
parent: 7f377ffc9ad3
child: 4a91807ed74a
author: Mechiel Lukkien <mechiel@ueber.net>
date: Mon, 20 Aug 2007 17:33:35 +0200
permissions: -rw-r--r--
description: that xxx does not really have to change.
1 implement Vac;
2 
3 include "sys.m";
4 include "string.m";
5 include "venti.m";
6 include "vac.m";
7 
8 sys: Sys;
9 str: String;
10 venti: Venti;
11 
12 werrstr, sprint, fprint, fildes: import sys;
13 Root, Rootsize, Entry, Entrysize, Roottype, Dirtype, Pointertype0, Datatype: import venti;
14 Pointervarmask, Entryactive, Entrydir, Entrydepthshift, Entrydepthmask, Entrylocal, Entryvarblocks: import venti;
15 Score, Session, Scoresize: import venti;
16 
17 debug := 0;
18 
19 # from venti.b
20 BIT8SZ: con 1;
21 BIT16SZ: con 2;
22 BIT32SZ: con 4;
23 BIT48SZ: con 6;
24 BIT64SZ: con 8;
25 
26 Rootnamelen: con 128;
27 Rootversion: con 2;
28 Maxstringsize: con 1000;
29 
30 blankroot: Root;
31 blankentry: Entry;
32 blankdirentry: Direntry;
33 blankmetablock: Metablock;
34 blankmetaentry: Metaentry;
35 
36 
37 init()
38 {
39  sys = load Sys Sys->PATH;
40  str = load String String->PATH;
41  venti = load Venti Venti->PATH;
42  venti->init();
43 }
44 
45 vread(session: ref Session, s: Score, t: int, maxsize: int): array of byte
46 {
47  d := session.read(s, t, maxsize);
48  blocksread++;
49  bytesread += big len d;
50  return d;
51 }
52 
53 vwrite(session: ref Session, t: int, d: array of byte): (int, Score)
54 {
55  (ok, score) := session.write(t, d);
56  blockswritten++;
57  byteswritten += big len d;
58  return (ok, score);
59 }
60 
61 pstring(a: array of byte, o: int, s: string): int
62 {
63  sa := array of byte s; # could do conversion ourselves
64  n := len sa;
65  a[o] = byte (n >> 8);
66  a[o+1] = byte n;
67  a[o+2:] = sa;
68  return o+BIT16SZ+n;
69 }
70 
71 gstring(a: array of byte, o: int): (string, int)
72 {
73  if(o < 0 || o+BIT16SZ > len a)
74  return (nil, -1);
75  l := (int a[o] << 8) | int a[o+1];
76  if(l > Maxstringsize)
77  return (nil, -1);
78  o += BIT16SZ;
79  e := o+l;
80  if(e > len a)
81  return (nil, -1);
82  return (string a[o:e], e);
83 }
84 
85 Direntry.new(): ref Direntry
86 {
87  return ref Direntry(9, "", 0, 0, 0, 0, big 0, "", "", "", 0, 0, 0, 0, 0, 0);
88 }
89 
90 Direntry.mk(d: Sys->Dir): ref Direntry
91 {
92  atime := 0; # d.atime;
93  mode := d.mode&Modeperm;
94  if(d.mode&sys->DMAPPEND)
95  mode |= Modeappend;
96  if(d.mode&sys->DMEXCL)
97  mode |= Modeexcl;
98  if(d.mode&sys->DMDIR)
99  mode |= Modedir;
100  if(d.mode&sys->DMTMP)
101  mode |= Modetemp;
102  return ref Direntry(9, d.name, 0, 0, 0, 0, d.qid.path, d.uid, d.gid, d.muid, d.mtime, d.qid.vers, 0, atime, mode, d.mode);
103 }
104 
105 Direntry.mkdir(de: self ref Direntry): ref Sys->Dir
106 {
107  d := ref sys->nulldir;
108  d.name = de.elem;
109  d.uid = de.uid;
110  d.gid = de.gid;
111  d.muid = de.mid;
112  d.qid.path = de.qid;
113  d.qid.vers = 0;
114  d.qid.qtype = de.emode>>24;
115  d.mode = de.emode;
116  d.atime = de.atime;
117  d.mtime = de.mtime;
118  d.length = big 0;
119  return d;
120 }
121 
122 strlen(s: string): int
123 {
124  return 2+len array of byte s;
125 }
126 
127 Direntry.pack(de: self ref Direntry): array of byte
128 {
129  # assume version 9
130  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?
131 
132  d := array[length] of byte;
133  i := 0;
134  i = p32(d, i, Direntrymagic);
135  i = p16(d, i, de.version);
136  i = pstring(d, i, de.elem);
137  i = p32(d, i, de.entry);
138  if(de.version == 9) {
139  i = p32(d, i, de.gen);
140  i = p32(d, i, de.mentry);
141  i = p32(d, i, de.mgen);
142  }
143  i = p64(d, i, de.qid);
144  i = pstring(d, i, de.uid);
145  i = pstring(d, i, de.gid);
146  i = pstring(d, i, de.mid);
147  i = p32(d, i, de.mtime);
148  i = p32(d, i, de.mcount);
149  i = p32(d, i, de.ctime);
150  i = p32(d, i, de.atime);
151  i = p32(d, i, de.mode);
152  if(i != len d) {
153  werrstr(sprint("bad length for direntry (expected %d, have %d)", len d, i));
154  return nil;
155  }
156  return d;
157 }
158 
159 Direntry.unpack(d: array of byte): ref Direntry
160 {
161  {
162  de := ref blankdirentry;
163  i := 0;
164  magic: int;
165  (magic, i) = eg32(d, i);
166  if(magic != Direntrymagic) {
167  werrstr(sprint("bad magic (%x, want %x)", magic, Direntrymagic));
168  return nil;
169  }
170  (de.version, i) = eg16(d, i);
171  if(de.version != 8 && de.version != 9) {
172  werrstr(sprint("bad version (%d)", de.version));
173  return nil;
174  }
175  (de.elem, i) = egstring(d, i);
176  (de.entry, i) = eg32(d, i);
177  case de.version {
178  8 =>
179  de.gen = 0;
180  de.mentry = de.entry+1;
181  de.mgen = 0;
182  9 =>
183  (de.gen, i) = eg32(d, i);
184  (de.mentry, i) = eg32(d, i);
185  (de.mgen, i) = eg32(d, i);
186  }
187  (de.qid, i) = eg64(d, i);
188  (de.uid, i) = egstring(d, i);
189  (de.gid, i) = egstring(d, i);
190  (de.mid, i) = egstring(d, i);
191  (de.mtime, i) = eg32(d, i);
192  (de.mcount, i) = eg32(d, i);
193  (de.ctime, i) = eg32(d, i);
194  (de.atime, i) = eg32(d, i);
195  (de.mode, i) = eg32(d, i);
196  de.emode = de.mode&Modeperm;
197  if(de.mode&Modeappend)
198  de.emode |= sys->DMAPPEND;
199  if(de.mode&Modeexcl)
200  de.emode |= sys->DMEXCL;
201  if(de.mode&Modedir)
202  de.emode |= sys->DMDIR;
203  if(de.mode&Modetemp)
204  de.emode |= sys->DMTMP;
205  if(de.version == 9)
206  ; # xxx handle qid space?, can be in here
207  return de;
208  } exception e {
209  "too small:*" =>
210  werrstr("direntry "+e);
211  return nil;
212  * =>
213  raise e;
214  }
215 }
216 
217 
218 Metablock.new(): ref Metablock
219 {
220  return ref Metablock(0, 0, 0, 0);
221 }
222 
223 Metablock.pack(mb: self ref Metablock, d: array of byte)
224 {
225  i := 0;
226  i = p32(d, i, Metablockmagic);
227  i = p16(d, i, mb.size);
228  i = p16(d, i, mb.free);
229  i = p16(d, i, mb.maxindex);
230  i = p16(d, i, mb.nindex);
231 }
232 
233 Metablock.unpack(d: array of byte): ref Metablock
234 {
235  if(len d < Metablocksize) {
236  werrstr(sprint("bad length for metablock (%d, want %d)", len d, Metablocksize));
237  return nil;
238  }
239  i := 0;
240  magic := g32(d, i);
241  if(magic != Metablockmagic && magic != Metablockmagic+1) {
242  werrstr(sprint("bad magic for metablock (%x, need %x)", magic, Metablockmagic));
243  return nil;
244  }
245  i += BIT32SZ;
246 
247  mb := ref blankmetablock;
248  mb.size = g16(d, i);
249  i += BIT16SZ;
250  mb.free = g16(d, i);
251  i += BIT16SZ;
252  mb.maxindex = g16(d, i);
253  i += BIT16SZ;
254  mb.nindex = g16(d, i);
255  i += BIT16SZ;
256  if(mb.nindex == 0) {
257  werrstr("bad metablock, nindex=0");
258  return nil;
259  }
260  return mb;
261 }
262 
263 Metaentry.pack(me: self ref Metaentry, d: array of byte)
264 {
265  i := 0;
266  i = p16(d, i, me.offset);
267  i = p16(d, i, me.size);
268 }
269 
270 Metaentry.unpack(d: array of byte, i: int): ref Metaentry
271 {
272  o := Metablocksize+i*Metaentrysize;
273  if(o+Metaentrysize > len d) {
274  werrstr(sprint("meta entry lies outside meta block, i=%d", i));
275  return nil;
276  }
277 
278  me := ref blankmetaentry;
279  me.offset = g16(d, o);
280  o += BIT16SZ;
281  me.size = g16(d, o);
282  o += BIT16SZ;
283  if(me.offset+me.size > len d) {
284  werrstr(sprint("meta entry points outside meta block, i=%d", i));
285  return nil;
286  }
287  return me;
288 }
289 
290 
291 Page.new(dsize: int, varblocks: int): ref Page
292 {
293  esize := Scoresize;
294  treesize := big 0;
295  if(varblocks)
296  esize += BIT64SZ;
297  else
298  treesize = ~big 0;
299  psize := (dsize/esize)*esize;
300  return ref Page(array[psize] of byte, 0, esize, treesize);
301 }
302 
303 Page.npointers(p: self ref Page): int
304 {
305  return p.o/p.esize;
306 }
307 
308 Page.add(p: self ref Page, s: Score, size: big)
309 {
310  p.d[p.o:] = s.a;
311  p.o += Scoresize;
312  if(p.treesize != ~big 0) {
313  p64(p.d, p.o, size);
314  p.o += BIT64SZ;
315  p.treesize += size;
316  }
317 }
318 
319 Page.full(p: self ref Page): int
320 {
321  return p.o+p.esize> len p.d;
322 }
323 
324 Page.data(p: self ref Page): array of byte
325 {
326  if(p.treesize != ~big 0)
327  return p.d[:p.o];
328  for(i := p.o; i >= Scoresize; i -= Scoresize)
329  if(!Score(p.d[i-Scoresize:i]).eq(Score.zero()))
330  break;
331  return p.d[:i];
332 }
333 
334 
335 File.new(s: ref Session, dtype, dsize, varblocks: int): ref File
336 {
337  p := array[1] of ref Page;
338  p[0] = Page.new(dsize, varblocks);
339  return ref File(p, dtype, dsize, big 0, s, varblocks);
340 }
341 
342 fflush(f: ref File, last: int): (int, ref Entry)
343 {
344  for(i := 0; i < len f.p; i++) {
345  if(!last && !f.p[i].full())
346  return (0, nil);
347  if(last && f.p[i].npointers() == 1) {
348  flags := Entryactive;
349  flags |= i<<Entrydepthshift;
350  if(f.dtype & Dirtype)
351  flags |= Entrydir;
352  if(f.varblocks)
353  flags |= Entryvarblocks;
354  d := f.p[i].data();
355  if(len d == 0)
356  d = Score.zero().a;
357  d = d[:Scoresize];
358  score := Score(d);
359  dsize := f.dsize;
360  if(f.varblocks)
361  dsize = 0;
362  return (0, Entry.new(len f.p[i].d, dsize, flags, f.size, score));
363  }
364  t := Pointertype0+i;
365  if(f.varblocks)
366  t |= Pointervarmask;
367  (ok, score) := vwrite(f.s, t, f.p[i].data());
368  if(ok < 0)
369  return (-1, nil);
370  treesize := f.p[i].treesize;
371  f.p[i] = Page.new(f.dsize, f.varblocks);
372  if(i+1 == len f.p) {
373  newp := array[len f.p+1] of ref Page;
374  newp[:] = f.p;
375  newp[len newp-1] = Page.new(f.dsize, f.varblocks);
376  f.p = newp;
377  }
378  f.p[i+1].add(score, treesize);
379  }
380  werrstr("internal error in fflush");
381  return (-1, nil);
382 }
383 
384 File.write(f: self ref File, d: array of byte): int
385 {
386  (fok, nil) := fflush(f, 0);
387  if(fok < 0)
388  return -1;
389  length := len d;
390  for(i := len d; i > 0; i--)
391  if(d[i-1] != byte 0)
392  break;
393  d = d[:i];
394  (ok, score) := vwrite(f.s, f.dtype, d);
395  if(ok < 0)
396  return -1;
397  f.size += big length;
398  f.p[0].add(score, big length);
399  return 0;
400 }
401 
402 File.finish(f: self ref File): ref Entry
403 {
404  (ok, e) := fflush(f, 1);
405  if(ok < 0)
406  return nil;
407  return e;
408 }
409 
410 File.mkstate(session: ref Venti->Session, e: ref Entry, varblocks: int): ref File
411 {
412  s := e.score;
413  dsize := e.dsize;
414  if(dsize == 0)
415  dsize = Venti->Dsize; # xxx not clean
416  if(e.depth == 0) {
417  p := Page.new(dsize, varblocks);
418  return ref File(array[1] of {p}, Datatype, dsize, big 0, session, varblocks);
419  }
420  f := ref File(array[e.depth] of ref Page, Datatype, dsize, big 0, session, varblocks);
421 say(sprint("mkstate, depth=%d", e.depth));
422  for(i := 0; i < e.depth; i++) {
423 say(sprint("mkstate: reading score=%s", e.score.text()));
424  t := Pointertype0+e.depth-1-i;
425  if(f.varblocks)
426  t |= Pointervarmask;
427  d := vread(session, s, t, Venti->Maxlumpsize);
428  if(d == nil)
429  return nil;
430  p := Page.new(dsize, varblocks);
431  p.d[:] = d;
432  p.o = len d-p.esize;
433  s = Score(p.d[p.o:p.o+Scoresize]);
434  f.p[i] = p;
435  }
436  if(varblocks) {
437  lp := f.p[len f.p-1];
438  bsize := g64(lp.d, lp.o+Scoresize);
439  f.size = e.size-bsize;
440  for(i = 0; i < len f.p; i++)
441  f.p[i].treesize -= bsize;
442  } else {
443  ls := e.size % big e.dsize;
444  if(ls == big 0)
445  ls = big e.dsize;
446  f.size = e.size-ls;
447  }
448 say(sprint("mkstate: have file, size=%bd", f.size));
449  return f;
450 }
451 
452 
453 Sink.new(s: ref Venti->Session, dsize: int): ref Sink
454 {
455  dirdsize := (dsize/Entrysize)*Entrysize;
456  return ref Sink(File.new(s, Dirtype, dsize, 0), array[dirdsize] of byte, 0, 0);
457 }
458 
459 Sink.add(m: self ref Sink, e: ref Entry): int
460 {
461  ed := e.pack();
462  if(ed == nil)
463  return -1;
464  n := len m.d - m.nd;
465  if(n > len ed)
466  n = len ed;
467  m.d[m.nd:] = ed[:n];
468  m.nd += n;
469  if(n < len ed) {
470  if(m.f.write(m.d) < 0)
471  return -1;
472  m.nd = len ed - n;
473  m.d[:] = ed[n:];
474  }
475  return m.ne++;
476 }
477 
478 Sink.finish(m: self ref Sink): ref Entry
479 {
480  if(m.nd > 0)
481  if(m.f.write(m.d[:m.nd]) < 0)
482  return nil;
483  e := m.f.finish();
484  e.dsize = len m.d;
485  return e;
486 }
487 
488 
489 elemcmp(a, b: array of byte, fossil: int): int
490 {
491  for(i := 0; i < len a && i < len b; i++)
492  if(a[i] != b[i])
493  return (int a[i] - int b[i]);
494  if(fossil)
495  return len a - len b;
496  return len b - len a;
497 }
498 
499 Mentry.cmp(a, b: ref Mentry): int
500 {
501  return elemcmp(array of byte a.elem, array of byte b.elem, 0);
502 }
503 
504 MSink.new(s: ref Venti->Session, dsize: int): ref MSink
505 {
506  return ref MSink(File.new(s, Datatype, dsize, 0), array[dsize] of byte, 0, nil);
507 }
508 
509 l2a[T](l: list of T): array of T
510 {
511  a := array[len l] of T;
512  i := 0;
513  for(; l != nil; l = tl l)
514  a[i++] = hd l;
515  return a;
516 }
517 
518 insertsort[T](a: array of T)
519  for { T => cmp: fn(a, b: T): int; }
520 {
521  for(i := 1; i < len a; i++) {
522  tmp := a[i];
523  for(j := i; j > 0 && T.cmp(a[j-1], tmp) > 0; j--)
524  a[j] = a[j-1];
525  a[j] = tmp;
526  }
527 }
528 
529 mflush(m: ref MSink, last: int): int
530 {
531  d := array[len m.de] of byte;
532 
533  me := l2a(m.l);
534  insertsort(me);
535  o := Metablocksize;
536  deo := o+len m.l*Metaentrysize;
537  for(i := 0; i < len me; i++) {
538  me[i].me.offset += deo;
539  me[i].me.pack(d[o:]);
540  o += Metaentrysize;
541  }
542  d[o:] = m.de[:m.nde];
543  o += m.nde;
544  if(!last)
545  while(o < len d)
546  d[o++] = byte 0;
547 
548  mb := Metablock.new();
549  mb.nindex = len m.l;
550  mb.maxindex = mb.nindex;
551  mb.free = 0;
552  mb.size = o;
553  mb.pack(d);
554 
555  if(m.f.write(d[:o]) < 0)
556  return -1;
557  m.nde = 0;
558  m.l = nil;
559  return 0;
560 }
561 
562 MSink.add(m: self ref MSink, de: ref Direntry): int
563 {
564  d := de.pack();
565  if(d == nil)
566  return -1;
567 say(sprint("msink: adding direntry, length %d", len d));
568  if(Metablocksize+len m.l*Metaentrysize+m.nde + Metaentrysize+len d > len m.de)
569  if(mflush(m, 0) < 0)
570  return -1;
571  m.de[m.nde:] = d;
572  m.l = ref Mentry(de.elem, ref Metaentry(m.nde, len d))::m.l;
573  m.nde += len d;
574  return 0;
575 }
576 
577 MSink.finish(m: self ref MSink): ref Entry
578 {
579  if(m.nde > 0)
580  mflush(m, 1);
581  return m.f.finish();
582 }
583 
584 Source.new(s: ref Session, e: ref Entry): ref Source
585 {
586  return ref Source(s, e);
587 }
588 
589 power(b, e: int): big
590 {
591  r := big 1;
592  while(e-- > 0)
593  r *= big b;
594  return r;
595 }
596 
597 blocksize(e: ref Entry): int
598 {
599  if(e.psize > e.dsize)
600  return e.psize;
601  return e.dsize;
602 }
603 
604 Source.get(s: self ref Source, i: big, d: array of byte): int
605 {
606  npages := (s.e.size+big (s.e.dsize-1))/big s.e.dsize;
607  if(i*big s.e.dsize >= s.e.size)
608  return 0;
609 
610  want := s.e.dsize;
611  if(i == npages-big 1)
612  want = int (s.e.size - i*big s.e.dsize);
613  last := s.e.score;
614  bsize := blocksize(s.e);
615  buf: array of byte;
616 
617  npp := s.e.psize/Scoresize; # scores per pointer block
618  np := power(npp, s.e.depth-1); # blocks referenced by score at this depth
619  for(depth := s.e.depth; depth >= 0; depth--) {
620  dtype := Pointertype0+depth-1;
621  if(depth == 0) {
622  dtype = Datatype;
623  if(s.e.flags & Entrydir)
624  dtype = Dirtype;
625  bsize = want;
626  }
627  buf = vread(s.session, last, dtype, bsize);
628  if(buf == nil)
629  return -1;
630  if(depth > 0) {
631  pi := int (i / np);
632  i %= np;
633  np /= big npp;
634  o := (pi+1)*Scoresize;
635  if(o <= len buf)
636  last = Score(buf[o-Scoresize:o]);
637  else
638  last = Score.zero();
639  }
640  }
641  for(j := len buf; j < want; j++)
642  d[j] = byte 0;
643  d[:] = buf;
644  return want;
645 }
646 
647 Source.oget(s: self ref Source, offset: big): array of byte
648 {
649  if(offset >= s.e.size)
650  return array[0] of byte;
651 
652  coffset := big 0;
653  esize := Scoresize;
654  if(s.e.flags&Entryvarblocks)
655  esize += 8;
656  last := s.e.score;
657  tlen := s.e.size;
658 
659 say(sprint("source.oget: offset=%bd last=%s tlen=%bd esize=%d", offset, last.text(), tlen, esize));
660 
661  for(depth := s.e.depth; depth > 0; depth--) {
662  dtype := Pointertype0+depth-1;
663  dtype |= Pointervarmask;
664  buf := vread(s.session, last, dtype, s.e.psize);
665  if(buf == nil)
666  return nil;
667 say(sprint("source.oget: coffset=%bd have buf, len=%d", coffset, len buf));
668 
669  (last, tlen) = (Score(buf[:Scoresize]), g64(buf, Scoresize));
670 say(sprint("source.oget: tmp last=%s tlen=%bd", last.text(), tlen));
671  for(o := esize; o+esize <= len buf; o += esize) {
672  if(offset < coffset+tlen)
673  break;
674  coffset += tlen;
675  tlen = g64(buf, o+Scoresize);
676  last = Score(buf[o:o+Scoresize]);
677 say(sprint("source.oget: tmp last=%s tlen=%bd", last.text(), tlen));
678  }
679 say(sprint("source.oget: new last=%s tlen=%bd", last.text(), tlen));
680  if(coffset+tlen < offset || coffset > offset) {
681 say(sprint("source.oget: bad... coffset=%bd offset=%bd", coffset, offset));
682  sys->werrstr("bad source.oget");
683  return nil;
684  }
685  }
686  buf := vread(s.session, last, Datatype, int tlen);
687  if(buf == nil)
688  return nil;
689  if(len buf != int tlen) {
690  sys->werrstr(sprint("unexpected block size, score=%s, want=%d have=%d", last.text(), int tlen, len buf));
691  return nil;
692  }
693  return buf[int (offset-coffset):];
694 }
695 
696 
697 Vacfile.mk(s: ref Source): ref Vacfile
698 {
699  return ref Vacfile(s, big 0);
700 }
701 
702 Vacfile.new(s: ref Session, e: ref Entry): ref Vacfile
703 {
704  return Vacfile.mk(Source.new(s, e));
705 }
706 
707 Vacfile.seek(v: self ref Vacfile, offset: big): big
708 {
709  v.o += offset;
710  if(v.o > v.s.e.size)
711  v.o = v.s.e.size;
712  return v.o;
713 }
714 
715 Vacfile.read(v: self ref Vacfile, d: array of byte, n: int): int
716 {
717  have := v.pread(d, n, v.o);
718  if(have > 0)
719  v.o += big have;
720  return have;
721 }
722 
723 Vacfile.pread(v: self ref Vacfile, d: array of byte, n: int, offset: big): int
724 {
725  if(v.s.e.flags&Entryvarblocks) {
726  buf := v.s.oget(offset);
727  if(buf == nil)
728  return -1;
729  if(len buf < n)
730  n = len buf;
731  d[:] = buf[:n];
732  return n;
733  } else {
734  dsize := v.s.e.dsize;
735  have := v.s.get(big (offset/big dsize), buf := array[dsize] of byte);
736  if(have <= 0)
737  return have;
738  say(sprint("vacfile.read: have=%d dsize=%d", have, dsize));
739  o := int (offset % big dsize);
740  have -= o;
741  if(have > n)
742  have = n;
743  if(have <= 0)
744  return 0;
745  d[:] = buf[o:o+have];
746  return have;
747  }
748 }
749 
750 
751 Vacdir.mk(vf: ref Vacfile, ms: ref Source): ref Vacdir
752 {
753  return ref Vacdir(vf, ms, big 0, 0);
754 }
755 
756 Vacdir.new(session: ref Session, e, me: ref Entry): ref Vacdir
757 {
758  vf := Vacfile.new(session, e);
759  ms := Source.new(session, me);
760  return Vacdir.mk(vf, ms);
761 
762 }
763 
764 mecmp(d: array of byte, i: int, elem: string, fromfossil: int): (int, int)
765 {
766  me := Metaentry.unpack(d, i);
767  if(me == nil)
768  return (0, 1);
769  o := me.offset+6;
770  n := g16(d, o);
771  o += BIT16SZ;
772  if(o+n > len d) {
773  werrstr("bad elem in direntry");
774  return (0, 1);
775  }
776  return (elemcmp(d[o:o+n], array of byte elem, fromfossil), 0);
777 }
778 
779 finddirentry(d: array of byte, elem: string): (int, ref Direntry)
780 {
781  mb := Metablock.unpack(d);
782  if(mb == nil)
783  return (-1, nil);
784  fromfossil := g32(d, 0) == Metablockmagic+1;
785 
786  left := 0;
787  right := mb.nindex;
788  while(left+1 != right) {
789  mid := (left+right)/2;
790  (c, err) := mecmp(d, mid, elem, fromfossil);
791  if(err)
792  return (-1, nil);
793  if(c <= 0)
794  left = mid;
795  else
796  right = mid;
797  if(c == 0)
798  break;
799  }
800  de := readdirentry(d, left);
801  if(de != nil && de.elem == elem)
802  return (1, de);
803  return (0, nil);
804 }
805 
806 Vacdir.walk(v: self ref Vacdir, elem: string): ref Direntry
807 {
808  i := big 0;
809  for(;;) {
810  n := v.ms.get(i, buf := array[v.ms.e.dsize] of byte);
811  if(n < 0)
812  return nil;
813  if(n == 0)
814  break;
815  (ok, de) := finddirentry(buf[:n], elem);
816  if(ok < 0)
817  return nil;
818  if(de != nil)
819  return de;
820  i++;
821  }
822  werrstr(sprint("no such file or directory"));
823  return nil;
824 }
825 
826 vfreadentry(vf: ref Vacfile, entry: int): ref Entry
827 {
828 say(sprint("vfreadentry: reading entry=%d", entry));
829  ebuf := array[Entrysize] of byte;
830  n := vf.pread(ebuf, len ebuf, big entry*big Entrysize);
831  if(n < 0)
832  return nil;
833  if(n != len ebuf) {
834  werrstr(sprint("bad archive, entry=%d not present", entry));
835  return nil;
836  }
837  e := Entry.unpack(ebuf);
838  if(~e.flags&Entryactive) {
839  werrstr("entry not active");
840  return nil;
841  }
842  if(e.flags&Entrylocal) {
843  werrstr("entry is local");
844  return nil;
845  }
846 say(sprint("vreadentry: have entry, score=%s", e.score.text()));
847  return e;
848 }
849 
850 Vacdir.open(vd: self ref Vacdir, de: ref Direntry): (ref Entry, ref Entry)
851 {
852 say(sprint("vacdir.open: opening entry=%d", de.entry));
853  e := vfreadentry(vd.vf, de.entry);
854  if(e == nil)
855  return (nil, nil);
856  isdir1 := de.mode & Modedir;
857  isdir2 := e.flags & Entrydir;
858  if(isdir1 && !isdir2 || !isdir1 && isdir2) {
859  werrstr("direntry directory bit does not match entry directory bit");
860  return (nil, nil);
861  }
862 say(sprint("vacdir.open: have entry, score=%s size=%bd", e.score.text(), e.size));
863  me: ref Entry;
864  if(de.mode&Modedir) {
865  me = vfreadentry(vd.vf, de.mentry);
866  if(me == nil)
867  return (nil, nil);
868 say(sprint("vacdir.open: have mentry, score=%s size=%bd", me.score.text(), e.size));
869  }
870  return (e, me);
871 }
872 
873 readdirentry(buf: array of byte, i: int): ref Direntry
874 {
875  me := Metaentry.unpack(buf, i);
876  if(me == nil)
877  return nil;
878  o := me.offset;
879  de := Direntry.unpack(buf[o:o+me.size]);
880  if(badelem(de.elem)) {
881  werrstr(sprint("bad direntry: %s", de.elem));
882  return nil;
883  }
884  return de;
885 }
886 
887 has(c: int, s: string): int
888 {
889  for(i := 0; i < len s; i++)
890  if(s[i] == c)
891  return 1;
892  return 0;
893 }
894 
895 badelem(elem: string): int
896 {
897  return elem == "" || elem == "." || elem == ".." || has('/', elem) || has(0, elem);
898 }
899 
900 Vacdir.readdir(vd: self ref Vacdir): (int, ref Direntry)
901 {
902 say(sprint("vacdir.readdir: ms.e.size=%bd vd.p=%bd vd.i=%d", vd.ms.e.size, vd.p, vd.i));
903  dsize := vd.ms.e.dsize;
904  n := vd.ms.get(vd.p, buf := array[dsize] of byte);
905  if(n <= 0)
906  return (n, nil);
907 say(sprint("vacdir.readdir: have buf, length=%d e.size=%bd", n, vd.ms.e.size));
908  mb := Metablock.unpack(buf);
909  if(mb == nil)
910  return (-1, nil);
911  de := readdirentry(buf, vd.i);
912  if(de == nil)
913  return (-1, nil);
914  vd.i++;
915  if(vd.i >= mb.nindex) {
916  vd.p++;
917  vd.i = 0;
918  }
919 say("vacdir.readdir: have entry");
920  return (1, de);
921 }
922 
923 Vacdir.rewind(vd: self ref Vacdir)
924 {
925  vd.p = big 0;
926  vd.i = 0;
927 }
928 
929 
930 openroot(session: ref Session, score: Venti->Score): (ref Vacdir, ref Direntry, string)
931 {
932  d := vread(session, score, Roottype, Rootsize);
933  if(d == nil)
934  return (nil, nil, sprint("reading vac score: %r"));
935  r := Root.unpack(d);
936  if(r == nil)
937  return (nil, nil, sprint("bad vac root block: %r"));
938  say("have root");
939  topscore := r.score;
940 
941  d = vread(session, topscore, Dirtype, 3*Entrysize);
942  if(d == nil)
943  return (nil, nil, sprint("reading rootdir score: %r"));
944  if(len d != 3*Entrysize) {
945  say("top entries not in directory of 3 elements, assuming it's from fossil");
946  if(len d % Entrysize != 0 && len d == 2*Entrysize != 0) # what's in the second 40 bytes? looks like 2nd 20 bytes of it is zero score
947  return (nil, nil, sprint("bad fossil rootdir, have %d bytes, need %d or %d", len d, Entrysize, 2*Entrysize));
948  e := Entry.unpack(d[:Entrysize]);
949  if(e == nil)
950  return (nil, nil, sprint("unpacking fossil top-level entry: %r"));
951  topscore = e.score;
952  d = vread(session, topscore, Dirtype, 3*Entrysize);
953  if(d == nil)
954  return (nil, nil, sprint("reading fossil rootdir block: %r"));
955  say("have fossil top entries");
956  }
957  say("have top entries");
958 
959  e := array[3] of ref Entry;
960  j := 0;
961  for(i := 0; i+Entrysize <= len d; i += Entrysize) {
962  e[j] = Entry.unpack(d[i:i+Entrysize]);
963  if(e[j] == nil)
964  return (nil, nil, sprint("reading root entry %d: %r", j));
965  j++;
966  }
967  say("top entries unpacked");
968 
969  mroot := Vacdir.new(session, nil, e[2]);
970  (ok, de) := mroot.readdir();
971  if(ok <= 0)
972  return (nil, nil, sprint("reading root meta entry: %r"));
973 
974 say(sprint("openroot: new score=%s", score.text()));
975  return (Vacdir.new(session, e[0], e[1]), de, nil);
976 }
977 
978 readscore(path: string): (string, ref Venti->Score, string)
979 {
980  f := sys->open(path, Sys->OREAD);
981  if(f == nil)
982  return (nil, nil, sprint("open: %r"));
983  n := sys->read(f, d := array[Rootnamelen+1+2*Scoresize+1] of byte, len d);
984  if(n < 0)
985  return (nil, nil, sprint("read: %r"));
986 
987  (tag, scorestr) := str->splitstrr(string d[:n], ":");
988  if(tag != nil)
989  tag = tag[:len tag-1];
990  if(tag != "vac")
991  return (nil, nil, "unknown score type: "+tag);
992  if(len scorestr == 2*Scoresize+1)
993  scorestr = scorestr[:len scorestr-1];
994  (ok, s) := Score.parse(scorestr);
995  if(ok != 0)
996  return (nil, nil, "bad score: "+scorestr);
997  return (tag, ref s, nil);
998 }
999 
1000 g16(f: array of byte, i: int): int
1001 {
1002  return (int f[i] << 8) | int f[i+1];
1003 }
1004 
1005 g32(f: array of byte, i: int): int
1006 {
1007  return (((((int f[i+0] << 8) | int f[i+1]) << 8) | int f[i+2]) << 8) | int f[i+3];
1008 }
1009 
1010 g48(f: array of byte, i: int): big
1011 {
1012  b1 := (((((int f[i+0] << 8) | int f[i+1]) << 8) | int f[i+2]) << 8) | int f[i+3];
1013  b0 := (int f[i+4] << 8) | int f[i+5];
1014  return (big b1 << 16) | big b0;
1015 }
1016 
1017 g64(f: array of byte, i: int): big
1018 {
1019  b0 := (((((int f[i+0] << 8) | int f[i+1]) << 8) | int f[i+2]) << 8) | int f[i+3];
1020  b1 := (((((int f[i+4] << 8) | int f[i+5]) << 8) | int f[i+6]) << 8) | int f[i+7];
1021  return (big b0 << 32) | (big b1 & 16rFFFFFFFF);
1022 }
1023 
1024 p16(d: array of byte, i: int, v: int): int
1025 {
1026  d[i+0] = byte (v>>8);
1027  d[i+1] = byte v;
1028  return i+BIT16SZ;
1029 }
1030 
1031 p32(d: array of byte, i: int, v: int): int
1032 {
1033  p16(d, i+0, v>>16);
1034  p16(d, i+2, v);
1035  return i+BIT32SZ;
1036 }
1037 
1038 p48(d: array of byte, i: int, v: big): int
1039 {
1040  p16(d, i+0, int (v>>32));
1041  p32(d, i+2, int v);
1042  return i+BIT48SZ;
1043 }
1044 
1045 p64(d: array of byte, i: int, v: big): int
1046 {
1047  p32(d, i+0, int (v>>32));
1048  p32(d, i+4, int v);
1049  return i+BIT64SZ;
1050 }
1051 
1052 echeck(f: array of byte, i: int, l: int)
1053 {
1054  if(i+l > len f)
1055  raise sprint("too small: buffer length is %d, requested %d bytes starting at offset %d", len f, l, i);
1056 }
1057 
1058 egstring(a: array of byte, o: int): (string, int)
1059 {
1060  (s, no) := gstring(a, o);
1061  if(no == -1)
1062  raise sprint("too small: string runs outside buffer (length %d)", len a);
1063  return (s, no);
1064 }
1065 
1066 eg16(f: array of byte, i: int): (int, int)
1067 {
1068  echeck(f, i, BIT16SZ);
1069  return (g16(f, i), i+BIT16SZ);
1070 }
1071 
1072 eg32(f: array of byte, i: int): (int, int)
1073 {
1074  echeck(f, i, BIT32SZ);
1075  return (g32(f, i), i+BIT32SZ);
1076 }
1077 
1078 eg48(f: array of byte, i: int): (big, int)
1079 {
1080  echeck(f, i, BIT48SZ);
1081  return (g48(f, i), i+BIT48SZ);
1082 }
1083 
1084 eg64(f: array of byte, i: int): (big, int)
1085 {
1086  echeck(f, i, BIT64SZ);
1087  return (g64(f, i), i+BIT64SZ);
1088 }
1089 
1090 say(s: string)
1091 {
1092  if(debug)
1093  fprint(fildes(2), "%s\n", s);
1094 }