code: purgatorio

ref: d916a4c3823f55227ffae35738c2497256e307b5
dir: /appl/cmd/install/arch.b/

View raw version
implement Arch;

include "sys.m";
	sys: Sys;
include "draw.m";
include "daytime.m";
	daytime : Daytime;
include "string.m";
	str : String;
include "bufio.m";
	bufio : Bufio;
	Iobuf : import bufio;
include "sh.m";
include "arch.m";

addp := 1;

buf := array[Sys->ATOMICIO] of byte;

init(bio: Bufio)
{
	sys = load Sys Sys->PATH;
	if(bio == nil)
		bufio = load Bufio Bufio->PATH;
	else
		bufio = bio;
	daytime = load Daytime Daytime->PATH;
	str = load String String->PATH;
}

addperms(p: int)
{
	addp = p;
}

openarch(file : string) : ref Archive
{
	return openarch0(file, 1);
}

openarchfs(file : string) : ref Archive
{
	return openarch0(file, 0);
}

openarch0(file : string, newpgrp : int) : ref Archive
{
	pid := 0;
	canseek := 1;
	b := bufio->open(file, Bufio->OREAD);
	if (b == nil)
		return nil;
	if (b.getb() == 16r1f && ((c := b.getb()) == 16r8b || c == 16r9d)) {
		# spawn gunzip
		canseek = 0;
		(b, pid) = gunzipstream(file, newpgrp);
		if (b == nil)
			return nil;
	}
	else
		b.seek(big 0, Bufio->SEEKSTART);
	ar := ref Archive;
	ar.b = b;
	ar.nexthdr = 0;
	ar.canseek = canseek;
	ar.pid = pid;
	ar.hdr = ref Ahdr;
	ar.hdr.d = ref Sys->Dir;
	return ar;
}

EOARCH : con "end of archive\n";
PREMEOARCH : con "premature end of archive";
NFLDS : con 6;

openarchgz(file : string) : (string, ref Sys->FD)
{
	ar := openarch(file);
	if (ar == nil || ar.canseek)
		return (nil, nil);
	(newfile, fd) := opentemp("wrap.gz");
	if (fd == nil)
		return (nil, nil);
	bout := bufio->fopen(fd, Bufio->OWRITE);
	if (bout == nil)
		return (nil, nil);
	while ((a := gethdr(ar)) != nil) {
		if (len a.name >= 5 && a.name[0:5] == "/wrap") {
			puthdr(bout, a.name, a.d);
			getfile(ar, bout, int a.d.length);
		}
		else
			break;
	}
	closearch(ar);
	bout.puts(EOARCH);
	bout.flush();
	sys->seek(fd, big 0, Sys->SEEKSTART);
	return (newfile, fd);
}

gunzipstream(file : string, newpgrp : int) : (ref Iobuf, int)
{
	p := array[2] of ref Sys->FD;
	if (sys->pipe(p) < 0)
		return (nil, 0);
	fd := sys->open(file, Sys->OREAD);
	if (fd == nil)
		return (nil, 0);
	b := bufio->fopen(p[0], Bufio->OREAD);
	if (b == nil)
		return (nil, 0);
	c := chan of int;
	spawn gunzip(fd, p[1], c, newpgrp);
	pid := <- c;
	p[0] = p[1] = nil;
	if (pid < 0)
		return (nil, 0);
	return (b, pid);
}

GUNZIP : con "/dis/gunzip.dis";

gunzip(stdin : ref Sys->FD, stdout : ref Sys->FD, c : chan of int, newpgrp : int)
{
	if (newpgrp)
		pid := sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil);
	else
		pid = sys->pctl(Sys->FORKFD, nil);
	sys->dup(stdin.fd, 0);
	sys->dup(stdout.fd, 1);
	sys->dup(1, 2);
	stdin = stdout = nil;
	cmd := load Command GUNZIP;
	if (cmd == nil) {
		c <-= -1;
		return;
	}
	c <-= pid;
	cmd->init(nil, GUNZIP :: nil);
}

closearch(ar : ref Archive)
{
	if (ar.pid != 0) {
		fd := sys->open("#p/" + string ar.pid + "/ctl", sys->OWRITE);
		if (fd != nil)
			sys->fprint(fd, "killgrp");
	}
	ar.b.close();
	ar.b = nil;
}

