summaryrefslogtreecommitdiff
path: root/sys/src/cmd/rio/fsys.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/rio/fsys.c700
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);
+}