ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/install/arch.b/
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";
}