ref: 9f76a7f6819ac04552b4fb6588156f3e4089d1d7
dir: /appl/cmd/install/archfs.b/
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;
}