13 char Eperm[] = "permission denied";
14 char Eexist[] = "file does not exist";
15 char Enotdir[] = "not a directory";
16 char Ebadfcall[] = "bad fcall type";
17 char Eoffset[] = "illegal offset";
18 char Enomem[] = "out of memory";
20 int messagesize = 8192+IOHDRSZ; /* good start */
24 { ".", QTDIR, Qdir, 0500|DMDIR },
25 { "cons", QTFILE, Qcons, 0600 },
26 { "cursor", QTFILE, Qcursor, 0600 },
27 { "consctl", QTFILE, Qconsctl, 0200 },
28 { "winid", QTFILE, Qwinid, 0400 },
29 { "winname", QTFILE, Qwinname, 0400 },
30 { "label", QTFILE, Qlabel, 0600 },
31 { "kbd", QTFILE, Qkbd, 0600 },
32 { "mouse", QTFILE, Qmouse, 0600 },
33 { "screen", QTFILE, Qscreen, 0400 },
34 { "snarf", QTFILE, Qsnarf, 0600 },
35 { "text", QTFILE, Qtext, 0400 },
36 { "wdir", QTFILE, Qwdir, 0600 },
37 { "wctl", QTFILE, Qwctl, 0600 },
38 { "window", QTFILE, Qwindow, 0400 },
39 { "wsys", QTDIR, Qwsys, 0500|DMDIR },
43 static uint getclock(void);
44 static void filsysproc(void*);
45 static Fid* newfid(Filsys*, int);
46 static int dostat(Filsys*, int, Dirtab*, uchar*, int, uint);
54 static Xfid* filsysflush(Filsys*, Xfid*, Fid*);
55 static Xfid* filsysversion(Filsys*, Xfid*, Fid*);
56 static Xfid* filsysauth(Filsys*, Xfid*, Fid*);
57 static Xfid* filsysnop(Filsys*, Xfid*, Fid*);
58 static Xfid* filsysattach(Filsys*, Xfid*, Fid*);
59 static Xfid* filsyswalk(Filsys*, Xfid*, Fid*);
60 static Xfid* filsysopen(Filsys*, Xfid*, Fid*);
61 static Xfid* filsyscreate(Filsys*, Xfid*, Fid*);
62 static Xfid* filsysread(Filsys*, Xfid*, Fid*);
63 static Xfid* filsyswrite(Filsys*, Xfid*, Fid*);
64 static Xfid* filsysclunk(Filsys*, Xfid*, Fid*);
65 static Xfid* filsysremove(Filsys*, Xfid*, Fid*);
66 static Xfid* filsysstat(Filsys*, Xfid*, Fid*);
67 static Xfid* filsyswstat(Filsys*, Xfid*, Fid*);
69 Xfid* (*fcall[Tmax])(Filsys*, Xfid*, Fid*) =
71 [Tflush] = filsysflush,
72 [Tversion] = filsysversion,
74 [Tattach] = filsysattach,
77 [Tcreate] = filsyscreate,
79 [Twrite] = filsyswrite,
80 [Tclunk] = filsysclunk,
81 [Tremove]= filsysremove,
83 [Twstat] = filsyswstat,
87 post(char *name, char *envname, int srvfd)
92 fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600);
95 snprint(buf, sizeof(buf), "%d", srvfd);
96 if(write(fd, buf, strlen(buf)) != strlen(buf))
98 putenv(envname, name);
102 * Build pipe with OCEXEC set on second fd.
103 * Can't put it on both because we want to post one in /srv.
106 cexecpipe(int *p0, int *p1)
108 /* pipe the hard way to get close on exec */
109 if(bind("#|", "/mnt/temp", MREPL) < 0)
111 *p0 = open("/mnt/temp/data", ORDWR);
112 *p1 = open("/mnt/temp/data1", ORDWR|OCEXEC);
113 unmount(nil, "/mnt/temp");
120 filsysinit(Channel *cxfidalloc)
127 fs = emalloc(sizeof(Filsys));
128 if(cexecpipe(&fs->cfd, &fs->sfd) < 0)
130 fmtinstall('F', fcallfmt);
131 clockfd = open("/dev/time", OREAD|OCEXEC);
132 fd = open("/dev/user", OREAD);
133 strcpy(buf, "Jean-Paul_Belmondo");
135 n = read(fd, buf, sizeof buf-1);
140 fs->user = estrdup(buf);
141 fs->csyncflush = chancreate(sizeof(int), 0);
142 if(fs->csyncflush == nil)
143 error("chancreate syncflush");
144 fs->cxfidalloc = cxfidalloc;
148 * Create and post wctl pipe
150 if(cexecpipe(&p0, &wctlfd) < 0)
152 snprint(srvwctl, sizeof(srvwctl), "/srv/riowctl.%s.%d", fs->user, pid);
153 post(srvwctl, "wctl", p0);
157 * Start server processes
159 c = chancreate(sizeof(char*), 0);
161 error("wctl channel");
162 proccreate(wctlproc, c, 4096);
163 threadcreate(wctlthread, c, 4096);
164 proccreate(filsysproc, fs, 10000);
169 snprint(srvpipe, sizeof(srvpipe), "/srv/rio.%s.%d", fs->user, pid);
170 post(srvpipe, "wsys", fs->cfd);
181 filsysproc(void *arg)
190 threadsetname("FILSYSPROC");
195 buf = malloc(messagesize+UTFmax); /* UTFmax for appending partial rune in xfidwrite */
198 while((n = read9pmsg(fs->sfd, buf, messagesize)) == 0)
201 yield(); /* if threadexitsall'ing, will not return */
202 fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n);
203 errorshouldabort = 0;
204 error("eof or i/o error on server channel");
207 send(fs->cxfidalloc, nil);
208 recv(fs->cxfidalloc, &x);
212 if(convM2S(buf, n, x) != n)
213 error("convert error in convM2S");
215 fprint(2, "rio:<-%F\n", &x->Fcall);
216 if(fcall[x->type] == nil)
217 x = filsysrespond(fs, x, &t, Ebadfcall);
219 if(x->type==Tversion || x->type==Tauth)
222 f = newfid(fs, x->fid);
224 x = (*fcall[x->type])(fs, x, f);
231 * Called only from a different FD group
234 filsysmount(Filsys *fs, int id)
238 close(fs->sfd); /* close server end so mount won't hang if exiting */
239 sprint(buf, "%d", id);
240 if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) < 0){
241 fprint(2, "mount failed: %r\n");
244 if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
245 fprint(2, "bind failed: %r\n");
252 filsysrespond(Filsys *fs, Xfid *x, Fcall *t, char *err)
264 error("no buffer in respond");
265 n = convS2M(t, x->buf, messagesize);
267 error("convert error in convS2M");
268 if(write(fs->sfd, x->buf, n) != n)
269 error("write error in respond");
271 fprint(2, "rio:->%F\n", t);
279 filsyscancel(Xfid *x)
289 filsysversion(Filsys *fs, Xfid *x, Fid*)
294 return filsysrespond(x->fs, x, &t, "version request not first message");
296 return filsysrespond(x->fs, x, &t, "version: message size too small");
297 messagesize = x->msize;
298 t.msize = messagesize;
299 if(strncmp(x->version, "9P2000", 6) != 0)
300 return filsysrespond(x->fs, x, &t, "unrecognized 9P version");
301 t.version = "9P2000";
302 return filsysrespond(fs, x, &t, nil);
307 filsysauth(Filsys *fs, Xfid *x, Fid*)
311 return filsysrespond(fs, x, &t, "rio: authentication not required");
316 filsysflush(Filsys *fs, Xfid *x, Fid*)
318 sendp(x->c, xfidflush);
321 * flushes need to be replied in order. xfidflush() will
322 * awaken us when the flush has been queued.
324 recv(fs->csyncflush, nil);
331 filsysattach(Filsys *, Xfid *x, Fid *f)
335 if(strcmp(x->uname, x->fs->user) != 0)
336 return filsysrespond(x->fs, x, &t, Eperm);
344 sendp(x->c, xfidattach);
362 /* don't serve these if it's provided in the environment */
363 if(snarffd>=0 && strcmp(name, "snarf")==0)
365 if(gotscreen && strcmp(name, "screen")==0)
367 if(!servekbd && strcmp(name, "kbd")==0)
374 filsyswalk(Filsys *fs, Xfid *x, Fid *f)
387 return filsysrespond(fs, x, &t, "walk of open file");
389 if(x->fid != x->newfid){
390 /* BUG: check exists */
391 nf = newfid(fs, x->newfid);
393 return filsysrespond(fs, x, &t, "clone to busy fid");
400 nf->nrpart = 0; /* not open, so must be zero */
407 /* update f->qid, f->dir only if walk completes */
412 for(i=0; i<x->nwname; i++){
413 if((qid.type & QTDIR) == 0){
417 if(strcmp(x->wname[i], "..") == 0){
421 if(FILE(qid) == Qwsysdir)
426 err = "name too long";
431 qid.path = QID(id, path);
432 t.wqid[t.nwqid++] = qid;
436 if(qid.path == Qwsys){
437 /* is it a numeric name? */
438 if(!numeric(x->wname[i]))
440 /* yes: it's a directory */
441 id = atoi(x->wname[i]);
452 sendp(winclosechan, f->w);
457 if(skipdir(x->wname[i]))
463 if(strcmp(x->wname[i], d->name) == 0){
470 break; /* file not found */
477 if(err!=nil || t.nwqid<x->nwname){
480 sendp(winclosechan, nf->w);
484 }else if(t.nwqid == x->nwname){
489 return filsysrespond(fs, x, &t, err);
494 filsysopen(Filsys *fs, Xfid *x, Fid *f)
499 /* can't truncate anything, so just disregard */
500 x->mode &= ~(OTRUNC|OCEXEC);
501 /* can't execute or remove anything */
502 if(x->mode==OEXEC || (x->mode&ORCLOSE))
517 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
520 sendp(x->c, xfidopen);
524 return filsysrespond(fs, x, &t, Eperm);
529 filsyscreate(Filsys *fs, Xfid *x, Fid*)
533 return filsysrespond(fs, x, &t, Eperm);
538 idcmp(void *a, void *b)
540 return *(int*)a - *(int*)b;
545 filsysread(Filsys *fs, Xfid *x, Fid *f)
549 int i, n, o, e, len, j, k, *ids;
554 if((f->qid.type & QTDIR) == 0){
555 sendp(x->c, xfidread);
559 e = x->offset+x->count;
561 b = malloc(messagesize-IOHDRSZ); /* avoid memset of emalloc */
563 return filsysrespond(fs, x, &t, Enomem);
565 switch(FILE(f->qid)){
569 d++; /* first entry is '.' */
570 for(i=0; d->name!=nil && i<e; d++){
573 len = dostat(fs, WIN(x->f->qid), d, b+n, x->count-n, clock);
583 ids = emalloc(nwindow*sizeof(int));
584 for(j=0; j<nwindow; j++)
585 ids[j] = window[j]->id;
587 qsort(ids, nwindow, sizeof ids[0], idcmp);
589 for(i=0, j=0; j<nwindow && i<e; i+=len){
591 sprint(dt.name, "%d", k);
592 dt.qid = QID(k, Qdir);
594 dt.perm = DMDIR|0700;
595 len = dostat(fs, k, &dt, b+n, x->count-n, clock);
607 filsysrespond(fs, x, &t, nil);
614 filsyswrite(Filsys*, Xfid *x, Fid*)
616 sendp(x->c, xfidwrite);
622 filsysclunk(Filsys *fs, Xfid *x, Fid *f)
629 sendp(x->c, xfidclose);
633 sendp(winclosechan, f->w);
636 return filsysrespond(fs, x, &t, nil);
641 filsysremove(Filsys *fs, Xfid *x, Fid*)
645 return filsysrespond(fs, x, &t, Eperm);
650 filsysstat(Filsys *fs, Xfid *x, Fid *f)
654 t.stat = emalloc(messagesize-IOHDRSZ);
655 t.nstat = dostat(fs, WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
656 x = filsysrespond(fs, x, &t, nil);
663 filsyswstat(Filsys *fs, Xfid *x, Fid*)
667 return filsysrespond(fs, x, &t, Eperm);
672 newfid(Filsys *fs, int fid)
677 fh = &fs->fids[fid&(Nhash-1)];
678 for(f=*fh; f; f=f->next)
681 else if(ff==nil && f->busy==FALSE)
687 f = emalloc(sizeof *f);
701 read(clockfd, buf, sizeof buf);
707 dostat(Filsys *fs, int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
711 d.qid.path = QID(id, dir->qid);
712 if(dir->qid == Qsnarf)
713 d.qid.vers = snarfversion;
716 d.qid.type = dir->type;
718 d.length = 0; /* would be nice to do better */
725 return convD2M(&d, buf, nbuf);