diff options
Diffstat (limited to 'appl/acme/fsys.b')
| -rw-r--r-- | appl/acme/fsys.b | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/appl/acme/fsys.b b/appl/acme/fsys.b new file mode 100644 index 00000000..65ce4982 --- /dev/null +++ b/appl/acme/fsys.b @@ -0,0 +1,866 @@ +implement Fsys; + +include "common.m"; + +sys : Sys; +styx : Styx; +styxaux : Styxaux; +acme : Acme; +dat : Dat; +utils : Utils; +look : Look; +windowm : Windowm; +xfidm : Xfidm; + +QTDIR, QTFILE, QTAPPEND : import Sys; +DMDIR, DMAPPEND, Qid, ORCLOSE, OTRUNC, OREAD, OWRITE, ORDWR, Dir : import Sys; +sprint : import sys; +MAXWELEM, Rerror : import Styx; +Qdir,Qacme,Qcons,Qconsctl,Qdraw,Qeditout,Qindex,Qlabel,Qnew,QWaddr,QWbody,QWconsctl,QWctl,QWdata,QWeditout,QWevent,QWrdsel,QWwrsel,QWtag,QMAX : import Dat; +TRUE, FALSE : import Dat; +cxfidalloc, cerr : import dat; +Mntdir, Fid, Dirtab, Lock, Ref, Smsg0 : import dat; +Tmsg, Rmsg : import styx; +msize, version, fid, uname, aname, newfid, name, mode, offset, count, setmode : import styxaux; +Xfid : import xfidm; +row : import dat; +Column : import Columnm; +Window : import windowm; +lookid : import look; +warning, error : import utils; + +init(mods : ref Dat->Mods) +{ + messagesize = Styx->MAXRPC; + + sys = mods.sys; + styx = mods.styx; + styxaux = mods.styxaux; + acme = mods.acme; + dat = mods.dat; + utils = mods.utils; + look = mods.look; + windowm = mods.windowm; + xfidm = mods.xfidm; +} + +sfd, cfd : ref Sys->FD; + +Nhash : con 16; +DEBUG : con 0; + +fids := array[Nhash] of ref Fid; + +Eperm := "permission denied"; +Eexist := "file does not exist"; +Enotdir := "not a directory"; + +dirtab := array[10] of { + Dirtab ( ".", QTDIR, Qdir, 8r500|DMDIR ), + Dirtab ( "acme", QTDIR, Qacme, 8r500|DMDIR ), + Dirtab ( "cons", QTFILE, Qcons, 8r600 ), + Dirtab ( "consctl", QTFILE, Qconsctl, 8r000 ), + Dirtab ( "draw", QTDIR, Qdraw, 8r000|DMDIR ), + Dirtab ( "editout", QTFILE, Qeditout, 8r200 ), + Dirtab ( "index", QTFILE, Qindex, 8r400 ), + Dirtab ( "label", QTFILE, Qlabel, 8r600 ), + Dirtab ( "new", QTDIR, Qnew, 8r500|DMDIR ), + Dirtab ( nil, 0, 0, 0 ), +}; + +dirtabw := array[12] of { + Dirtab ( ".", QTDIR, Qdir, 8r500|DMDIR ), + Dirtab ( "addr", QTFILE, QWaddr, 8r600 ), + Dirtab ( "body", QTAPPEND, QWbody, 8r600|DMAPPEND ), + Dirtab ( "ctl", QTFILE, QWctl, 8r600 ), + Dirtab ( "consctl", QTFILE, QWconsctl, 8r200 ), + Dirtab ( "data", QTFILE, QWdata, 8r600 ), + Dirtab ( "editout", QTFILE, QWeditout, 8r200 ), + Dirtab ( "event", QTFILE, QWevent, 8r600 ), + Dirtab ( "rdsel", QTFILE, QWrdsel, 8r400 ), + Dirtab ( "wrsel", QTFILE, QWwrsel, 8r200 ), + Dirtab ( "tag", QTAPPEND, QWtag, 8r600|DMAPPEND ), + Dirtab ( nil, 0, 0, 0 ), +}; + +Mnt : adt { + qlock : ref Lock; + id : int; + md : ref Mntdir; +}; + +mnt : Mnt; +user : string; +clockfd : ref Sys->FD; +closing := 0; + +fsysinit() +{ + p : array of ref Sys->FD; + + p = array[2] of ref Sys->FD; + if(sys->pipe(p) < 0) + error("can't create pipe"); + cfd = p[0]; + sfd = p[1]; + clockfd = sys->open("/dev/time", Sys->OREAD); + user = utils->getuser(); + if (user == nil) + user = "Wile. E. Coyote"; + mnt.qlock = Lock.init(); + mnt.id = 0; + spawn fsysproc(); +} + +fsyscfd() : int +{ + return cfd.fd; +} + +QID(w, q : int) : int +{ + return (w<<8)|q; +} + +FILE(q : Qid) : int +{ + return int q.path & 16rFF; +} + +WIN(q : Qid) : int +{ + return (int q.path>>8) & 16rFFFFFF; +} + +# nullsmsg : Smsg; +nullsmsg0 : Smsg0; + +fsysproc() +{ + n, ok : int; + x : ref Xfid; + f : ref Fid; + t : Smsg0; + + acme->fsyspid = sys->pctl(0, nil); + x = nil; + for(;;){ + if(x == nil){ + cxfidalloc <-= nil; + x = <-cxfidalloc; + } + n = sys->read(sfd, x.buf, messagesize); + if(n <= 0) { + if (closing) + break; + error("i/o error on server channel"); + } + (ok, x.fcall) = Tmsg.unpack(x.buf[0:n]); + if(ok < 0) + error("convert error in convM2S"); + if(DEBUG) + utils->debug(sprint("%d:%s\n", x.tid, x.fcall.text())); + pick fc := x.fcall { + Version => + f = nil; + Auth => + f = nil; + * => + f = allocfid(fid(x.fcall)); + } + x.f = f; + pick fc := x.fcall { + Readerror => x = fsyserror(); + Flush => x = fsysflush(x); + Version => x = fsysversion(x); + Auth => x = fsysauth(x); + Attach => x = fsysattach(x, f); + Walk => x = fsyswalk(x, f); + Open => x = fsysopen(x, f); + Create => x = fsyscreate(x); + Read => x = fsysread(x, f); + Write => x = fsyswrite(x); + Clunk => x = fsysclunk(x, f); + Remove => x = fsysremove(x); + Stat => x = fsysstat(x, f); + Wstat => x = fsyswstat(x); + # Clone => x = fsysclone(x, f); + * => + x = respond(x, t, "bad fcall type"); + } + } +} + +fsysaddid(dir : string, ndir : int, incl : array of string, nincl : int) : ref Mntdir +{ + m : ref Mntdir; + id : int; + + mnt.qlock.lock(); + id = ++mnt.id; + m = ref Mntdir; + m.id = id; + m.dir = dir; + m.refs = 1; # one for Command, one will be incremented in attach + m.ndir = ndir; + m.next = mnt.md; + m.incl = incl; + m.nincl = nincl; + mnt.md = m; + mnt.qlock.unlock(); + return m; +} + +fsysdelid(idm : ref Mntdir) +{ + m, prev : ref Mntdir; + i : int; + + if(idm == nil) + return; + mnt.qlock.lock(); + if(--idm.refs > 0){ + mnt.qlock.unlock(); + return; + } + prev = nil; + for(m=mnt.md; m != nil; m=m.next){ + if(m == idm){ + if(prev != nil) + prev.next = m.next; + else + mnt.md = m.next; + for(i=0; i<m.nincl; i++) + m.incl[i] = nil; + m.incl = nil; + m.dir = nil; + m = nil; + mnt.qlock.unlock(); + return; + } + prev = m; + } + mnt.qlock.unlock(); + buf := sys->sprint("fsysdelid: can't find id %d\n", idm.id); + cerr <-= buf; +} + +# +# Called only in exec.l:run(), from a different FD group +# +fsysmount(dir : string, ndir : int, incl : array of string, nincl : int) : ref Mntdir +{ + m : ref Mntdir; + + # close server side so don't hang if acme is half-exited + # sfd = nil; + m = fsysaddid(dir, ndir, incl, nincl); + buf := sys->sprint("%d", m.id); + if(sys->mount(cfd, nil, "/mnt/acme", Sys->MREPL, buf) < 0){ + fsysdelid(m); + return nil; + } + # cfd = nil; + sys->bind("/mnt/acme", "/chan", Sys->MBEFORE); # was MREPL + if(sys->bind("/mnt/acme", "/dev", Sys->MBEFORE) < 0){ + fsysdelid(m); + return nil; + } + return m; +} + +fsysclose() +{ + closing = 1; + # sfd = cfd = nil; +} + +respond(x : ref Xfid, t0 : Smsg0, err : string) : ref Xfid +{ + t : ref Rmsg; + + # t = nullsmsg; + tag := x.fcall.tag; + # fid := fid(x.fcall); + qid := t0.qid; + if(err != nil) + t = ref Rmsg.Error(tag, err); + else + pick fc := x.fcall { + Readerror => t = ref Rmsg.Error(tag, err); + Flush => t = ref Rmsg.Flush(tag); + Version => t = ref Rmsg.Version(tag, t0.msize, t0.version); + Auth => t = ref Rmsg.Auth(tag, qid); + # Clone => t = ref Rmsg.Clone(tag, fid); + Attach => t = ref Rmsg.Attach(tag, qid); + Walk => t = ref Rmsg.Walk(tag, t0.qids); + Open => t = ref Rmsg.Open(tag, qid, t0.iounit); + Create => t = ref Rmsg.Create(tag, qid, 0); + Read => if(t0.count == len t0.data) + t = ref Rmsg.Read(tag, t0.data); + else + t = ref Rmsg.Read(tag, t0.data[0: t0.count]); + Write => t = ref Rmsg.Write(tag, t0.count); + Clunk => t = ref Rmsg.Clunk(tag); + Remove => t = ref Rmsg.Remove(tag); + Stat => t = ref Rmsg.Stat(tag, t0.stat); + Wstat => t = ref Rmsg.Wstat(tag); + + } + # t.qid = t0.qid; + # t.count = t0.count; + # t.data = t0.data; + # t.stat = t0.stat; + # t.fid = x.fcall.fid; + # t.tag = x.fcall.tag; + buf := t.pack(); + if(buf == nil) + error("convert error in convS2M"); + if(sys->write(sfd, buf, len buf) != len buf) + error("write error in respond"); + buf = nil; + if(DEBUG) + utils->debug(sprint("%d:r: %s\n", x.tid, t.text())); + return x; +} + +# fsysnop(x : ref Xfid) : ref Xfid +# { +# t : Smsg0; +# +# return respond(x, t, nil); +# } + +fsyserror() : ref Xfid +{ + error("sys error : Terror"); + return nil; +} + +fsyssession(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + # BUG: should shut everybody down ?? + t = nullsmsg0; + return respond(x, t, nil); +} + +fsysversion(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + pick m := x.fcall { + Version => + (t.msize, t.version) = styx->compatible(m, messagesize, nil); + messagesize = t.msize; + return respond(x, t, nil); + } + return respond(x, t, "acme: bad version"); + + # ms := msize(x.fcall); + # if(ms < 256) + # return respond(x, t, "version: message size too small"); + # t.msize = messagesize = ms; + # v := version(x.fcall); + # if(len v < 6 || v[0: 6] != "9P2000") + # return respond(x, t, "unrecognized 9P version"); + # t.version = "9P2000"; + # return respond(x, t, nil); +} + +fsysauth(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + return respond(x, t, "acme: authentication not required"); +} + +fsysflush(x : ref Xfid) : ref Xfid +{ + x.c <-= Xfidm->Xflush; + return nil; +} + +fsysattach(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + id : int; + m : ref Mntdir; + + if (uname(x.fcall) != user) + return respond(x, t, Eperm); + f.busy = TRUE; + f.open = FALSE; + f.qid = (Qid)(big Qdir, 0, QTDIR); + f.dir = dirtab; + f.nrpart = 0; + f.w = nil; + t.qid = f.qid; + f.mntdir = nil; + id = int aname(x.fcall); + mnt.qlock.lock(); + for(m=mnt.md; m != nil; m=m.next) + if(m.id == id){ + f.mntdir = m; + m.refs++; + break; + } + if(m == nil) + cerr <-= "unknown id in attach"; + mnt.qlock.unlock(); + return respond(x, t, nil); +} + +fsyswalk(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + c, i, j, id : int; + path, qtype : int; + d, dir : array of Dirtab; + w : ref Window; + nf : ref Fid; + + if(f.open) + return respond(x, t, "walk of open file"); + if(fid(x.fcall) != newfid(x.fcall)){ + nf = allocfid(newfid(x.fcall)); + if(nf.busy) + return respond(x, t, "newfid already in use"); + nf.busy = TRUE; + nf.open = FALSE; + nf.mntdir = f.mntdir; + if(f.mntdir != nil) + f.mntdir.refs++; + nf.dir = f.dir; + nf.qid = f.qid; + nf.w = f.w; + nf.nrpart = 0; # not open, so must be zero + if(nf.w != nil) + nf.w.refx.inc(); + f = nf; # walk f + } + + qtype = QTFILE; + wqids: list of Qid; + err := string nil; + id = WIN(f.qid); + q := f.qid; + names := styxaux->names(x.fcall); + nwname := len names; + + if(nwname > 0){ + for(i = 0; i < nwname; i++){ + if((q.qtype & QTDIR) == 0){ + err = Enotdir; + break; + } + + name := names[i]; + if(name == ".."){ + path = Qdir; + qtype = QTDIR; + id = 0; + if(w != nil){ + w.close(); + w = nil; + } + if(i == MAXWELEM){ + err = "name too long"; + break; + } + q.qtype = qtype; + q.vers = 0; + q.path = big QID(id, path); + wqids = q :: wqids; + continue; + } + + # is it a numeric name? + regular := 0; + for(j=0; j < len name; j++) { + c = name[j]; + if(c<'0' || '9'<c) { + regular = 1; + break; + } + } + + if (!regular) { + # yes: it's a directory + if(w != nil) # name has form 27/23; get out before losing w + break; + id = int name; + row.qlock.lock(); + w = lookid(id, FALSE); + if(w == nil){ + row.qlock.unlock(); + break; + } + w.refx.inc(); + path = Qdir; + qtype = QTDIR; + row.qlock.unlock(); + dir = dirtabw; + if(i == MAXWELEM){ + err = "name too long"; + break; + } + q.qtype = qtype; + q.vers = 0; + q.path = big QID(id, path); + wqids = q :: wqids; + continue; + } + else { + # if(FILE(f.qid) == Qacme) # empty directory + # break; + if(name == "new"){ + if(w != nil) + error("w set in walk to new"); + cw := chan of ref Window; + spawn x.walk(cw); + w = <- cw; + w.refx.inc(); + path = QID(w.id, Qdir); + qtype = QTDIR; + id = w.id; + dir = dirtabw; + # x.c <-= Xfidm->Xwalk; + if(i == MAXWELEM){ + err = "name too long"; + break; + } + q.qtype = qtype; + q.vers = 0; + q.path = big QID(id, path); + wqids = q :: wqids; + continue; + } + + if(id == 0) + d = dirtab; + else + d = dirtabw; + k := 1; # skip '.' + found := 0; + for( ; d[k].name != nil; k++){ + if(name == d[k].name){ + path = d[k].qid; + qtype = d[k].qtype; + dir = d[k:]; + if(i == MAXWELEM){ + err = "name too long"; + break; + } + q.qtype = qtype; + q.vers = 0; + q.path = big QID(id, path); + wqids = q :: wqids; + found = 1; + break; + } + } + if(found) + continue; + break; # file not found + } + } + + if(i == 0 && err == nil) + err = Eexist; + } + + nwqid := len wqids; + if(nwqid > 0){ + t.qids = array[nwqid] of Qid; + for(i = nwqid-1; i >= 0; i--){ + t.qids[i] = hd wqids; + wqids = tl wqids; + } + } + if(err != nil || nwqid < nwname){ + if(nf != nil){ + nf.busy = FALSE; + fsysdelid(nf.mntdir); + } + } + else if(nwqid == nwname){ + if(w != nil){ + f.w = w; + w = nil; + } + if(dir != nil) + f.dir = dir; + f.qid = q; + } + + if(w != nil) + w.close(); + + return respond(x, t, err); +} + +fsysopen(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + m : int; + + # can't truncate anything, so just disregard + setmode(x.fcall, mode(x.fcall)&~OTRUNC); + # can't execute or remove anything + if(mode(x.fcall)&ORCLOSE) + return respond(x, t, Eperm); + case(mode(x.fcall)){ + OREAD => + m = 8r400; + OWRITE => + m = 8r200; + ORDWR => + m = 8r600; + * => + return respond(x, t, Eperm); + } + if(((f.dir[0].perm&~(DMDIR|DMAPPEND))&m) != m) + return respond(x, t, Eperm); + x.c <-= Xfidm->Xopen; + return nil; +} + +fsyscreate(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + return respond(x, t, Eperm); +} + +idcmp(a, b : int) : int +{ + return a-b; +} + +qsort(a : array of int, n : int) +{ + i, j : int; + t : int; + + while(n > 1) { + i = n>>1; + t = a[0]; a[0] = a[i]; a[i] = t; + i = 0; + j = n; + for(;;) { + do + i++; + while(i < n && idcmp(a[i], a[0]) < 0); + do + j--; + while(j > 0 && idcmp(a[j], a[0]) > 0); + if(j < i) + break; + t = a[i]; a[i] = a[j]; a[j] = t; + } + t = a[0]; a[0] = a[j]; a[j] = t; + n = n-j-1; + if(j >= n) { + qsort(a, j); + a = a[j+1:]; + } else { + qsort(a[j+1:], n); + n = j; + } + } +} + +fsysread(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + b : array of byte; + i, id, n, o, e, j, k, nids : int; + ids : array of int; + d : array of Dirtab; + dt : Dirtab; + c : ref Column; + clock : int; + + b = nil; + if(f.qid.qtype & QTDIR){ + # if(int offset(x.fcall) % DIRLEN) + # return respond(x, t, "illegal offset in directory"); + if(FILE(f.qid) == Qacme){ # empty dir + t.data = nil; + t.count = 0; + respond(x, t, nil); + return x; + } + o = int offset(x.fcall); + e = int offset(x.fcall)+count(x.fcall); + clock = getclock(); + b = array[messagesize] of byte; + id = WIN(f.qid); + n = 0; + if(id > 0) + d = dirtabw; + else + d = dirtab; + k = 1; # first entry is '.' + leng := 0; + for(i=0; d[k].name!=nil && i<e; i+=leng){ + bb := styx->packdir(dostat(WIN(x.f.qid), d[k], clock)); + leng = len bb; + for (kk := 0; kk < leng; kk++) + b[kk+n] = bb[kk]; + bb = nil; + if(leng <= Styx->BIT16SZ) + break; + if(i >= o) + n += leng; + k++; + } + if(id == 0){ + row.qlock.lock(); + nids = 0; + ids = nil; + for(j=0; j<row.ncol; j++){ + c = row.col[j]; + for(k=0; k<c.nw; k++){ + oids := ids; + ids = array[nids+1] of int; + ids[0:] = oids[0:nids]; + oids = nil; + ids[nids++] = c.w[k].id; + } + } + row.qlock.unlock(); + qsort(ids, nids); + j = 0; + for(; j<nids && i<e; i+=leng){ + k = ids[j]; + dt.name = sys->sprint("%d", k); + dt.qid = QID(k, 0); + dt.qtype = QTDIR; + dt.perm = DMDIR|8r700; + bb := styx->packdir(dostat(k, dt, clock)); + leng = len bb; + for (kk := 0; kk < leng; kk++) + b[kk+n] = bb[kk]; + bb = nil; + if(leng == 0) + break; + if(i >= o) + n += leng; + j++; + } + ids = nil; + } + t.data = b; + t.count = n; + respond(x, t, nil); + b = nil; + return x; + } + x.c <-= Xfidm->Xread; + return nil; +} + +fsyswrite(x : ref Xfid) : ref Xfid +{ + x.c <-= Xfidm->Xwrite; + return nil; +} + +fsysclunk(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + + fsysdelid(f.mntdir); + if(f.open){ + f.busy = FALSE; + f.open = FALSE; + x.c <-= Xfidm->Xclose; + return nil; + } + if(f.w != nil) + f.w.close(); + f.busy = FALSE; + f.open = FALSE; + return respond(x, t, nil); +} + +fsysremove(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + return respond(x, t, Eperm); +} + +fsysstat(x : ref Xfid, f : ref Fid) : ref Xfid +{ + t : Smsg0; + + t.stat = dostat(WIN(x.f.qid), f.dir[0], getclock()); + return respond(x, t, nil); +} + +fsyswstat(x : ref Xfid) : ref Xfid +{ + t : Smsg0; + + return respond(x, t, Eperm); +} + +allocfid(fid : int) : ref Fid +{ + f, ff : ref Fid; + fh : int; + + ff = nil; + fh = fid&(Nhash-1); + for(f=fids[fh]; f != nil; f=f.next) + if(f.fid == fid) + return f; + else if(ff==nil && f.busy==FALSE) + ff = f; + if(ff != nil){ + ff.fid = fid; + return ff; + } + f = ref Fid; + f.busy = FALSE; + f.rpart = array[Sys->UTFmax] of byte; + f.nrpart = 0; + f.fid = fid; + f.next = fids[fh]; + fids[fh] = f; + return f; +} + +cbuf := array[32] of byte; + +getclock() : int +{ + sys->seek(clockfd, big 0, 0); + n := sys->read(clockfd, cbuf, len cbuf); + return int string cbuf[0:n]; +} + +dostat(id : int, dir : Dirtab, clock : int) : Sys->Dir +{ + d : Dir; + + d.qid.path = big QID(id, dir.qid); + d.qid.vers = 0; + d.qid.qtype = dir.qtype; + d.mode = dir.perm; + d.length = big 0; # would be nice to do better + d.name = dir.name; + d.uid = user; + d.gid = user; + d.atime = clock; + d.mtime = clock; + d.dtype = d.dev = 0; + return d; + # buf := styx->convD2M(d); + # d = nil; + # return buf; +} |
