diff options
Diffstat (limited to 'appl/cmd/install/arch.b')
| -rw-r--r-- | appl/cmd/install/arch.b | 288 |
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"; +} |
