#include #include #include #include #include #include #include #include #include #include #include #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; inincl; 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; ifcall.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'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.nwqidfcall.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 && if->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; jnw; 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(; jfcall.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); }