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 <plumb.h>
11 #include "dat.h"
12 #include "fns.h"
13
14 static int cfd;
15 static int sfd;
16
17 enum
18 {
19 Nhash = 16,
20 DEBUG = 0
21 };
22
23 static Fid *fids[Nhash];
24
25 Fid *newfid(int);
26
27 static Xfid* fsysflush(Xfid*, Fid*);
28 static Xfid* fsysauth(Xfid*, Fid*);
29 static Xfid* fsysversion(Xfid*, Fid*);
30 static Xfid* fsysattach(Xfid*, Fid*);
31 static Xfid* fsyswalk(Xfid*, Fid*);
32 static Xfid* fsysopen(Xfid*, Fid*);
33 static Xfid* fsyscreate(Xfid*, Fid*);
34 static Xfid* fsysread(Xfid*, Fid*);
35 static Xfid* fsyswrite(Xfid*, Fid*);
36 static Xfid* fsysclunk(Xfid*, Fid*);
37 static Xfid* fsysremove(Xfid*, Fid*);
38 static Xfid* fsysstat(Xfid*, Fid*);
39 static Xfid* fsyswstat(Xfid*, Fid*);
40
41 Xfid* (*fcall[Tmax])(Xfid*, Fid*) =
42 {
43 [Tflush] = fsysflush,
44 [Tversion] = fsysversion,
45 [Tauth] = fsysauth,
46 [Tattach] = fsysattach,
47 [Twalk] = fsyswalk,
48 [Topen] = fsysopen,
49 [Tcreate] = fsyscreate,
50 [Tread] = fsysread,
51 [Twrite] = fsyswrite,
52 [Tclunk] = fsysclunk,
53 [Tremove]= fsysremove,
54 [Tstat] = fsysstat,
55 [Twstat] = fsyswstat,
56 };
57
58 char Eperm[] = "permission denied";
59 char Eexist[] = "file does not exist";
60 char Enotdir[] = "not a directory";
61
62 Dirtab dirtab[]=
63 {
64 { ".", QTDIR, Qdir, 0500|DMDIR },
65 { "acme", QTDIR, Qacme, 0500|DMDIR },
66 { "cons", QTFILE, Qcons, 0600 },
67 { "consctl", QTFILE, Qconsctl, 0000 },
68 { "draw", QTDIR, Qdraw, 0000|DMDIR }, /* to suppress graphics progs started in acme */
69 { "editout", QTFILE, Qeditout, 0200 },
70 { "index", QTFILE, Qindex, 0400 },
71 { "label", QTFILE, Qlabel, 0600 },
72 { "new", QTDIR, Qnew, 0500|DMDIR },
73 { nil, }
74 };
75
76 Dirtab dirtabw[]=
77 {
78 { ".", QTDIR, Qdir, 0500|DMDIR },
79 { "addr", QTFILE, QWaddr, 0600 },
80 { "body", QTAPPEND, QWbody, 0600|DMAPPEND },
81 { "ctl", QTFILE, QWctl, 0600 },
82 { "data", QTFILE, QWdata, 0600 },
83 { "editout", QTFILE, QWeditout, 0200 },
84 { "errors", QTFILE, QWerrors, 0200 },
85 { "event", QTFILE, QWevent, 0600 },
86 { "rdsel", QTFILE, QWrdsel, 0400 },
87 { "wrsel", QTFILE, QWwrsel, 0200 },
88 { "tag", QTAPPEND, QWtag, 0600|DMAPPEND },
89 { "xdata", QTFILE, QWxdata, 0600 },
90 { nil, }
91 };
92
93 typedef struct Mnt Mnt;
94 struct Mnt
95 {
96 QLock;
97 int id;
98 Mntdir *md;
99 };
100
101 Mnt mnt;
102
103 Xfid* respond(Xfid*, Fcall*, char*);
104 int dostat(int, Dirtab*, uchar*, int, uint);
105 uint getclock(void);
106
107 char *user = "Wile E. Coyote";
108 int clockfd;
109 static int closing = 0;
110 int messagesize = Maxblock+IOHDRSZ; /* good start */
111
112 void fsysproc(void *);
113
114 void
fsysinit(void)115 fsysinit(void)
116 {
117 int p[2];
118 int n, fd;
119 char buf[256];
120
121 if(pipe(p) < 0)
122 error("can't create pipe");
123 cfd = p[0];
124 sfd = p[1];
125 fmtinstall('F', fcallfmt);
126 clockfd = open("/dev/time", OREAD|OCEXEC);
127 fd = open("/dev/user", OREAD);
128 if(fd >= 0){
129 n = read(fd, buf, sizeof buf-1);
130 if(n > 0){
131 buf[n] = 0;
132 user = estrdup(buf);
133 }
134 close(fd);
135 }
136 proccreate(fsysproc, nil, STACK);
137 }
138
139 void
fsysproc(void *)140 fsysproc(void *)
141 {
142 int n;
143 Xfid *x;
144 Fid *f;
145 Fcall t;
146 uchar *buf;
147
148 threadsetname("fsysproc");
149
150 x = nil;
151 for(;;){
152 buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */
153 n = read9pmsg(sfd, buf, messagesize);
154 if(n <= 0){
155 if(closing)
156 break;
157 error("i/o error on server channel");
158 }
159 if(x == nil){
160 sendp(cxfidalloc, nil);
161 x = recvp(cxfidalloc);
162 }
163 x->buf = buf;
164 if(convM2S(buf, n, x) != n)
165 error("convert error in convM2S");
166 if(DEBUG)
167 fprint(2, "%F\n", &x->Fcall);
168 if(fcall[x->type] == nil)
169 x = respond(x, &t, "bad fcall type");
170 else{
171 switch(x->type){
172 case Tversion:
173 case Tauth:
174 case Tflush:
175 f = nil;
176 break;
177 case Tattach:
178 f = newfid(x->fid);
179 break;
180 default:
181 f = newfid(x->fid);
182 if(!f->busy){
183 x->f = f;
184 x = respond(x, &t, "fid not in use");
185 continue;
186 }
187 break;
188 }
189 x->f = f;
190 x = (*fcall[x->type])(x, f);
191 }
192 }
193 }
194
195 Mntdir*
fsysaddid(Rune * dir,int ndir,Rune ** incl,int nincl)196 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
197 {
198 Mntdir *m;
199 int id;
200
201 qlock(&mnt);
202 id = ++mnt.id;
203 m = emalloc(sizeof *m);
204 m->id = id;
205 m->dir = dir;
206 m->ref = 1; /* one for Command, one will be incremented in attach */
207 m->ndir = ndir;
208 m->next = mnt.md;
209 m->incl = incl;
210 m->nincl = nincl;
211 mnt.md = m;
212 qunlock(&mnt);
213 return m;
214 }
215
216 void
fsysincid(Mntdir * m)217 fsysincid(Mntdir *m)
218 {
219 qlock(&mnt);
220 m->ref++;
221 qunlock(&mnt);
222 }
223
224 void
fsysdelid(Mntdir * idm)225 fsysdelid(Mntdir *idm)
226 {
227 Mntdir *m, *prev;
228 int i;
229 char buf[64];
230
231 if(idm == nil)
232 return;
233 qlock(&mnt);
234 if(--idm->ref > 0){
235 qunlock(&mnt);
236 return;
237 }
238 prev = nil;
239 for(m=mnt.md; m; m=m->next){
240 if(m == idm){
241 if(prev)
242 prev->next = m->next;
243 else
244 mnt.md = m->next;
245 for(i=0; i<m->nincl; i++)
246 free(m->incl[i]);
247 free(m->incl);
248 free(m->dir);
249 free(m);
250 qunlock(&mnt);
251 return;
252 }
253 prev = m;
254 }
255 qunlock(&mnt);
256 sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
257 sendp(cerr, estrdup(buf));
258 }
259
260 /*
261 * Called only in exec.c:/^run(), from a different FD group
262 */
263 Mntdir*
fsysmount(Rune * dir,int ndir,Rune ** incl,int nincl)264 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
265 {
266 char buf[256];
267 Mntdir *m;
268
269 /* close server side so don't hang if acme is half-exited */
270 close(sfd);
271 m = fsysaddid(dir, ndir, incl, nincl);
272 sprint(buf, "%d", m->id);
273 if(mount(cfd, -1, "/mnt/acme", MREPL, buf) < 0){
274 fsysdelid(m);
275 return nil;
276 }
277 close(cfd);
278 bind("/mnt/acme", "/mnt/wsys", MREPL);
279 if(bind("/mnt/acme", "/dev", MBEFORE) < 0){
280 fsysdelid(m);
281 return nil;
282 }
283 return m;
284 }
285
286 void
fsysclose(void)287 fsysclose(void)
288 {
289 closing = 1;
290 close(cfd);
291 close(sfd);
292 }
293
294 Xfid*
respond(Xfid * x,Fcall * t,char * err)295 respond(Xfid *x, Fcall *t, char *err)
296 {
297 int n;
298
299 if(err){
300 t->type = Rerror;
301 t->ename = err;
302 }else
303 t->type = x->type+1;
304 t->fid = x->fid;
305 t->tag = x->tag;
306 if(x->buf == nil)
307 x->buf = emalloc(messagesize);
308 n = convS2M(t, x->buf, messagesize);
309 if(n <= 0)
310 error("convert error in convS2M");
311 if(write(sfd, x->buf, n) != n)
312 error("write error in respond");
313 free(x->buf);
314 x->buf = nil;
315 if(DEBUG)
316 fprint(2, "r: %F\n", t);
317 return x;
318 }
319
320 static
321 Xfid*
fsysversion(Xfid * x,Fid *)322 fsysversion(Xfid *x, Fid*)
323 {
324 Fcall t;
325
326 if(x->msize < 256)
327 return respond(x, &t, "version: message size too small");
328 if(x->msize < messagesize)
329 messagesize = x->msize;
330 t.msize = messagesize;
331 if(strncmp(x->version, "9P2000", 6) != 0)
332 return respond(x, &t, "unrecognized 9P version");
333 t.version = "9P2000";
334 return respond(x, &t, nil);
335 }
336
337 static
338 Xfid*
fsysauth(Xfid * x,Fid *)339 fsysauth(Xfid *x, Fid*)
340 {
341 Fcall t;
342
343 return respond(x, &t, "acme: authentication not required");
344 }
345
346 static
347 Xfid*
fsysflush(Xfid * x,Fid *)348 fsysflush(Xfid *x, Fid*)
349 {
350 sendp(x->c, xfidflush);
351 return nil;
352 }
353
354 static
355 Xfid*
fsysattach(Xfid * x,Fid * f)356 fsysattach(Xfid *x, Fid *f)
357 {
358 Fcall t;
359 int id;
360 Mntdir *m;
361
362 if(strcmp(x->uname, user) != 0)
363 return respond(x, &t, Eperm);
364 f->busy = TRUE;
365 f->open = FALSE;
366 f->qid.path = Qdir;
367 f->qid.type = QTDIR;
368 f->qid.vers = 0;
369 f->dir = dirtab;
370 f->nrpart = 0;
371 f->w = nil;
372 t.qid = f->qid;
373 f->mntdir = nil;
374 id = atoi(x->aname);
375 qlock(&mnt);
376 for(m=mnt.md; m; m=m->next)
377 if(m->id == id){
378 f->mntdir = m;
379 m->ref++;
380 break;
381 }
382 if(m == nil)
383 sendp(cerr, estrdup("unknown id in attach"));
384 qunlock(&mnt);
385 return respond(x, &t, nil);
386 }
387
388 static
389 Xfid*
fsyswalk(Xfid * x,Fid * f)390 fsyswalk(Xfid *x, Fid *f)
391 {
392 Fcall t;
393 int c, i, j, id;
394 Qid q;
395 uchar type;
396 ulong path;
397 Fid *nf;
398 Dirtab *d, *dir;
399 Window *w;
400 char *err;
401
402 nf = nil;
403 w = nil;
404 if(f->open)
405 return respond(x, &t, "walk of open file");
406 if(x->fid != x->newfid){
407 nf = newfid(x->newfid);
408 if(nf->busy)
409 return respond(x, &t, "newfid already in use");
410 nf->busy = TRUE;
411 nf->open = FALSE;
412 nf->mntdir = f->mntdir;
413 if(f->mntdir)
414 f->mntdir->ref++;
415 nf->dir = f->dir;
416 nf->qid = f->qid;
417 nf->w = f->w;
418 nf->nrpart = 0; /* not open, so must be zero */
419 if(nf->w)
420 incref(nf->w);
421 f = nf; /* walk f */
422 }
423
424 t.nwqid = 0;
425 err = nil;
426 dir = nil;
427 id = WIN(f->qid);
428 q = f->qid;
429
430 if(x->nwname > 0){
431 for(i=0; i<x->nwname; i++){
432 if((q.type & QTDIR) == 0){
433 err = Enotdir;
434 break;
435 }
436
437 if(strcmp(x->wname[i], "..") == 0){
438 type = QTDIR;
439 path = Qdir;
440 id = 0;
441 if(w){
442 winclose(w);
443 w = nil;
444 }
445 Accept:
446 if(i == MAXWELEM){
447 err = "name too long";
448 break;
449 }
450 q.type = type;
451 q.vers = 0;
452 q.path = QID(id, path);
453 t.wqid[t.nwqid++] = q;
454 continue;
455 }
456
457 /* is it a numeric name? */
458 for(j=0; (c=x->wname[i][j]); j++)
459 if(c<'0' || '9'<c)
460 goto Regular;
461 /* yes: it's a directory */
462 if(w) /* name has form 27/23; get out before losing w */
463 break;
464 id = atoi(x->wname[i]);
465 qlock(&row);
466 w = lookid(id, FALSE);
467 if(w == nil){
468 qunlock(&row);
469 break;
470 }
471 incref(w); /* we'll drop reference at end if there's an error */
472 path = Qdir;
473 type = QTDIR;
474 qunlock(&row);
475 dir = dirtabw;
476 goto Accept;
477
478 Regular:
479 // if(FILE(f->qid) == Qacme) /* empty directory */
480 // break;
481 if(strcmp(x->wname[i], "new") == 0){
482 if(w)
483 error("w set in walk to new");
484 sendp(cnewwindow, nil); /* signal newwindowthread */
485 w = recvp(cnewwindow); /* receive new window */
486 incref(w);
487 type = QTDIR;
488 path = QID(w->id, Qdir);
489 id = w->id;
490 dir = dirtabw;
491 goto Accept;
492 }
493
494 if(id == 0)
495 d = dirtab;
496 else
497 d = dirtabw;
498 d++; /* skip '.' */
499 for(; d->name; d++)
500 if(strcmp(x->wname[i], d->name) == 0){
501 path = d->qid;
502 type = d->type;
503 dir = d;
504 goto Accept;
505 }
506
507 break; /* file not found */
508 }
509
510 if(i==0 && err == nil)
511 err = Eexist;
512 }
513
514 if(err!=nil || t.nwqid<x->nwname){
515 if(nf){
516 nf->busy = FALSE;
517 fsysdelid(nf->mntdir);
518 }
519 }else if(t.nwqid == x->nwname){
520 if(w){
521 f->w = w;
522 w = nil; /* don't drop the reference */
523 }
524 if(dir)
525 f->dir = dir;
526 f->qid = q;
527 }
528
529 if(w != nil)
530 winclose(w);
531
532 return respond(x, &t, err);
533 }
534
535 static
536 Xfid*
fsysopen(Xfid * x,Fid * f)537 fsysopen(Xfid *x, Fid *f)
538 {
539 Fcall t;
540 int m;
541
542 /* can't truncate anything, so just disregard */
543 x->mode &= ~(OTRUNC|OCEXEC);
544 /* can't execute or remove anything */
545 if(x->mode==OEXEC || (x->mode&ORCLOSE))
546 goto Deny;
547 switch(x->mode){
548 default:
549 goto Deny;
550 case OREAD:
551 m = 0400;
552 break;
553 case OWRITE:
554 m = 0200;
555 break;
556 case ORDWR:
557 m = 0600;
558 break;
559 }
560 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
561 goto Deny;
562
563 sendp(x->c, xfidopen);
564 return nil;
565
566 Deny:
567 return respond(x, &t, Eperm);
568 }
569
570 static
571 Xfid*
fsyscreate(Xfid * x,Fid *)572 fsyscreate(Xfid *x, Fid*)
573 {
574 Fcall t;
575
576 return respond(x, &t, Eperm);
577 }
578
579 static
580 int
idcmp(void * a,void * b)581 idcmp(void *a, void *b)
582 {
583 return *(int*)a - *(int*)b;
584 }
585
586 static
587 Xfid*
fsysread(Xfid * x,Fid * f)588 fsysread(Xfid *x, Fid *f)
589 {
590 Fcall t;
591 uchar *b;
592 int i, id, n, o, e, j, k, *ids, nids;
593 Dirtab *d, dt;
594 Column *c;
595 uint clock, len;
596 char buf[16];
597
598 if(f->qid.type & QTDIR){
599 if(FILE(f->qid) == Qacme){ /* empty dir */
600 t.data = nil;
601 t.count = 0;
602 respond(x, &t, nil);
603 return x;
604 }
605 o = x->offset;
606 e = x->offset+x->count;
607 clock = getclock();
608 b = emalloc(messagesize);
609 id = WIN(f->qid);
610 n = 0;
611 if(id > 0)
612 d = dirtabw;
613 else
614 d = dirtab;
615 d++; /* first entry is '.' */
616 for(i=0; d->name!=nil && i<e; i+=len){
617 len = dostat(WIN(x->f->qid), d, b+n, x->count-n, clock);
618 if(len <= BIT16SZ)
619 break;
620 if(i >= o)
621 n += len;
622 d++;
623 }
624 if(id == 0){
625 qlock(&row);
626 nids = 0;
627 ids = nil;
628 for(j=0; j<row.ncol; j++){
629 c = row.col[j];
630 for(k=0; k<c->nw; k++){
631 ids = erealloc(ids, (nids+1)*sizeof(int));
632 ids[nids++] = c->w[k]->id;
633 }
634 }
635 qunlock(&row);
636 qsort(ids, nids, sizeof ids[0], idcmp);
637 j = 0;
638 dt.name = buf;
639 for(; j<nids && i<e; i+=len){
640 k = ids[j];
641 sprint(dt.name, "%d", k);
642 dt.qid = QID(k, Qdir);
643 dt.type = QTDIR;
644 dt.perm = DMDIR|0700;
645 len = dostat(k, &dt, b+n, x->count-n, clock);
646 if(len == 0)
647 break;
648 if(i >= o)
649 n += len;
650 j++;
651 }
652 free(ids);
653 }
654 t.data = (char*)b;
655 t.count = n;
656 respond(x, &t, nil);
657 free(b);
658 return x;
659 }
660 sendp(x->c, xfidread);
661 return nil;
662 }
663
664 static
665 Xfid*
fsyswrite(Xfid * x,Fid *)666 fsyswrite(Xfid *x, Fid*)
667 {
668 sendp(x->c, xfidwrite);
669 return nil;
670 }
671
672 static
673 Xfid*
fsysclunk(Xfid * x,Fid * f)674 fsysclunk(Xfid *x, Fid *f)
675 {
676 fsysdelid(f->mntdir);
677 sendp(x->c, xfidclose);
678 return nil;
679 }
680
681 static
682 Xfid*
fsysremove(Xfid * x,Fid *)683 fsysremove(Xfid *x, Fid*)
684 {
685 Fcall t;
686
687 return respond(x, &t, Eperm);
688 }
689
690 static
691 Xfid*
fsysstat(Xfid * x,Fid * f)692 fsysstat(Xfid *x, Fid *f)
693 {
694 Fcall t;
695
696 t.stat = emalloc(messagesize-IOHDRSZ);
697 t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
698 x = respond(x, &t, nil);
699 free(t.stat);
700 return x;
701 }
702
703 static
704 Xfid*
fsyswstat(Xfid * x,Fid *)705 fsyswstat(Xfid *x, Fid*)
706 {
707 Fcall t;
708
709 return respond(x, &t, Eperm);
710 }
711
712 Fid*
newfid(int fid)713 newfid(int fid)
714 {
715 Fid *f, *ff, **fh;
716
717 ff = nil;
718 fh = &fids[fid&(Nhash-1)];
719 for(f=*fh; f; f=f->next)
720 if(f->fid == fid)
721 return f;
722 else if(ff==nil && f->busy==FALSE)
723 ff = f;
724 if(ff){
725 ff->fid = fid;
726 return ff;
727 }
728 f = emalloc(sizeof *f);
729 f->fid = fid;
730 f->next = *fh;
731 *fh = f;
732 return f;
733 }
734
735 uint
getclock()736 getclock()
737 {
738 char buf[32];
739
740 buf[0] = '\0';
741 pread(clockfd, buf, sizeof buf, 0);
742 return atoi(buf);
743 }
744
745 int
dostat(int id,Dirtab * dir,uchar * buf,int nbuf,uint clock)746 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
747 {
748 Dir d;
749
750 d.qid.path = QID(id, dir->qid);
751 d.qid.vers = 0;
752 d.qid.type = dir->type;
753 d.mode = dir->perm;
754 d.length = 0; /* would be nice to do better */
755 d.name = dir->name;
756 d.uid = user;
757 d.gid = user;
758 d.muid = user;
759 d.atime = clock;
760 d.mtime = clock;
761 return convD2M(&d, buf, nbuf);
762 }
763