diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/rio/fsys.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/rio/fsys.c')
-rwxr-xr-x | sys/src/cmd/rio/fsys.c | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/sys/src/cmd/rio/fsys.c b/sys/src/cmd/rio/fsys.c new file mode 100755 index 000000000..f8cfa3e14 --- /dev/null +++ b/sys/src/cmd/rio/fsys.c @@ -0,0 +1,700 @@ +#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 "dat.h" +#include "fns.h" + +char Eperm[] = "permission denied"; +char Eexist[] = "file does not exist"; +char Enotdir[] = "not a directory"; +char Ebadfcall[] = "bad fcall type"; +char Eoffset[] = "illegal offset"; + +int messagesize = 8192+IOHDRSZ; /* good start */ + +enum{ + DEBUG = 0 +}; + +Dirtab dirtab[]= +{ + { ".", QTDIR, Qdir, 0500|DMDIR }, + { "cons", QTFILE, Qcons, 0600 }, + { "cursor", QTFILE, Qcursor, 0600 }, + { "consctl", QTFILE, Qconsctl, 0200 }, + { "winid", QTFILE, Qwinid, 0400 }, + { "winname", QTFILE, Qwinname, 0400 }, + { "kbdin", QTFILE, Qkbdin, 0200 }, + { "label", QTFILE, Qlabel, 0600 }, + { "mouse", QTFILE, Qmouse, 0600 }, + { "screen", QTFILE, Qscreen, 0400 }, + { "snarf", QTFILE, Qsnarf, 0600 }, + { "text", QTFILE, Qtext, 0400 }, + { "wdir", QTFILE, Qwdir, 0600 }, + { "wctl", QTFILE, Qwctl, 0600 }, + { "window", QTFILE, Qwindow, 0400 }, + { "wsys", QTDIR, Qwsys, 0500|DMDIR }, + { nil, } +}; + +static uint getclock(void); +static void filsysproc(void*); +static Fid* newfid(Filsys*, int); +static int dostat(Filsys*, int, Dirtab*, uchar*, int, uint); + +int clockfd; +int firstmessage = 1; + +char srvpipe[64]; +char srvwctl[64]; + +static Xfid* filsysflush(Filsys*, Xfid*, Fid*); +static Xfid* filsysversion(Filsys*, Xfid*, Fid*); +static Xfid* filsysauth(Filsys*, Xfid*, Fid*); +static Xfid* filsysnop(Filsys*, Xfid*, Fid*); +static Xfid* filsysattach(Filsys*, Xfid*, Fid*); +static Xfid* filsyswalk(Filsys*, Xfid*, Fid*); +static Xfid* filsysopen(Filsys*, Xfid*, Fid*); +static Xfid* filsyscreate(Filsys*, Xfid*, Fid*); +static Xfid* filsysread(Filsys*, Xfid*, Fid*); +static Xfid* filsyswrite(Filsys*, Xfid*, Fid*); +static Xfid* filsysclunk(Filsys*, Xfid*, Fid*); +static Xfid* filsysremove(Filsys*, Xfid*, Fid*); +static Xfid* filsysstat(Filsys*, Xfid*, Fid*); +static Xfid* filsyswstat(Filsys*, Xfid*, Fid*); + +Xfid* (*fcall[Tmax])(Filsys*, Xfid*, Fid*) = +{ + [Tflush] = filsysflush, + [Tversion] = filsysversion, + [Tauth] = filsysauth, + [Tattach] = filsysattach, + [Twalk] = filsyswalk, + [Topen] = filsysopen, + [Tcreate] = filsyscreate, + [Tread] = filsysread, + [Twrite] = filsyswrite, + [Tclunk] = filsysclunk, + [Tremove]= filsysremove, + [Tstat] = filsysstat, + [Twstat] = filsyswstat, +}; + +void +post(char *name, char *envname, int srvfd) +{ + int fd; + char buf[32]; + + fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600); + if(fd < 0) + error(name); + sprint(buf, "%d",srvfd); + if(write(fd, buf, strlen(buf)) != strlen(buf)) + error("srv write"); + putenv(envname, name); +} + +/* + * Build pipe with OCEXEC set on second fd. + * Can't put it on both because we want to post one in /srv. + */ +int +cexecpipe(int *p0, int *p1) +{ + /* pipe the hard way to get close on exec */ + if(bind("#|", "/mnt/temp", MREPL) < 0) + return -1; + *p0 = open("/mnt/temp/data", ORDWR); + *p1 = open("/mnt/temp/data1", ORDWR|OCEXEC); + unmount(nil, "/mnt/temp"); + if(*p0<0 || *p1<0) + return -1; + return 0; +} + +Filsys* +filsysinit(Channel *cxfidalloc) +{ + int n, fd, pid, p0; + Filsys *fs; + Channel *c; + char buf[128]; + + fs = emalloc(sizeof(Filsys)); + if(cexecpipe(&fs->cfd, &fs->sfd) < 0) + goto Rescue; + fmtinstall('F', fcallfmt); + clockfd = open("/dev/time", OREAD|OCEXEC); + fd = open("/dev/user", OREAD); + strcpy(buf, "Jean-Paul_Belmondo"); + if(fd >= 0){ + n = read(fd, buf, sizeof buf-1); + if(n > 0) + buf[n] = 0; + close(fd); + } + fs->user = estrdup(buf); + fs->cxfidalloc = cxfidalloc; + pid = getpid(); + + /* + * Create and post wctl pipe + */ + if(cexecpipe(&p0, &wctlfd) < 0) + goto Rescue; + sprint(srvwctl, "/srv/riowctl.%s.%d", fs->user, pid); + post(srvwctl, "wctl", p0); + close(p0); + + /* + * Start server processes + */ + c = chancreate(sizeof(char*), 0); + if(c == nil) + error("wctl channel"); + proccreate(wctlproc, c, 4096); + threadcreate(wctlthread, c, 4096); + proccreate(filsysproc, fs, 10000); + + /* + * Post srv pipe + */ + sprint(srvpipe, "/srv/rio.%s.%d", fs->user, pid); + post(srvpipe, "wsys", fs->cfd); + + return fs; + +Rescue: + free(fs); + return nil; +} + +static +void +filsysproc(void *arg) +{ + int n; + Xfid *x; + Fid *f; + Fcall t; + uchar *buf; + Filsys *fs; + + threadsetname("FILSYSPROC"); + fs = arg; + fs->pid = getpid(); + x = nil; + for(;;){ + buf = emalloc(messagesize+UTFmax); /* UTFmax for appending partial rune in xfidwrite */ + n = read9pmsg(fs->sfd, buf, messagesize); + if(n <= 0){ + yield(); /* if threadexitsall'ing, will not return */ + fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n); + errorshouldabort = 0; + error("eof or i/o error on server channel"); + } + if(x == nil){ + send(fs->cxfidalloc, nil); + recv(fs->cxfidalloc, &x); + x->fs = fs; + } + x->buf = buf; + if(convM2S(buf, n, x) != n) + error("convert error in convM2S"); + if(DEBUG) + fprint(2, "rio:<-%F\n", &x->Fcall); + if(fcall[x->type] == nil) + x = filsysrespond(fs, x, &t, Ebadfcall); + else{ + if(x->type==Tversion || x->type==Tauth) + f = nil; + else + f = newfid(fs, x->fid); + x->f = f; + x = (*fcall[x->type])(fs, x, f); + } + firstmessage = 0; + } +} + +/* + * Called only from a different FD group + */ +int +filsysmount(Filsys *fs, int id) +{ + char buf[32]; + + close(fs->sfd); /* close server end so mount won't hang if exiting */ + sprint(buf, "%d", id); + if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) < 0){ + fprint(2, "mount failed: %r\n"); + return -1; + } + if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){ + fprint(2, "bind failed: %r\n"); + return -1; + } + return 0; +} + +Xfid* +filsysrespond(Filsys *fs, Xfid *x, Fcall *t, char *err) +{ + int n; + + if(err){ + t->type = Rerror; + t->ename = err; + }else + t->type = x->type+1; + t->fid = x->fid; + t->tag = x->tag; + if(x->buf == nil) + x->buf = malloc(messagesize); + n = convS2M(t, x->buf, messagesize); + if(n <= 0) + error("convert error in convS2M"); + if(write(fs->sfd, x->buf, n) != n) + error("write error in respond"); + if(DEBUG) + fprint(2, "rio:->%F\n", t); + free(x->buf); + x->buf = nil; + return x; +} + +void +filsyscancel(Xfid *x) +{ + if(x->buf){ + free(x->buf); + x->buf = nil; + } +} + +static +Xfid* +filsysversion(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + if(!firstmessage) + return filsysrespond(x->fs, x, &t, "version request not first message"); + if(x->msize < 256) + return filsysrespond(x->fs, x, &t, "version: message size too small"); + messagesize = x->msize; + t.msize = messagesize; + if(strncmp(x->version, "9P2000", 6) != 0) + return filsysrespond(x->fs, x, &t, "unrecognized 9P version"); + t.version = "9P2000"; + return filsysrespond(fs, x, &t, nil); +} + +static +Xfid* +filsysauth(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + return filsysrespond(fs, x, &t, "rio: authentication not required"); +} + +static +Xfid* +filsysflush(Filsys*, Xfid *x, Fid*) +{ + sendp(x->c, xfidflush); + return nil; +} + +static +Xfid* +filsysattach(Filsys *, Xfid *x, Fid *f) +{ + Fcall t; + + if(strcmp(x->uname, x->fs->user) != 0) + return filsysrespond(x->fs, 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; + sendp(x->c, xfidattach); + return nil; +} + +static +int +numeric(char *s) +{ + for(; *s!='\0'; s++) + if(*s<'0' || '9'<*s) + return 0; + return 1; +} + +static +Xfid* +filsyswalk(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + Fid *nf; + int i, id; + uchar type; + ulong path; + Dirtab *d, *dir; + Window *w; + char *err; + Qid qid; + + if(f->open) + return filsysrespond(fs, x, &t, "walk of open file"); + nf = nil; + if(x->fid != x->newfid){ + /* BUG: check exists */ + nf = newfid(fs, x->newfid); + if(nf->busy) + return filsysrespond(fs, x, &t, "clone to busy fid"); + nf->busy = TRUE; + nf->open = FALSE; + nf->dir = f->dir; + nf->qid = f->qid; + nf->w = f->w; + incref(f->w); + nf->nrpart = 0; /* not open, so must be zero */ + f = nf; /* walk f */ + } + + t.nwqid = 0; + err = nil; + + /* update f->qid, f->dir only if walk completes */ + qid = f->qid; + dir = f->dir; + + if(x->nwname > 0){ + for(i=0; i<x->nwname; i++){ + if((qid.type & QTDIR) == 0){ + err = Enotdir; + break; + } + if(strcmp(x->wname[i], "..") == 0){ + type = QTDIR; + path = Qdir; + dir = dirtab; + if(FILE(qid) == Qwsysdir) + path = Qwsys; + id = 0; + Accept: + if(i == MAXWELEM){ + err = "name too long"; + break; + } + qid.type = type; + qid.vers = 0; + qid.path = QID(id, path); + t.wqid[t.nwqid++] = qid; + continue; + } + + if(qid.path == Qwsys){ + /* is it a numeric name? */ + if(!numeric(x->wname[i])) + break; + /* yes: it's a directory */ + id = atoi(x->wname[i]); + qlock(&all); + w = wlookid(id); + if(w == nil){ + qunlock(&all); + break; + } + path = Qwsysdir; + type = QTDIR; + qunlock(&all); + incref(w); + sendp(winclosechan, f->w); + f->w = w; + dir = dirtab; + goto Accept; + } + + if(snarffd>=0 && strcmp(x->wname[i], "snarf")==0) + break; /* don't serve /dev/snarf if it's provided in the environment */ + id = WIN(f->qid); + d = dirtab; + d++; /* skip '.' */ + for(; d->name; d++) + if(strcmp(x->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->nwname){ + if(nf){ + if(nf->w) + sendp(winclosechan, nf->w); + nf->open = FALSE; + nf->busy = FALSE; + } + }else if(t.nwqid == x->nwname){ + f->dir = dir; + f->qid = qid; + } + + return filsysrespond(fs, x, &t, err); +} + +static +Xfid* +filsysopen(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + int m; + + /* can't truncate anything, so just disregard */ + x->mode &= ~(OTRUNC|OCEXEC); + /* can't execute or remove anything */ + if(x->mode==OEXEC || (x->mode&ORCLOSE)) + goto Deny; + switch(x->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, xfidopen); + return nil; + + Deny: + return filsysrespond(fs, x, &t, Eperm); +} + +static +Xfid* +filsyscreate(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + return filsysrespond(fs, x, &t, Eperm); +} + +static +int +idcmp(void *a, void *b) +{ + return *(int*)a - *(int*)b; +} + +static +Xfid* +filsysread(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + uchar *b; + int i, n, o, e, len, j, k, *ids; + Dirtab *d, dt; + uint clock; + char buf[16]; + + if((f->qid.type & QTDIR) == 0){ + sendp(x->c, xfidread); + return nil; + } + o = x->offset; + e = x->offset+x->count; + clock = getclock(); + b = malloc(messagesize-IOHDRSZ); /* avoid memset of emalloc */ + if(b == nil) + return filsysrespond(fs, x, &t, "out of memory"); + n = 0; + switch(FILE(f->qid)){ + case Qdir: + case Qwsysdir: + d = dirtab; + d++; /* first entry is '.' */ + for(i=0; d->name!=nil && i<e; i+=len){ + len = dostat(fs, WIN(x->f->qid), d, b+n, x->count-n, clock); + if(len <= BIT16SZ) + break; + if(i >= o) + n += len; + d++; + } + break; + case Qwsys: + qlock(&all); + ids = emalloc(nwindow*sizeof(int)); + for(j=0; j<nwindow; j++) + ids[j] = window[j]->id; + qunlock(&all); + qsort(ids, nwindow, sizeof ids[0], idcmp); + dt.name = buf; + for(i=0, j=0; j<nwindow && 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(fs, k, &dt, b+n, x->count-n, clock); + if(len == 0) + break; + if(i >= o) + n += len; + j++; + } + free(ids); + break; + } + t.data = (char*)b; + t.count = n; + filsysrespond(fs, x, &t, nil); + free(b); + return x; +} + +static +Xfid* +filsyswrite(Filsys*, Xfid *x, Fid*) +{ + sendp(x->c, xfidwrite); + return nil; +} + +static +Xfid* +filsysclunk(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + + if(f->open){ + f->busy = FALSE; + f->open = FALSE; + sendp(x->c, xfidclose); + return nil; + } + if(f->w) + sendp(winclosechan, f->w); + f->busy = FALSE; + f->open = FALSE; + return filsysrespond(fs, x, &t, nil); +} + +static +Xfid* +filsysremove(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + return filsysrespond(fs, x, &t, Eperm); +} + +static +Xfid* +filsysstat(Filsys *fs, Xfid *x, Fid *f) +{ + Fcall t; + + t.stat = emalloc(messagesize-IOHDRSZ); + t.nstat = dostat(fs, WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock()); + x = filsysrespond(fs, x, &t, nil); + free(t.stat); + return x; +} + +static +Xfid* +filsyswstat(Filsys *fs, Xfid *x, Fid*) +{ + Fcall t; + + return filsysrespond(fs, x, &t, Eperm); +} + +static +Fid* +newfid(Filsys *fs, int fid) +{ + Fid *f, *ff, **fh; + + ff = nil; + fh = &fs->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; +} + +static +uint +getclock(void) +{ + char buf[32]; + + seek(clockfd, 0, 0); + read(clockfd, buf, sizeof buf); + return atoi(buf); +} + +static +int +dostat(Filsys *fs, int id, Dirtab *dir, uchar *buf, int nbuf, uint clock) +{ + Dir d; + + d.qid.path = QID(id, dir->qid); + if(dir->qid == Qsnarf) + d.qid.vers = snarfversion; + else + 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 = fs->user; + d.gid = fs->user; + d.muid = fs->user; + d.atime = clock; + d.mtime = clock; + return convD2M(&d, buf, nbuf); +} |