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 { nil, }
91 };
93 typedef struct Mnt Mnt;
94 struct Mnt
95 {
96 QLock lk;
97 int id;
98 Mntdir *md;
99 };
101 Mnt mnt;
103 Xfid* respond(Xfid*, Fcall*, char*);
104 int dostat(int, Dirtab*, uchar*, int, uint);
105 uint getclock(void);
107 char *user = "Wile E. Coyote";
108 static int closing = 0;
109 int messagesize = Maxblock+IOHDRSZ; /* good start */
111 void fsysproc(void *);
113 void
114 fsysinit(void)
116 int p[2];
117 char *u;
119 initfcall();
120 if(pipe(p) < 0)
121 error("can't create pipe");
122 if(post9pservice(p[0], "acme") < 0)
123 error("can't post service");
124 sfd = p[1];
125 fmtinstall('F', fcallfmt);
126 if((u = getuser()) != nil)
127 user = estrdup(u);
128 threadcreate(fsysproc, nil, STACK);
131 void
132 fsysproc(void *v)
134 int n;
135 Xfid *x;
136 Fid *f;
137 Fcall t;
138 uchar *buf;
140 USED(v);
141 x = nil;
142 for(;;){
143 buf = emalloc(messagesize+UTFmax); /* overflow for appending partial rune in xfidwrite */
144 n = threadread9pmsg(sfd, buf, messagesize);
145 if(n <= 0){
146 if(closing)
147 break;
148 error("i/o error on server channel");
150 if(x == nil){
151 sendp(cxfidalloc, nil);
152 x = recvp(cxfidalloc);
154 x->buf = buf;
155 if(convM2S(buf, n, &x->fcall) != n)
156 error("convert error in convM2S");
157 if(DEBUG)
158 fprint(2, "%F\n", &x->fcall);
159 if(fcall[x->fcall.type] == 0)
160 x = respond(x, &t, "bad fcall type");
161 else{
162 if(x->fcall.type==Tversion || x->fcall.type==Tauth)
163 f = nil;
164 else
165 f = newfid(x->fcall.fid);
166 x->f = f;
167 x = (*fcall[x->fcall.type])(x, f);
172 Mntdir*
173 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
175 Mntdir *m;
176 int id;
178 qlock(&mnt.lk);
179 id = ++mnt.id;
180 m = emalloc(sizeof *m);
181 m->id = id;
182 m->dir = dir;
183 m->ref = 1; /* one for Command, one will be incremented in attach */
184 m->ndir = ndir;
185 m->next = mnt.md;
186 m->incl = incl;
187 m->nincl = nincl;
188 mnt.md = m;
189 qunlock(&mnt.lk);
190 return m;
193 void
194 fsysincid(Mntdir *m)
196 qlock(&mnt.lk);
197 m->ref++;
198 qunlock(&mnt.lk);
201 void
202 fsysdelid(Mntdir *idm)
204 Mntdir *m, *prev;
205 int i;
206 char buf[64];
208 if(idm == nil)
209 return;
210 qlock(&mnt.lk);
211 if(--idm->ref > 0){
212 qunlock(&mnt.lk);
213 return;
215 prev = nil;
216 for(m=mnt.md; m; m=m->next){
217 if(m == idm){
218 if(prev)
219 prev->next = m->next;
220 else
221 mnt.md = m->next;
222 for(i=0; i<m->nincl; i++)
223 free(m->incl[i]);
224 free(m->incl);
225 free(m->dir);
226 free(m);
227 qunlock(&mnt.lk);
228 return;
230 prev = m;
232 qunlock(&mnt.lk);
233 sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
234 sendp(cerr, estrdup(buf));
237 /*
238 * Called only in exec.c:/^run(), from a different FD group
239 */
240 Mntdir*
241 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
243 return fsysaddid(dir, ndir, incl, nincl);
246 void
247 fsysclose(void)
249 closing = 1;
250 close(sfd);
253 Xfid*
254 respond(Xfid *x, Fcall *t, char *err)
256 int n;
258 if(err){
259 t->type = Rerror;
260 t->ename = err;
261 }else
262 t->type = x->fcall.type+1;
263 t->fid = x->fcall.fid;
264 t->tag = x->fcall.tag;
265 if(x->buf == nil)
266 x->buf = emalloc(messagesize);
267 n = convS2M(t, x->buf, messagesize);
268 if(n <= 0)
270 fprint(2, "convert error (n=%d, msgsize %d): have %F\n", n, messagesize, &x->fcall);
271 fprint(2, "\tresponse: %F\n", t);
272 error("convert error in convS2M");
274 if(write(sfd, x->buf, n) != n)
275 error("write error in respond");
276 free(x->buf);
277 x->buf = nil;
278 if(DEBUG)
279 fprint(2, "r: %F\n", t);
280 return x;
283 static
284 Xfid*
285 fsysversion(Xfid *x, Fid *f)
287 Fcall t;
289 USED(f);
290 if(x->fcall.msize < 256)
291 return respond(x, &t, "version: message size too small");
292 messagesize = x->fcall.msize;
293 t.msize = messagesize;
294 if(strncmp(x->fcall.version, "9P2000", 6) != 0)
295 return respond(x, &t, "unrecognized 9P version");
296 t.version = "9P2000";
297 return respond(x, &t, nil);
300 static
301 Xfid*
302 fsysauth(Xfid *x, Fid *f)
304 USED(f);
305 return respond(x, nil, "acme: authentication not required");
308 static
309 Xfid*
310 fsysflush(Xfid *x, Fid *f)
312 USED(f);
313 sendp(x->c, (void*)xfidflush);
314 return nil;
317 static
318 Xfid*
319 fsysattach(Xfid *x, Fid *f)
321 Fcall t;
322 int id;
323 Mntdir *m;
324 char buf[128];
326 if(strcmp(x->fcall.uname, user) != 0)
327 return respond(x, &t, Eperm);
328 f->busy = TRUE;
329 f->open = FALSE;
330 f->qid.path = Qdir;
331 f->qid.type = QTDIR;
332 f->qid.vers = 0;
333 f->dir = dirtab;
334 f->nrpart = 0;
335 f->w = nil;
336 t.qid = f->qid;
337 f->mntdir = nil;
338 id = atoi(x->fcall.aname);
339 qlock(&mnt.lk);
340 for(m=mnt.md; m; m=m->next)
341 if(m->id == id){
342 f->mntdir = m;
343 m->ref++;
344 break;
346 if(m == nil && x->fcall.aname[0]){
347 snprint(buf, sizeof buf, "unknown id '%s' in attach", x->fcall.aname);
348 sendp(cerr, estrdup(buf));
350 qunlock(&mnt.lk);
351 return respond(x, &t, nil);
354 static
355 Xfid*
356 fsyswalk(Xfid *x, Fid *f)
358 Fcall t;
359 int c, i, j, id;
360 Qid q;
361 uchar type;
362 ulong path;
363 Fid *nf;
364 Dirtab *d, *dir;
365 Window *w;
366 char *err;
368 nf = nil;
369 w = nil;
370 if(f->open)
371 return respond(x, &t, "walk of open file");
372 if(x->fcall.fid != x->fcall.newfid){
373 nf = newfid(x->fcall.newfid);
374 if(nf->busy)
375 return respond(x, &t, "newfid already in use");
376 nf->busy = TRUE;
377 nf->open = FALSE;
378 nf->mntdir = f->mntdir;
379 if(f->mntdir)
380 f->mntdir->ref++;
381 nf->dir = f->dir;
382 nf->qid = f->qid;
383 nf->w = f->w;
384 nf->nrpart = 0; /* not open, so must be zero */
385 if(nf->w)
386 incref(&nf->w->ref);
387 f = nf; /* walk f */
390 t.nwqid = 0;
391 err = nil;
392 dir = nil;
393 id = WIN(f->qid);
394 q = f->qid;
396 if(x->fcall.nwname > 0){
397 for(i=0; i<x->fcall.nwname; i++){
398 if((q.type & QTDIR) == 0){
399 err = Enotdir;
400 break;
403 if(strcmp(x->fcall.wname[i], "..") == 0){
404 type = QTDIR;
405 path = Qdir;
406 id = 0;
407 if(w){
408 winclose(w);
409 w = nil;
411 Accept:
412 if(i == MAXWELEM){
413 err = "name too long";
414 break;
416 q.type = type;
417 q.vers = 0;
418 q.path = QID(id, path);
419 t.wqid[t.nwqid++] = q;
420 continue;
423 /* is it a numeric name? */
424 for(j=0; (c=x->fcall.wname[i][j]); j++)
425 if(c<'0' || '9'<c)
426 goto Regular;
427 /* yes: it's a directory */
428 if(w) /* name has form 27/23; get out before losing w */
429 break;
430 id = atoi(x->fcall.wname[i]);
431 qlock(&row.lk);
432 w = lookid(id, FALSE);
433 if(w == nil){
434 qunlock(&row.lk);
435 break;
437 incref(&w->ref); /* we'll drop reference at end if there's an error */
438 path = Qdir;
439 type = QTDIR;
440 qunlock(&row.lk);
441 dir = dirtabw;
442 goto Accept;
444 Regular:
445 // if(FILE(f->qid) == Qacme) /* empty directory */
446 // break;
447 if(strcmp(x->fcall.wname[i], "new") == 0){
448 if(w)
449 error("w set in walk to new");
450 sendp(cnewwindow, nil); /* signal newwindowthread */
451 w = recvp(cnewwindow); /* receive new window */
452 incref(&w->ref);
453 type = QTDIR;
454 path = QID(w->id, Qdir);
455 id = w->id;
456 dir = dirtabw;
457 goto Accept;
460 if(id == 0)
461 d = dirtab;
462 else
463 d = dirtabw;
464 d++; /* skip '.' */
465 for(; d->name; d++)
466 if(strcmp(x->fcall.wname[i], d->name) == 0){
467 path = d->qid;
468 type = d->type;
469 dir = d;
470 goto Accept;
473 break; /* file not found */
476 if(i==0 && err == nil)
477 err = Eexist;
480 if(err!=nil || t.nwqid<x->fcall.nwname){
481 if(nf){
482 nf->busy = FALSE;
483 fsysdelid(nf->mntdir);
485 }else if(t.nwqid == x->fcall.nwname){
486 if(w){
487 f->w = w;
488 w = nil; /* don't drop the reference */
490 if(dir)
491 f->dir = dir;
492 f->qid = q;
495 if(w != nil)
496 winclose(w);
498 return respond(x, &t, err);
501 static
502 Xfid*
503 fsysopen(Xfid *x, Fid *f)
505 Fcall t;
506 int m;
508 /* can't truncate anything, so just disregard */
509 x->fcall.mode &= ~(OTRUNC|OCEXEC);
510 /* can't execute or remove anything */
511 if(x->fcall.mode==OEXEC || (x->fcall.mode&ORCLOSE))
512 goto Deny;
513 switch(x->fcall.mode){
514 default:
515 goto Deny;
516 case OREAD:
517 m = 0400;
518 break;
519 case OWRITE:
520 m = 0200;
521 break;
522 case ORDWR:
523 m = 0600;
524 break;
526 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
527 goto Deny;
529 sendp(x->c, (void*)xfidopen);
530 return nil;
532 Deny:
533 return respond(x, &t, Eperm);
536 static
537 Xfid*
538 fsyscreate(Xfid *x, Fid *f)
540 Fcall t;
542 USED(f);
543 return respond(x, &t, Eperm);
546 static
547 int
548 idcmp(const void *a, const void *b)
550 return *(int*)a - *(int*)b;
553 static
554 Xfid*
555 fsysread(Xfid *x, Fid *f)
557 Fcall t;
558 uchar *b;
559 int i, id, n, o, e, j, k, *ids, nids;
560 Dirtab *d, dt;
561 Column *c;
562 uint clock, len;
563 char buf[16];
565 if(f->qid.type & QTDIR){
566 if(FILE(f->qid) == Qacme){ /* empty dir */
567 t.data = nil;
568 t.count = 0;
569 respond(x, &t, nil);
570 return x;
572 o = x->fcall.offset;
573 e = x->fcall.offset+x->fcall.count;
574 clock = getclock();
575 b = emalloc(messagesize);
576 id = WIN(f->qid);
577 n = 0;
578 if(id > 0)
579 d = dirtabw;
580 else
581 d = dirtab;
582 d++; /* first entry is '.' */
583 for(i=0; d->name!=nil && i<e; i+=len){
584 len = dostat(WIN(x->f->qid), d, b+n, x->fcall.count-n, clock);
585 if(len <= BIT16SZ)
586 break;
587 if(i >= o)
588 n += len;
589 d++;
591 if(id == 0){
592 qlock(&row.lk);
593 nids = 0;
594 ids = nil;
595 for(j=0; j<row.ncol; j++){
596 c = row.col[j];
597 for(k=0; k<c->nw; k++){
598 ids = realloc(ids, (nids+1)*sizeof(int));
599 ids[nids++] = c->w[k]->id;
602 qunlock(&row.lk);
603 qsort(ids, nids, sizeof ids[0], idcmp);
604 j = 0;
605 dt.name = buf;
606 for(; j<nids && i<e; i+=len){
607 k = ids[j];
608 sprint(dt.name, "%d", k);
609 dt.qid = QID(k, Qdir);
610 dt.type = QTDIR;
611 dt.perm = DMDIR|0700;
612 len = dostat(k, &dt, b+n, x->fcall.count-n, clock);
613 if(len == 0)
614 break;
615 if(i >= o)
616 n += len;
617 j++;
619 free(ids);
621 t.data = (char*)b;
622 t.count = n;
623 respond(x, &t, nil);
624 free(b);
625 return x;
627 sendp(x->c, (void*)xfidread);
628 return nil;
631 static
632 Xfid*
633 fsyswrite(Xfid *x, Fid *f)
635 USED(f);
636 sendp(x->c, (void*)xfidwrite);
637 return nil;
640 static
641 Xfid*
642 fsysclunk(Xfid *x, Fid *f)
644 fsysdelid(f->mntdir);
645 sendp(x->c, (void*)xfidclose);
646 return nil;
649 static
650 Xfid*
651 fsysremove(Xfid *x, Fid *f)
653 Fcall t;
655 USED(f);
656 return respond(x, &t, Eperm);
659 static
660 Xfid*
661 fsysstat(Xfid *x, Fid *f)
663 Fcall t;
665 t.stat = emalloc(messagesize-IOHDRSZ);
666 t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
667 x = respond(x, &t, nil);
668 free(t.stat);
669 return x;
672 static
673 Xfid*
674 fsyswstat(Xfid *x, Fid *f)
676 Fcall t;
678 USED(f);
679 return respond(x, &t, Eperm);
682 Fid*
683 newfid(int fid)
685 Fid *f, *ff, **fh;
687 ff = nil;
688 fh = &fids[fid&(Nhash-1)];
689 for(f=*fh; f; f=f->next)
690 if(f->fid == fid)
691 return f;
692 else if(ff==nil && f->busy==FALSE)
693 ff = f;
694 if(ff){
695 ff->fid = fid;
696 return ff;
698 f = emalloc(sizeof *f);
699 f->fid = fid;
700 f->next = *fh;
701 *fh = f;
702 return f;
705 uint
706 getclock(void)
708 /*
709 char buf[32];
711 buf[0] = '\0';
712 pread(clockfd, buf, sizeof buf, 0);
713 return atoi(buf);
714 */
715 return time(0);
718 int
719 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
721 Dir d;
723 d.qid.path = QID(id, dir->qid);
724 d.qid.vers = 0;
725 d.qid.type = dir->type;
726 d.mode = dir->perm;
727 d.length = 0; /* would be nice to do better */
728 d.name = dir->name;
729 d.uid = user;
730 d.gid = user;
731 d.muid = user;
732 d.atime = clock;
733 d.mtime = clock;
734 return convD2M(&d, buf, nbuf);