| #include <u.h> |
| #include <libc.h> |
| #include <draw.h> |
| #include <thread.h> |
| #include <cursor.h> |
| #include <mouse.h> |
| #include <keyboard.h> |
| #include <frame.h> |
| #include <fcall.h> |
| #include <plumb.h> |
| #include "dat.h" |
| #include "fns.h" |
| |
| static int sfd; |
| |
| enum |
| { |
| Nhash = 16, |
| DEBUG = 0 |
| }; |
| |
| static Fid *fids[Nhash]; |
| |
| Fid *newfid(int); |
| |
| static Xfid* fsysflush(Xfid*, Fid*); |
| static Xfid* fsysauth(Xfid*, Fid*); |
| static Xfid* fsysversion(Xfid*, Fid*); |
| static Xfid* fsysattach(Xfid*, Fid*); |
| static Xfid* fsyswalk(Xfid*, Fid*); |
| static Xfid* fsysopen(Xfid*, Fid*); |
| static Xfid* fsyscreate(Xfid*, Fid*); |
| static Xfid* fsysread(Xfid*, Fid*); |
| static Xfid* fsyswrite(Xfid*, Fid*); |
| static Xfid* fsysclunk(Xfid*, Fid*); |
| static Xfid* fsysremove(Xfid*, Fid*); |
| static Xfid* fsysstat(Xfid*, Fid*); |
| static Xfid* fsyswstat(Xfid*, Fid*); |
| |
| Xfid* (*fcall[Tmax])(Xfid*, Fid*); |
| |
| static void |
| initfcall(void) |
| { |
| fcall[Tflush] = fsysflush; |
| fcall[Tversion] = fsysversion; |
| fcall[Tauth] = fsysauth; |
| fcall[Tattach] = fsysattach; |
| fcall[Twalk] = fsyswalk; |
| fcall[Topen] = fsysopen; |
| fcall[Tcreate] = fsyscreate; |
| fcall[Tread] = fsysread; |
| fcall[Twrite] = fsyswrite; |
| fcall[Tclunk] = fsysclunk; |
| fcall[Tremove]= fsysremove; |
| fcall[Tstat] = fsysstat; |
| fcall[Twstat] = fsyswstat; |
| } |
| |
| char Eperm[] = "permission denied"; |
| char Eexist[] = "file does not exist"; |
| char Enotdir[] = "not a directory"; |
| |
| Dirtab dirtab[]= |
| { |
| { ".", QTDIR, Qdir, 0500|DMDIR }, |
| { "acme", QTDIR, Qacme, 0500|DMDIR }, |
| { "cons", QTFILE, Qcons, 0600 }, |
| { "consctl", QTFILE, Qconsctl, 0000 }, |
| { "draw", QTDIR, Qdraw, 0000|DMDIR }, /* to suppress graphics progs started in acme */ |
| { "editout", QTFILE, Qeditout, 0200 }, |
| { "index", QTFILE, Qindex, 0400 }, |
| { "label", QTFILE, Qlabel, 0600 }, |
| { "log", QTFILE, Qlog, 0400 }, |
| { "new", QTDIR, Qnew, 0500|DMDIR }, |
| { nil, } |
| }; |
| |
| Dirtab dirtabw[]= |
| { |
| { ".", QTDIR, Qdir, 0500|DMDIR }, |
| { "addr", QTFILE, QWaddr, 0600 }, |
| { "body", QTAPPEND, QWbody, 0600|DMAPPEND }, |
| { "ctl", QTFILE, QWctl, 0600 }, |
| { "data", QTFILE, QWdata, 0600 }, |
| { "editout", QTFILE, QWeditout, 0200 }, |
| { "errors", QTFILE, QWerrors, 0200 }, |
| { "event", QTFILE, QWevent, 0600 }, |
| { "rdsel", QTFILE, QWrdsel, 0400 }, |
| { "wrsel", QTFILE, QWwrsel, 0200 }, |
| { "tag", QTAPPEND, QWtag, 0600|DMAPPEND }, |
| { "xdata", QTFILE, QWxdata, 0600 }, |
| { nil, } |
| }; |
| |
| typedef struct Mnt Mnt; |
| struct Mnt |
| { |
| QLock lk; |
| int id; |
| Mntdir *md; |
| }; |
| |
| Mnt mnt; |
| |
| Xfid* respond(Xfid*, Fcall*, char*); |
| int dostat(int, Dirtab*, uchar*, int, uint); |
| uint getclock(void); |
| |
| char *user = "Wile E. Coyote"; |
| static int closing = 0; |
| int messagesize = Maxblock+IOHDRSZ; /* good start */ |
| |
| void fsysproc(void *); |
| |
| void |
| fsysinit(void) |
| { |
| int p[2]; |
| char *u; |
| |
| initfcall(); |
| if(pipe(p) < 0) |
| error("can't create pipe"); |
| if(post9pservice(p[0], "acme", mtpt) < 0) |
| error("can't post service"); |
| sfd = p[1]; |
| fmtinstall('F', fcallfmt); |
| if((u = getuser()) != nil) |
| user = estrdup(u); |
| proccreate(fsysproc, nil, STACK); |
| } |
| |
| void |
| fsysproc(void *v) |
| { |
| int n; |
| Xfid *x; |
| Fid *f; |
| Fcall t; |
| uchar *buf; |
| |
| threadsetname("fsysproc"); |
| |
| USED(v); |
| x = nil; |
| for(;;){ |
| buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */ |
| n = read9pmsg(sfd, buf, messagesize); |
| if(n <= 0){ |
| if(closing) |
| break; |
| error("i/o error on server channel"); |
| } |
| if(x == nil){ |
| sendp(cxfidalloc, nil); |
| x = recvp(cxfidalloc); |
| } |
| x->buf = buf; |
| if(convM2S(buf, n, &x->fcall) != n) |
| error("convert error in convM2S"); |
| if(DEBUG) |
| fprint(2, "%F\n", &x->fcall); |
| if(fcall[x->fcall.type] == nil) |
| x = respond(x, &t, "bad fcall type"); |
| else{ |
| switch(x->fcall.type){ |
| case Tversion: |
| case Tauth: |
| case Tflush: |
| f = nil; |
| break; |
| case Tattach: |
| f = newfid(x->fcall.fid); |
| break; |
| default: |
| f = newfid(x->fcall.fid); |
| if(!f->busy){ |
| x->f = f; |
| x = respond(x, &t, "fid not in use"); |
| continue; |
| } |
| break; |
| } |
| x->f = f; |
| x = (*fcall[x->fcall.type])(x, f); |
| } |
| } |
| } |
| |
| Mntdir* |
| fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl) |
| { |
| Mntdir *m; |
| int id; |
| |
| qlock(&mnt.lk); |
| id = ++mnt.id; |
| m = emalloc(sizeof *m); |
| m->id = id; |
| m->dir = dir; |
| m->ref = 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; |
| qunlock(&mnt.lk); |
| return m; |
| } |
| |
| void |
| fsysincid(Mntdir *m) |
| { |
| qlock(&mnt.lk); |
| m->ref++; |
| qunlock(&mnt.lk); |
| } |
| |
| void |
| fsysdelid(Mntdir *idm) |
| { |
| Mntdir *m, *prev; |
| int i; |
| char buf[64]; |
| |
| if(idm == nil) |
| return; |
| qlock(&mnt.lk); |
| if(--idm->ref > 0){ |
| qunlock(&mnt.lk); |
| return; |
| } |
| prev = nil; |
| for(m=mnt.md; m; m=m->next){ |
| if(m == idm){ |
| if(prev) |
| prev->next = m->next; |
| else |
| mnt.md = m->next; |
| for(i=0; i<m->nincl; i++) |
| free(m->incl[i]); |
| free(m->incl); |
| free(m->dir); |
| free(m); |
| qunlock(&mnt.lk); |
| return; |
| } |
| prev = m; |
| } |
| qunlock(&mnt.lk); |
| sprint(buf, "fsysdelid: can't find id %d\n", idm->id); |
| sendp(cerr, estrdup(buf)); |
| } |
| |
| /* |
| * Called only in exec.c:/^run(), from a different FD group |
| */ |
| Mntdir* |
| fsysmount(Rune *dir, int ndir, Rune **incl, int nincl) |
| { |
| return fsysaddid(dir, ndir, incl, nincl); |
| } |
| |
| void |
| fsysclose(void) |
| { |
| closing = 1; |
| /* |
| * apparently this is not kosher on openbsd. |
| * perhaps because fsysproc is reading from sfd right now, |
| * the close hangs indefinitely. |
| close(sfd); |
| */ |
| } |
| |
| Xfid* |
| respond(Xfid *x, Fcall *t, char *err) |
| { |
| int n; |
| |
| if(err){ |
| t->type = Rerror; |
| t->ename = err; |
| }else |
| t->type = x->fcall.type+1; |
| t->fid = x->fcall.fid; |
| t->tag = x->fcall.tag; |
| if(x->buf == nil) |
| x->buf = emalloc(messagesize); |
| n = convS2M(t, x->buf, messagesize); |
| if(n <= 0) |
| error("convert error in convS2M"); |
| if(write(sfd, x->buf, n) != n) |
| error("write error in respond"); |
| free(x->buf); |
| x->buf = nil; |
| if(DEBUG) |
| fprint(2, "r: %F\n", t); |
| return x; |
| } |
| |
| static |
| Xfid* |
| fsysversion(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| |
| USED(f); |
| if(x->fcall.msize < 256) |
| return respond(x, &t, "version: message size too small"); |
| messagesize = x->fcall.msize; |
| t.msize = messagesize; |
| if(strncmp(x->fcall.version, "9P2000", 6) != 0) |
| return respond(x, &t, "unrecognized 9P version"); |
| t.version = "9P2000"; |
| return respond(x, &t, nil); |
| } |
| |
| static |
| Xfid* |
| fsysauth(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| |
| USED(f); |
| return respond(x, &t, "acme: authentication not required"); |
| } |
| |
| static |
| Xfid* |
| fsysflush(Xfid *x, Fid *f) |
| { |
| USED(f); |
| sendp(x->c, (void*)xfidflush); |
| return nil; |
| } |
| |
| static |
| Xfid* |
| fsysattach(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| int id; |
| Mntdir *m; |
| char buf[128]; |
| |
| if(strcmp(x->fcall.uname, user) != 0) |
| return respond(x, &t, Eperm); |
| f->busy = TRUE; |
| f->open = FALSE; |
| f->qid.path = Qdir; |
| f->qid.type = QTDIR; |
| f->qid.vers = 0; |
| f->dir = dirtab; |
| f->nrpart = 0; |
| f->w = nil; |
| t.qid = f->qid; |
| f->mntdir = nil; |
| id = atoi(x->fcall.aname); |
| qlock(&mnt.lk); |
| for(m=mnt.md; m; m=m->next) |
| if(m->id == id){ |
| f->mntdir = m; |
| m->ref++; |
| break; |
| } |
| if(m == nil && x->fcall.aname[0]){ |
| snprint(buf, sizeof buf, "unknown id '%s' in attach", x->fcall.aname); |
| sendp(cerr, estrdup(buf)); |
| } |
| qunlock(&mnt.lk); |
| return respond(x, &t, nil); |
| } |
| |
| static |
| Xfid* |
| fsyswalk(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| int c, i, j, id; |
| Qid q; |
| uchar type; |
| ulong path; |
| Fid *nf; |
| Dirtab *d, *dir; |
| Window *w; |
| char *err; |
| |
| nf = nil; |
| w = nil; |
| if(f->open) |
| return respond(x, &t, "walk of open file"); |
| if(x->fcall.fid != x->fcall.newfid){ |
| nf = newfid(x->fcall.newfid); |
| if(nf->busy) |
| return respond(x, &t, "newfid already in use"); |
| nf->busy = TRUE; |
| nf->open = FALSE; |
| nf->mntdir = f->mntdir; |
| if(f->mntdir) |
| f->mntdir->ref++; |
| nf->dir = f->dir; |
| nf->qid = f->qid; |
| nf->w = f->w; |
| nf->nrpart = 0; /* not open, so must be zero */ |
| if(nf->w) |
| incref(&nf->w->ref); |
| f = nf; /* walk f */ |
| } |
| |
| t.nwqid = 0; |
| err = nil; |
| dir = nil; |
| id = WIN(f->qid); |
| q = f->qid; |
| |
| if(x->fcall.nwname > 0){ |
| for(i=0; i<x->fcall.nwname; i++){ |
| if((q.type & QTDIR) == 0){ |
| err = Enotdir; |
| break; |
| } |
| |
| if(strcmp(x->fcall.wname[i], "..") == 0){ |
| type = QTDIR; |
| path = Qdir; |
| id = 0; |
| if(w){ |
| winclose(w); |
| w = nil; |
| } |
| Accept: |
| if(i == MAXWELEM){ |
| err = "name too long"; |
| break; |
| } |
| q.type = type; |
| q.vers = 0; |
| q.path = QID(id, path); |
| t.wqid[t.nwqid++] = q; |
| continue; |
| } |
| |
| /* is it a numeric name? */ |
| for(j=0; (c=x->fcall.wname[i][j]); j++) |
| if(c<'0' || '9'<c) |
| goto Regular; |
| /* yes: it's a directory */ |
| if(w) /* name has form 27/23; get out before losing w */ |
| break; |
| id = atoi(x->fcall.wname[i]); |
| qlock(&row.lk); |
| w = lookid(id, FALSE); |
| if(w == nil){ |
| qunlock(&row.lk); |
| break; |
| } |
| incref(&w->ref); /* we'll drop reference at end if there's an error */ |
| path = Qdir; |
| type = QTDIR; |
| qunlock(&row.lk); |
| dir = dirtabw; |
| goto Accept; |
| |
| Regular: |
| if(strcmp(x->fcall.wname[i], "new") == 0){ |
| if(w) |
| error("w set in walk to new"); |
| sendp(cnewwindow, nil); /* signal newwindowthread */ |
| w = recvp(cnewwindow); /* receive new window */ |
| incref(&w->ref); |
| type = QTDIR; |
| path = QID(w->id, Qdir); |
| id = w->id; |
| dir = dirtabw; |
| goto Accept; |
| } |
| |
| if(id == 0) |
| d = dirtab; |
| else |
| d = dirtabw; |
| d++; /* skip '.' */ |
| for(; d->name; d++) |
| if(strcmp(x->fcall.wname[i], d->name) == 0){ |
| path = d->qid; |
| type = d->type; |
| dir = d; |
| goto Accept; |
| } |
| |
| break; /* file not found */ |
| } |
| |
| if(i==0 && err == nil) |
| err = Eexist; |
| } |
| |
| if(err!=nil || t.nwqid<x->fcall.nwname){ |
| if(nf){ |
| nf->busy = FALSE; |
| fsysdelid(nf->mntdir); |
| } |
| }else if(t.nwqid == x->fcall.nwname){ |
| if(w){ |
| f->w = w; |
| w = nil; /* don't drop the reference */ |
| } |
| if(dir) |
| f->dir = dir; |
| f->qid = q; |
| } |
| |
| if(w != nil) |
| winclose(w); |
| |
| return respond(x, &t, err); |
| } |
| |
| static |
| Xfid* |
| fsysopen(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| int m; |
| |
| /* can't truncate anything, so just disregard */ |
| x->fcall.mode &= ~(OTRUNC|OCEXEC); |
| /* can't execute or remove anything */ |
| if(x->fcall.mode==OEXEC || (x->fcall.mode&ORCLOSE)) |
| goto Deny; |
| switch(x->fcall.mode){ |
| default: |
| goto Deny; |
| case OREAD: |
| m = 0400; |
| break; |
| case OWRITE: |
| m = 0200; |
| break; |
| case ORDWR: |
| m = 0600; |
| break; |
| } |
| if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m) |
| goto Deny; |
| |
| sendp(x->c, (void*)xfidopen); |
| return nil; |
| |
| Deny: |
| return respond(x, &t, Eperm); |
| } |
| |
| static |
| Xfid* |
| fsyscreate(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| |
| USED(f); |
| return respond(x, &t, Eperm); |
| } |
| |
| static |
| int |
| idcmp(const void *a, const void *b) |
| { |
| return *(int*)a - *(int*)b; |
| } |
| |
| static |
| Xfid* |
| fsysread(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| uchar *b; |
| int i, id, n, o, e, j, k, *ids, nids; |
| Dirtab *d, dt; |
| Column *c; |
| uint clock, len; |
| char buf[16]; |
| |
| if(f->qid.type & QTDIR){ |
| if(FILE(f->qid) == Qacme){ /* empty dir */ |
| t.data = nil; |
| t.count = 0; |
| respond(x, &t, nil); |
| return x; |
| } |
| o = x->fcall.offset; |
| e = x->fcall.offset+x->fcall.count; |
| clock = getclock(); |
| b = emalloc(messagesize); |
| id = WIN(f->qid); |
| n = 0; |
| if(id > 0) |
| d = dirtabw; |
| else |
| d = dirtab; |
| d++; /* first entry is '.' */ |
| for(i=0; d->name!=nil && i<e; i+=len){ |
| len = dostat(WIN(x->f->qid), d, b+n, x->fcall.count-n, clock); |
| if(len <= BIT16SZ) |
| break; |
| if(i >= o) |
| n += len; |
| d++; |
| } |
| if(id == 0){ |
| qlock(&row.lk); |
| nids = 0; |
| ids = nil; |
| for(j=0; j<row.ncol; j++){ |
| c = row.col[j]; |
| for(k=0; k<c->nw; k++){ |
| ids = realloc(ids, (nids+1)*sizeof(int)); |
| ids[nids++] = c->w[k]->id; |
| } |
| } |
| qunlock(&row.lk); |
| qsort(ids, nids, sizeof ids[0], idcmp); |
| j = 0; |
| dt.name = buf; |
| for(; j<nids && i<e; i+=len){ |
| k = ids[j]; |
| sprint(dt.name, "%d", k); |
| dt.qid = QID(k, Qdir); |
| dt.type = QTDIR; |
| dt.perm = DMDIR|0700; |
| len = dostat(k, &dt, b+n, x->fcall.count-n, clock); |
| if(len == 0) |
| break; |
| if(i >= o) |
| n += len; |
| j++; |
| } |
| free(ids); |
| } |
| t.data = (char*)b; |
| t.count = n; |
| respond(x, &t, nil); |
| free(b); |
| return x; |
| } |
| sendp(x->c, (void*)xfidread); |
| return nil; |
| } |
| |
| static |
| Xfid* |
| fsyswrite(Xfid *x, Fid *f) |
| { |
| USED(f); |
| sendp(x->c, (void*)xfidwrite); |
| return nil; |
| } |
| |
| static |
| Xfid* |
| fsysclunk(Xfid *x, Fid *f) |
| { |
| fsysdelid(f->mntdir); |
| sendp(x->c, (void*)xfidclose); |
| return nil; |
| } |
| |
| static |
| Xfid* |
| fsysremove(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| |
| USED(f); |
| return respond(x, &t, Eperm); |
| } |
| |
| static |
| Xfid* |
| fsysstat(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| |
| t.stat = emalloc(messagesize-IOHDRSZ); |
| t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock()); |
| x = respond(x, &t, nil); |
| free(t.stat); |
| return x; |
| } |
| |
| static |
| Xfid* |
| fsyswstat(Xfid *x, Fid *f) |
| { |
| Fcall t; |
| |
| USED(f); |
| return respond(x, &t, Eperm); |
| } |
| |
| Fid* |
| newfid(int fid) |
| { |
| Fid *f, *ff, **fh; |
| |
| ff = nil; |
| fh = &fids[fid&(Nhash-1)]; |
| for(f=*fh; f; f=f->next) |
| if(f->fid == fid) |
| return f; |
| else if(ff==nil && f->busy==FALSE) |
| ff = f; |
| if(ff){ |
| ff->fid = fid; |
| return ff; |
| } |
| f = emalloc(sizeof *f); |
| f->fid = fid; |
| f->next = *fh; |
| *fh = f; |
| return f; |
| } |
| |
| uint |
| getclock(void) |
| { |
| return time(0); |
| } |
| |
| int |
| dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock) |
| { |
| Dir d; |
| |
| d.qid.path = QID(id, dir->qid); |
| d.qid.vers = 0; |
| d.qid.type = dir->type; |
| d.mode = dir->perm; |
| d.length = 0; /* would be nice to do better */ |
| d.name = dir->name; |
| d.uid = user; |
| d.gid = user; |
| d.muid = user; |
| d.atime = clock; |
| d.mtime = clock; |
| return convD2M(&d, buf, nbuf); |
| } |