implement Archfs; include "sys.m"; sys: Sys;include "draw.m"; include "bufio.m"; bufio: Bufio; include "string.m"; str: String; include "daytime.m"; daytime: Daytime; include "styx.m"; styx: Styx; NOFID: import Styx; include "arg.m"; Archfs: module{ init: fn(nil: ref Draw->Context, nil: list of string);}; Ahdr: adt { name: string; modestr: string; d: ref Sys->Dir;}; Archive: adt { b: ref Bufio->Iobuf; nexthdr: big; canseek: int; hdr: ref Ahdr; err: string;}; 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"; UID: con "inferno";GID: con "inferno"; debug := 0; Dir: adt { dir: Sys->Dir; offset: big; parent: cyclic ref Dir; child: cyclic ref Dir; sibling: cyclic ref Dir;}; Fid: adt { fid: int; open: int; dir: ref Dir;}; HTSZ: con 32;fidtab := array[HTSZ] of list of ref Fid; root: ref Dir;qid: int;mtpt := "/mnt/arch";bio: ref Iobuf;buf: array of byte;skip := 0; init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; str = load String String->PATH; daytime = load Daytime Daytime->PATH; styx = load Styx Styx->PATH; if(bufio == nil || styx == nil || daytime == nil || str == nil) fatal("failed to load modules"); styx->init(); flags := Sys->MREPL; arg := load Arg Arg->PATH; if(arg == nil) fatal("failed to load "+Arg->PATH); arg->init(args); arg->setusage("archfs [-ab] [-m mntpt] archive [prefix ...]"); while((c := arg->opt()) != 0){ case c { 'D' => debug = 1; 'a' => flags = Sys->MAFTER; 'b' => flags = Sys->MBEFORE; 'm' => mtpt = arg->earg(); 's' => skip = 1; * => arg->usage(); } } args = arg->argv(); if(args == nil) arg->usage(); arg = nil; buf = array[Sys->ATOMICIO] of byte; # root = newdir("/", UID, GID, 8r755|Sys->DMDIR, daytime->now()); root = newdir(basename(mtpt), UID, GID, 8r555|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"); pidch := chan of int; spawn serve(p[1], pidch); <- pidch; if(sys->mount(p[0], nil, mtpt, flags, nil) < 0) fatal(sys->sprint("cannot mount archive on %s: %r", mtpt));} reply(fd: ref Sys->FD, m: ref Rmsg): int{ if(debug) sys->fprint(sys->fildes(2), "-> %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));} serve(fd: ref Sys->FD, pidch: chan of int){ e: string; f: ref Fid; pidch <-= sys->pctl(Sys->NEWNS|Sys->NEWFD, 1 :: 2 :: fd.fd :: bio.fd.fd :: nil); bio.fd = sys->fildes(bio.fd.fd); fd = sys->fildes(fd.fd);Work: while((m0 := Tmsg.read(fd, Styx->MAXRPC)) != nil){ if(debug) sys->fprint(sys->fildes(2), "<- %s\n", m0.text()); pick m := m0 { Readerror => fatal("read error on styx server"); Version => (s, v) := styx->compatible(m, Styx->MAXRPC, Styx->VERSION); reply(fd, ref Rmsg.Version(m.tag, s, v)); Auth => error(fd, m, "authentication not 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; } 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); continue Work; } break; } dir = lookup(dir, m.names[k]); if(dir == nil){ if(k == 0){ error(fd, m, Enotfound); continue Work; } break; } qids[nq++] = dir.dir.qid; } } 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 Work; } } 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->OREAD){ 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 := read(f.dir, 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"); } }} newfid(fid: int): ref Fid{ if(fid == NOFID) return nil; hv := hashval(fid); ff: ref Fid; for(l := fidtab[hv]; l != nil; l = tl l){ f := hd l; if(f.fid == fid) return nil; if(ff == nil && f.fid == NOFID) ff = f; } if((f := ff) == nil){ f = ref Fid; fidtab[hv] = f :: fidtab[hv]; } f.fid = fid; f.open = 0; return f;} freefid(f: ref Fid){ hv := hashval(f.fid); for(l := fidtab[hv]; l != nil; l = tl l) if(hd l == f){ f.fid = NOFID; f.dir = nil; f.open = 0; return; } fatal("cannot find fid");} mapfid(fid: int): (ref Fid, string){ if(fid == NOFID) return (nil, Ebadfid); hv := hashval(fid); for(l := fidtab[hv]; l != nil; l = tl l){ f := hd l; if(f.fid == fid){ if(f.dir == nil) return (nil, Enotfound); return (f, nil); } } return (nil, Ebadfid);} hashval(n: int): int{ n %= HTSZ; if(n < 0) n += HTSZ; return n;} readarch(f: string, args: list of string){ ar := openarch(f); if(ar == nil || ar.b == nil) fatal(sys->sprint("cannot open %s: %r", f)); bio = ar.b; while((a := gethdr(ar)) != nil){ if(args != nil){ if(!selected(a.name, args)){ if(skip) return; #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 = bio.offset(); } #drain(ar, int a.d.length); } if(ar.err != nil) fatal(ar.err);} 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"); if(p[0] != '/') fatal("p0 not / in split"); 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; }} read(d: ref Dir, offset: big, n: int): array of byte{ if(d.dir.mode & Sys->DMDIR) return readdir(d, int offset, n); return readfile(d, offset, n);} readdir(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];} readfile(d: ref Dir, offset: big, n: int): array of byte{ if(offset+big n > d.dir.length) n = int(d.dir.length-offset); if(n <= 0 || offset < big 0) return nil; bio.seek(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"); 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)); return; } if(prev.child == nil) prev.child = d; else { for(s := prev.child; s.sibling != nil; s = s.sibling) ; s.sibling = d;Lines 1–500 of 630. Load more
implement Archfs; include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; include "string.m"; str: String; include "daytime.m"; daytime: Daytime; include "styx.m"; styx: Styx; NOFID: import Styx; include "arg.m"; Archfs: module { init: fn(nil: ref Draw->Context, nil: list of string); }; Ahdr: adt { name: string; modestr: string; d: ref Sys->Dir; }; Archive: adt { b: ref Bufio->Iobuf; nexthdr: big; canseek: int; hdr: ref Ahdr; err: string; }; 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"; UID: con "inferno"; GID: con "inferno"; debug := 0; Dir: adt { dir: Sys->Dir; offset: big; parent: cyclic ref Dir; child: cyclic ref Dir; sibling: cyclic ref Dir; }; Fid: adt { fid: int; open: int; dir: ref Dir; }; HTSZ: con 32; fidtab := array[HTSZ] of list of ref Fid; root: ref Dir; qid: int; mtpt := "/mnt/arch"; bio: ref Iobuf; buf: array of byte; skip := 0; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; str = load String String->PATH; daytime = load Daytime Daytime->PATH; styx = load Styx Styx->PATH; if(bufio == nil || styx == nil || daytime == nil || str == nil) fatal("failed to load modules"); styx->init(); flags := Sys->MREPL; arg := load Arg Arg->PATH; if(arg == nil) fatal("failed to load "+Arg->PATH); arg->init(args); arg->setusage("archfs [-ab] [-m mntpt] archive [prefix ...]"); while((c := arg->opt()) != 0){ case c { 'D' => debug = 1; 'a' => flags = Sys->MAFTER; 'b' => flags = Sys->MBEFORE; 'm' => mtpt = arg->earg(); 's' => skip = 1; * => arg->usage(); } } args = arg->argv(); if(args == nil) arg->usage(); arg = nil; buf = array[Sys->ATOMICIO] of byte; # root = newdir("/", UID, GID, 8r755|Sys->DMDIR, daytime->now()); root = newdir(basename(mtpt), UID, GID, 8r555|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"); pidch := chan of int; spawn serve(p[1], pidch); <- pidch; if(sys->mount(p[0], nil, mtpt, flags, nil) < 0) fatal(sys->sprint("cannot mount archive on %s: %r", mtpt)); } reply(fd: ref Sys->FD, m: ref Rmsg): int { if(debug) sys->fprint(sys->fildes(2), "-> %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)); } serve(fd: ref Sys->FD, pidch: chan of int) { e: string; f: ref Fid; pidch <-= sys->pctl(Sys->NEWNS|Sys->NEWFD, 1 :: 2 :: fd.fd :: bio.fd.fd :: nil); bio.fd = sys->fildes(bio.fd.fd); fd = sys->fildes(fd.fd); Work: while((m0 := Tmsg.read(fd, Styx->MAXRPC)) != nil){ if(debug) sys->fprint(sys->fildes(2), "<- %s\n", m0.text()); pick m := m0 { Readerror => fatal("read error on styx server"); Version => (s, v) := styx->compatible(m, Styx->MAXRPC, Styx->VERSION); reply(fd, ref Rmsg.Version(m.tag, s, v)); Auth => error(fd, m, "authentication not 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; } 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); continue Work; } break; } dir = lookup(dir, m.names[k]); if(dir == nil){ if(k == 0){ error(fd, m, Enotfound); continue Work; } break; } qids[nq++] = dir.dir.qid; } } 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 Work; } } 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->OREAD){ 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 := read(f.dir, 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"); } } } newfid(fid: int): ref Fid { if(fid == NOFID) return nil; hv := hashval(fid); ff: ref Fid; for(l := fidtab[hv]; l != nil; l = tl l){ f := hd l; if(f.fid == fid) return nil; if(ff == nil && f.fid == NOFID) ff = f; } if((f := ff) == nil){ f = ref Fid; fidtab[hv] = f :: fidtab[hv]; } f.fid = fid; f.open = 0; return f; } freefid(f: ref Fid) { hv := hashval(f.fid); for(l := fidtab[hv]; l != nil; l = tl l) if(hd l == f){ f.fid = NOFID; f.dir = nil; f.open = 0; return; } fatal("cannot find fid"); } mapfid(fid: int): (ref Fid, string) { if(fid == NOFID) return (nil, Ebadfid); hv := hashval(fid); for(l := fidtab[hv]; l != nil; l = tl l){ f := hd l; if(f.fid == fid){ if(f.dir == nil) return (nil, Enotfound); return (f, nil); } } return (nil, Ebadfid); } hashval(n: int): int { n %= HTSZ; if(n < 0) n += HTSZ; return n; } readarch(f: string, args: list of string) { ar := openarch(f); if(ar == nil || ar.b == nil) fatal(sys->sprint("cannot open %s: %r", f)); bio = ar.b; while((a := gethdr(ar)) != nil){ if(args != nil){ if(!selected(a.name, args)){ if(skip) return; #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 = bio.offset(); } #drain(ar, int a.d.length); } if(ar.err != nil) fatal(ar.err); } 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"); if(p[0] != '/') fatal("p0 not / in split"); 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; } } read(d: ref Dir, offset: big, n: int): array of byte { if(d.dir.mode & Sys->DMDIR) return readdir(d, int offset, n); return readfile(d, offset, n); } readdir(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]; } readfile(d: ref Dir, offset: big, n: int): array of byte { if(offset+big n > d.dir.length) n = int(d.dir.length-offset); if(n <= 0 || offset < big 0) return nil; bio.seek(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"); 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)); return; } if(prev.child == nil) prev.child = d; else { for(s := prev.child; s.sibling != nil; s = s.sibling) ; s.sibling = d;