Blob


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