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 { "event", QTFILE, QWevent, 0600 },
87 { "rdsel", QTFILE, QWrdsel, 0400 },
88 { "wrsel", QTFILE, QWwrsel, 0200 },
89 { "tag", QTAPPEND, QWtag, 0600|DMAPPEND },
90 { "xdata", QTFILE, QWxdata, 0600 },
91 { nil, }
92 };
94 typedef struct Mnt Mnt;
95 struct Mnt
96 {
97 QLock lk;
98 int id;
99 Mntdir *md;
100 };
102 Mnt mnt;
104 Xfid* respond(Xfid*, Fcall*, char*);
105 int dostat(int, Dirtab*, uchar*, int, uint);
106 uint getclock(void);
108 char *user = "Wile E. Coyote";
109 static int closing = 0;
110 int messagesize = Maxblock+IOHDRSZ; /* good start */
112 void fsysproc(void *);
114 void
115 fsysinit(void)
117 int p[2];
118 char *u;
120 initfcall();
121 if(pipe(p) < 0)
122 error("can't create pipe");
123 if(post9pservice(p[0], "acme") < 0)
124 error("can't post service");
125 sfd = p[1];
126 fmtinstall('F', fcallfmt);
127 if((u = getuser()) != nil)
128 user = estrdup(u);
129 threadcreate(fsysproc, nil, STACK);
132 void
133 fsysproc(void *v)
135 int n;
136 Xfid *x;
137 Fid *f;
138 Fcall t;
139 uchar *buf;
141 USED(v);
142 x = nil;
143 for(;;){
144 buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */
145 n = threadread9pmsg(sfd, buf, messagesize);
146 if(n <= 0){
147 if(closing)
148 break;
149 error("i/o error on server channel");
151 if(x == nil){
152 sendp(cxfidalloc, nil);
153 x = recvp(cxfidalloc);
155 x->buf = buf;
156 if(convM2S(buf, n, &x->fcall) != n)
157 error("convert error in convM2S");
158 if(DEBUG)
159 fprint(2, "%F\n", &x->fcall);
160 if(fcall[x->fcall.type] == 0)
161 x = respond(x, &t, "bad fcall type");
162 else{
163 if(x->fcall.type==Tversion || x->fcall.type==Tauth)
164 f = nil;
165 else
166 f = newfid(x->fcall.fid);
167 x->f = f;
168 x = (*fcall[x->fcall.type])(x, f);
173 Mntdir*
174 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
176 Mntdir *m;
177 int id;
179 qlock(&mnt.lk);
180 id = ++mnt.id;
181 m = emalloc(sizeof *m);
182 m->id = id;
183 m->dir = dir;
184 m->ref = 1; /* one for Command, one will be incremented in attach */
185 m->ndir = ndir;
186 m->next = mnt.md;
187 m->incl = incl;
188 m->nincl = nincl;
189 mnt.md = m;
190 qunlock(&mnt.lk);
191 return m;
194 void
195 fsysincid(Mntdir *m)
197 qlock(&mnt.lk);
198 m->ref++;
199 qunlock(&mnt.lk);
202 void
203 fsysdelid(Mntdir *idm)
205 Mntdir *m, *prev;
206 int i;
207 char buf[64];
209 if(idm == nil)
210 return;
211 qlock(&mnt.lk);
212 if(--idm->ref > 0){
213 qunlock(&mnt.lk);
214 return;
216 prev = nil;
217 for(m=mnt.md; m; m=m->next){
218 if(m == idm){
219 if(prev)
220 prev->next = m->next;
221 else
222 mnt.md = m->next;
223 for(i=0; i<m->nincl; i++)
224 free(m->incl[i]);
225 free(m->incl);
226 free(m->dir);
227 free(m);
228 qunlock(&mnt.lk);
229 return;
231 prev = m;
233 qunlock(&mnt.lk);
234 sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
235 sendp(cerr, estrdup(buf));
238 /*
239 * Called only in exec.c:/^run(), from a different FD group
240 */
241 Mntdir*
242 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
244 return fsysaddid(dir, ndir, incl, nincl);
247 void
248 fsysclose(void)
250 closing = 1;
251 close(sfd);
254 Xfid*
255 respond(Xfid *x, Fcall *t, char *err)
257 int n;
259 if(err){
260 t->type = Rerror;
261 t->ename = err;
262 }else
263 t->type = x->fcall.type+1;
264 t->fid = x->fcall.fid;
265 t->tag = x->fcall.tag;
266 if(x->buf == nil)
267 x->buf = emalloc(messagesize);
268 n = convS2M(t, x->buf, messagesize);
269 if(n <= 0)
271 fprint(2, "convert error (n=%d, msgsize %d): have %F\n", n, messagesize, &x->fcall);
272 fprint(2, "\tresponse: %F\n", t);
273 error("convert error in convS2M");
275 if(write(sfd, x->buf, n) != n)
276 error("write error in respond");
277 free(x->buf);
278 x->buf = nil;
279 if(DEBUG)
280 fprint(2, "r: %F\n", t);
281 return x;
284 static
285 Xfid*
286 fsysversion(Xfid *x, Fid *f)
288 Fcall t;
290 USED(f);
291 if(x->fcall.msize < 256)
292 return respond(x, &t, "version: message size too small");
293 messagesize = x->fcall.msize;
294 t.msize = messagesize;
295 if(strncmp(x->fcall.version, "9P2000", 6) != 0)
296 return respond(x, &t, "unrecognized 9P version");
297 t.version = "9P2000";
298 return respond(x, &t, nil);
301 static
302 Xfid*
303 fsysauth(Xfid *x, Fid *f)
305 USED(f);
306 return respond(x, nil, "acme: authentication not required");
309 static
310 Xfid*
311 fsysflush(Xfid *x, Fid *f)
313 USED(f);
314 sendp(x->c, (void*)xfidflush);
315 return nil;
318 static
319 Xfid*
320 fsysattach(Xfid *x, Fid *f)
322 Fcall t;
323 int id;
324 Mntdir *m;
325 char buf[128];
327 if(strcmp(x->fcall.uname, user) != 0)
328 return respond(x, &t, Eperm);
329 f->busy = TRUE;
330 f->open = FALSE;
331 f->qid.path = Qdir;
332 f->qid.type = QTDIR;
333 f->qid.vers = 0;
334 f->dir = dirtab;
335 f->nrpart = 0;
336 f->w = nil;
337 t.qid = f->qid;
338 f->mntdir = nil;
339 id = atoi(x->fcall.aname);
340 qlock(&mnt.lk);
341 for(m=mnt.md; m; m=m->next)
342 if(m->id == id){
343 f->mntdir = m;
344 m->ref++;
345 break;
347 if(m == nil && x->fcall.aname[0]){
348 snprint(buf, sizeof buf, "unknown id '%s' in attach", x->fcall.aname);
349 sendp(cerr, estrdup(buf));
351 qunlock(&mnt.lk);
352 return respond(x, &t, nil);
355 static
356 Xfid*
357 fsyswalk(Xfid *x, Fid *f)
359 Fcall t;
360 int c, i, j, id;
361 Qid q;
362 uchar type;
363 ulong path;
364 Fid *nf;
365 Dirtab *d, *dir;
366 Window *w;
367 char *err;
369 nf = nil;
370 w = nil;
371 if(f->open)
372 return respond(x, &t, "walk of open file");
373 if(x->fcall.fid != x->fcall.newfid){
374 nf = newfid(x->fcall.newfid);
375 if(nf->busy)
376 return respond(x, &t, "newfid already in use");
377 nf->busy = TRUE;
378 nf->open = FALSE;
379 nf->mntdir = f->mntdir;
380 if(f->mntdir)
381 f->mntdir->ref++;
382 nf->dir = f->dir;
383 nf->qid = f->qid;
384 nf->w = f->w;
385 nf->nrpart = 0; /* not open, so must be zero */
386 if(nf->w)
387 incref(&nf->w->ref);
388 f = nf; /* walk f */
391 t.nwqid = 0;
392 err = nil;
393 dir = nil;
394 id = WIN(f->qid);
395 q = f->qid;
397 if(x->fcall.nwname > 0){
398 for(i=0; i<x->fcall.nwname; i++){
399 if((q.type & QTDIR) == 0){
400 err = Enotdir;
401 break;
404 if(strcmp(x->fcall.wname[i], "..") == 0){
405 type = QTDIR;
406 path = Qdir;
407 id = 0;
408 if(w){
409 winclose(w);
410 w = nil;
412 Accept:
413 if(i == MAXWELEM){
414 err = "name too long";
415 break;
417 q.type = type;
418 q.vers = 0;
419 q.path = QID(id, path);
420 t.wqid[t.nwqid++] = q;
421 continue;
424 /* is it a numeric name? */
425 for(j=0; (c=x->fcall.wname[i][j]); j++)
426 if(c<'0' || '9'<c)
427 goto Regular;
428 /* yes: it's a directory */
429 if(w) /* name has form 27/23; get out before losing w */
430 break;
431 id = atoi(x->fcall.wname[i]);
432 qlock(&row.lk);
433 w = lookid(id, FALSE);
434 if(w == nil){
435 qunlock(&row.lk);
436 break;
438 incref(&w->ref); /* we'll drop reference at end if there's an error */
439 path = Qdir;
440 type = QTDIR;
441 qunlock(&row.lk);
442 dir = dirtabw;
443 goto Accept;
445 Regular:
446 // if(FILE(f->qid) == Qacme) /* empty directory */
447 // break;
448 if(strcmp(x->fcall.wname[i], "new") == 0){
449 if(w)
450 error("w set in walk to new");
451 sendp(cnewwindow, nil); /* signal newwindowthread */
452 w = recvp(cnewwindow); /* receive new window */
453 incref(&w->ref);
454 type = QTDIR;
455 path = QID(w->id, Qdir);
456 id = w->id;
457 dir = dirtabw;
458 goto Accept;
461 if(id == 0)
462 d = dirtab;
463 else
464 d = dirtabw;
465 d++; /* skip '.' */
466 for(; d->name; d++)
467 if(strcmp(x->fcall.wname[i], d->name) == 0){
468 path = d->qid;
469 type = d->type;
470 dir = d;
471 goto Accept;
474 break; /* file not found */
477 if(i==0 && err == nil)
478 err = Eexist;
481 if(err!=nil || t.nwqid<x->fcall.nwname){
482 if(nf){
483 nf->busy = FALSE;
484 fsysdelid(nf->mntdir);
486 }else if(t.nwqid == x->fcall.nwname){
487 if(w){
488 f->w = w;
489 w = nil; /* don't drop the reference */
491 if(dir)
492 f->dir = dir;
493 f->qid = q;
496 if(w != nil)
497 winclose(w);
499 return respond(x, &t, err);
502 static
503 Xfid*
504 fsysopen(Xfid *x, Fid *f)
506 Fcall t;
507 int m;
509 /* can't truncate anything, so just disregard */
510 x->fcall.mode &= ~(OTRUNC|OCEXEC);
511 /* can't execute or remove anything */
512 if(x->fcall.mode==OEXEC || (x->fcall.mode&ORCLOSE))
513 goto Deny;
514 switch(x->fcall.mode){
515 default:
516 goto Deny;
517 case OREAD:
518 m = 0400;
519 break;
520 case OWRITE:
521 m = 0200;
522 break;
523 case ORDWR:
524 m = 0600;
525 break;
527 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
528 goto Deny;
530 sendp(x->c, (void*)xfidopen);
531 return nil;
533 Deny:
534 return respond(x, &t, Eperm);
537 static
538 Xfid*
539 fsyscreate(Xfid *x, Fid *f)
541 Fcall t;
543 USED(f);
544 return respond(x, &t, Eperm);
547 static
548 int
549 idcmp(const void *a, const void *b)
551 return *(int*)a - *(int*)b;
554 static
555 Xfid*
556 fsysread(Xfid *x, Fid *f)
558 Fcall t;
559 uchar *b;
560 int i, id, n, o, e, j, k, *ids, nids;
561 Dirtab *d, dt;
562 Column *c;
563 uint clock, len;
564 char buf[16];
566 if(f->qid.type & QTDIR){
567 if(FILE(f->qid) == Qacme){ /* empty dir */
568 t.data = nil;
569 t.count = 0;
570 respond(x, &t, nil);
571 return x;
573 o = x->fcall.offset;
574 e = x->fcall.offset+x->fcall.count;
575 clock = getclock();
576 b = emalloc(messagesize);
577 id = WIN(f->qid);
578 n = 0;
579 if(id > 0)
580 d = dirtabw;
581 else
582 d = dirtab;
583 d++; /* first entry is '.' */
584 for(i=0; d->name!=nil && i<e; i+=len){
585 len = dostat(WIN(x->f->qid), d, b+n, x->fcall.count-n, clock);
586 if(len <= BIT16SZ)
587 break;
588 if(i >= o)
589 n += len;
590 d++;
592 if(id == 0){
593 qlock(&row.lk);
594 nids = 0;
595 ids = nil;
596 for(j=0; j<row.ncol; j++){
597 c = row.col[j];
598 for(k=0; k<c->nw; k++){
599 ids = realloc(ids, (nids+1)*sizeof(int));
600 ids[nids++] = c->w[k]->id;
603 qunlock(&row.lk);
604 qsort(ids, nids, sizeof ids[0], idcmp);
605 j = 0;
606 dt.name = buf;
607 for(; j<nids && i<e; i+=len){
608 k = ids[j];
609 sprint(dt.name, "%d", k);
610 dt.qid = QID(k, Qdir);
611 dt.type = QTDIR;
612 dt.perm = DMDIR|0700;
613 len = dostat(k, &dt, b+n, x->fcall.count-n, clock);
614 if(len == 0)
615 break;
616 if(i >= o)
617 n += len;
618 j++;
620 free(ids);
622 t.data = (char*)b;
623 t.count = n;
624 respond(x, &t, nil);
625 free(b);
626 return x;
628 sendp(x->c, (void*)xfidread);
629 return nil;
632 static
633 Xfid*
634 fsyswrite(Xfid *x, Fid *f)
636 USED(f);
637 sendp(x->c, (void*)xfidwrite);
638 return nil;
641 static
642 Xfid*
643 fsysclunk(Xfid *x, Fid *f)
645 fsysdelid(f->mntdir);
646 sendp(x->c, (void*)xfidclose);
647 return nil;
650 static
651 Xfid*
652 fsysremove(Xfid *x, Fid *f)
654 Fcall t;
656 USED(f);
657 return respond(x, &t, Eperm);
660 static
661 Xfid*
662 fsysstat(Xfid *x, Fid *f)
664 Fcall t;
666 t.stat = emalloc(messagesize-IOHDRSZ);
667 t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
668 x = respond(x, &t, nil);
669 free(t.stat);
670 return x;
673 static
674 Xfid*
675 fsyswstat(Xfid *x, Fid *f)
677 Fcall t;
679 USED(f);
680 return respond(x, &t, Eperm);
683 Fid*
684 newfid(int fid)
686 Fid *f, *ff, **fh;
688 ff = nil;
689 fh = &fids[fid&(Nhash-1)];
690 for(f=*fh; f; f=f->next)
691 if(f->fid == fid)
692 return f;
693 else if(ff==nil && f->busy==FALSE)
694 ff = f;
695 if(ff){
696 ff->fid = fid;
697 return ff;
699 f = emalloc(sizeof *f);
700 f->fid = fid;
701 f->next = *fh;
702 *fh = f;
703 return f;
706 uint
707 getclock(void)
709 /*
710 char buf[32];
712 buf[0] = '\0';
713 pread(clockfd, buf, sizeof buf, 0);
714 return atoi(buf);
715 */
716 return time(0);
719 int
720 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
722 Dir d;
724 d.qid.path = QID(id, dir->qid);
725 d.qid.vers = 0;
726 d.qid.type = dir->type;
727 d.mode = dir->perm;
728 d.length = 0; /* would be nice to do better */
729 d.name = dir->name;
730 d.uid = user;
731 d.gid = user;
732 d.muid = user;
733 d.atime = clock;
734 d.mtime = clock;
735 return convD2M(&d, buf, nbuf);