]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rio/fsys.c
rio: fix handling "resize" wctl for hidden windows
[plan9front.git] / sys / src / cmd / rio / fsys.c
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 "dat.h"
11 #include "fns.h"
12
13 char Eperm[] = "permission denied";
14 char Eexist[] = "file does not exist";
15 char Enotdir[] = "not a directory";
16 char Ebadfcall[] = "bad fcall type";
17 char Eoffset[] = "illegal offset";
18 char Enomem[] = "out of memory";
19
20 int     messagesize = 8192+IOHDRSZ;     /* good start */
21
22 Dirtab dirtab[]=
23 {
24         { ".",                  QTDIR,  Qdir,                   0500|DMDIR },
25         { "cons",               QTFILE, Qcons,          0600 },
26         { "cursor",             QTFILE, Qcursor,                0600 },
27         { "consctl",    QTFILE, Qconsctl,               0200 },
28         { "winid",              QTFILE, Qwinid,         0400 },
29         { "winname",    QTFILE, Qwinname,       0400 },
30         { "label",              QTFILE, Qlabel,         0600 },
31         { "kbd",        QTFILE, Qkbd,           0600 },
32         { "mouse",      QTFILE, Qmouse,         0600 },
33         { "screen",             QTFILE, Qscreen,                0400 },
34         { "snarf",              QTFILE, Qsnarf,         0600 },
35         { "text",               QTFILE, Qtext,          0400 },
36         { "wdir",               QTFILE, Qwdir,          0600 },
37         { "wctl",               QTFILE, Qwctl,          0600 },
38         { "window",     QTFILE, Qwindow,                0400 },
39         { "wsys",               QTDIR,  Qwsys,          0500|DMDIR },
40         { nil, }
41 };
42
43 static uint             getclock(void);
44 static void             filsysproc(void*);
45 static Fid*             newfid(Filsys*, int);
46 static int              dostat(Filsys*, int, Dirtab*, uchar*, int, uint);
47
48 int     clockfd;
49 int     firstmessage = 1;
50
51 char    srvpipe[64];
52 char    srvwctl[64];
53
54 static  Xfid*   filsysflush(Filsys*, Xfid*, Fid*);
55 static  Xfid*   filsysversion(Filsys*, Xfid*, Fid*);
56 static  Xfid*   filsysauth(Filsys*, Xfid*, Fid*);
57 static  Xfid*   filsysnop(Filsys*, Xfid*, Fid*);
58 static  Xfid*   filsysattach(Filsys*, Xfid*, Fid*);
59 static  Xfid*   filsyswalk(Filsys*, Xfid*, Fid*);
60 static  Xfid*   filsysopen(Filsys*, Xfid*, Fid*);
61 static  Xfid*   filsyscreate(Filsys*, Xfid*, Fid*);
62 static  Xfid*   filsysread(Filsys*, Xfid*, Fid*);
63 static  Xfid*   filsyswrite(Filsys*, Xfid*, Fid*);
64 static  Xfid*   filsysclunk(Filsys*, Xfid*, Fid*);
65 static  Xfid*   filsysremove(Filsys*, Xfid*, Fid*);
66 static  Xfid*   filsysstat(Filsys*, Xfid*, Fid*);
67 static  Xfid*   filsyswstat(Filsys*, Xfid*, Fid*);
68
69 Xfid*   (*fcall[Tmax])(Filsys*, Xfid*, Fid*) =
70 {
71         [Tflush]        = filsysflush,
72         [Tversion]      = filsysversion,
73         [Tauth] = filsysauth,
74         [Tattach]       = filsysattach,
75         [Twalk] = filsyswalk,
76         [Topen] = filsysopen,
77         [Tcreate]       = filsyscreate,
78         [Tread] = filsysread,
79         [Twrite]        = filsyswrite,
80         [Tclunk]        = filsysclunk,
81         [Tremove]= filsysremove,
82         [Tstat] = filsysstat,
83         [Twstat]        = filsyswstat,
84 };
85
86 void
87 post(char *name, char *envname, int srvfd)
88 {
89         int fd;
90         char buf[32];
91
92         fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600);
93         if(fd < 0)
94                 error(name);
95         snprint(buf, sizeof(buf), "%d", srvfd);
96         if(write(fd, buf, strlen(buf)) != strlen(buf))
97                 error("srv write");
98         putenv(envname, name);
99 }
100
101 /*
102  * Build pipe with OCEXEC set on second fd.
103  * Can't put it on both because we want to post one in /srv.
104  */
105 int
106 cexecpipe(int *p0, int *p1)
107 {
108         /* pipe the hard way to get close on exec */
109         if(bind("#|", "/mnt/temp", MREPL) < 0)
110                 return -1;
111         *p0 = open("/mnt/temp/data", ORDWR);
112         *p1 = open("/mnt/temp/data1", ORDWR|OCEXEC);
113         unmount(nil, "/mnt/temp");
114         if(*p0<0 || *p1<0)
115                 return -1;
116         return 0;
117 }
118
119 Filsys*
120 filsysinit(Channel *cxfidalloc)
121 {
122         int n, fd, pid, p0;
123         Filsys *fs;
124         Channel *c;
125         char buf[128];
126
127         fs = emalloc(sizeof(Filsys));
128         if(cexecpipe(&fs->cfd, &fs->sfd) < 0)
129                 goto Rescue;
130         fmtinstall('F', fcallfmt);
131         clockfd = open("/dev/time", OREAD|OCEXEC);
132         fd = open("/dev/user", OREAD);
133         strcpy(buf, "Jean-Paul_Belmondo");
134         if(fd >= 0){
135                 n = read(fd, buf, sizeof buf-1);
136                 if(n > 0)
137                         buf[n] = 0;
138                 close(fd);
139         }
140         fs->user = estrdup(buf);
141         fs->csyncflush = chancreate(sizeof(int), 0);
142         if(fs->csyncflush == nil)
143                 error("chancreate syncflush");
144         fs->cxfidalloc = cxfidalloc;
145         pid = getpid();
146
147         /*
148          * Create and post wctl pipe
149          */
150         if(cexecpipe(&p0, &wctlfd) < 0)
151                 goto Rescue;
152         snprint(srvwctl, sizeof(srvwctl), "/srv/riowctl.%s.%d", fs->user, pid);
153         post(srvwctl, "wctl", p0);
154         close(p0);
155
156         /*
157          * Start server processes
158          */
159         c = chancreate(sizeof(char*), 0);
160         if(c == nil)
161                 error("wctl channel");
162         proccreate(wctlproc, c, 4096);
163         threadcreate(wctlthread, c, 4096);
164         proccreate(filsysproc, fs, 10000);
165
166         /*
167          * Post srv pipe
168          */
169         snprint(srvpipe, sizeof(srvpipe), "/srv/rio.%s.%d", fs->user, pid);
170         post(srvpipe, "wsys", fs->cfd);
171
172         return fs;
173
174 Rescue:
175         free(fs);
176         return nil;
177 }
178
179 static
180 void
181 filsysproc(void *arg)
182 {
183         int n;
184         Xfid *x;
185         Fid *f;
186         Fcall t;
187         uchar *buf;
188         Filsys *fs;
189
190         threadsetname("FILSYSPROC");
191         fs = arg;
192         fs->pid = getpid();
193         x = nil;
194         for(;;){
195                 buf = malloc(messagesize+UTFmax);       /* UTFmax for appending partial rune in xfidwrite */
196                 if(buf == nil)
197                         error(Enomem);
198                 while((n = read9pmsg(fs->sfd, buf, messagesize)) == 0)
199                         yield();
200                 if(n < 0){
201                         yield();        /* if threadexitsall'ing, will not return */
202                         fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n);
203                         errorshouldabort = 0;
204                         error("eof or i/o error on server channel");
205                 }
206                 if(x == nil){
207                         send(fs->cxfidalloc, nil);
208                         recv(fs->cxfidalloc, &x);
209                         x->fs = fs;
210                 }
211                 x->buf = buf;
212                 if(convM2S(buf, n, x) != n)
213                         error("convert error in convM2S");
214                 if(debug)
215                         fprint(2, "rio:<-%F\n", &x->Fcall);
216                 if(fcall[x->type] == nil)
217                         x = filsysrespond(fs, x, &t, Ebadfcall);
218                 else{
219                         if(x->type==Tversion || x->type==Tauth)
220                                 f = nil;
221                         else
222                                 f = newfid(fs, x->fid);
223                         x->f = f;
224                         x  = (*fcall[x->type])(fs, x, f);
225                 }
226                 firstmessage = 0;
227         }
228 }
229
230 /*
231  * Called only from a different FD group
232  */
233 int
234 filsysmount(Filsys *fs, int id)
235 {
236         char buf[32];
237
238         close(fs->sfd); /* close server end so mount won't hang if exiting */
239         sprint(buf, "%d", id);
240         if(mount(fs->cfd, -1, "/mnt/wsys", MREPL, buf) < 0){
241                 fprint(2, "mount failed: %r\n");
242                 return -1;
243         }
244         if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){
245                 fprint(2, "bind failed: %r\n");
246                 return -1;
247         }
248         return 0;
249 }
250
251 Xfid*
252 filsysrespond(Filsys *fs, Xfid *x, Fcall *t, char *err)
253 {
254         int n;
255
256         if(err){
257                 t->type = Rerror;
258                 t->ename = err;
259         }else
260                 t->type = x->type+1;
261         t->fid = x->fid;
262         t->tag = x->tag;
263         if(x->buf == nil)
264                 error("no buffer in respond");
265         n = convS2M(t, x->buf, messagesize);
266         if(n <= 0)
267                 error("convert error in convS2M");
268         if(write(fs->sfd, x->buf, n) != n)
269                 error("write error in respond");
270         if(debug)
271                 fprint(2, "rio:->%F\n", t);
272         free(x->buf);
273         x->buf = nil;
274         x->flushtag = -1;
275         return x;
276 }
277
278 void
279 filsyscancel(Xfid *x)
280 {
281         if(x->buf){
282                 free(x->buf);
283                 x->buf = nil;
284         }
285 }
286
287 static
288 Xfid*
289 filsysversion(Filsys *fs, Xfid *x, Fid*)
290 {
291         Fcall t;
292
293         if(!firstmessage)
294                 return filsysrespond(x->fs, x, &t, "version request not first message");
295         if(x->msize < 256)
296                 return filsysrespond(x->fs, x, &t, "version: message size too small");
297         messagesize = x->msize;
298         t.msize = messagesize;
299         if(strncmp(x->version, "9P2000", 6) != 0)
300                 return filsysrespond(x->fs, x, &t, "unrecognized 9P version");
301         t.version = "9P2000";
302         return filsysrespond(fs, x, &t, nil);
303 }
304
305 static
306 Xfid*
307 filsysauth(Filsys *fs, Xfid *x, Fid*)
308 {
309         Fcall t;
310
311         return filsysrespond(fs, x, &t, "rio: authentication not required");
312 }
313
314 static
315 Xfid*
316 filsysflush(Filsys *fs, Xfid *x, Fid*)
317 {
318         sendp(x->c, xfidflush);
319
320         /*
321          * flushes need to be replied in order. xfidflush() will
322          * awaken us when the flush has been queued.
323          */
324         recv(fs->csyncflush, nil);
325
326         return nil;
327 }
328
329 static
330 Xfid*
331 filsysattach(Filsys *, Xfid *x, Fid *f)
332 {
333         Fcall t;
334
335         if(strcmp(x->uname, x->fs->user) != 0)
336                 return filsysrespond(x->fs, x, &t, Eperm);
337         f->busy = TRUE;
338         f->open = FALSE;
339         f->qid.path = Qdir;
340         f->qid.type = QTDIR;
341         f->qid.vers = 0;
342         f->dir = dirtab;
343         f->nrpart = 0;
344         sendp(x->c, xfidattach);
345         return nil;
346 }
347
348 static
349 int
350 numeric(char *s)
351 {
352         for(; *s!='\0'; s++)
353                 if(*s<'0' || '9'<*s)
354                         return 0;
355         return 1;
356 }
357
358 static
359 int
360 skipdir(char *name)
361 {
362         /* don't serve these if it's provided in the environment */
363         if(snarffd>=0 && strcmp(name, "snarf")==0)
364                 return 1;
365         if(gotscreen && strcmp(name, "screen")==0)
366                 return 1;
367         if(!servekbd && strcmp(name, "kbd")==0)
368                 return 1;
369         return 0;
370 }
371
372 static
373 Xfid*
374 filsyswalk(Filsys *fs, Xfid *x, Fid *f)
375 {
376         Fcall t;
377         Fid *nf;
378         int i, id;
379         uchar type;
380         ulong path;
381         Dirtab *d, *dir;
382         Window *w;
383         char *err;
384         Qid qid;
385
386         if(f->open)
387                 return filsysrespond(fs, x, &t, "walk of open file");
388         nf = nil;
389         if(x->fid  != x->newfid){
390                 /* BUG: check exists */
391                 nf = newfid(fs, x->newfid);
392                 if(nf->busy)
393                         return filsysrespond(fs, x, &t, "clone to busy fid");
394                 nf->busy = TRUE;
395                 nf->open = FALSE;
396                 nf->dir = f->dir;
397                 nf->qid = f->qid;
398                 nf->w = f->w;
399                 incref(f->w);
400                 nf->nrpart = 0; /* not open, so must be zero */
401                 f = nf; /* walk f */
402         }
403
404         t.nwqid = 0;
405         err = nil;
406
407         /* update f->qid, f->dir only if walk completes */
408         qid = f->qid;
409         dir = f->dir;
410
411         if(x->nwname > 0){
412                 for(i=0; i<x->nwname; i++){
413                         if((qid.type & QTDIR) == 0){
414                                 err = Enotdir;
415                                 break;
416                         }
417                         if(strcmp(x->wname[i], "..") == 0){
418                                 type = QTDIR;
419                                 path = Qdir;
420                                 dir = dirtab;
421                                 if(FILE(qid) == Qwsysdir)
422                                         path = Qwsys;
423                                 id = 0;
424     Accept:
425                                 if(i == MAXWELEM){
426                                         err = "name too long";
427                                         break;
428                                 }
429                                 qid.type = type;
430                                 qid.vers = 0;
431                                 qid.path = QID(id, path);
432                                 t.wqid[t.nwqid++] = qid;
433                                 continue;
434                         }
435
436                         if(qid.path == Qwsys){
437                                 /* is it a numeric name? */
438                                 if(!numeric(x->wname[i]))
439                                         break;
440                                 /* yes: it's a directory */
441                                 id = atoi(x->wname[i]);
442                                 qlock(&all);
443                                 w = wlookid(id);
444                                 if(w == nil){
445                                         qunlock(&all);
446                                         break;
447                                 }
448                                 path = Qwsysdir;
449                                 type = QTDIR;
450                                 qunlock(&all);
451                                 incref(w);
452                                 sendp(winclosechan, f->w);
453                                 f->w = w;
454                                 dir = dirtab;
455                                 goto Accept;
456                         }
457                         if(skipdir(x->wname[i]))
458                                 break;
459                         id = WIN(f->qid);
460                         d = dirtab;
461                         d++;    /* skip '.' */
462                         for(; d->name; d++)
463                                 if(strcmp(x->wname[i], d->name) == 0){
464                                         path = d->qid;
465                                         type = d->type;
466                                         dir = d;
467                                         goto Accept;
468                                 }
469
470                         break;  /* file not found */
471                 }
472
473                 if(i==0 && err==nil)
474                         err = Eexist;
475         }
476
477         if(err!=nil || t.nwqid<x->nwname){
478                 if(nf){
479                         if(nf->w)
480                                 sendp(winclosechan, nf->w);
481                         nf->open = FALSE;
482                         nf->busy = FALSE;
483                 }
484         }else if(t.nwqid == x->nwname){
485                 f->dir = dir;
486                 f->qid = qid;
487         }
488
489         return filsysrespond(fs, x, &t, err);
490 }
491
492 static
493 Xfid*
494 filsysopen(Filsys *fs, Xfid *x, Fid *f)
495 {
496         Fcall t;
497         int m;
498
499         /* can't truncate anything, so just disregard */
500         x->mode &= ~(OTRUNC|OCEXEC);
501         /* can't execute or remove anything */
502         if(x->mode==OEXEC || (x->mode&ORCLOSE))
503                 goto Deny;
504         switch(x->mode){
505         default:
506                 goto Deny;
507         case OREAD:
508                 m = 0400;
509                 break;
510         case OWRITE:
511                 m = 0200;
512                 break;
513         case ORDWR:
514                 m = 0600;
515                 break;
516         }
517         if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
518                 goto Deny;
519                 
520         sendp(x->c, xfidopen);
521         return nil;
522
523     Deny:
524         return filsysrespond(fs, x, &t, Eperm);
525 }
526
527 static
528 Xfid*
529 filsyscreate(Filsys *fs, Xfid *x, Fid*)
530 {
531         Fcall t;
532
533         return filsysrespond(fs, x, &t, Eperm);
534 }
535
536 static
537 int
538 idcmp(void *a, void *b)
539 {
540         return *(int*)a - *(int*)b;
541 }
542
543 static
544 Xfid*
545 filsysread(Filsys *fs, Xfid *x, Fid *f)
546 {
547         Fcall t;
548         uchar *b;
549         int i, n, o, e, len, j, k, *ids;
550         Dirtab *d, dt;
551         uint clock;
552         char buf[32];
553
554         if((f->qid.type & QTDIR) == 0){
555                 sendp(x->c, xfidread);
556                 return nil;
557         }
558         o = x->offset;
559         e = x->offset+x->count;
560         clock = getclock();
561         b = malloc(messagesize-IOHDRSZ);        /* avoid memset of emalloc */
562         if(b == nil)
563                 return filsysrespond(fs, x, &t, Enomem);
564         n = 0;
565         switch(FILE(f->qid)){
566         case Qdir:
567         case Qwsysdir:
568                 d = dirtab;
569                 d++;    /* first entry is '.' */
570                 for(i=0; d->name!=nil && i<e; d++){
571                         if(skipdir(d->name))
572                                 continue;
573                         len = dostat(fs, WIN(x->f->qid), d, b+n, x->count-n, clock);
574                         if(len <= BIT16SZ)
575                                 break;
576                         if(i >= o)
577                                 n += len;
578                         i += len;
579                 }
580                 break;
581         case Qwsys:
582                 qlock(&all);
583                 ids = emalloc(nwindow*sizeof(int));
584                 for(j=0; j<nwindow; j++)
585                         ids[j] = window[j]->id;
586                 qunlock(&all);
587                 qsort(ids, nwindow, sizeof ids[0], idcmp);
588                 dt.name = buf;
589                 for(i=0, j=0; j<nwindow && i<e; i+=len){
590                         k = ids[j];
591                         sprint(dt.name, "%d", k);
592                         dt.qid = QID(k, Qdir);
593                         dt.type = QTDIR;
594                         dt.perm = DMDIR|0700;
595                         len = dostat(fs, k, &dt, b+n, x->count-n, clock);
596                         if(len == 0)
597                                 break;
598                         if(i >= o)
599                                 n += len;
600                         j++;
601                 }
602                 free(ids);
603                 break;
604         }
605         t.data = (char*)b;
606         t.count = n;
607         filsysrespond(fs, x, &t, nil);
608         free(b);
609         return x;
610 }
611
612 static
613 Xfid*
614 filsyswrite(Filsys*, Xfid *x, Fid*)
615 {
616         sendp(x->c, xfidwrite);
617         return nil;
618 }
619
620 static
621 Xfid*
622 filsysclunk(Filsys *fs, Xfid *x, Fid *f)
623 {
624         Fcall t;
625
626         if(f->open){
627                 f->busy = FALSE;
628                 f->open = FALSE;
629                 sendp(x->c, xfidclose);
630                 return nil;
631         }
632         if(f->w)
633                 sendp(winclosechan, f->w);
634         f->busy = FALSE;
635         f->open = FALSE;
636         return filsysrespond(fs, x, &t, nil);
637 }
638
639 static
640 Xfid*
641 filsysremove(Filsys *fs, Xfid *x, Fid*)
642 {
643         Fcall t;
644
645         return filsysrespond(fs, x, &t, Eperm);
646 }
647
648 static
649 Xfid*
650 filsysstat(Filsys *fs, Xfid *x, Fid *f)
651 {
652         Fcall t;
653
654         t.stat = emalloc(messagesize-IOHDRSZ);
655         t.nstat = dostat(fs, WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
656         x = filsysrespond(fs, x, &t, nil);
657         free(t.stat);
658         return x;
659 }
660
661 static
662 Xfid*
663 filsyswstat(Filsys *fs, Xfid *x, Fid*)
664 {
665         Fcall t;
666
667         return filsysrespond(fs, x, &t, Eperm);
668 }
669
670 static
671 Fid*
672 newfid(Filsys *fs, int fid)
673 {
674         Fid *f, *ff, **fh;
675
676         ff = nil;
677         fh = &fs->fids[fid&(Nhash-1)];
678         for(f=*fh; f; f=f->next)
679                 if(f->fid == fid)
680                         return f;
681                 else if(ff==nil && f->busy==FALSE)
682                         ff = f;
683         if(ff){
684                 ff->fid = fid;
685                 return ff;
686         }
687         f = emalloc(sizeof *f);
688         f->fid = fid;
689         f->next = *fh;
690         *fh = f;
691         return f;
692 }
693
694 static
695 uint
696 getclock(void)
697 {
698         char buf[32];
699
700         seek(clockfd, 0, 0);
701         read(clockfd, buf, sizeof buf);
702         return atoi(buf);
703 }
704
705 static
706 int
707 dostat(Filsys *fs, int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
708 {
709         Dir d;
710
711         d.qid.path = QID(id, dir->qid);
712         if(dir->qid == Qsnarf)
713                 d.qid.vers = snarfversion;
714         else
715                 d.qid.vers = 0;
716         d.qid.type = dir->type;
717         d.mode = dir->perm;
718         d.length = 0;   /* would be nice to do better */
719         d.name = dir->name;
720         d.uid = fs->user;
721         d.gid = fs->user;
722         d.muid = fs->user;
723         d.atime = clock;
724         d.mtime = clock;
725         return convD2M(&d, buf, nbuf);
726 }