#include #include #include #include #include #include #include #include "libframe/frame.h" #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; 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); }