diff options
Diffstat (limited to 'appl/cmd/install/archfs.b')
| -rw-r--r-- | appl/cmd/install/archfs.b | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/appl/cmd/install/archfs.b b/appl/cmd/install/archfs.b new file mode 100644 index 00000000..3705aee9 --- /dev/null +++ b/appl/cmd/install/archfs.b @@ -0,0 +1,579 @@ +implement Archfs; + +include "sys.m"; + sys : Sys; +include "draw.m"; +include "bufio.m"; + bufio : Bufio; +include "arg.m"; + arg : Arg; +include "string.m"; + str : String; +include "daytime.m"; + daytime : Daytime; +include "styx.m"; + styx: Styx; +include "archfs.m"; +include "arch.m"; + arch : Arch; + +# add write some day + +Iobuf : import bufio; +Tmsg, Rmsg: import styx; + +Einuse : con "fid already in use"; +Ebadfid : con "bad fid"; +Eopen : con "fid already opened"; +Enotfound : con "file does not exist"; +Enotdir : con "not a directory"; +Eperm : con "permission denied"; +Ebadarg : con "bad argument"; +Eexists : con "file already exists"; + +UID : con "inferno"; +GID : con "inferno"; + +DEBUG: con 0; + +Dir : adt { + dir : Sys->Dir; + offset : int; + parent : cyclic ref Dir; + child : cyclic ref Dir; + sibling : cyclic ref Dir; +}; + +Fid : adt { + fid : int; + open: int; + dir : ref Dir; + next : cyclic ref Fid; +}; + +HTSZ : con 32; +fidtab := array[HTSZ] of ref Fid; + +root : ref Dir; +qid : int; +mtpt := "/mnt"; +bio : ref Iobuf; +buf : array of byte; +skip := 0; + +# Archfs : module +# { +# init : fn(ctxt : ref Draw->Context, args : list of string); +# }; + +init(nil : ref Draw->Context, args : list of string) +{ + init0(nil, args, nil); +} + +initc(args : list of string, c : chan of int) +{ + init0(nil, args, c); +} + +chanint : chan of int; + +init0(nil : ref Draw->Context, args : list of string, chi : chan of int) +{ + chanint = chi; + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + arg = load Arg Arg->PATH; + str = load String String->PATH; + daytime = load Daytime Daytime->PATH; + styx = load Styx Styx->PATH; + arch = load Arch Arch->PATH; + if (bufio == nil || arg == nil || styx == nil || arch == nil) + fatal("failed to load modules", 1); + styx->init(); + arch->init(bufio); + arg->init(args); + while ((c := arg->opt()) != 0) { + case c { + 'm' => + mtpt = arg->arg(); + if (mtpt == nil) + fatal("mount point missing", 1); + 's' => + skip = 1; + } + } + args = arg->argv(); + if (args == nil) + fatal("missing archive file", 1); + buf = array[Sys->ATOMICIO] of byte; + # root = newdir("/", UID, GID, 8r755|Sys->DMDIR, daytime->now()); + root = newdir(basename(mtpt), UID, GID, 8r755|Sys->DMDIR, daytime->now()); + root.parent = root; + readarch(hd args, tl args); + p := array[2] of ref Sys->FD; + if(sys->pipe(p) < 0) + fatal("can't create pipe", 1); + ch := chan of ref Tmsg; + sync := chan of int; + spawn reader(p[1], ch, sync); + <- sync; + pidch := chan of int; + spawn serve(p[1], ch, pidch); + pid := <- pidch; + if(sys->mount(p[0], nil, mtpt, Sys->MREPL, nil) < 0) + fatal(sys->sprint("cannot mount archive on %s: %r", mtpt), 1); + p[0] = p[1] = nil; + if (chi != nil) { + chi <-= pid; + chanint = nil; + } +} + +reply(fd: ref Sys->FD, m: ref Rmsg): int +{ + if(DEBUG) + sys->fprint(sys->fildes(2), "R: %s\n", m.text()); + s := m.pack(); + if(s == nil) + return -1; + return sys->write(fd, s, len s); +} + +error(fd: ref Sys->FD, m: ref Tmsg, e : string) +{ + reply(fd, ref Rmsg.Error(m.tag, e)); +} + +reader(fd: ref Sys->FD, ch: chan of ref Tmsg, sync: chan of int) +{ + sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil); + sync <-= 1; + while((m := Tmsg.read(fd, Styx->MAXRPC)) != nil && tagof m != tagof Tmsg.Readerror) + ch <-= m; + ch <-= m; +} + +serve(fd: ref Sys->FD, ch : chan of ref Tmsg, pidch : chan of int) +{ + e : string; + f : ref Fid; + + pidch <-= sys->pctl(0, nil); + for (;;) { + m0 := <- ch; + if (m0 == nil) + return; + if(DEBUG) + sys->fprint(sys->fildes(2), "T: %s\n", m0.text()); + pick m := m0 { + Readerror => + fatal("read error on styx server", 1); + Version => + (s, v) := styx->compatible(m, Styx->MAXRPC, Styx->VERSION); + reply(fd, ref Rmsg.Version(m.tag, s, v)); + Auth => + error(fd, m, "no authentication required"); + Flush => + reply(fd, ref Rmsg.Flush(m.tag)); + Walk => + (f, e) = mapfid(m.fid); + if (e != nil) { + error(fd, m, e); + continue; + } + if (f.open) { + error(fd, m, Eopen); + continue; + } + err := 0; + dir := f.dir; + nq := 0; + nn := len m.names; + qids := array[nn] of Sys->Qid; + if(nn > 0){ + for(k := 0; k < nn; k++){ + if ((dir.dir.mode & Sys->DMDIR) == 0) { + if(k == 0){ + error(fd, m, Enotdir); + err = 1; + } + break; + } + dir = lookup(dir, m.names[k]); + if (dir == nil) { + if(k == 0){ + error(fd, m, Enotfound); + err = 1; + } + break; + } + qids[nq++] = dir.dir.qid; + } + } + if(err) + continue; + if(nq < nn) + qids = qids[0: nq]; + if(nq == nn){ + if(m.newfid != m.fid){ + f = newfid(m.newfid); + if (f == nil) { + error(fd, m, Einuse); + continue; + } + } + f.dir = dir; + } + reply(fd, ref Rmsg.Walk(m.tag, qids)); + Open => + (f, e) = mapfid(m.fid); + if (e != nil) { + error(fd, m, e); + continue; + } + if (m.mode & (Sys->OWRITE|Sys->ORDWR|Sys->OTRUNC|Sys->ORCLOSE)) { + error(fd, m, Eperm); + continue; + } + f.open = 1; + reply(fd, ref Rmsg.Open(m.tag, f.dir.dir.qid, Styx->MAXFDATA)); + Create => + error(fd, m, Eperm); + Read => + (f, e) = mapfid(m.fid); + if (e != nil) { + error(fd, m, e); + continue; + } + data := readdir(f.dir, int m.offset, m.count); + reply(fd, ref Rmsg.Read(m.tag, data)); + Write => + error(fd, m, Eperm); + Clunk => + (f, e) = mapfid(m.fid); + if (e != nil) { + error(fd, m, e); + continue; + } + freefid(f); + reply(fd, ref Rmsg.Clunk(m.tag)); + Stat => + (f, e) = mapfid(m.fid); + if (e != nil) { + error(fd, m, e); + continue; + } + reply(fd, ref Rmsg.Stat(m.tag, f.dir.dir)); + Remove => + error(fd, m, Eperm); + Wstat => + error(fd, m, Eperm); + Attach => + f = newfid(m.fid); + if (f == nil) { + error(fd, m, Einuse); + continue; + } + f.dir = root; + reply(fd, ref Rmsg.Attach(m.tag, f.dir.dir.qid)); + * => + fatal("unknown styx message", 1); + } + } +} + +newfid(fid : int) : ref Fid +{ + (f, nil) := mapfid(fid); + if(f != nil) + return nil; + f = ref Fid; + f.fid = fid; + f.open = 0; + hv := hashval(fid); + f.next = fidtab[hv]; + fidtab[hv] = f; + return f; +} + +freefid(f: ref Fid) +{ + hv := hashval(f.fid); + lf : ref Fid; + for(ff := fidtab[hv]; ff != nil; ff = ff.next){ + if(f == ff){ + if(lf == nil) + fidtab[hv] = ff.next; + else + lf.next = ff.next; + return; + } + lf = ff; + } + fatal("cannot find fid", 1); +} + +mapfid(fid : int) : (ref Fid, string) +{ + hv := hashval(fid); + for (f := fidtab[hv]; f != nil; f = f.next) + if (int f.fid == fid) + break; + if (f == nil) + return (nil, Ebadfid); + if (f.dir == nil) + return (nil, Enotfound); + return (f, nil); +} + +hashval(n : int) : int +{ + return (n & ~Sys->DMDIR)%HTSZ; +} + +readarch(f : string, args : list of string) +{ + ar := arch->openarchfs(f); + if(ar == nil || ar.b == nil) + fatal(sys->sprint("cannot open %s(%r)\n", f), 1); + bio = ar.b; + while ((a := arch->gethdr(ar)) != nil) { + if (args != nil) { + if (!selected(a.name, args)) { + if (skip) + return; + arch->drain(ar, int a.d.length); + continue; + } + mkdirs("/", a.name); + } + d := mkdir(a.name, a.d.mode, a.d.mtime, a.d.uid, a.d.gid, 0); + if((a.d.mode & Sys->DMDIR) == 0) { + d.dir.length = a.d.length; + d.offset = int bio.offset(); + } + arch->drain(ar, int a.d.length); + } + if (ar.err != nil) + fatal(ar.err, 0); +} + +selected(s: string, args: list of string): int +{ + for(; args != nil; args = tl args) + if(fileprefix(hd args, s)) + return 1; + return 0; +} + +fileprefix(prefix, s: string): int +{ + n := len prefix; + m := len s; + if(n > m || !str->prefix(prefix, s)) + return 0; + if(m > n && s[n] != '/') + return 0; + return 1; +} + +basename(f : string) : string +{ + for (i := len f; i > 0; ) + if (f[--i] == '/') + return f[i+1:]; + return f; +} + +split(p : string) : (string, string) +{ + if (p == nil) + fatal("nil string in split", 1); + if (p[0] != '/') + fatal("p0 not / in split", 1); + while (p[0] == '/') + p = p[1:]; + i := 0; + while (i < len p && p[i] != '/') + i++; + if (i == len p) + return (p, nil); + else + return (p[0:i], p[i:]); +} + +mkdirs(basedir, name: string) +{ + (nil, names) := sys->tokenize(name, "/"); + while(names != nil) { + # sys->print("mkdir %s\n", basedir); + mkdir(basedir, 8r775|Sys->DMDIR, daytime->now(), UID, GID, 1); + if(tl names == nil) + break; + basedir = basedir + "/" + hd names; + names = tl names; + } +} + +readdir(d : ref Dir, offset : int, n : int) : array of byte +{ + if (d.dir.mode & Sys->DMDIR) + return readd(d, offset, n); + else + return readf(d, offset, n); +} + +readd(d : ref Dir, o : int, n : int) : array of byte +{ + k := 0; + m := 0; + b := array[n] of byte; + for (s := d.child; s != nil; s = s.sibling) { + l := styx->packdirsize(s.dir); + if(k < o){ + k += l; + continue; + } + if(m+l > n) + break; + b[m: ] = styx->packdir(s.dir); + m += l; + } + return b[0: m]; +} + +readf(d : ref Dir, offset : int, n : int) : array of byte +{ + leng := int d.dir.length; + if (offset+n > leng) + n = leng-offset; + if (n <= 0 || offset < 0) + return nil; + bio.seek(big (d.offset+offset), Bufio->SEEKSTART); + a := array[n] of byte; + p := 0; + m := 0; + for ( ; n != 0; n -= m) { + l := len buf; + if (n < l) + l = n; + m = bio.read(buf, l); + if (m <= 0 || m != l) + fatal("premature eof", 1); + a[p:] = buf[0:m]; + p += m; + } + return a; +} + +mkdir(f : string, mode : int, mtime : int, uid : string, gid : string, existsok : int) : ref Dir +{ + if (f == "/") + return nil; + d := newdir(basename(f), uid, gid, mode, mtime); + addfile(d, f, existsok); + return d; +} + +addfile(d : ref Dir, path : string, existsok : int) +{ + elem : string; + + opath := path; + p := prev := root; + basedir := ""; +# sys->print("addfile %s : %s\n", d.dir.name, path); + while (path != nil) { + (elem, path) = split(path); + basedir += "/" + elem; + op := p; + p = lookup(p, elem); + if (path == nil) { + if (p != nil) { + if (!existsok && (p.dir.mode&Sys->DMDIR) == 0) + sys->fprint(sys->fildes(2), "addfile: %s already there", opath); + # fatal(sys->sprint("addfile: %s already there", opath), 1); + return; + } + if (prev.child == nil) + prev.child = d; + else { + for (s := prev.child; s.sibling != nil; s = s.sibling) + ; + s.sibling = d; + } + d.parent = prev; + } + else { + if (p == nil) { + mkdir(basedir, 8r775|Sys->DMDIR, daytime->now(), UID, GID, 1); + p = lookup(op, elem); + if (p == nil) + fatal("bad file system", 1); + } + } + prev = p; + } +} + +lookup(p : ref Dir, f : string) : ref Dir +{ + if ((p.dir.mode&Sys->DMDIR) == 0) + fatal("not a directory in lookup", 1); + if (f == ".") + return p; + if (f == "..") + return p.parent; + for (d := p.child; d != nil; d = d.sibling) + if (d.dir.name == f) + return d; + return nil; +} + +newdir(name, uid, gid : string, mode, mtime : int) : ref Dir +{ + dir : Sys->Dir; + + dir.name = name; + dir.uid = uid; + dir.gid = gid; + dir.qid.path = big (qid++); + if(mode&Sys->DMDIR) + dir.qid.qtype = Sys->QTDIR; + else + dir.qid.qtype = Sys->QTFILE; + dir.qid.vers = 0; + dir.mode = mode; + dir.atime = dir.mtime = mtime; + dir.length = big 0; + dir.dtype = 'X'; + dir.dev = 0; + + d := ref Dir; + d.dir = dir; + d.offset = 0; + return d; +} + +# pr(d : ref Dir) +# { +# dir := d.dir; +# sys->print("%s %s %s %x %x %x %d %d %d %d %d %d\n", +# dir.name, dir.uid, dir.gid, dir.qid.path, dir.qid.vers, dir.mode, dir.atime, dir.mtime, dir.length, dir.dtype, dir.dev, d.offset); +# } + +fatal(e : string, pr: int) +{ + if(pr){ + sys->fprint(sys->fildes(2), "fatal: %s\n", e); + if (chanint != nil) + chanint <-= -1; + } + else{ + # probably not an archive file + if (chanint != nil) + chanint <-= -2; + } + exit; +} |
