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