]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rio/fsys.c
ip/ipconfig: default onlink and autoflag to 1
[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,          0600 },
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*   filsysattach(Filsys*, Xfid*, Fid*);
58 static  Xfid*   filsyswalk(Filsys*, Xfid*, Fid*);
59 static  Xfid*   filsysopen(Filsys*, Xfid*, Fid*);
60 static  Xfid*   filsyscreate(Filsys*, Xfid*, Fid*);
61 static  Xfid*   filsysread(Filsys*, Xfid*, Fid*);
62 static  Xfid*   filsyswrite(Filsys*, Xfid*, Fid*);
63 static  Xfid*   filsysclunk(Filsys*, Xfid*, Fid*);
64 static  Xfid*   filsysremove(Filsys*, Xfid*, Fid*);
65 static  Xfid*   filsysstat(Filsys*, Xfid*, Fid*);
66 static  Xfid*   filsyswstat(Filsys*, Xfid*, Fid*);
67
68 Xfid*   (*fcall[Tmax])(Filsys*, Xfid*, Fid*) =
69 {
70         [Tflush]        = filsysflush,
71         [Tversion]      = filsysversion,
72         [Tauth] = filsysauth,
73         [Tattach]       = filsysattach,
74         [Twalk] = filsyswalk,
75         [Topen] = filsysopen,
76         [Tcreate]       = filsyscreate,
77         [Tread] = filsysread,
78         [Twrite]        = filsyswrite,
79         [Tclunk]        = filsysclunk,
80         [Tremove]= filsysremove,
81         [Tstat] = filsysstat,
82         [Twstat]        = filsyswstat,
83 };
84
85 void
86 post(char *name, char *envname, int srvfd)
87 {
88         int fd;
89         char buf[32];
90
91         fd = create(name, OWRITE|ORCLOSE|OCEXEC, 0600);
92         if(fd < 0)
93                 error(name);
94         snprint(buf, sizeof(buf), "%d", srvfd);
95         if(write(fd, buf, strlen(buf)) != strlen(buf))
96                 error("srv write");
97         putenv(envname, name);
98 }
99
100 /*
101  * Build pipe with OCEXEC set on second fd.
102  * Can't put it on both because we want to post one in /srv.
103  */
104 int
105 cexecpipe(int *p0, int *p1)
106 {
107         /* pipe the hard way to get close on exec */
108         if(bind("#|", "/mnt/temp", MREPL) < 0)
109                 return -1;
110         *p0 = open("/mnt/temp/data", ORDWR);
111         *p1 = open("/mnt/temp/data1", ORDWR|OCEXEC);
112         unmount(nil, "/mnt/temp");
113         if(*p0<0 || *p1<0)
114                 return -1;
115         return 0;
116 }
117
118 Filsys*
119 filsysinit(Channel *cxfidalloc)
120 {
121         int n, fd, pid, p0;
122         Filsys *fs;
123         Channel *c;
124         char buf[128];
125
126         fs = emalloc(sizeof(Filsys));
127         if(cexecpipe(&fs->cfd, &fs->sfd) < 0)
128                 goto Rescue;
129         fmtinstall('F', fcallfmt);
130         clockfd = open("/dev/time", OREAD|OCEXEC);
131         fd = open("/dev/user", OREAD);
132         strcpy(buf, "Jean-Paul_Belmondo");
133         if(fd >= 0){
134                 n = read(fd, buf, sizeof buf-1);
135                 if(n > 0)
136                         buf[n] = 0;
137                 close(fd);
138         }
139         fs->user = estrdup(buf);
140         fs->csyncflush = chancreate(sizeof(int), 0);
141         if(fs->csyncflush == nil)
142                 error("chancreate syncflush");
143         fs->cxfidalloc = cxfidalloc;
144         pid = getpid();
145
146         /*
147          * Create and post wctl pipe
148          */
149         if(cexecpipe(&p0, &wctlfd) < 0)
150                 goto Rescue;
151         snprint(srvwctl, sizeof(srvwctl), "/srv/riowctl.%s.%d", fs->user, pid);
152         post(srvwctl, "wctl", p0);
153         close(p0);
154
155         /*
156          * Start server processes
157          */
158         c = chancreate(sizeof(char*), 0);
159         if(c == nil)
160                 error("wctl channel");
161         proccreate(wctlproc, c, 4096);
162         threadcreate(wctlthread, c, 4096);
163         proccreate(filsysproc, fs, 10000);
164
165         /*
166          * Post srv pipe
167          */
168         snprint(srvpipe, sizeof(srvpipe), "/srv/rio.%s.%d", fs->user, pid);
169         post(srvpipe, "wsys", fs->cfd);
170
171         return fs;
172
173 Rescue:
174         free(fs);
175         return nil;
176 }
177
178 static
179 void
180 filsysproc(void *arg)
181 {
182         int n;
183         Xfid *x;
184         Fid *f;
185         Fcall t;
186         uchar *buf;
187         Filsys *fs;
188
189         threadsetname("FILSYSPROC");
190         fs = arg;
191         fs->pid = getpid();
192         x = nil;
193         for(;;){
194                 buf = malloc(messagesize+UTFmax);       /* UTFmax for appending partial rune in xfidwrite */
195                 if(buf == nil)
196                         error(Enomem);
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                 error("no buffer in respond");
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         x->flushtag = -1;
273         return x;
274 }
275
276 void
277 filsyscancel(Xfid *x)
278 {
279         if(x->buf){
280                 free(x->buf);
281                 x->buf = nil;
282         }
283 }
284
285 static
286 Xfid*
287 filsysversion(Filsys *fs, Xfid *x, Fid*)
288 {
289         Fcall t;
290
291         if(!firstmessage)
292                 return filsysrespond(x->fs, x, &t, "version request not first message");
293         if(x->msize < 256)
294                 return filsysrespond(x->fs, x, &t, "version: message size too small");
295         messagesize = x->msize;
296         t.msize = messagesize;
297         if(strncmp(x->version, "9P2000", 6) != 0)
298                 return filsysrespond(x->fs, x, &t, "unrecognized 9P version");
299         t.version = "9P2000";
300         return filsysrespond(fs, x, &t, nil);
301 }
302
303 static
304 Xfid*
305 filsysauth(Filsys *fs, Xfid *x, Fid*)
306 {
307         Fcall t;
308
309         return filsysrespond(fs, x, &t, "rio: authentication not required");
310 }
311
312 static
313 Xfid*
314 filsysflush(Filsys *fs, Xfid *x, Fid*)
315 {
316         sendp(x->c, xfidflush);
317
318         /*
319          * flushes need to be replied in order. xfidflush() will
320          * awaken us when the flush has been queued.
321          */
322         recv(fs->csyncflush, nil);
323
324         return nil;
325 }
326
327 static
328 Xfid*
329 filsysattach(Filsys *, Xfid *x, Fid *f)
330 {
331         Fcall t;
332
333         if(strcmp(x->uname, x->fs->user) != 0)
334                 return filsysrespond(x->fs, x, &t, Eperm);
335         f->busy = TRUE;
336         f->open = FALSE;
337         f->qid.path = Qdir;
338         f->qid.type = QTDIR;
339         f->qid.vers = 0;
340         f->dir = dirtab;
341         f->nrpart = 0;
342         sendp(x->c, xfidattach);
343         return nil;
344 }
345
346 static
347 int
348 numeric(char *s)
349 {
350         for(; *s!='\0'; s++)
351                 if(*s<'0' || '9'<*s)
352                         return 0;
353         return 1;
354 }
355
356 static
357 int
358 skipdir(char *name)
359 {
360         /* don't serve these if it's provided in the environment */
361         if(snarffd>=0 && strcmp(name, "snarf")==0)
362                 return 1;
363         if(gotscreen && strcmp(name, "screen")==0)
364                 return 1;
365         if(!servekbd && strcmp(name, "kbd")==0)
366                 return 1;
367         return 0;
368 }
369
370 static
371 Xfid*
372 filsyswalk(Filsys *fs, Xfid *x, Fid *f)
373 {
374         Fcall t;
375         Fid *nf;
376         int i, id;
377         uchar type;
378         ulong path;
379         Dirtab *d, *dir;
380         Window *w;
381         char *err;
382         Qid qid;
383
384         if(f->open)
385                 return filsysrespond(fs, x, &t, "walk of open file");
386         nf = nil;
387         if(x->fid  != x->newfid){
388                 /* BUG: check exists */
389                 nf = newfid(fs, x->newfid);
390                 if(nf->busy)
391                         return filsysrespond(fs, x, &t, "clone to busy fid");
392                 nf->busy = TRUE;
393                 nf->open = FALSE;
394                 nf->dir = f->dir;
395                 nf->qid = f->qid;
396                 nf->w = f->w;
397                 incref(f->w);
398                 nf->nrpart = 0; /* not open, so must be zero */
399                 f = nf; /* walk f */
400         }
401
402         t.nwqid = 0;
403         err = nil;
404
405         /* update f->qid, f->dir only if walk completes */
406         qid = f->qid;
407         dir = f->dir;
408
409         if(x->nwname > 0){
410                 for(i=0; i<x->nwname; i++){
411                         if((qid.type & QTDIR) == 0){
412                                 err = Enotdir;
413                                 break;
414                         }
415                         if(strcmp(x->wname[i], "..") == 0){
416                                 type = QTDIR;
417                                 path = Qdir;
418                                 dir = dirtab;
419                                 if(FILE(qid) == Qwsysdir)
420                                         path = Qwsys;
421                                 id = 0;
422     Accept:
423                                 if(i == MAXWELEM){
424                                         err = "name too long";
425                                         break;
426                                 }
427                                 qid.type = type;
428                                 qid.vers = 0;
429                                 qid.path = QID(id, path);
430                                 t.wqid[t.nwqid++] = qid;
431                                 continue;
432                         }
433
434                         if(qid.path == Qwsys){
435                                 /* is it a numeric name? */
436                                 if(!numeric(x->wname[i]))
437                                         break;
438                                 /* yes: it's a directory */
439                                 id = atoi(x->wname[i]);
440                                 qlock(&all);
441                                 w = wlookid(id);
442                                 if(w == nil){
443                                         qunlock(&all);
444                                         break;
445                                 }
446                                 path = Qwsysdir;
447                                 type = QTDIR;
448                                 qunlock(&all);
449                                 incref(w);
450                                 sendp(winclosechan, f->w);
451                                 f->w = w;
452                                 dir = dirtab;
453                                 goto Accept;
454                         }
455                         if(skipdir(x->wname[i]))
456                                 break;
457                         id = WIN(f->qid);
458                         d = dirtab;
459                         d++;    /* skip '.' */
460                         for(; d->name; d++)
461                                 if(strcmp(x->wname[i], d->name) == 0){
462                                         path = d->qid;
463                                         type = d->type;
464                                         dir = d;
465                                         goto Accept;
466                                 }
467
468                         break;  /* file not found */
469                 }
470
471                 if(i==0 && err==nil)
472                         err = Eexist;
473         }
474
475         if(err!=nil || t.nwqid<x->nwname){
476                 if(nf){
477                         if(nf->w)
478                                 sendp(winclosechan, nf->w);
479                         nf->open = FALSE;
480                         nf->busy = FALSE;
481                 }
482         }else if(t.nwqid == x->nwname){
483                 f->dir = dir;
484                 f->qid = qid;
485         }
486
487         return filsysrespond(fs, x, &t, err);
488 }
489
490 static
491 Xfid*
492 filsysopen(Filsys *fs, Xfid *x, Fid *f)
493 {
494         Fcall t;
495         int m;
496
497         /* can't truncate anything but Qtext, so just disregard */
498         if(FILE(f->qid) != Qtext)
499                 x->mode &= ~OTRUNC;
500         x->mode &= ~OCEXEC;
501         /* can't execute or remove anything */
502         if(x->mode==OEXEC || (x->mode&ORCLOSE))
503                 goto Deny;
504         switch(x->mode & ~OTRUNC){
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 }