changelog shortlog tags branches changeset files revisions annotate raw help

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

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