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 threadsetname("fsysproc");
144 USED(v);
145 x = nil;
146 for(;;){
147 buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */
148 n = read9pmsg(sfd, buf, messagesize);
149 if(n <= 0){
150 if(closing)
151 break;
152 error("i/o error on server channel");
154 if(x == nil){
155 sendp(cxfidalloc, nil);
156 x = recvp(cxfidalloc);
158 x->buf = buf;
159 if(convM2S(buf, n, &x->fcall) != n)
160 error("convert error in convM2S");
161 if(DEBUG)
162 fprint(2, "%F\n", &x->fcall);
163 if(fcall[x->fcall.type] == nil)
164 x = respond(x, &t, "bad fcall type");
165 else{
166 switch(x->fcall.type){
167 case Tversion:
168 case Tauth:
169 case Tflush:
170 f = nil;
171 break;
172 case Tattach:
173 f = newfid(x->fcall.fid);
174 break;
175 default:
176 f = newfid(x->fcall.fid);
177 if(!f->busy){
178 x->f = f;
179 x = respond(x, &t, "fid not in use");
180 continue;
182 break;
184 x->f = f;
185 x = (*fcall[x->fcall.type])(x, f);
190 Mntdir*
191 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
193 Mntdir *m;
194 int id;
196 qlock(&mnt.lk);
197 id = ++mnt.id;
198 m = emalloc(sizeof *m);
199 m->id = id;
200 m->dir = dir;
201 m->ref = 1; /* one for Command, one will be incremented in attach */
202 m->ndir = ndir;
203 m->next = mnt.md;
204 m->incl = incl;
205 m->nincl = nincl;
206 mnt.md = m;
207 qunlock(&mnt.lk);
208 return m;
211 void
212 fsysincid(Mntdir *m)
214 qlock(&mnt.lk);
215 m->ref++;
216 qunlock(&mnt.lk);
219 void
220 fsysdelid(Mntdir *idm)
222 Mntdir *m, *prev;
223 int i;
224 char buf[64];
226 if(idm == nil)
227 return;
228 qlock(&mnt.lk);
229 if(--idm->ref > 0){
230 qunlock(&mnt.lk);
231 return;
233 prev = nil;
234 for(m=mnt.md; m; m=m->next){
235 if(m == idm){
236 if(prev)
237 prev->next = m->next;
238 else
239 mnt.md = m->next;
240 for(i=0; i<m->nincl; i++)
241 free(m->incl[i]);
242 free(m->incl);
243 free(m->dir);
244 free(m);
245 qunlock(&mnt.lk);
246 return;
248 prev = m;
250 qunlock(&mnt.lk);
251 sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
252 sendp(cerr, estrdup(buf));
255 /*
256 * Called only in exec.c:/^run(), from a different FD group
257 */
258 Mntdir*
259 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
261 return fsysaddid(dir, ndir, incl, nincl);
264 void
265 fsysclose(void)
267 closing = 1;
268 /*
269 * apparently this is not kosher on openbsd.
270 * perhaps because fsysproc is reading from sfd right now,
271 * the close hangs indefinitely.
272 close(sfd);
273 */
276 Xfid*
277 respond(Xfid *x, Fcall *t, char *err)
279 int n;
281 if(err){
282 t->type = Rerror;
283 t->ename = err;
284 }else
285 t->type = x->fcall.type+1;
286 t->fid = x->fcall.fid;
287 t->tag = x->fcall.tag;
288 if(x->buf == nil)
289 x->buf = emalloc(messagesize);
290 n = convS2M(t, x->buf, messagesize);
291 if(n <= 0)
293 fprint(2, "convert error (n=%d, msgsize %d): have %F\n", n, messagesize, &x->fcall);
294 fprint(2, "\tresponse: %F\n", t);
295 error("convert error in convS2M");
297 if(write(sfd, x->buf, n) != n)
298 error("write error in respond");
299 free(x->buf);
300 x->buf = nil;
301 if(DEBUG)
302 fprint(2, "r: %F\n", t);
303 return x;
306 static
307 Xfid*
308 fsysversion(Xfid *x, Fid *f)
310 Fcall t;
312 USED(f);
313 if(x->fcall.msize < 256)
314 return respond(x, &t, "version: message size too small");
315 messagesize = x->fcall.msize;
316 t.msize = messagesize;
317 if(strncmp(x->fcall.version, "9P2000", 6) != 0)
318 return respond(x, &t, "unrecognized 9P version");
319 t.version = "9P2000";
320 return respond(x, &t, nil);
323 static
324 Xfid*
325 fsysauth(Xfid *x, Fid *f)
327 Fcall t;
329 USED(f);
330 return respond(x, &t, "acme: authentication not required");
333 static
334 Xfid*
335 fsysflush(Xfid *x, Fid *f)
337 USED(f);
338 sendp(x->c, (void*)xfidflush);
339 return nil;
342 static
343 Xfid*
344 fsysattach(Xfid *x, Fid *f)
346 Fcall t;
347 int id;
348 Mntdir *m;
349 char buf[128];
351 if(strcmp(x->fcall.uname, user) != 0)
352 return respond(x, &t, Eperm);
353 f->busy = TRUE;
354 f->open = FALSE;
355 f->qid.path = Qdir;
356 f->qid.type = QTDIR;
357 f->qid.vers = 0;
358 f->dir = dirtab;
359 f->nrpart = 0;
360 f->w = nil;
361 t.qid = f->qid;
362 f->mntdir = nil;
363 id = atoi(x->fcall.aname);
364 qlock(&mnt.lk);
365 for(m=mnt.md; m; m=m->next)
366 if(m->id == id){
367 f->mntdir = m;
368 m->ref++;
369 break;
371 if(m == nil && x->fcall.aname[0]){
372 snprint(buf, sizeof buf, "unknown id '%s' in attach", x->fcall.aname);
373 sendp(cerr, estrdup(buf));
375 qunlock(&mnt.lk);
376 return respond(x, &t, nil);
379 static
380 Xfid*
381 fsyswalk(Xfid *x, Fid *f)
383 Fcall t;
384 int c, i, j, id;
385 Qid q;
386 uchar type;
387 ulong path;
388 Fid *nf;
389 Dirtab *d, *dir;
390 Window *w;
391 char *err;
393 nf = nil;
394 w = nil;
395 if(f->open)
396 return respond(x, &t, "walk of open file");
397 if(x->fcall.fid != x->fcall.newfid){
398 nf = newfid(x->fcall.newfid);
399 if(nf->busy)
400 return respond(x, &t, "newfid already in use");
401 nf->busy = TRUE;
402 nf->open = FALSE;
403 nf->mntdir = f->mntdir;
404 if(f->mntdir)
405 f->mntdir->ref++;
406 nf->dir = f->dir;
407 nf->qid = f->qid;
408 nf->w = f->w;
409 nf->nrpart = 0; /* not open, so must be zero */
410 if(nf->w)
411 incref(&nf->w->ref);
412 f = nf; /* walk f */
415 t.nwqid = 0;
416 err = nil;
417 dir = nil;
418 id = WIN(f->qid);
419 q = f->qid;
421 if(x->fcall.nwname > 0){
422 for(i=0; i<x->fcall.nwname; i++){
423 if((q.type & QTDIR) == 0){
424 err = Enotdir;
425 break;
428 if(strcmp(x->fcall.wname[i], "..") == 0){
429 type = QTDIR;
430 path = Qdir;
431 id = 0;
432 if(w){
433 winclose(w);
434 w = nil;
436 Accept:
437 if(i == MAXWELEM){
438 err = "name too long";
439 break;
441 q.type = type;
442 q.vers = 0;
443 q.path = QID(id, path);
444 t.wqid[t.nwqid++] = q;
445 continue;
448 /* is it a numeric name? */
449 for(j=0; (c=x->fcall.wname[i][j]); j++)
450 if(c<'0' || '9'<c)
451 goto Regular;
452 /* yes: it's a directory */
453 if(w) /* name has form 27/23; get out before losing w */
454 break;
455 id = atoi(x->fcall.wname[i]);
456 qlock(&row.lk);
457 w = lookid(id, FALSE);
458 if(w == nil){
459 qunlock(&row.lk);
460 break;
462 incref(&w->ref); /* we'll drop reference at end if there's an error */
463 path = Qdir;
464 type = QTDIR;
465 qunlock(&row.lk);
466 dir = dirtabw;
467 goto Accept;
469 Regular:
470 /*
471 if(FILE(f->qid) == Qacme) // empty directory
472 break;
473 */
474 if(strcmp(x->fcall.wname[i], "new") == 0){
475 if(w)
476 error("w set in walk to new");
477 sendp(cnewwindow, nil); /* signal newwindowthread */
478 w = recvp(cnewwindow); /* receive new window */
479 incref(&w->ref);
480 type = QTDIR;
481 path = QID(w->id, Qdir);
482 id = w->id;
483 dir = dirtabw;
484 goto Accept;
487 if(id == 0)
488 d = dirtab;
489 else
490 d = dirtabw;
491 d++; /* skip '.' */
492 for(; d->name; d++)
493 if(strcmp(x->fcall.wname[i], d->name) == 0){
494 path = d->qid;
495 type = d->type;
496 dir = d;
497 goto Accept;
500 break; /* file not found */
503 if(i==0 && err == nil)
504 err = Eexist;
507 if(err!=nil || t.nwqid<x->fcall.nwname){
508 if(nf){
509 nf->busy = FALSE;
510 fsysdelid(nf->mntdir);
512 }else if(t.nwqid == x->fcall.nwname){
513 if(w){
514 f->w = w;
515 w = nil; /* don't drop the reference */
517 if(dir)
518 f->dir = dir;
519 f->qid = q;
522 if(w != nil)
523 winclose(w);
525 return respond(x, &t, err);
528 static
529 Xfid*
530 fsysopen(Xfid *x, Fid *f)
532 Fcall t;
533 int m;
535 /* can't truncate anything, so just disregard */
536 x->fcall.mode &= ~(OTRUNC|OCEXEC);
537 /* can't execute or remove anything */
538 if(x->fcall.mode==OEXEC || (x->fcall.mode&ORCLOSE))
539 goto Deny;
540 switch(x->fcall.mode){
541 default:
542 goto Deny;
543 case OREAD:
544 m = 0400;
545 break;
546 case OWRITE:
547 m = 0200;
548 break;
549 case ORDWR:
550 m = 0600;
551 break;
553 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
554 goto Deny;
556 sendp(x->c, (void*)xfidopen);
557 return nil;
559 Deny:
560 return respond(x, &t, Eperm);
563 static
564 Xfid*
565 fsyscreate(Xfid *x, Fid *f)
567 Fcall t;
569 USED(f);
570 return respond(x, &t, Eperm);
573 static
574 int
575 idcmp(const void *a, const void *b)
577 return *(int*)a - *(int*)b;
580 static
581 Xfid*
582 fsysread(Xfid *x, Fid *f)
584 Fcall t;
585 uchar *b;
586 int i, id, n, o, e, j, k, *ids, nids;
587 Dirtab *d, dt;
588 Column *c;
589 uint clock, len;
590 char buf[16];
592 if(f->qid.type & QTDIR){
593 if(FILE(f->qid) == Qacme){ /* empty dir */
594 t.data = nil;
595 t.count = 0;
596 respond(x, &t, nil);
597 return x;
599 o = x->fcall.offset;
600 e = x->fcall.offset+x->fcall.count;
601 clock = getclock();
602 b = emalloc(messagesize);
603 id = WIN(f->qid);
604 n = 0;
605 if(id > 0)
606 d = dirtabw;
607 else
608 d = dirtab;
609 d++; /* first entry is '.' */
610 for(i=0; d->name!=nil && i<e; i+=len){
611 len = dostat(WIN(x->f->qid), d, b+n, x->fcall.count-n, clock);
612 if(len <= BIT16SZ)
613 break;
614 if(i >= o)
615 n += len;
616 d++;
618 if(id == 0){
619 qlock(&row.lk);
620 nids = 0;
621 ids = nil;
622 for(j=0; j<row.ncol; j++){
623 c = row.col[j];
624 for(k=0; k<c->nw; k++){
625 ids = realloc(ids, (nids+1)*sizeof(int));
626 ids[nids++] = c->w[k]->id;
629 qunlock(&row.lk);
630 qsort(ids, nids, sizeof ids[0], idcmp);
631 j = 0;
632 dt.name = buf;
633 for(; j<nids && i<e; i+=len){
634 k = ids[j];
635 sprint(dt.name, "%d", k);
636 dt.qid = QID(k, Qdir);
637 dt.type = QTDIR;
638 dt.perm = DMDIR|0700;
639 len = dostat(k, &dt, b+n, x->fcall.count-n, clock);
640 if(len == 0)
641 break;
642 if(i >= o)
643 n += len;
644 j++;
646 free(ids);
648 t.data = (char*)b;
649 t.count = n;
650 respond(x, &t, nil);
651 free(b);
652 return x;
654 sendp(x->c, (void*)xfidread);
655 return nil;
658 static
659 Xfid*
660 fsyswrite(Xfid *x, Fid *f)
662 USED(f);
663 sendp(x->c, (void*)xfidwrite);
664 return nil;
667 static
668 Xfid*
669 fsysclunk(Xfid *x, Fid *f)
671 fsysdelid(f->mntdir);
672 sendp(x->c, (void*)xfidclose);
673 return nil;
676 static
677 Xfid*
678 fsysremove(Xfid *x, Fid *f)
680 Fcall t;
682 USED(f);
683 return respond(x, &t, Eperm);
686 static
687 Xfid*
688 fsysstat(Xfid *x, Fid *f)
690 Fcall t;
692 t.stat = emalloc(messagesize-IOHDRSZ);
693 t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
694 x = respond(x, &t, nil);
695 free(t.stat);
696 return x;
699 static
700 Xfid*
701 fsyswstat(Xfid *x, Fid *f)
703 Fcall t;
705 USED(f);
706 return respond(x, &t, Eperm);
709 Fid*
710 newfid(int fid)
712 Fid *f, *ff, **fh;
714 ff = nil;
715 fh = &fids[fid&(Nhash-1)];
716 for(f=*fh; f; f=f->next)
717 if(f->fid == fid)
718 return f;
719 else if(ff==nil && f->busy==FALSE)
720 ff = f;
721 if(ff){
722 ff->fid = fid;
723 return ff;
725 f = emalloc(sizeof *f);
726 f->fid = fid;
727 f->next = *fh;
728 *fh = f;
729 return f;
732 uint
733 getclock(void)
735 /*
736 char buf[32];
738 buf[0] = '\0';
739 pread(clockfd, buf, sizeof buf, 0);
740 return atoi(buf);
741 */
742 return time(0);
745 int
746 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
748 Dir d;
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);