ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/install/wrap.b/
implement Wrap;
include "sys.m";
sys : Sys;
include "draw.m";
include "bufio.m";
bufio : Bufio;
Iobuf : import bufio;
include "keyring.m";
keyring : Keyring;
include "sh.m";
include "arch.m";
arch : Arch;
include "wrap.m";
include "archfs.m";
archpid := -1;
gzfd: ref Sys->FD;
gzfile: string;
init(bio: Bufio)
{
sys = load Sys Sys->PATH;
if(bio == nil)
bufio = load Bufio Bufio->PATH;
else
bufio = bio;
keyring = load Keyring Keyring->PATH;
arch = load Arch Arch->PATH;
arch->init(bufio);
}
end()
{
if(gzfile != nil)
sys->remove(gzfile);
if (archpid > 0){
fd := sys->open("#p/" + string archpid + "/ctl", sys->OWRITE);
if (fd != nil)
sys->fprint(fd, "killgrp");
}
}
archfs(f : string, mtpt : string, all : int, c : chan of int)
{
sys->pctl(Sys->NEWPGRP, nil);
cmd := "/dis/install/archfs.dis";
m := load Archfs Archfs->PATH;
if(m == nil) {
c <-= -1;
return;
}
ch := chan of int;
if (all)
spawn m->initc(cmd :: "-m" :: mtpt :: f :: nil, ch);
else
spawn m->initc(cmd :: "-s" :: "-m" :: mtpt :: f :: "/wrap" :: nil, ch);
pid := <- ch;
c <-= pid;
}
mountarch(f : string, mtpt : string, all : int) : int
{
c := chan of int;
spawn archfs(f, mtpt, all, c);
pid := <- c;
if (pid < 0) {
if(pid == -1)
sys->fprint(sys->fildes(2), "fatal: cannot run archfs\n");
# else probably not an archive file
return -1;
}
archpid = pid;
return 0;
}
openmount(f : string, d : string) : ref Wrapped
{
if (f == nil) {
p := d+"/wrap";
f = getfirstdir(p);
if (f == nil)
return nil;
}
w := ref Wrapped;
w.name = f;
w.root = d;
# p := d + "/wrap/" + f;
p := pathcat(d, pathcat("wrap", f));
(w.u, w.nu, w.tfull) = openupdate(p);
if (w.nu < 0) {
closewrap(w);
return nil;
}
return w;
}
closewrap(w : ref Wrapped)
{
w = nil;
}
openwraphdr(f : string, d : string, argl : list of string, all : int) : ref Wrapped
{
argl = nil;
(ok, dir) := sys->stat(f);
if (ok < 0 || dir.mode & Sys->DMDIR)
return openwrap(f, d, all);
(nf, fd) := arch->openarchgz(f);
if (nf != nil) {
gzfile = nf;
f = nf;
gzfd = fd;
}
return openwrap(f, "/mnt/wrap", all);
}
openwrap(f : string, d : string, all : int) : ref Wrapped
{
if (d == nil)
d = "/";
if((w := openmount(f, d)) != nil)
return w; # don't mess about if /wrap/ structure exists
(ok, dir) := sys->stat(f);
if (ok < 0)
return nil;
# accept root/ or root/wrap/pkgname
if (dir.mode & Sys->DMDIR) {
d = f;
if ((i := strstr(f, "/wrap/")) >= 0) {
f = f[i+6:];
d = d[0:i+6];
}
else
f = nil;
return openmount(f, d);
}
(ok, dir) = sys->stat(f);
if (ok < 0 || dir.mode & Sys->DMDIR)
return openmount(f, d); # ?
if (mountarch(f, d, all) < 0)
return nil;
return openmount(nil, d);
}
getfirstdir(d : string) : string
{
if ((fd := sys->open(d, Sys->OREAD)) == nil)
return nil;
for(;;){
(n, dir) := sys->dirread(fd);
if(n <= 0)
break;
for(i:=0; i<n; i++)
if(dir[i].mode & Sys->DMDIR)
return dir[i].name;
}
return nil;
}
NONE : con 0;
sniffdir(base : string, elem : string) : (int, int)
{
# t := int elem;
t := string2now(elem, 0);
if (t == 0)
return (NONE, 0);
# buf := sys->sprint("%ud", t);
# if (buf != elem)
# return (NONE, 0);
rv := NONE;
p := base + "/" + elem + "/package";
(ok, nil) := sys->stat(p);
if (ok >= 0)
rv |= FULL;
p = base + "/" + elem + "/update";
(ok, nil) = sys->stat(p);
if (ok >= 0)
rv |= UPD;
return (rv, t);
}
openupdate(d : string) : (array of Update, int, int)
{
u : array of Update;
if ((fd := sys->open(d, Sys->OREAD)) == nil)
return (nil, -1, 0);
#
# We are looking to find the most recent full
# package; anything before that is irrelevant.
# Also figure out the most recent package update.
# Non-package updates before that are irrelevant.
# If there are no packages installed,
# grab all the updates we can find.
#
tbase := -1;
tfull := -1;
nu := 0;
for(;;){
(n, dir) := sys->dirread(fd);
if(n <= 0)
break;
for(i := 0; i < n; i++){
(k, t) := sniffdir(d, dir[i].name);
case (k) {
FULL =>
nu++;
if (t > tfull)
tfull = t;
if (t > tbase)
tbase = t;
FULL|UPD =>
nu++;
if (t > tfull)
tfull = t;
UPD =>
nu++;
}
}
}
if (nu == 0)
return (nil, -1, 0);
u = nil;
nu = 0;
if ((fd = sys->open(d, Sys->OREAD)) == nil)
return (nil, -1, 0);
for(;;){
(n, dir) := sys->dirread(fd);
if(n <= 0)
break;
for(i := 0; i < n; i++){
(k, t) := sniffdir(d, dir[i].name);
if (k == 0)
continue;
if (t < tbase)
continue;
if (t < tfull && k == UPD)
continue;
if (nu%8 == 0) {
newu := array[nu+8] of Update;
newu[0:] = u[0:nu];
u = newu;
}
u[nu].typ = k;
if (readupdate(u, nu, d, dir[i].name) != nil)
nu++;
}
}
if (nu == 0)
return (nil, -1, 0);
qsort(u, nu);
return (u, nu, tfull);
}
readupdate(u : array of Update, ui : int, base : string, elem : string) : array of Update
{
# u[ui].dir = base + "/" + elem;
u[ui].dir = pathcat(base, elem);
p := u[ui].dir + "/desc";
u[ui].desc = readfile(p);
# u[ui].time = int elem;
u[ui].time = string2now(elem, 0);
p = u[ui].dir + "/md5sum";
u[ui].bmd5 = bufio->open(p, Bufio->OREAD);
p = u[ui].dir + "/update";
q := readfile(p);
if (q != nil)
u[ui].utime = int q;
else
u[ui].utime = 0;
if (u[ui].bmd5 == nil)
return nil;
return u;
}
readfile(s : string) : string
{
(ok, d) := sys->stat(s);
if (ok < 0)
return nil;
buf := array[int d.length] of byte;
if ((fd := sys->open(s, Sys->OREAD)) == nil || sys->read(fd, buf, int d.length) != int d.length)
return nil;
s = string buf;
ls := len s;
if (s[ls-1] == '\n')
s = s[0:ls-1];
return s;
}
hex(c : int) : int
{
if (c >= '0' && c <= '9')
return c-'0';
if (c >= 'a' && c <= 'f')
return c-'a'+10;
if (c >= 'A' && c <= 'F')
return c-'A'+10;
return -1;
}
getfileinfo(w : ref Wrapped, f : string, rdigest : array of byte, wdigest : array of byte, ardigest: array of byte) : (int, int)
{
p : string;
if (w == nil)
return (-1, 0);
digest := array[keyring->MD5dlen] of { * => byte 0 };
for (i := w.nu-1; i >= 0; i--){
if ((p = bsearch(w.u[i].bmd5, f)) == nil)
continue;
if (p == nil)
continue;
k := 0;
while (k < len p && p[k] != ' ')
k++;
if (k == len p)
continue;
q := p[k+1:];
if (q == nil)
continue;
if (len q != 2*Keyring->MD5dlen+1)
continue;
for (j := 0; j < Keyring->MD5dlen; j++) {
a := hex(q[2*j]);
b := hex(q[2*j+1]);
if (a < 0 || b < 0)
break;
digest[j] = byte ((a<<4)|b);
}
if(j != Keyring->MD5dlen)
continue;
if(rdigest == nil || memcmp(rdigest, digest, keyring->MD5dlen) == 0 || (ardigest != nil && memcmp(ardigest, digest, keyring->MD5dlen) == 0))
break;
else
return (-1, 0); # NEW
}
if(i < 0)
return (-1, 0);
if(wdigest != nil)
wdigest[0:] = rdigest;
return (0, w.u[i].time);
}
bsearch(b : ref Bufio->Iobuf, p : string) : string
{
if (b == nil)
return nil;
lo := 0;
b.seek(big 0, Bufio->SEEKEND);
hi := int b.offset();
l := len p;
while (lo < hi) {
m := (lo+hi)/2;
b.seek(big m, Bufio->SEEKSTART);
b.gets('\n');
if (int b.offset() == hi) {
bgetbackc(b);
m = int b.offset();
while (m-- > lo) {
if (bgetbackc(b) == '\n') {
b.getc();
break;
}
}
}
s := b.gets('\n');
if (len s >= l+1 && s[0:l] == p && (s[l] == ' ' || s[l] == '\n'))
return s;
if (s < p)
lo = int b.offset();
else
hi = int b.offset()-len s;
}
return nil;
}
bgetbackc(b : ref Bufio->Iobuf) : int
{
m := int b.offset();
b.seek(big (m-1), Bufio->SEEKSTART);
c := b.getc();
b.ungetc();
return c;
}
strstr(s : string, p : string) : int
{
lp := len p;
ls := len s;
for (i := 0; i < ls-lp; i++)
if (s[i:i+lp] == p)
return i;
return -1;
}
qsort(a : array of Update, n : int)
{
i, j : int;
t : Update;
while(n > 1) {
i = n>>1;
t = a[0]; a[0] = a[i]; a[i] = t;
i = 0;
j = n;
for(;;) {
do
i++;
while(i < n && a[i].time < a[0].time);
do
j--;
while(j > 0 && a[j].time > a[0].time);
if(j < i)
break;
t = a[i]; a[i] = a[j]; a[j] = t;
}
t = a[0]; a[0] = a[j]; a[j] = t;
n = n-j-1;
if(j >= n) {
qsort(a, j);
a = a[j+1:];
} else {
qsort(a[j+1:], n);
n = j;
}
}
}
md5file(file : string, digest : array of byte) : int
{
(ok, d) := sys->stat(file);
if (ok < 0)
return -1;
if (d.mode & Sys->DMDIR)
return 0;
bio := bufio->open(file, Bufio->OREAD);
if (bio == nil)
return -1;
# return md5sum(bio, digest, d.length);
buff := array[Sys->ATOMICIO] of byte;
ds := keyring->md5(nil, 0, nil, nil);
while ((n := bio.read(buff, len buff)) > 0)
keyring->md5(buff, n, nil, ds);
keyring->md5(nil, 0, digest, ds);
bio = nil;
return 0;
}
md5sum(b : ref Iobuf, digest : array of byte, leng : int) : int
{
ds := keyring->md5(nil, 0, nil, nil);
buff := array[Sys->ATOMICIO] of byte;
while (leng > 0) {
if (leng > len buff)
n := len buff;
else
n = leng;
if ((n = b.read(buff, n)) <= 0)
return -1;
keyring->md5(buff, n, nil, ds);
leng -= n;
}
keyring->md5(nil, 0, digest, ds);
return 0;
}
md5conv(d : array of byte) : string
{
s : string = nil;
for (i := 0; i < keyring->MD5dlen; i++)
s += sys->sprint("%.2ux", int d[i]);
return s;
}
zd : Sys->Dir;
newd(time : int, uid : string, gid : string) : ref Sys->Dir
{
d := ref Sys->Dir;
*d = zd;
d.uid = uid;
d.gid = gid;
d.mtime = time;
return d;
}
putwrapfile(b : ref Iobuf, name : string, time : int, elem : string, file : string, uid : string, gid : string)
{
d := newd(time, uid, gid);
d.mode = 8r444;
(ok, dir) := sys->stat(file);
if (ok < 0)
sys->fprint(sys->fildes(2), "cannot stat %s: %r", file);
d.length = dir.length;
# s := "/wrap/"+name+"/"+sys->sprint("%ud", time)+"/"+elem;
s := "/wrap/"+name+"/"+now2string(time, 0)+"/"+elem;
arch->puthdr(b, s, d);
arch->putfile(b, file, int d.length);
}
putwrap(b : ref Iobuf, name : string, time : int, desc : string, utime : int, pkg : int, uid : string, gid : string)
{
if (!(utime || pkg))
sys->fprint(sys->fildes(2), "bad precondition in putwrap()");
d := newd(time, uid, gid);
d.mode = Sys->DMDIR|8r775;
s := "/wrap";
arch->puthdr(b, s, d);
s += "/"+name;
arch->puthdr(b, s, d);
# s += "/"+sys->sprint("%ud", time);
s += "/"+now2string(time, 0);
arch->puthdr(b, s, d);
d.mode = 8r444;
s += "/";
dir := s;
if (utime) {
s = dir+"update";
d.length = big 23;
arch->puthdr(b, s, d);
arch->putstring(b, sys->sprint("%22ud\n", utime));
}
if (pkg) {
s = dir+"package";
d.length = big 0;
arch->puthdr(b, s, d);
}
if (desc != nil) {
s = dir+"desc";
d.length = big (len desc+1);
d.mode = 8r444;
arch->puthdr(b, s, d);
arch->putstring(b, desc+"\n");
}
}
memcmp(b1, b2 : array of byte, n : int) : int
{
for (i := 0; i < n; i++)
if (b1[i] < b2[i])
return -1;
else if (b1[i] > b2[i])
return 1;
return 0;
}
strprefix(s: string, pre: string): int
{
return len s >= (l := len pre) && s[0:l] == pre;
}
match(s: string, pre: list of string): int
{
if(pre == nil || s == "/wrap" || strprefix(s, "/wrap/"))
return 1;
for( ; pre != nil; pre = tl pre)
if(strprefix(s, hd pre))
return 1;
return 0;
}
notmatch(s: string, pre: list of string): int
{
if(pre == nil || s == "/wrap" || strprefix(s, "/wrap/"))
return 1;
for( ; pre != nil; pre = tl pre)
if(strprefix(s, hd pre))
return 0;
return 1;
}
pathcat(s : string, t : string) : string
{
if (s == nil) return t;
if (t == nil) return s;
slashs := s[len s - 1] == '/';
slasht := t[0] == '/';
if (slashs && slasht)
return s + t[1:];
if (!slashs && !slasht)
return s + "/" + t;
return s + t;
}
md5filea(file : string, digest : array of byte) : int
{
n, n0: int;
(ok, d) := sys->stat(file);
if (ok < 0)
return -1;
if (d.mode & Sys->DMDIR)
return 0;
bio := bufio->open(file, Bufio->OREAD);
if (bio == nil)
return -1;
buff := array[Sys->ATOMICIO] of byte;
m := len buff;
ds := keyring->md5(nil, 0, nil, nil);
r := 0;
while(1){
if(r){
if((n = bio.read(buff[1:], m-1)) <= 0)
break;
n++;
}
else{
if ((n = bio.read(buff, m)) <= 0)
break;
}
(n0, r) = remcr(buff, n);
if(r){
keyring->md5(buff, n0-1, nil, ds);
buff[0] = byte '\r';
}
else
keyring->md5(buff, n0, nil, ds);
}
if(r)
keyring->md5(buff, 1, nil, ds);
keyring->md5(nil, 0, digest, ds);
bio = nil;
return 0;
}
remcr(b: array of byte, n: int): (int, int)
{
if(n == 0)
return (0, 0);
for(i := 0; i < n; ){
if(b[i] == byte '\r' && i+1 < n && b[i+1] == byte '\n')
b[i:] = b[i+1:n--];
else
i++;
}
return (n, b[n-1] == byte '\r');
}
TEN2EIGHT: con 100000000;
now2string(n: int, flag: int): string
{
if(flag == 0)
return sys->sprint("%ud", n);
if(n < 0)
return nil;
q := n/TEN2EIGHT;
s := "0" + string (n-TEN2EIGHT*q);
while(len s < 9)
s = "0" + s;
if(q <= 9)
s[0] = '0' + q - 0;
else if(q <= 21)
s[0] = 'A' + q - 10;
else
return nil;
return s;
}
string2now(s: string, flag: int): int
{
if(flag == 0 && s[0] != 'A')
return int s;
if(len s != 9)
return 0;
r := int s[1: ];
c := s[0];
if(c >= '0' && c <= '9')
q := c - '0' + 0;
else if(c >= 'A' && c <= 'L')
q = c - 'A' + 10;
else
return 0;
n := TEN2EIGHT*q + r;
if(n < 0)
return 0;
return n;
}