diff -Nru /sys/src/cmd/aux/vmware/all.h /sys/src/cmd/aux/vmware/all.h --- /sys/src/cmd/aux/vmware/all.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/vmware/all.h Tue Jan 18 00:00:00 2022 @@ -0,0 +1,84 @@ +#include +#include +#include +#include + +typedef struct Msgchan Msgchan; +typedef struct Ureg Ureg; + +typedef ulong uint32; +typedef int int32; +typedef short int16; +typedef char int8; +typedef uvlong uint64; +typedef vlong int64; +typedef char Bool; + +struct Msgchan +{ + ushort id; + uchar *a; + int na; +}; + +enum +{ + /* low bits of u.cx */ + BackGetmhz = 1, + BackApm = 2, + BackGetdiskgeo = 3, + BackGetptrloc = 4, + BackSetptrloc = 5, + BackGetsellength = 6, + BackGetnextpiece = 7, + BackSetsellength = 8, + BackSetnextpiece = 9, + BackGetversion = 10, + BackGetdevlistel = 11, + BackToggledev = 12, + BackGetguiopt = 13, + BackSetguiopt = 14, + BackGetscreensize = 15, + BackGetpcisvgaen = 16, + BackSetpcisvgaen = 17, + /* 18-20 not used */ + BackHostcopy = 21, + BackGetos2intcurs = 22, + BackGettime = 23, + BackStopcatchup = 24, + BackPutchr = 25, + BackEnablemsg = 26, + BackGototcl = 27, + BackInitscsiprom = 28, + BackInt13 = 29, + BackMessage = 30, + + BackMagic = 0x564D5868, + VersionMagic = 6, + BackPort = 0x5658, + +}; + +void asmbackdoor(Ureg*); +void backdoor(Ureg*, int); +int backdoorbell(void*, char*); +int closemsg(Msgchan*); +int getdeviceinfo(uint, uint, uint*); +Point getmousepoint(void); +int getsnarflength(void); +uint getsnarfpiece(void); +Msgchan* openmsg(ulong); +int recvmsg(Msgchan*, void**); +int sendmsg(Msgchan*, void*, int); +int setdevicestate(uint, int); +void setmousepoint(Point); +void setsnarflength(uint); +void setsnarfpiece(uint); +int getversion(void); +void setguistate(uint); +uint getguistate(void); +uint copystep(uint); +void gettime(uint*, uint*, uint*); +void stopcatchup(void); + +extern jmp_buf backdoorjmp; diff -Nru /sys/src/cmd/aux/vmware/asmbackdoor.s /sys/src/cmd/aux/vmware/asmbackdoor.s --- /sys/src/cmd/aux/vmware/asmbackdoor.s Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/vmware/asmbackdoor.s Tue Jan 18 00:00:00 2022 @@ -0,0 +1,28 @@ +TEXT asmbackdoor(SB), $0 + MOVL ureg+0(FP), BP + MOVL 16(BP), BX + MOVL 20(BP), DX + MOVL 24(BP), CX + MOVL 0(BP), AX + ANDL $1, AX + MOVL 28(BP), AX + JNZ out + +in: + INL + JMP done + +out: + OUTL + +done: + MOVL ureg+0(FP), BP + MOVL BX, 16(BP) + MOVL DX, 20(BP) + MOVL CX, 24(BP) + MOVL AX, 28(BP) + + RET + + + diff -Nru /sys/src/cmd/aux/vmware/backdoor.c /sys/src/cmd/aux/vmware/backdoor.c --- /sys/src/cmd/aux/vmware/backdoor.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/vmware/backdoor.c Tue Jan 18 00:00:00 2022 @@ -0,0 +1,34 @@ +#include "all.h" + +jmp_buf backdoorjmp; +static int doorerror; + +int +backdoorbell(void *v, char *msg) +{ + uchar *p; + Ureg *u; + + u = v; + p = (uchar*)u->pc; + /* ED is INL and EF is OUTL */ + if((*p==0xED || *p==0xEF) && strstr(msg, "sys: trap: general protection violation")){ + u->pc++; /* hop over INL/OUTL */ + doorerror = 1; + return 1; + } + return 0; +} + +void +backdoor(Ureg *u, int isout) +{ + u->ax = BackMagic; + u->dx &= ~0xFFFF; + u->dx |= BackPort; + u->di = isout; + doorerror = 0; + asmbackdoor(u); + if(doorerror) + longjmp(backdoorjmp, 1); +} diff -Nru /sys/src/cmd/aux/vmware/inout.c /sys/src/cmd/aux/vmware/inout.c --- /sys/src/cmd/aux/vmware/inout.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/vmware/inout.c Tue Jan 18 00:00:00 2022 @@ -0,0 +1,175 @@ +#include "all.h" + +void +setmousepoint(Point p) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackSetptrloc; + u.bx = (p.x<<16) | p.y; + backdoor(&u, 1); +} + +Point +getmousepoint(void) +{ + Point p; + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackGetptrloc; + backdoor(&u, 0); + p.x = (signed)((u.ax>>16)&0xFFFF); + p.y = (signed)(u.ax & 0xFFFF); + return p; +} + +int +getsnarflength(void) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackGetsellength; + backdoor(&u, 0); + return u.ax; +} + +uint +getsnarfpiece(void) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackGetnextpiece; + backdoor(&u, 0); + return u.ax; +} + +void +setsnarflength(uint len) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackSetsellength; + u.bx = len; + backdoor(&u, 1); +} + +void +setsnarfpiece(uint p) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackSetnextpiece; + u.bx = p; + backdoor(&u, 1); +} + +int +getversion(void) +{ + Ureg u; + jmp_buf jb; + + memset(&u, 0, sizeof u); + u.cx = BackGetversion; + memmove(jb, backdoorjmp, sizeof jb); + if(setjmp(backdoorjmp)){ + memmove(backdoorjmp, jb, sizeof jb); + return -1; + } + backdoor(&u, 0); + memmove(backdoorjmp, jb, sizeof jb); + if(u.ax != VersionMagic || u.bx != BackMagic) + return -1; + return 0; +} + +int +setdevicestate(uint id, int enable) +{ + Ureg u; + + if(enable) + id |= 0x80000000; + memset(&u, 0, sizeof u); + u.cx = BackToggledev; + u.bx = id; + backdoor(&u, 0); + return u.ax; +} + +int +getdeviceinfo(uint id, uint offset, uint *p) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.bx = (id<<16) | offset; + u.cx = BackGetdevlistel; + backdoor(&u, 0); + *p = u.bx; + return u.ax; +} + +void +setguistate(uint state) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.bx = state; + u.cx = BackSetguiopt; + backdoor(&u, 1); +} + +uint +getguistate(void) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackGetguiopt; + backdoor(&u, 0); + return u.ax; +} + +uint +copystep(uint x) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackHostcopy; + u.bx = x; + backdoor(&u, 0); + return u.ax; +} + +void +gettime(uint *sec, uint *micro, uint *lag) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackGettime; + backdoor(&u, 0); + + *sec = u.ax; + *micro = u.bx; + *lag = u.cx; +} + +void +stopcatchup(void) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = BackStopcatchup; + backdoor(&u, 0); +} diff -Nru /sys/src/cmd/aux/vmware/isvmware.c /sys/src/cmd/aux/vmware/isvmware.c --- /sys/src/cmd/aux/vmware/isvmware.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/vmware/isvmware.c Tue Jan 18 00:00:00 2022 @@ -0,0 +1,33 @@ +#include "all.h" + +int statusonly; + +void +usage(void) +{ + fprint(2, "usage: aux/isvmware [-s]\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + ARGBEGIN{ + case 's': + statusonly = 1; + break; + default: + usage(); + }ARGEND + + if(argc != 0) + usage(); + + atnotify(backdoorbell, 1); + if(getversion() < 0){ + if(!statusonly) + print("no vmware\n"); + exits("no vmware"); + } + exits(nil); +} diff -Nru /sys/src/cmd/aux/vmware/mkfile /sys/src/cmd/aux/vmware/mkfile --- /sys/src/cmd/aux/vmware/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/vmware/mkfile Tue Jan 18 00:00:00 2022 @@ -0,0 +1,12 @@ +objtype=386 + +#include +#include <9p.h> /* for emalloc9p */ + +enum +{ + /* high bits of u.cx */ + CmdOpen = 0x00, + CmdSendsize = 0x01, + CmdSenddata = 0x02, + CmdRecvsize = 0x03, + CmdRecvdata = 0x04, + CmdRecvstatus = 0x05, + CmdClose = 0x06, + + StatSuccess = 0x0001, /* request succeeded */ + StatHavedata = 0x0002, /* vmware has message available */ + StatClosed = 0x0004, /* channel got closed */ + StatUnsent = 0x0008, /* vmware removed message before it got delivered */ + StatChkpt = 0x0010, /* checkpoint occurred */ + StatPoweroff = 0x0020, /* underlying device is powering off */ +}; + + +Msgchan* +openmsg(ulong proto) +{ + Msgchan *c; + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = (CmdOpen<<16) | BackMessage; + u.bx = proto; + + backdoor(&u, 0); + +fprint(2, "msgopen %.8lux\n", u.cx); + if(!(u.cx & (StatSuccess<<16))){ + fprint(2, "message %.8lux\n", u.cx); + werrstr("unable to open message channel (%.8lux)", u.cx); + return nil; + } + + c = emalloc9p(sizeof(*c)); + c->id = u.dx>>16; + return c; +} + +enum +{ + Ok = 0, + ErrBad = -1, + ErrChkpt = -2, +}; + +static int +sendpiece(Msgchan *c, int ty, ulong a) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = (ty<<16) | BackMessage; + u.dx = c->id<<16; + u.bx = a; + + backdoor(&u, 0); + + if(!(u.cx & (StatSuccess<<16))){ +fprint(2, "cmd %x failed\n", ty); + if(u.cx & (StatChkpt<<16)) + return ErrChkpt; + return ErrBad; + } + return 0; +} + +int +sendmsg(Msgchan *c, void *a, int n) +{ + int i, m, e, left; + uchar *p; + ulong v; + +Again: + v = n; + if(sendpiece(c, CmdSendsize, v) < 0) + return -1; + + p = a; + left = n; + while(left > 0){ + m = left; + if(m > 4) + m = 4; + v = 0; + for(i=0; iid, ty); + memset(&u, 0, sizeof u); + u.cx = (ty<<16) | BackMessage; + u.bx = StatSuccess; + u.dx = c->id<<16; + + backdoor(&u, 0); + + if(!(u.cx & (StatSuccess<<16))){ +fprint(2, "no success %lux\n", u.cx); + if(u.cx & (StatChkpt<<16)) + return ErrChkpt; + return ErrBad; + } + *stat = u.cx; + if(ty == CmdRecvsize && !(u.cx&(StatHavedata<<16))){ +fprint(2, "poll got no data\n"); + return 0; + } + + if(ty != CmdRecvstatus && (u.dx>>16) != ty-2){ +fprint(2, "got wrong answer! %lux\n", u.dx); + werrstr("protocol error"); + return ErrBad; + } + *a = u.bx; +fprint(2, "got %lux\n", *a); + return 0; +} + +static void +signalerror(Msgchan *c, int ty) +{ + Ureg u; + + memset(&u, 0, sizeof u); + u.cx = (ty<<16) | BackMessage; + u.dx = c->id<<16; + u.bx = 0; + backdoor(&u, 0); +} + +int +recvmsg(Msgchan *c, void **pp) +{ + int left; + ulong v, stat, tot; + uchar *p; + +Top: + if(recvpiece(c, CmdRecvsize, &stat, &tot) < 0) + return -1; + + if(!(stat & (StatHavedata<<16))) + return 0; + + free(c->a); + c->a = emalloc9p(tot+5); + c->na = tot; + p = c->a; /* NUL terminate for callers */ + + left = tot; + while(left > 0){ + switch(recvpiece(c, CmdRecvdata, &stat, &v)){ + case ErrChkpt: + goto Top; + case ErrBad: + signalerror(c, CmdRecvdata); + return -1; + } + *(uint*)p = v; + p += 4; + left -= 4; + } + ((char*)c->a)[tot] = '\0'; + + switch(recvpiece(c, CmdRecvstatus, &stat, &v)){ + case ErrChkpt: + goto Top; + case ErrBad: + /* BUG: signal receipt of error */ + signalerror(c, CmdRecvstatus); + return -1; + } + + *pp = c->a; + return tot; +} + +int +closemsg(Msgchan *c) +{ + Ureg u; + + memset(&u, 0, sizeof u); + + u.cx = (CmdClose<<16) | BackMessage; + u.dx = c->id; + + backdoor(&u, 0); + + if(!(u.cx & (StatSuccess<<16))){ + werrstr("unable to close message channel"); + return -1; + } + + free(c->a); + c->a = nil; + free(c); + return 0; +} diff -Nru /sys/src/cmd/aux/vmware/vmmousepoll.c /sys/src/cmd/aux/vmware/vmmousepoll.c --- /sys/src/cmd/aux/vmware/vmmousepoll.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/vmware/vmmousepoll.c Tue Jan 18 00:00:00 2022 @@ -0,0 +1,44 @@ +#include +#include + +void +usage(void) +{ + fprint(2, "usage: aux/vmmousepoll [/mnt/vmware/mouse]\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int fd; + char *file; + char buf[50]; + + quotefmtinstall(); + ARGBEGIN{ + default: + usage(); + }ARGEND + + switch(argc){ + default: + usage(); + case 0: + file = "/mnt/vmware/mouse"; + break; + case 1: + file = argv[0]; + break; + } + + if((fd = open(file, OREAD)) < 0) + sysfatal("open %q: %r", file); + + for(;;){ + sleep(250); + if(pread(fd, buf, sizeof buf, 0) < 0) + break; + } + exits(nil); +} diff -Nru /sys/src/cmd/aux/vmware/vmwarefs.c /sys/src/cmd/aux/vmware/vmwarefs.c --- /sys/src/cmd/aux/vmware/vmwarefs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/aux/vmware/vmwarefs.c Tue Jan 18 00:00:00 2022 @@ -0,0 +1,657 @@ +#include "all.h" +#include +#include +#include <9p.h> + +char *mtpt = "/mnt/vmware"; +char *srvname; +uint time0; + +enum +{ + Qroot = 0, + Qmousepoint, + Qsnarf, + Qgui, + Qdev, + Qtime, + Qbintime, + Qmsg, +}; + +typedef struct Tab Tab; +struct Tab +{ + int qid; + char *name; + uint perm; + uint vers; + void (*open)(Req*); + void (*read)(Req*); + void (*write)(Req*); + void (*close)(Fid*); +}; + +static void +mousepointread(Req *r) +{ + char buf[32]; + Point p; + + p = getmousepoint(); + snprint(buf, sizeof buf, "%11d %11d ", p.x, p.y); + readstr(r, buf); + respond(r, nil); +} + +static void +mousepointwrite(Req *r) +{ + char buf[32], *f[3]; + int nf, n; + Point p; + + n = r->ifcall.count; + if(n >= sizeof buf){ + respond(r, "write too large"); + return; + } + memmove(buf, r->ifcall.data, n); + buf[n] = '\0'; + nf = tokenize(buf, f, nelem(f)); + if(nf != 2){ + respond(r, "bad point format"); + return; + } + p.x = atoi(f[0]); + p.y = atoi(f[1]); + setmousepoint(p); + respond(r, nil); +} + +static void +timeread(Req *r) +{ + char buf[32]; + uint sec, microsec, lag; + + gettime(&sec, µsec, &lag); + snprint(buf, sizeof buf, "%11d ", sec); + readstr(r, buf); + respond(r, nil); +} + +static uvlong uvorder = 0x0001020304050607ULL; +static uchar* +vlong2le(uchar *t, vlong from) +{ + uchar *f, *o; + int i; + + f = (uchar*)&from; + o = (uchar*)&uvorder; + for(i = 0; i < sizeof(vlong); i++) + t[i] = f[o[i]]; + return t+sizeof(vlong); +} + +static void +bintimeread(Req *r) +{ + uchar *b; + int i, n; + uint sec, microsec, lag; + vlong nsec; + + b = (uchar*)r->ofcall.data; + n = r->ifcall.count; + + i = 0; + if(n >= 8){ + gettime(&sec, µsec, &lag); + nsec = sec*1000000000LL+microsec*1000LL; + vlong2le(b, nsec); + i = 8; + } + if(n >= 16){ + vlong2le(b+8, nsec); + i = 16; + } + if(n >= 24){ + vlong2le(b+16, 1000000000LL); + i = 24; + } + r->ofcall.count = i; + respond(r, nil); +} + +char *snarf; +int nsnarf; +char *tsnarf; +int ntsnarf; + +static void +snarfread(Req *r) +{ + int i; + + if(r->ifcall.offset == 0){ + if(snarf) + free(snarf); + nsnarf = getsnarflength(); + snarf = emalloc9p(nsnarf+4+1); + for(i=0; iifcall.offset == 0){ + free(tsnarf); + tsnarf = nil; + ntsnarf = 0; + } + if(r->ifcall.offset > 100*1024){ + respond(r, "snarf buffer too long"); + return; + } + tsnarf = erealloc9p(tsnarf, ntsnarf+r->ifcall.count); + memmove(tsnarf+ntsnarf, r->ifcall.data, r->ifcall.count); + ntsnarf += r->ifcall.count; + r->ofcall.count = r->ifcall.count; + respond(r, nil); +} + +static void +snarfclose(Fid *fid) +{ + int i, n; + + if((fid->omode&3) == OREAD) + return; + + // read old snarf - dunno why but it helps + n = getsnarflength(); + for(i=0; iifcall.data, r->ifcall.count); + if(cb->nf != 2){ + respondcmderror(r, cb, "bad gui ctl"); + free(cb); + return; + } + + if(strcmp(cb->f[1], "off")==0) + on = 0; + else if(strcmp(cb->f[1], "on") == 0) + on = 1; + else{ + respondcmderror(r, cb, "bad gui ctl"); + free(cb); + return; + } + + for(i=0; if[0]) == 0) + goto Have; + respondcmderror(r, cb, "bad gui ctl"); + free(cb); + return; + +Have: + v = getguistate(); + if(on) + v |= guibit[i].bit; + else + v &= ~guibit[i].bit; + setguistate(v); + r->ofcall.count = r->ifcall.count; + free(cb); + respond(r, nil); +} + +typedef struct Info Info; +struct Info +{ + char name[32]; + uint uid; + uint enabled; +}; +static int +getinfo(uint id, Info *p) +{ + uint i; + + for(i=0; ifid->aux; + if(c == nil){ + respond(r, "message channel not open"); + return; + } + + if(r->ifcall.offset == 0){ + if(recvmsg(c, &s) < 0){ + respond(r, "no messages waiting"); + return; + } + } + if(c->a == nil){ + respond(r, "no messages waiting"); + return; + } + readbuf(r, c->a, c->na); + respond(r, nil); +} + +static void +fsmsgwrite(Req *r) +{ + char buf[32], *p; + int n; + Msgchan *c; + + if(r->ifcall.offset != 0){ + respond(r, "must write at offset zero"); + return; + } + + r->ofcall.count = r->ifcall.count; + c = r->fid->aux; + if(c == nil){ + if(r->ifcall.count >= sizeof buf){ + respond(r, "bad message channel number"); + return; + } + memmove(buf, r->ifcall.data, r->ifcall.count); + buf[r->ifcall.count] = 0; + p = buf; + n = strtol(buf, &p, 0); + if(p == buf){ + respond(r, "bad message channel number"); + return; + } + c = openmsg(n); + if(c == nil){ + respond(r, "could not open message channel"); + return; + } + r->fid->aux = c; + respond(r, nil); + return; + } + + if(sendmsg(c, r->ifcall.data, r->ifcall.count) < 0){ + respond(r, "could not send message"); + return; + } + respond(r, nil); +} + +static void +fsmsgclose(Fid *fid) +{ + Msgchan *c; + + c = fid->aux; + if(c) + closemsg(c); +} + +Tab tab[] = { + Qmousepoint, "mouse", 0666, 0, nil, mousepointread, mousepointwrite, nil, + Qsnarf, "snarf", 0666, 0, nil, snarfread, snarfwrite, snarfclose, + Qgui, "gui", 0666, 0, nil, guiread, guiwrite, nil, + Qdev, "dev", 0444, 0, nil, devread, nil, nil, + Qtime, "time", 0444, 0, nil, timeread, nil, nil, + Qbintime, "bintime", 0444, 0, nil, bintimeread, nil, nil, + Qmsg, "msg", 0666, 0, nil, fsmsgread, fsmsgwrite, fsmsgclose, +}; + +void +fsattach(Req *r) +{ + char *spec; + + spec = r->ifcall.aname; + if(spec && spec[0]){ + respond(r, "invalid attach specifier"); + return; + } + r->ofcall.qid = (Qid){Qroot, 0, QTDIR}; + r->fid->qid = r->ofcall.qid; + respond(r, nil); +} + +char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + int i; + + switch((int)fid->qid.path){ + case Qroot: + for(i=0; iqid.path = tab[i].qid; + fid->qid.type = tab[i].perm>>24;; + fid->qid.vers = tab[i].vers; + *qid = fid->qid; + return nil; + } + } + break; + } + + return "file not found"; +} + +void +fsstat(Req *r) +{ + int i, q; + Dir *d; + + d = &r->d; + memset(d, 0, sizeof *d); + q = r->fid->qid.path; + d->qid = r->fid->qid; + switch(q){ + case Qroot: + d->name = estrdup9p("/"); + d->mode = DMDIR|0777; + break; + + default: + for(i=0; iqid.vers = tab[i].vers; + d->qid.type = tab[i].perm>>24; + d->mode = tab[i].perm; + goto Out; + } + } + respond(r, "file not found"); + } + +Out: + d->atime = d->mtime = time0; + d->uid = estrdup9p("vmware"); + d->gid = estrdup9p("vmware"); + d->muid = estrdup9p(""); + respond(r, nil); +} + +int +dirgen(int off, Dir *d, void*) +{ + if(off >= nelem(tab)) + return -1; + + memset(d, 0, sizeof *d); + d->atime = d->mtime = time0; + d->name = estrdup9p(tab[off].name); + d->mode = tab[off].perm; + d->qid.path = tab[off].qid; + d->qid.vers = tab[off].vers; + d->qid.type = d->mode>>24; + d->uid = estrdup9p("vmware"); + d->gid = estrdup9p("vmware"); + d->muid = estrdup9p(""); + return 0; +} + +void +fsread(Req *r) +{ + int i, q; + + q = r->fid->qid.path; + switch(q){ + case Qroot: + dirread9p(r, dirgen, nil); + respond(r, nil); + return; + + default: + for(i=0; ifid->qid.path; + for(i=0; ifid->qid.path; + for(i=0; iifcall.mode&3){ + case OREAD: + if(!(tab[i].perm&0400)) + goto Eperm; + break; + case OWRITE: + if(!(tab[i].perm&0200)) + goto Eperm; + break; + case ORDWR: + if((tab[i].perm&0600) != 0600) + goto Eperm; + break; + case OEXEC: + Eperm: + respond(r, "permission denied"); + return; + } + if(tab[i].open) + tab[i].open(r); + else + respond(r, nil); + return; + } + + /* directory */ + if(r->ifcall.mode != OREAD) + respond(r, "permission denied"); + else + respond(r, nil); +} + +void +fsdestroyfid(Fid *fid) +{ + int i, q; + + q = fid->qid.path; + for(i=0; i