summaryrefslogtreecommitdiff
path: root/appl/cmd/install/arch.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/install/arch.b')
-rw-r--r--appl/cmd/install/arch.b288
1 files changed, 288 insertions, 0 deletions
diff --git a/appl/cmd/install/arch.b b/appl/cmd/install/arch.b
new file mode 100644
index 00000000..3f4d660d
--- /dev/null
+++ b/appl/cmd/install/arch.b
@@ -0,0 +1,288 @@
+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";
+}