1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include "dat.h"
11 #include "fns.h"
12
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
19 int messagesize = 8192+IOHDRSZ; /* good start */
20
21 enum{
22 DEBUG = 0
23 };
24
25 Dirtab dirtab[]=
26 {
27 { ".", QTDIR, Qdir, 0500|DMDIR },
28 { "cons", QTFILE, Qcons, 0600 },
29 { "cursor", QTFILE, Qcursor, 0600 },
30 { "consctl", QTFILE, Qconsctl, 0200 },
31 { "winid", QTFILE, Qwinid, 0400 },
32 { "winname", QTFILE, Qwinname, 0400 },
33 { "kbdin", QTFILE, Qkbdin, 0200 },
34 { "label", QTFILE, Qlabel, 0600 },
35 { "mouse", QTFILE, Qmouse, 0600 },
36 { "screen", QTFILE, Qscreen, 0400 },
37 { "snarf", QTFILE, Qsnarf, 0600 },
38 { "text", QTFILE, Qtext, 0400 },
39 { "wdir", QTFILE, Qwdir, 0600 },
40 { "wctl", QTFILE, Qwctl, 0600 },
41 { "window", QTFILE, Qwindow, 0400 },
42 { "wsys", QTDIR, Qwsys, 0500|DMDIR },
43 { nil, }
44 };
45
46 static uint getclock(void);
47 static void filsysproc(void*);
48 static Fid* newfid(Filsys*, int);
49 static int dostat(Filsys*, int, Dirtab*, uchar*, int, uint);
50
51 int clockfd;
52 int firstmessage = 1;
53
54 char srvpipe[64];
55 char srvwctl[64];
56
57 static Xfid* filsysflush(Filsys*, Xfid*, Fid*);
58 static Xfid* filsysversion(Filsys*, Xfid*, Fid*);
59 static Xfid* filsysauth(Filsys*, Xfid*, Fid*);
60 static Xfid* filsysnop(Filsys*, Xfid*, Fid*);
61 static Xfid* filsysattach(Filsys*, Xfid*, Fid*);
62 static Xfid* filsyswalk(Filsys*, Xfid*, Fid*);
63 static Xfid* filsysopen(Filsys*, Xfid*, Fid*);
64 static Xfid* filsyscreate(Filsys*, Xfid*, Fid*);
65 static Xfid* filsysread(Filsys*, Xfid*, Fid*);
66 static Xfid* filsyswrite(Filsys*, Xfid*, Fid*);
67 static Xfid* filsysclunk(Filsys*, Xfid*, Fid*);
68 static Xfid* filsysremove(Filsys*, Xfid*, Fid*);
69 static Xfid* filsysstat(Filsys*, Xfid*, Fid*);
70 static Xfid* filsyswstat(Filsys*, Xfid*, Fid*);
71
72 Xfid* (*fcall[Tmax])(Filsys*, Xfid*, Fid*) =
73 {
74 [Tflush] = filsysflush,
75 [Tversion] = filsysversion,
76 [Tauth] = filsysauth,
77 [Tattach] = filsysattach,
78 [Twalk] = filsyswalk,
79 [Topen] = filsysopen,
80 [Tcreate] = filsyscreate,
81 [Tread] = filsysread,
82 [Twrite] = filsyswrite,
83 [Tclunk] = filsysclunk,
84 [Tremove]= filsysremove,
85 [Tstat] = filsysstat,
86 [Twstat] = filsyswstat,
87 };
88
89 void
post(char * name,char * envname,int srvfd)90 post(char *name, char *envname, int srvfd)
91 {
92 int fd;
93 char buf[32];
94
95 fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600);
96 if(fd < 0)
97 error(name);
98 sprint(buf, "%d",srvfd);
99 if(write(fd, buf, strlen(buf)) != strlen(buf))
100 error("srv write");
101 putenv(envname, name);
102 }
103
104 /*
105 * Build pipe with OCEXEC set on second fd.
106 * Can't put it on both because we want to post one in /srv.
107 */
108 int
cexecpipe(int * p0,int * p1)109 cexecpipe(int *p0, int *p1)
110 {
111 /* pipe the hard way to get close on exec */
112 if(bind("#|", "/mnt/temp", MREPL) < 0)
113 return -1;
114 *p0 = open("/mnt/temp/data", ORDWR);
115 *p1 = open("/mnt/temp/data1", ORDWR|OCEXEC);
116 unmount(nil, "/mnt/temp");
117 if(*p0<0 || *p1<0)
118 return -1;
119 return 0;
120 }
121
122 Filsys*
filsysinit(Channel * cxfidalloc)123 filsysinit(Channel *cxfidalloc)
124 {
125 int n, fd, pid, p0;
126 Filsys *fs;
127 Channel *c;
128 char buf[128];
129
130 fs = emalloc(sizeof(Filsys));
131 if(cexecpipe(&fs->cfd, &fs->sfd) < 0)
132 goto Rescue;
133 fmtinstall('F', fcallfmt);
134 clockfd = open("/dev/time", OREAD|OCEXEC);
135 fd = open("/dev/user", OREAD);
136 strcpy(buf, "Jean-Paul_Belmondo");
137 if(fd >= 0){
138 n = read(fd, buf, sizeof buf-1);
139 if(n > 0)
140 buf[n] = 0;
141 close(fd);
142 }
143 fs->user = estrdup(buf);
144 fs->cxfidalloc = cxfidalloc;
145 pid = getpid();
146
147 /*
148 * Create and post wctl pipe
149 */
150 if(cexecpipe(&p0, &wctlfd) < 0)
151 goto Rescue;
152 sprint(srvwctl, "/srv/riowctl.%s.%d", fs->user, pid);
153 post(srvwctl, "wctl", p0);
154 close(p0);
155
156 /*
157 * Start server processes
158 */
159 c = chancreate(sizeof(char*), 0);
160 if(c == nil)
161 error("wctl channel");
162 proccreate(wctlproc, c, 4096);
163 threadcreate(wctlthread, c, 4096);
164 proccreate(filsysproc, fs, 10000);
165
166 /*
167 * Post srv pipe
168 */
169 sprint(srvpipe, "/srv/rio.%s.%d", fs->user, pid);
170 post(srvpipe, "wsys", fs->cfd);
171
172 return fs;
173
174 Rescue:
175 free(fs);
176 return nil;
177 }
178
179 static
180 void
filsysproc(void * arg)181 filsysproc(void *arg)
182 {
183 int n;
184 Xfid *x;
185 Fid *f;
186 Fcall t;
187 uchar *buf;
188 Filsys *fs;
189
190 threadsetname("FILSYSPROC");
191 fs = arg;
192 fs->pid = getpid();
193 x = nil;
194 for(;;){
195 buf = emalloc(messagesize+UTFmax); /* UTFmax for appending partial rune in xfidwrite */
196 n = read9pmsg(fs->sfd, buf, messagesize);
197 if(n <= 0){
198 yield(); /* if threadexitsall'ing, will not return */
199 fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n);
200 errorshouldabort = 0;
201 error("eof or i/o error on server channel");
202 }
203 if(x == nil){
204 send(fs->cxfidalloc, nil);
205 recv(fs->cxfidalloc, &x);
206 x->fs = fs;
207 }
208 x->buf = buf;
209 if(convM2S(buf, n, x) != n)
210 error("convert error in convM2S");
211 if(DEBUG)
212 fprint(2, "rio:<-%F\n", &x->Fcall);
213 if(fcall[x->type] == nil)
214 x = filsysrespond(fs, x, &t, Ebadfcall);
215 else{
216 if(x->type==Tversion || x->type==Tauth)
217 f = nil;
218 else
219 f = newfid(fs, x->fid);
220 x->f = f;
221 x = (*fcall[x->type])(fs, x, f);
222 }
223 firstmessage = 0;
224 }
225 }
226
227 /*
228 * Called only from a different FD group
229 */
230 int
filsysmount(Filsys * fs,int id)231 filsysmount(Filsys *fs, int id)
232 {
233 char buf[32];
234
235 close(fs->sfd); /* close server end so mount won't hang if exiting */
236 sprint(buf, "%d", id);
237 if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) < 0){
238 fprint(2, "mount failed: %r\n");
239 return -1;
240 }
241 if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
242 fprint(2, "bind failed: %r\n");
243 return -1;
244 }
245 return 0;
246 }
247
248 Xfid*
filsysrespond(Filsys * fs,Xfid * x,Fcall * t,char * err)249 filsysrespond(Filsys *fs, Xfid *x, Fcall *t, char *err)
250 {
251 int n;
252
253 if(err){
254 t->type = Rerror;
255 t->ename = err;
256 }else
257 t->type = x->type+1;
258 t->fid = x->fid;
259 t->tag = x->tag;
260 if(x->buf == nil)
261 x->buf = malloc(messagesize);
262 n = convS2M(t, x->buf, messagesize);
263 if(n <= 0)
264 error("convert error in convS2M");
265 if(write(fs->sfd, x->buf, n) != n)
266 error("write error in respond");
267 if(DEBUG)
268 fprint(2, "rio:->%F\n", t);
269 free(x->buf);
270 x->buf = nil;
271 return x;
272 }
273
274 void
filsyscancel(Xfid * x)275 filsyscancel(Xfid *x)
276 {
277 if(x->buf){
278 free(x->buf);
279 x->buf = nil;
280 }
281 }
282
283 static
284 Xfid*
filsysversion(Filsys * fs,Xfid * x,Fid *)285 filsysversion(Filsys *fs, Xfid *x, Fid*)
286 {
287 Fcall t;
288
289 if(!firstmessage)
290 return filsysrespond(x->fs, x, &t, "version request not first message");
291 if(x->msize < 256)
292 return filsysrespond(x->fs, x, &t, "version: message size too small");
293 messagesize = x->msize;
294 t.msize = messagesize;
295 if(strncmp(x->version, "9P2000", 6) != 0)
296 return filsysrespond(x->fs, x, &t, "unrecognized 9P version");
297 t.version = "9P2000";
298 return filsysrespond(fs, x, &t, nil);
299 }
300
301 static
302 Xfid*
filsysauth(Filsys * fs,Xfid * x,Fid *)303 filsysauth(Filsys *fs, Xfid *x, Fid*)
304 {
305 Fcall t;
306
307 return filsysrespond(fs, x, &t, "rio: authentication not required");
308 }
309
310 static
311 Xfid*
filsysflush(Filsys *,Xfid * x,Fid *)312 filsysflush(Filsys*, Xfid *x, Fid*)
313 {
314 sendp(x->c, xfidflush);
315 return nil;
316 }
317
318 static
319 Xfid*
filsysattach(Filsys *,Xfid * x,Fid * f)320 filsysattach(Filsys *, Xfid *x, Fid *f)
321 {
322 Fcall t;
323
324 if(strcmp(x->uname, x->fs->user) != 0)
325 return filsysrespond(x->fs, x, &t, Eperm);
326 f->busy = TRUE;
327 f->open = FALSE;
328 f->qid.path = Qdir;
329 f->qid.type = QTDIR;
330 f->qid.vers = 0;
331 f->dir = dirtab;
332 f->nrpart = 0;
333 sendp(x->c, xfidattach);
334 return nil;
335 }
336
337 static
338 int
numeric(char * s)339 numeric(char *s)
340 {
341 for(; *s!='\0'; s++)
342 if(*s<'0' || '9'<*s)
343 return 0;
344 return 1;
345 }
346
347 static
348 Xfid*
filsyswalk(Filsys * fs,Xfid * x,Fid * f)349 filsyswalk(Filsys *fs, Xfid *x, Fid *f)
350 {
351 Fcall t;
352 Fid *nf;
353 int i, id;
354 uchar type;
355 ulong path;
356 Dirtab *d, *dir;
357 Window *w;
358 char *err;
359 Qid qid;
360
361 if(f->open)
362 return filsysrespond(fs, x, &t, "walk of open file");
363 nf = nil;
364 if(x->fid != x->newfid){
365 /* BUG: check exists */
366 nf = newfid(fs, x->newfid);
367 if(nf->busy)
368 return filsysrespond(fs, x, &t, "clone to busy fid");
369 nf->busy = TRUE;
370 nf->open = FALSE;
371 nf->dir = f->dir;
372 nf->qid = f->qid;
373 nf->w = f->w;
374 incref(f->w);
375 nf->nrpart = 0; /* not open, so must be zero */
376 f = nf; /* walk f */
377 }
378
379 t.nwqid = 0;
380 err = nil;
381
382 /* update f->qid, f->dir only if walk completes */
383 qid = f->qid;
384 dir = f->dir;
385
386 if(x->nwname > 0){
387 for(i=0; i<x->nwname; i++){
388 if((qid.type & QTDIR) == 0){
389 err = Enotdir;
390 break;
391 }
392 if(strcmp(x->wname[i], "..") == 0){
393 type = QTDIR;
394 path = Qdir;
395 dir = dirtab;
396 if(FILE(qid) == Qwsysdir)
397 path = Qwsys;
398 id = 0;
399 Accept:
400 if(i == MAXWELEM){
401 err = "name too long";
402 break;
403 }
404 qid.type = type;
405 qid.vers = 0;
406 qid.path = QID(id, path);
407 t.wqid[t.nwqid++] = qid;
408 continue;
409 }
410
411 if(qid.path == Qwsys){
412 /* is it a numeric name? */
413 if(!numeric(x->wname[i]))
414 break;
415 /* yes: it's a directory */
416 id = atoi(x->wname[i]);
417 qlock(&all);
418 w = wlookid(id);
419 if(w == nil){
420 qunlock(&all);
421 break;
422 }
423 path = Qwsysdir;
424 type = QTDIR;
425 qunlock(&all);
426 incref(w);
427 sendp(winclosechan, f->w);
428 f->w = w;
429 dir = dirtab;
430 goto Accept;
431 }
432
433 if(snarffd>=0 && strcmp(x->wname[i], "snarf")==0)
434 break; /* don't serve /dev/snarf if it's provided in the environment */
435 id = WIN(f->qid);
436 d = dirtab;
437 d++; /* skip '.' */
438 for(; d->name; d++)
439 if(strcmp(x->wname[i], d->name) == 0){
440 path = d->qid;
441 type = d->type;
442 dir = d;
443 goto Accept;
444 }
445
446 break; /* file not found */
447 }
448
449 if(i==0 && err==nil)
450 err = Eexist;
451 }
452
453 if(err!=nil || t.nwqid<x->nwname){
454 if(nf){
455 if(nf->w)
456 sendp(winclosechan, nf->w);
457 nf->open = FALSE;
458 nf->busy = FALSE;
459 }
460 }else if(t.nwqid == x->nwname){
461 f->dir = dir;
462 f->qid = qid;
463 }
464
465 return filsysrespond(fs, x, &t, err);
466 }
467
468 static
469 Xfid*
filsysopen(Filsys * fs,Xfid * x,Fid * f)470 filsysopen(Filsys *fs, Xfid *x, Fid *f)
471 {
472 Fcall t;
473 int m;
474
475 /* can't truncate anything, so just disregard */
476 x->mode &= ~(OTRUNC|OCEXEC);
477 /* can't execute or remove anything */
478 if(x->mode==OEXEC || (x->mode&ORCLOSE))
479 goto Deny;
480 switch(x->mode){
481 default:
482 goto Deny;
483 case OREAD:
484 m = 0400;
485 break;
486 case OWRITE:
487 m = 0200;
488 break;
489 case ORDWR:
490 m = 0600;
491 break;
492 }
493 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
494 goto Deny;
495
496 sendp(x->c, xfidopen);
497 return nil;
498
499 Deny:
500 return filsysrespond(fs, x, &t, Eperm);
501 }
502
503 static
504 Xfid*
filsyscreate(Filsys * fs,Xfid * x,Fid *)505 filsyscreate(Filsys *fs, Xfid *x, Fid*)
506 {
507 Fcall t;
508
509 return filsysrespond(fs, x, &t, Eperm);
510 }
511
512 static
513 int
idcmp(void * a,void * b)514 idcmp(void *a, void *b)
515 {
516 return *(int*)a - *(int*)b;
517 }
518
519 static
520 Xfid*
filsysread(Filsys * fs,Xfid * x,Fid * f)521 filsysread(Filsys *fs, Xfid *x, Fid *f)
522 {
523 Fcall t;
524 uchar *b;
525 int i, n, o, e, len, j, k, *ids;
526 Dirtab *d, dt;
527 uint clock;
528 char buf[16];
529
530 if((f->qid.type & QTDIR) == 0){
531 sendp(x->c, xfidread);
532 return nil;
533 }
534 o = x->offset;
535 e = x->offset+x->count;
536 clock = getclock();
537 b = malloc(messagesize-IOHDRSZ); /* avoid memset of emalloc */
538 if(b == nil)
539 return filsysrespond(fs, x, &t, "out of memory");
540 n = 0;
541 switch(FILE(f->qid)){
542 case Qdir:
543 case Qwsysdir:
544 d = dirtab;
545 d++; /* first entry is '.' */
546 for(i=0; d->name!=nil && i<e; i+=len){
547 len = dostat(fs, WIN(x->f->qid), d, b+n, x->count-n, clock);
548 if(len <= BIT16SZ)
549 break;
550 if(i >= o)
551 n += len;
552 d++;
553 }
554 break;
555 case Qwsys:
556 qlock(&all);
557 ids = emalloc(nwindow*sizeof(int));
558 for(j=0; j<nwindow; j++)
559 ids[j] = window[j]->id;
560 qunlock(&all);
561 qsort(ids, nwindow, sizeof ids[0], idcmp);
562 dt.name = buf;
563 for(i=0, j=0; j<nwindow && i<e; i+=len){
564 k = ids[j];
565 sprint(dt.name, "%d", k);
566 dt.qid = QID(k, Qdir);
567 dt.type = QTDIR;
568 dt.perm = DMDIR|0700;
569 len = dostat(fs, k, &dt, b+n, x->count-n, clock);
570 if(len == 0)
571 break;
572 if(i >= o)
573 n += len;
574 j++;
575 }
576 free(ids);
577 break;
578 }
579 t.data = (char*)b;
580 t.count = n;
581 filsysrespond(fs, x, &t, nil);
582 free(b);
583 return x;
584 }
585
586 static
587 Xfid*
filsyswrite(Filsys *,Xfid * x,Fid *)588 filsyswrite(Filsys*, Xfid *x, Fid*)
589 {
590 sendp(x->c, xfidwrite);
591 return nil;
592 }
593
594 static
595 Xfid*
filsysclunk(Filsys * fs,Xfid * x,Fid * f)596 filsysclunk(Filsys *fs, Xfid *x, Fid *f)
597 {
598 Fcall t;
599
600 if(f->open){
601 f->busy = FALSE;
602 f->open = FALSE;
603 sendp(x->c, xfidclose);
604 return nil;
605 }
606 if(f->w)
607 sendp(winclosechan, f->w);
608 f->busy = FALSE;
609 f->open = FALSE;
610 return filsysrespond(fs, x, &t, nil);
611 }
612
613 static
614 Xfid*
filsysremove(Filsys * fs,Xfid * x,Fid *)615 filsysremove(Filsys *fs, Xfid *x, Fid*)
616 {
617 Fcall t;
618
619 return filsysrespond(fs, x, &t, Eperm);
620 }
621
622 static
623 Xfid*
filsysstat(Filsys * fs,Xfid * x,Fid * f)624 filsysstat(Filsys *fs, Xfid *x, Fid *f)
625 {
626 Fcall t;
627
628 t.stat = emalloc(messagesize-IOHDRSZ);
629 t.nstat = dostat(fs, WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
630 x = filsysrespond(fs, x, &t, nil);
631 free(t.stat);
632 return x;
633 }
634
635 static
636 Xfid*
filsyswstat(Filsys * fs,Xfid * x,Fid *)637 filsyswstat(Filsys *fs, Xfid *x, Fid*)
638 {
639 Fcall t;
640
641 return filsysrespond(fs, x, &t, Eperm);
642 }
643
644 static
645 Fid*
newfid(Filsys * fs,int fid)646 newfid(Filsys *fs, int fid)
647 {
648 Fid *f, *ff, **fh;
649
650 ff = nil;
651 fh = &fs->fids[fid&(Nhash-1)];
652 for(f=*fh; f; f=f->next)
653 if(f->fid == fid)
654 return f;
655 else if(ff==nil && f->busy==FALSE)
656 ff = f;
657 if(ff){
658 ff->fid = fid;
659 return ff;
660 }
661 f = emalloc(sizeof *f);
662 f->fid = fid;
663 f->next = *fh;
664 *fh = f;
665 return f;
666 }
667
668 static
669 uint
getclock(void)670 getclock(void)
671 {
672 char buf[32];
673
674 seek(clockfd, 0, 0);
675 read(clockfd, buf, sizeof buf);
676 return atoi(buf);
677 }
678
679 static
680 int
dostat(Filsys * fs,int id,Dirtab * dir,uchar * buf,int nbuf,uint clock)681 dostat(Filsys *fs, int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
682 {
683 Dir d;
684
685 d.qid.path = QID(id, dir->qid);
686 if(dir->qid == Qsnarf)
687 d.qid.vers = snarfversion;
688 else
689 d.qid.vers = 0;
690 d.qid.type = dir->type;
691 d.mode = dir->perm;
692 d.length = 0; /* would be nice to do better */
693 d.name = dir->name;
694 d.uid = fs->user;
695 d.gid = fs->user;
696 d.muid = fs->user;
697 d.atime = clock;
698 d.mtime = clock;
699 return convD2M(&d, buf, nbuf);
700 }
701