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; }