ref: 1049552fb14efa811ca879b35244b8ea684995c1
dir: /fs.c/
#include "inc.h"
enum {
Qroot,
Qwsys,
Qcons,
Qconsctl,
Qcursor,
Qwinid,
Qwinname,
Qlabel,
Qkbd,
Qmouse,
Qscreen,
Qsnarf,
Qtext,
Qwdir,
// Qwctl,
Qwindow,
// Qtap, //
NQids
};
typedef struct Dirent Dirent;
struct Dirent
{
int path;
int type;
char *name;
};
Dirent dirents[] = {
Qroot, QTDIR, ".",
Qwsys, QTDIR, "wsys",
Qwinid, QTFILE, "winid",
Qwinname, QTFILE, "winname",
Qwdir, QTFILE, "wdir",
Qlabel, QTFILE, "label",
Qsnarf, QTFILE, "snarf",
Qtext, QTFILE, "text",
Qcons, QTFILE, "cons",
Qconsctl, QTFILE, "consctl",
Qkbd, QTFILE, "kbd",
Qmouse, QTFILE, "mouse",
Qcursor, QTFILE, "cursor",
Qscreen, QTFILE, "screen",
Qwindow, QTFILE, "window",
};
char Eperm[] = "permission denied";
char Eexist[] = "file does not exist";
char Enotdir[] = "not a directory";
char Ebadfcall[] = "bad fcall type";
char Eoffset[] = "illegal offset";
char Enomem[] = "out of memory";
char Eflush[] = "interrupted";
char Einuse[] = "file in use";
char Edeleted[] = "window deleted";
char Etooshort[] = "buffer too small";
char Eshort[] = "short i/o request";
char Elong[] = "snarf buffer too long";
char Eunkid[] = "unknown id in attach";
char Ebadrect[] = "bad rectangle in attach";
char Ewindow[] = "cannot make window";
char Enowindow[] = "window has no image";
char Ebadmouse[] = "bad format on /dev/mouse";
/* Extension of a Fid, fid->aux */
typedef struct Xfid Xfid;
struct Xfid
{
Fid *fid;
Channel *xc;
Channel *flush; // cancel read/write
Window *w;
RuneConvBuf cnv;
Xfid *next;
};
static Xfid *xfidfree;
typedef struct XfidMsg XfidMsg;
struct XfidMsg
{
Req *r;
void (*f)(Req*);
};
static void
xfidthread(void *a)
{
Xfid *xf = a;
XfidMsg xm;
for(;;){
recv(xf->xc, &xm);
(*xm.f)(xm.r);
}
}
static void
toxfid(Req *r, void (*f)(Req*))
{
Xfid *xf;
XfidMsg xm;
xf = r->fid->aux;
xm.r = r;
xm.f = f;
send(xf->xc, &xm);
}
static Xfid*
getxfid(Fid *fid, Window *w)
{
Xfid *xf;
if(xfidfree){
xf = xfidfree;
xfidfree = xf->next;
}else{
xf = emalloc(sizeof(Xfid));
xf->xc = chancreate(sizeof(XfidMsg), 0);
xf->flush = chancreate(sizeof(int), 0);
threadcreate(xfidthread, xf, mainstacksize);
}
memset(&xf->cnv, 0, sizeof(xf->cnv));
xf->fid = fid;
xf->w = w;
incref(w);
xf->next = nil;
return xf;
}
static void
freexfid(Xfid *xf)
{
wrelease(xf->w);
free(xf->cnv.buf);
xf->fid = nil;
xf->w = nil;
xf->next = xfidfree;
xfidfree = xf;
}
#define QID(w, q) ((w)<<8|(q))
#define QWIN(q) ((q)>>8)
#define QFILE(q) ((q)&0xFF)
static void
fsattach(Req *r)
{
Window *w;
char *end;
int id;
w = nil;
if(strcmp(r->ifcall.aname, "new") == 0){
w = wcreate(rectaddpt(newrect(), screen->r.min));
wsetpid(w, -1, 1);
wsetname(w);
flushimage(display, 1);
decref(w); /* don't delete, xfid will take it */
}else if(id = strtol(r->ifcall.aname, &end, 10), *end == '\0'){
w = wfind(id);
}
if(w == nil){
respond(r, "bad attach name");
return;
}
r->fid->aux = getxfid(r->fid, w);
r->fid->qid = (Qid){QID(w->id,Qroot),0,QTDIR};
r->ofcall.qid = r->fid->qid;
respond(r, nil);
}
static char*
fsclone(Fid *fid, Fid *newfid)
{
Xfid *xf;
xf = fid->aux;
if(xf)
newfid->aux = getxfid(newfid, xf->w);
return nil;
}
int
skipfile(char *name)
{
return gotscreen && strcmp(name, "screen") == 0 ||
snarffd >= 0 && strcmp(name, "snarf") == 0 ||
!servekbd && strcmp(name, "kbd") == 0;
}
static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
int i;
Dirent *d;
Xfid *xf;
Window *w;
int dir;
xf = fid->aux;
w = xf->w;
dir = QFILE(fid->qid.path);
if(dir == Qroot){
if(strcmp(name, "..") == 0){
/* This sucks because we don't know which window we came from
* error out for now */
return "vorwärts immer, rückwärts nimmer";
}
for(i = 0; i < nelem(dirents); i++){
d = &dirents[i];
if(!skipfile(d->name) && strcmp(name, d->name) == 0){
fid->qid = (Qid){QID(w->id,d->path), 0, d->type};
*qid = fid->qid;
return nil;
}
}
}else if(dir == Qwsys){
char *end;
int id;
if(strcmp(name, "..") == 0){
fid->qid = (Qid){QID(w->id,Qroot), 0, QTDIR};
*qid = fid->qid;
return nil;
}
if(id = strtol(name, &end, 10), *end == '\0'){
w = wfind(id);
if(w){
incref(w);
wrelease(xf->w);
xf->w = w;
fid->qid = (Qid){QID(w->id,Qroot), 0, QTDIR};
*qid = fid->qid;
return nil;
}
}
}
return "no such file";
}
static int
genrootdir(int n, Dir *d, void *a)
{
Window *w = a;
int i;
n++; /* -1 is root dir */
i = 0;
while(n--){
i++;
if(i >= nelem(dirents))
return -1;
/* we know the last file is never skipped */
while(skipfile(dirents[i].name)) i++;
}
d->atime = time(nil);
d->mtime = d->atime;
d->uid = estrdup9p(getuser());
d->gid = estrdup9p(d->uid);
d->muid = estrdup9p(d->uid);
d->qid = (Qid){QID(w->id,dirents[i].path), 0, dirents[i].type};
d->mode = 0664;
if(dirents[i].type & QTDIR)
d->mode |= 0111;
d->name = estrdup9p(dirents[i].name);
d->length = 0;
return 0;
}
static int
genwsysdir(int n, Dir *d, void*)
{
d->atime = time(nil);
d->mtime = d->atime;
d->uid = estrdup9p(getuser());
d->gid = estrdup9p(d->uid);
d->muid = estrdup9p(d->uid);
if(n == -1){
d->qid = (Qid){Qwsys, 0, QTDIR};
d->mode = 0775;
d->name = estrdup9p("wsys");
d->length = 0;
return 0;
}
if(n < nwindows){
int id = windows[n]->id;
d->qid = (Qid){QID(id,Qroot), 0, QTDIR};
d->mode = 0775;
d->name = smprint("%d", id);
d->length = 0;
return 0;
}
return -1;
}
static int ntsnarf;
static char *tsnarf;
static void
xfopen(Req *r)
{
Xfid *xf;
Window *w;
xf = r->fid->aux;
w = xf->w;
if(w == nil || w->deleted){
respond(r, Edeleted);
return;
}
switch(QFILE(xf->fid->qid.path)){
case Qsnarf:
r->ifcall.mode &= ~OTRUNC;
if(r->ifcall.mode==ORDWR || r->ifcall.mode==OWRITE)
ntsnarf = 0;
break;
case Qconsctl:
if(w->consctlopen){
respond(r, Einuse);
return;
}
w->consctlopen = TRUE;
break;
case Qkbd:
if(w->kbdopen){
respond(r, Einuse);
return;
}
w->kbdopen = TRUE;
break;
case Qmouse:
if(w->mouseopen){
respond(r, Einuse);
return;
}
// TODO: copy comment from rio
w->resized = FALSE;
w->mouseopen = TRUE;
break;
}
respond(r, nil);
}
static void
xfclose(Xfid *xf)
{
Window *w;
Text *x;
w = xf->w;
x = &w->text;
switch(QFILE(xf->fid->qid.path)){
/* replace snarf buffer when /dev/snarf is closed */
case Qsnarf:
if(xf->fid->omode==ORDWR || xf->fid->omode==OWRITE){
setsnarf(tsnarf, ntsnarf);
ntsnarf = 0;
}
break;
case Qconsctl:
if(x->rawmode){
x->rawmode = 0;
wsendmsg(w, Rawoff, ZR, nil);
}
if(w->holdmode > 0){
w->holdmode = 1;
wsendmsg(w, Holdoff, ZR, nil);
}
w->consctlopen = FALSE;
break;
case Qkbd:
w->kbdopen = FALSE;
break;
case Qmouse:
w->mouseopen = FALSE;
w->resized = FALSE;
wsendmsg(w, Refresh, ZR, nil);
break;
case Qcursor:
w->cursorp = nil;
wsetcursor(w);
break;
}
}
static int
readimgdata(Image *i, char *t, Rectangle r, int offset, int n)
{
int ww, oo, y, m;
uchar *tt;
ww = bytesperline(r, i->depth);
r.min.y += offset/ww;
if(r.min.y >= r.max.y)
return 0;
y = r.min.y + (n + ww-1)/ww;
if(y < r.max.y)
r.max.y = y;
m = ww * Dy(r);
oo = offset % ww;
if(oo == 0 && n >= m)
return unloadimage(i, r, (uchar*)t, n);
if((tt = malloc(m)) == nil)
return -1;
m = unloadimage(i, r, tt, m) - oo;
if(m > 0){
if(n < m) m = n;
memmove(t, tt + oo, m);
}
free(tt);
return m;
}
/* Fill request from image,
* returns only either header or data */
char*
readimg(Req *r, Image *img)
{
char *head;
char cbuf[30];
Rectangle rect;
int n;
rect = img->r;
if(r->ifcall.offset < 5*12){
head = smprint("%11s %11d %11d %11d %11d ",
chantostr(cbuf, img->chan),
rect.min.x, rect.min.y, rect.max.x, rect.max.y);
readstr(r, head);
free(head);
}else{
/* count is unsigned, so check with n */
n = readimgdata(img, r->ofcall.data, rect, r->ifcall.offset-5*12, r->ifcall.count);
if(n < 0)
return Enomem;
r->ofcall.count = n;
}
return nil;
}
static char*
readblocking(Req *r, Channel *readchan)
{
Xfid *xf;
Window *w;
Channel *chan;
Stringpair pair;
enum { Adata, Agone, Aflush, NALT };
Alt alts[NALT+1];
xf = r->fid->aux;
w = xf->w;
alts[Adata] = ALT(readchan, &chan, CHANRCV);
alts[Agone] = ALT(w->gone, nil, CHANRCV);
alts[Aflush] = ALT(xf->flush, nil, CHANRCV);
alts[NALT].op = CHANEND;
switch(alt(alts)){
case Adata:
pair.s = r->ofcall.data;
pair.ns = r->ifcall.count;
send(chan, &pair);
recv(chan, &pair);
r->ofcall.count = pair.ns;
return nil;
case Agone:
return Edeleted;
case Aflush:
return Eflush;
}
return nil; /* can't happen */
}
static void
xfread(Req *r)
{
Xfid *xf;
Window *w;
char *data;
xf = r->fid->aux;
w = xf->w;
if(w == nil || w->deleted){
respond(r, Edeleted);
return;
}
switch(QFILE(xf->fid->qid.path)){
case Qwinid:
data = smprint("%11d ", w->id);
readstr(r, data);
free(data);
break;
case Qwinname:
readstr(r, w->name);
break;
case Qlabel:
readstr(r, w->label);
break;
case Qsnarf:
data = smprint("%.*S", nsnarf, snarf);
readstr(r, data);
free(data);
break;
case Qtext:
data = smprint("%.*S", w->text.nr, w->text.r);
readstr(r, data);
free(data);
break;
case Qcons:
respond(r, readblocking(r, w->consread));
return;
case Qkbd:
respond(r, readblocking(r, w->kbdread));
return;
case Qmouse:
respond(r, readblocking(r, w->mouseread));
return;
case Qcursor:
respond(r, "cursor read not implemented");
return;
case Qscreen:
respond(r, readimg(r, screen));
return;
case Qwindow:
respond(r, readimg(r, w->img));
return;
default:
respond(r, "cannot read");
return;
}
respond(r, nil);
}
static void
xfwrite(Req *r)
{
Xfid *xf;
Window *w;
Text *x;
vlong offset;
u32int count;
char *data, *p;
Channel *kbd;
Stringpair pair;
enum { Adata, Agone, Aflush, NALT };
Alt alts[NALT+1];
xf = r->fid->aux;
w = xf->w;
x = &w->text;
offset = r->ifcall.offset;
count = r->ifcall.count;
data = r->ifcall.data;
if(w == nil || w->deleted){
respond(r, Edeleted);
return;
}
int f = QFILE(r->fid->qid.path);
switch(f){
case Qcons:
alts[Adata] = ALT(w->conswrite, &kbd, CHANRCV);
alts[Agone] = ALT(w->gone, nil, CHANRCV);
alts[Aflush] = ALT(xf->flush, nil, CHANRCV);
alts[NALT].op = CHANEND;
switch(alt(alts)){
case Adata:
cnvsize(&xf->cnv, count);
memmove(xf->cnv.buf+xf->cnv.n, data, count);
xf->cnv.n += count;
pair = b2r(&xf->cnv);
r->ofcall.count = r->ifcall.count;
send(kbd, &pair);
break;
case Agone:
respond(r, Edeleted);
return;
case Aflush:
respond(r, Eflush);
return;
}
break;
case Qconsctl:
if(strncmp(data, "holdon", 6) == 0){
wsendmsg(w, Holdon, ZR, nil);
break;
}
if(strncmp(data, "holdoff", 7) == 0){
wsendmsg(w, Holdoff, ZR, nil);
break;
}
if(strncmp(data, "rawon", 5) == 0){
// TODO: apparently we turn of hold mode here
if(x->rawmode++ == 0)
wsendmsg(w, Rawon, ZR, nil);
break;
}
if(strncmp(data, "rawoff", 6) == 0){
if(--x->rawmode == 0)
wsendmsg(w, Rawoff, ZR, nil);
break;
}
respond(r, "unknown control message");
return;
case Qcursor:
if(count < 2*4+2*2*16)
w->cursorp = nil;
else{
w->cursor.offset.x = BGLONG(data+0*4);
w->cursor.offset.y = BGLONG(data+1*4);
memmove(w->cursor.clr, data+2*4, 2*2*16);
w->cursorp = &w->cursor;
}
cursor = (void*)(uintptr)~0; /* invalide cache */
wsetcursor(w);
break;
case Qlabel:
if(offset != 0){
respond(r, "non-zero offset writing label");
return;
}
w->label = realloc(w->label, count+1);
memmove(w->label, data, count);
w->label[count] = 0;
break;
case Qsnarf:
if(count == 0)
break;
/* always append only */
if(ntsnarf > MAXSNARF){ /* avoid thrashing when people cut huge text */
respond(r, Elong);
return;
}
p = realloc(tsnarf, ntsnarf+count);
if(p == nil){
respond(r, Enomem);
return;
}
tsnarf = p;
memmove(tsnarf+ntsnarf, data, count);
ntsnarf += count;
break;
case Qwdir:
if(count > 0 && data[count-1] == '\n')
data[--count] = '\0';
if(count == 0)
break;
/* assume data comes in a single write */
if(data[0] == '/')
p = smprint("%.*s", count, data);
else
p = smprint("%s/%.*s", w->dir, count, data);
if(p == nil){
respond(r, Enomem);
return;
}
free(w->dir);
w->dir = cleanname(p);
break;
default:
respond(r, "cannot write");
return;
}
respond(r, nil);
}
static void
fsopen(Req *r)
{
toxfid(r, xfopen);
}
static void
freefid(Fid *fid)
{
Xfid *xf;
xf = fid->aux;
if(xf){
xfclose(xf);
freexfid(xf);
}
fid->aux = nil;
}
static void
fsread(Req *r)
{
Xfid *xf;
if((r->fid->qid.type & QTDIR) == 0){
toxfid(r, xfread);
return;
}
switch(QFILE(r->fid->qid.path)){
case Qroot:
xf = r->fid->aux;
dirread9p(r, genrootdir, xf->w);
break;
case Qwsys:
dirread9p(r, genwsysdir, nil);
break;
}
respond(r, nil);
}
static void
fswrite(Req *r)
{
toxfid(r, xfwrite);
}
static void
fsflush(Req *r)
{
Req *or;
Xfid *xf;
int dummy = 0;
or = r->oldreq;
xf = or->fid->aux;
assert(xf);
/* TODO: not entirely sure this is right.
* is it possible no-one is listening? */
send(xf->flush, &dummy);
respond(r, nil);
}
static void
fsstat(Req *r)
{
Xfid *xf;
int f;
xf = r->fid->aux;
f = QFILE(r->fid->qid.path);
genrootdir(f-1, &r->d, xf->w);
respond(r, nil);
}
Srv fsys = {
.attach fsattach,
.open fsopen,
.read fsread,
.write fswrite,
.stat fsstat,
.flush fsflush,
.walk1 fswalk1,
.clone fsclone,
.destroyfid freefid,
nil
};
void
post(char *name, int srvfd)
{
char buf[80];
int fd;
snprint(buf, sizeof buf, "/srv/%s", name);
fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
if(fd < 0)
panic(buf);
if(fprint(fd, "%d", srvfd) < 0)
panic("post");
putenv("wsys", buf);
/* leave fd open */
}
static Ioproc *io9p;
/* copy & paste from /sys/src/libc/9sys/read9pmsg.c
* changed to use ioreadn instead of readn */
int
read9pmsg(int fd, void *abuf, uint n)
{
int m, len;
uchar *buf;
buf = abuf;
/* read count */
m = ioreadn(io9p, fd, buf, BIT32SZ);
if(m != BIT32SZ){
if(m < 0)
return -1;
return 0;
}
len = GBIT32(buf);
if(len <= BIT32SZ || len > n){
werrstr("bad length in 9P2000 message header");
return -1;
}
len -= BIT32SZ;
m = ioreadn(io9p, fd, buf+BIT32SZ, len);
if(m < len)
return 0;
return BIT32SZ+m;
}
int fsysfd;
char srvpipe[64];
//char srvwctl[64];
void
fs(void)
{
io9p = ioproc();
int fd[2];
if(pipe(fd) < 0)
panic("pipe");
fsysfd = fd[0]; /* don't close for children */
fsys.infd = fsys.outfd = fd[1];
snprint(srvpipe, sizeof(srvpipe), "lola.%s.%lud", getuser(), (ulong)getpid());
post(srvpipe, fd[0]);
// chatty9p++;
srv(&fsys);
}
int
fsmount(int id)
{ char buf[32];
close(fsys.infd); /* close server end so mount won't hang if exiting */
snprint(buf, sizeof buf, "%d", id);
if(mount(fsysfd, -1, "/mnt/wsys", MREPL, buf) == -1){
fprint(2, "mount failed: %r\n");
return -1;
}
if(bind("/mnt/wsys", "/dev", MBEFORE) == -1){
fprint(2, "bind failed: %r\n");
return -1;
}
return 0;
}