gethdr(ar : ref Archive) : ref Ahdr
{
	a := ar.hdr;
	b := ar.b;
	m := int b.offset();
	n := ar.nexthdr;
	if (m != n) {
		if (ar.canseek)
			b.seek(big n, Bufio->SEEKSTART);
		else {
			if (m > n)
				fatal(sys->sprint("bad offset in gethdr: m=%d n=%d", m, n));
			if(drain(ar, n-m) < 0)
				return nil;
		}
	}
	if ((s := b.gets('\n')) == nil) {
		ar.err = PREMEOARCH;
		return nil;
	}
# fd := sys->open("./debug", Sys->OWRITE);
# sys->seek(fd, 0, Sys->SEEKEND);
# sys->fprint(fd, "gethdr: %d %d %d %d %s\n", ar.canseek, m, n, b.offset(), s);
# fd = nil;
	if (s == EOARCH)
		return nil;
	(nf, fs) := sys->tokenize(s, " \t\n");
	if(nf != NFLDS) {
		ar.err = "too few fields in file header";
		return nil;
	}
	a.name = hd fs;						fs = tl fs;
	(a.d.mode, nil) = str->toint(hd fs, 8);		fs = tl fs;
	a.d.uid = hd fs;						fs = tl fs;
	a.d.gid = hd fs;						fs = tl fs;
	(a.d.mtime, nil) = str->toint(hd fs, 10);	fs = tl fs;
	(tmp, nil) := str->toint(hd fs, 10);		fs = tl fs;
	a.d.length = big tmp;
	ar.nexthdr = int (b.offset()+a.d.length);
	return a;
}

getfile(ar : ref Archive, bout : ref Bufio->Iobuf, n : int) : string
{
	err: string;
	bin := ar.b;
	while (n > 0) {
		m := len buf;
		if (n < m)
			m = n;
		p := bin.read(buf, m);
		if (p != m)
			return PREMEOARCH;
		p = bout.write(buf, m);
		if (p != m)
			err = sys->sprint("cannot write: %r");
		n -= m;
	}
	return err;	
}

puthdr(b : ref Iobuf, name : string, d : ref Sys->Dir)
{
	mode := d.mode;
	if(addp){
		mode |= 8r664;
		if(mode & Sys->DMDIR || mode & 8r111)
			mode |= 8r111;
	}
	b.puts(sys->sprint("%s %uo %s %s %ud %d\n", name, mode, d.uid, d.gid, d.mtime, int d.length));
}

putstring(b : ref Iobuf, s : string)
{
	b.puts(s);
}

putfile(b : ref Iobuf, f : string, n : int) : string
{
	fd := sys->open(f, Sys->OREAD);
	if (fd == nil)
		return sys->sprint("cannot open %s: %r", f);
	i := 0;
	for (;;) {
		m := sys->read(fd, buf, len buf);
		if (m < 0)
			return sys->sprint("cannot read %s: %r", f);
		if (m == 0)
			break;
		if (b.write(buf, m) != m)
			return sys->sprint("%s: cannot write: %r", f);
		i += m;
	}
	if (i != n) {
		b.seek(big (n-i), Sys->SEEKRELA);
		return sys->sprint("%s: %d bytes written: should be %d", f, i, n);
	}
	return nil;
}

putend(b : ref Iobuf)
{
	b.puts(EOARCH);
	b.flush();
}

drain(ar : ref Archive, n : int) : int
{
	while (n > 0) {
		m := n;
		if (m > len buf)
			m = len buf;
		p := ar.b.read(buf, m);
		if (p != m){
			ar.err = "unexpectedly short read";
			return -1;
		}
		n -= m;
	}
	return 0;	
}

opentemp(prefix: string): (string, ref Sys->FD)
{
	name := sys->sprint("/tmp/%s.%ud.%d", prefix, daytime->now(), sys->pctl(0, nil));
	# would use ORCLOSE here but it messes up under Nt
	fd := sys->create(name, Sys->ORDWR, 8r600);
	return (name, fd);
}

fatal(s : string)
{
	sys->fprint(sys->fildes(2), "%s\n", s);
	raise "fail:error";
}