ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/acme/fsys.b/
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;
}