changelog shortlog tags branches changeset files revisions annotate raw help

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

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