code: 9ferno

ref: da7d6df6faf18e289fe0f3f61524dcc7fddeef18
dir: /appl/cmd/install/wrap.b/

View raw version
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;
}