]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/acme/fsys.c
stats: show amount of reclaimable pages (add -r flag)
[plan9front.git] / sys / src / cmd / acme / 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 <plumb.h>
11 #include "dat.h"
12 #include "fns.h"
13
14 static  int     cfd;
15 static  int     sfd;
16
17 enum
18 {
19         Nhash   = 16,
20         DEBUG   = 0
21 };
22
23 static  Fid     *fids[Nhash];
24
25 Fid     *newfid(int);
26
27 static  Xfid*   fsysflush(Xfid*, Fid*);
28 static  Xfid*   fsysauth(Xfid*, Fid*);
29 static  Xfid*   fsysversion(Xfid*, Fid*);
30 static  Xfid*   fsysattach(Xfid*, Fid*);
31 static  Xfid*   fsyswalk(Xfid*, Fid*);
32 static  Xfid*   fsysopen(Xfid*, Fid*);
33 static  Xfid*   fsyscreate(Xfid*, Fid*);
34 static  Xfid*   fsysread(Xfid*, Fid*);
35 static  Xfid*   fsyswrite(Xfid*, Fid*);
36 static  Xfid*   fsysclunk(Xfid*, Fid*);
37 static  Xfid*   fsysremove(Xfid*, Fid*);
38 static  Xfid*   fsysstat(Xfid*, Fid*);
39 static  Xfid*   fsyswstat(Xfid*, Fid*);
40
41 Xfid*   (*fcall[Tmax])(Xfid*, Fid*) =
42 {
43         [Tflush]        = fsysflush,
44         [Tversion]      = fsysversion,
45         [Tauth] = fsysauth,
46         [Tattach]       = fsysattach,
47         [Twalk] = fsyswalk,
48         [Topen] = fsysopen,
49         [Tcreate]       = fsyscreate,
50         [Tread] = fsysread,
51         [Twrite]        = fsyswrite,
52         [Tclunk]        = fsysclunk,
53         [Tremove]= fsysremove,
54         [Tstat] = fsysstat,
55         [Twstat]        = fsyswstat,
56 };
57
58 char Eperm[] = "permission denied";
59 char Eexist[] = "file does not exist";
60 char Enotdir[] = "not a directory";
61
62 Dirtab dirtab[]=
63 {
64         { ".",                  QTDIR,  Qdir,           0500|DMDIR },
65         { "acme",               QTDIR,  Qacme,  0500|DMDIR },
66         { "cons",               QTFILE, Qcons,  0600 },
67         { "consctl",    QTFILE, Qconsctl,       0000 },
68         { "draw",               QTDIR,  Qdraw,  0000|DMDIR },   /* to suppress graphics progs started in acme */
69         { "editout",    QTFILE, Qeditout,       0200 },
70         { "index",              QTFILE, Qindex, 0400 },
71         { "label",              QTFILE, Qlabel, 0600 },
72         { "new",                QTDIR,  Qnew,   0500|DMDIR },
73         { nil, }
74 };
75
76 Dirtab dirtabw[]=
77 {
78         { ".",                  QTDIR,          Qdir,                   0500|DMDIR },
79         { "addr",               QTFILE,         QWaddr,         0600 },
80         { "body",               QTAPPEND,       QWbody,         0600|DMAPPEND },
81         { "ctl",                QTFILE,         QWctl,          0600 },
82         { "data",               QTFILE,         QWdata,         0600 },
83         { "editout",    QTFILE,         QWeditout,      0200 },
84         { "errors",             QTFILE,         QWerrors,               0200 },
85         { "event",              QTFILE,         QWevent,                0600 },
86         { "rdsel",              QTFILE,         QWrdsel,                0400 },
87         { "wrsel",              QTFILE,         QWwrsel,                0200 },
88         { "tag",                QTAPPEND,       QWtag,          0600|DMAPPEND },
89         { "xdata",              QTFILE,         QWxdata,                0600 },
90         { nil, }
91 };
92
93 typedef struct Mnt Mnt;
94 struct Mnt
95 {
96         QLock;
97         int             id;
98         Mntdir  *md;
99 };
100
101 Mnt     mnt;
102
103 Xfid*   respond(Xfid*, Fcall*, char*);
104 int             dostat(int, Dirtab*, uchar*, int, uint);
105 uint    getclock(void);
106
107 char    *user = "Wile E. Coyote";
108 int     clockfd;
109 static int closing = 0;
110 int     messagesize = Maxblock+IOHDRSZ; /* good start */
111
112 void    fsysproc(void *);
113
114 void
115 fsysinit(void)
116 {
117         int p[2];
118         int n, fd;
119         char buf[256];
120
121         if(pipe(p) < 0)
122                 error("can't create pipe");
123         cfd = p[0];
124         sfd = p[1];
125         fmtinstall('F', fcallfmt);
126         clockfd = open("/dev/time", OREAD|OCEXEC);
127         fd = open("/dev/user", OREAD);
128         if(fd >= 0){
129                 n = read(fd, buf, sizeof buf-1);
130                 if(n > 0){
131                         buf[n] = 0;
132                         user = estrdup(buf);
133                 }
134                 close(fd);
135         }
136         proccreate(fsysproc, nil, STACK);
137 }
138
139 void
140 fsysproc(void *)
141 {
142         int n;
143         Xfid *x;
144         Fid *f;
145         Fcall t;
146         uchar *buf;
147
148         x = nil;
149         for(;;){
150                 buf = emalloc(messagesize+UTFmax);      /* overflow for appending partial rune in xfidwrite */
151                 n = read9pmsg(sfd, buf, messagesize);
152                 if(n <= 0){
153                         if(closing)
154                                 break;
155                         error("i/o error on server channel");
156                 }
157                 if(x == nil){
158                         sendp(cxfidalloc, nil);
159                         x = recvp(cxfidalloc);
160                 }
161                 x->buf = buf;
162                 if(convM2S(buf, n, x) != n)
163                         error("convert error in convM2S");
164                 if(DEBUG)
165                         fprint(2, "%F\n", &x->Fcall);
166                 if(fcall[x->type] == nil)
167                         x = respond(x, &t, "bad fcall type");
168                 else{
169                         switch(x->type){
170                         case Tversion:
171                         case Tauth:
172                         case Tflush:
173                                 f = nil;
174                                 break;
175                         case Tattach:
176                                 f = newfid(x->fid);
177                                 break;
178                         default:
179                                 f = newfid(x->fid);
180                                 if(!f->busy){
181                                         x->f = f;
182                                         x = respond(x, &t, "fid not in use");
183                                         continue;
184                                 }
185                                 break;
186                         }
187                         x->f = f;
188                         x  = (*fcall[x->type])(x, f);
189                 }
190         }
191 }
192
193 Mntdir*
194 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
195 {
196         Mntdir *m;
197         int id;
198
199         qlock(&mnt);
200         id = ++mnt.id;
201         m = emalloc(sizeof *m);
202         m->id = id;
203         m->dir =  dir;
204         m->ref = 1;     /* one for Command, one will be incremented in attach */
205         m->ndir = ndir;
206         m->next = mnt.md;
207         m->incl = incl;
208         m->nincl = nincl;
209         mnt.md = m;
210         qunlock(&mnt);
211         return m;
212 }
213
214 void
215 fsysincid(Mntdir *m)
216 {
217         qlock(&mnt);
218         m->ref++;
219         qunlock(&mnt);
220 }
221
222 void
223 fsysdelid(Mntdir *idm)
224 {
225         Mntdir *m, *prev;
226         int i;
227         char buf[64];
228
229         if(idm == nil)
230                 return;
231         qlock(&mnt);
232         if(--idm->ref > 0){
233                 qunlock(&mnt);
234                 return;
235         }
236         prev = nil;
237         for(m=mnt.md; m; m=m->next){
238                 if(m == idm){
239                         if(prev)
240                                 prev->next = m->next;
241                         else
242                                 mnt.md = m->next;
243                         for(i=0; i<m->nincl; i++)
244                                 free(m->incl[i]);
245                         free(m->incl);
246                         free(m->dir);
247                         free(m);
248                         qunlock(&mnt);
249                         return;
250                 }
251                 prev = m;
252         }
253         qunlock(&mnt);
254         sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
255         sendp(cerr, estrdup(buf));
256 }
257
258 /*
259  * Called only in exec.c:/^run(), from a different FD group
260  */
261 Mntdir*
262 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
263 {
264         char buf[256];
265         Mntdir *m;
266
267         /* close server side so don't hang if acme is half-exited */
268         close(sfd);
269         m = fsysaddid(dir, ndir, incl, nincl);
270         sprint(buf, "%d", m->id);
271         if(mount(cfd, -1, "/mnt/acme", MREPL, buf) < 0){
272                 fsysdelid(m);
273                 return nil;
274         }
275         close(cfd);
276         bind("/mnt/acme", "/mnt/wsys", MREPL);
277         if(bind("/mnt/acme", "/dev", MBEFORE) < 0){
278                 fsysdelid(m);
279                 return nil;
280         }
281         return m;
282 }
283
284 void
285 fsysclose(void)
286 {
287         closing = 1;
288         close(cfd);
289         close(sfd);
290 }
291
292 Xfid*
293 respond(Xfid *x, Fcall *t, char *err)
294 {
295         int n;
296
297         if(err){
298                 t->type = Rerror;
299                 t->ename = err;
300         }else
301                 t->type = x->type+1;
302         t->fid = x->fid;
303         t->tag = x->tag;
304         if(x->buf == nil)
305                 x->buf = emalloc(messagesize);
306         n = convS2M(t, x->buf, messagesize);
307         if(n <= 0)
308                 error("convert error in convS2M");
309         if(write(sfd, x->buf, n) != n)
310                 error("write error in respond");
311         free(x->buf);
312         x->buf = nil;
313         if(DEBUG)
314                 fprint(2, "r: %F\n", t);
315         return x;
316 }
317
318 static
319 Xfid*
320 fsysversion(Xfid *x, Fid*)
321 {
322         Fcall t;
323
324         if(x->msize < 256)
325                 return respond(x, &t, "version: message size too small");
326         messagesize = x->msize;
327         t.msize = messagesize;
328         if(strncmp(x->version, "9P2000", 6) != 0)
329                 return respond(x, &t, "unrecognized 9P version");
330         t.version = "9P2000";
331         return respond(x, &t, nil);
332 }
333
334 static
335 Xfid*
336 fsysauth(Xfid *x, Fid*)
337 {
338         Fcall t;
339
340         return respond(x, &t, "acme: authentication not required");
341 }
342
343 static
344 Xfid*
345 fsysflush(Xfid *x, Fid*)
346 {
347         sendp(x->c, xfidflush);
348         return nil;
349 }
350
351 static
352 Xfid*
353 fsysattach(Xfid *x, Fid *f)
354 {
355         Fcall t;
356         int id;
357         Mntdir *m;
358
359         if(strcmp(x->uname, user) != 0)
360                 return respond(x, &t, Eperm);
361         f->busy = TRUE;
362         f->open = FALSE;
363         f->qid.path = Qdir;
364         f->qid.type = QTDIR;
365         f->qid.vers = 0;
366         f->dir = dirtab;
367         f->nrpart = 0;
368         f->w = nil;
369         t.qid = f->qid;
370         f->mntdir = nil;
371         id = atoi(x->aname);
372         qlock(&mnt);
373         for(m=mnt.md; m; m=m->next)
374                 if(m->id == id){
375                         f->mntdir = m;
376                         m->ref++;
377                         break;
378                 }
379         if(m == nil)
380                 sendp(cerr, estrdup("unknown id in attach"));
381         qunlock(&mnt);
382         return respond(x, &t, nil);
383 }
384
385 static
386 Xfid*
387 fsyswalk(Xfid *x, Fid *f)
388 {
389         Fcall t;
390         int c, i, j, id;
391         Qid q;
392         uchar type;
393         ulong path;
394         Fid *nf;
395         Dirtab *d, *dir;
396         Window *w;
397         char *err;
398
399         nf = nil;
400         w = nil;
401         if(f->open)
402                 return respond(x, &t, "walk of open file");
403         if(x->fid != x->newfid){
404                 nf = newfid(x->newfid);
405                 if(nf->busy)
406                         return respond(x, &t, "newfid already in use");
407                 nf->busy = TRUE;
408                 nf->open = FALSE;
409                 nf->mntdir = f->mntdir;
410                 if(f->mntdir)
411                         f->mntdir->ref++;
412                 nf->dir = f->dir;
413                 nf->qid = f->qid;
414                 nf->w = f->w;
415                 nf->nrpart = 0; /* not open, so must be zero */
416                 if(nf->w)
417                         incref(nf->w);
418                 f = nf; /* walk f */
419         }
420
421         t.nwqid = 0;
422         err = nil;
423         dir = nil;
424         id = WIN(f->qid);
425         q = f->qid;
426
427         if(x->nwname > 0){
428                 for(i=0; i<x->nwname; i++){
429                         if((q.type & QTDIR) == 0){
430                                 err = Enotdir;
431                                 break;
432                         }
433
434                         if(strcmp(x->wname[i], "..") == 0){
435                                 type = QTDIR;
436                                 path = Qdir;
437                                 id = 0;
438                                 if(w){
439                                         winclose(w);
440                                         w = nil;
441                                 }
442     Accept:
443                                 if(i == MAXWELEM){
444                                         err = "name too long";
445                                         break;
446                                 }
447                                 q.type = type;
448                                 q.vers = 0;
449                                 q.path = QID(id, path);
450                                 t.wqid[t.nwqid++] = q;
451                                 continue;
452                         }
453
454                         /* is it a numeric name? */
455                         for(j=0; (c=x->wname[i][j]); j++)
456                                 if(c<'0' || '9'<c)
457                                         goto Regular;
458                         /* yes: it's a directory */
459                         if(w)   /* name has form 27/23; get out before losing w */
460                                 break;
461                         id = atoi(x->wname[i]);
462                         qlock(&row);
463                         w = lookid(id, FALSE);
464                         if(w == nil){
465                                 qunlock(&row);
466                                 break;
467                         }
468                         incref(w);      /* we'll drop reference at end if there's an error */
469                         path = Qdir;
470                         type = QTDIR;
471                         qunlock(&row);
472                         dir = dirtabw;
473                         goto Accept;
474         
475     Regular:
476 //                      if(FILE(f->qid) == Qacme)       /* empty directory */
477 //                              break;
478                         if(strcmp(x->wname[i], "new") == 0){
479                                 if(w)
480                                         error("w set in walk to new");
481                                 sendp(cnewwindow, nil); /* signal newwindowthread */
482                                 w = recvp(cnewwindow);  /* receive new window */
483                                 incref(w);
484                                 type = QTDIR;
485                                 path = QID(w->id, Qdir);
486                                 id = w->id;
487                                 dir = dirtabw;
488                                 goto Accept;
489                         }
490
491                         if(id == 0)
492                                 d = dirtab;
493                         else
494                                 d = dirtabw;
495                         d++;    /* skip '.' */
496                         for(; d->name; d++)
497                                 if(strcmp(x->wname[i], d->name) == 0){
498                                         path = d->qid;
499                                         type = d->type;
500                                         dir = d;
501                                         goto Accept;
502                                 }
503
504                         break;  /* file not found */
505                 }
506
507                 if(i==0 && err == nil)
508                         err = Eexist;
509         }
510
511         if(err!=nil || t.nwqid<x->nwname){
512                 if(nf){
513                         nf->busy = FALSE;
514                         fsysdelid(nf->mntdir);
515                 }
516         }else if(t.nwqid  == x->nwname){
517                 if(w){
518                         f->w = w;
519                         w = nil;        /* don't drop the reference */
520                 }
521                 if(dir)
522                         f->dir = dir;
523                 f->qid = q;
524         }
525
526         if(w != nil)
527                 winclose(w);
528
529         return respond(x, &t, err);
530 }
531
532 static
533 Xfid*
534 fsysopen(Xfid *x, Fid *f)
535 {
536         Fcall t;
537         int m;
538
539         /* can't truncate anything, so just disregard */
540         x->mode &= ~(OTRUNC|OCEXEC);
541         /* can't execute or remove anything */
542         if(x->mode==OEXEC || (x->mode&ORCLOSE))
543                 goto Deny;
544         switch(x->mode){
545         default:
546                 goto Deny;
547         case OREAD:
548                 m = 0400;
549                 break;
550         case OWRITE:
551                 m = 0200;
552                 break;
553         case ORDWR:
554                 m = 0600;
555                 break;
556         }
557         if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
558                 goto Deny;
559
560         sendp(x->c, xfidopen);
561         return nil;
562
563     Deny:
564         return respond(x, &t, Eperm);
565 }
566
567 static
568 Xfid*
569 fsyscreate(Xfid *x, Fid*)
570 {
571         Fcall t;
572
573         return respond(x, &t, Eperm);
574 }
575
576 static
577 int
578 idcmp(void *a, void *b)
579 {
580         return *(int*)a - *(int*)b;
581 }
582
583 static
584 Xfid*
585 fsysread(Xfid *x, Fid *f)
586 {
587         Fcall t;
588         uchar *b;
589         int i, id, n, o, e, j, k, *ids, nids;
590         Dirtab *d, dt;
591         Column *c;
592         uint clock, len;
593         char buf[16];
594
595         if(f->qid.type & QTDIR){
596                 if(FILE(f->qid) == Qacme){      /* empty dir */
597                         t.data = nil;
598                         t.count = 0;
599                         respond(x, &t, nil);
600                         return x;
601                 }
602                 o = x->offset;
603                 e = x->offset+x->count;
604                 clock = getclock();
605                 b = emalloc(messagesize);
606                 id = WIN(f->qid);
607                 n = 0;
608                 if(id > 0)
609                         d = dirtabw;
610                 else
611                         d = dirtab;
612                 d++;    /* first entry is '.' */
613                 for(i=0; d->name!=nil && i<e; i+=len){
614                         len = dostat(WIN(x->f->qid), d, b+n, x->count-n, clock);
615                         if(len <= BIT16SZ)
616                                 break;
617                         if(i >= o)
618                                 n += len;
619                         d++;
620                 }
621                 if(id == 0){
622                         qlock(&row);
623                         nids = 0;
624                         ids = nil;
625                         for(j=0; j<row.ncol; j++){
626                                 c = row.col[j];
627                                 for(k=0; k<c->nw; k++){
628                                         ids = realloc(ids, (nids+1)*sizeof(int));
629                                         ids[nids++] = c->w[k]->id;
630                                 }
631                         }
632                         qunlock(&row);
633                         qsort(ids, nids, sizeof ids[0], idcmp);
634                         j = 0;
635                         dt.name = buf;
636                         for(; j<nids && i<e; i+=len){
637                                 k = ids[j];
638                                 sprint(dt.name, "%d", k);
639                                 dt.qid = QID(k, Qdir);
640                                 dt.type = QTDIR;
641                                 dt.perm = DMDIR|0700;
642                                 len = dostat(k, &dt, b+n, x->count-n, clock);
643                                 if(len == 0)
644                                         break;
645                                 if(i >= o)
646                                         n += len;
647                                 j++;
648                         }
649                         free(ids);
650                 }
651                 t.data = (char*)b;
652                 t.count = n;
653                 respond(x, &t, nil);
654                 free(b);
655                 return x;
656         }
657         sendp(x->c, xfidread);
658         return nil;
659 }
660
661 static
662 Xfid*
663 fsyswrite(Xfid *x, Fid*)
664 {
665         sendp(x->c, xfidwrite);
666         return nil;
667 }
668
669 static
670 Xfid*
671 fsysclunk(Xfid *x, Fid *f)
672 {
673         fsysdelid(f->mntdir);
674         sendp(x->c, xfidclose);
675         return nil;
676 }
677
678 static
679 Xfid*
680 fsysremove(Xfid *x, Fid*)
681 {
682         Fcall t;
683
684         return respond(x, &t, Eperm);
685 }
686
687 static
688 Xfid*
689 fsysstat(Xfid *x, Fid *f)
690 {
691         Fcall t;
692
693         t.stat = emalloc(messagesize-IOHDRSZ);
694         t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
695         x = respond(x, &t, nil);
696         free(t.stat);
697         return x;
698 }
699
700 static
701 Xfid*
702 fsyswstat(Xfid *x, Fid*)
703 {
704         Fcall t;
705
706         return respond(x, &t, Eperm);
707 }
708
709 Fid*
710 newfid(int fid)
711 {
712         Fid *f, *ff, **fh;
713
714         ff = nil;
715         fh = &fids[fid&(Nhash-1)];
716         for(f=*fh; f; f=f->next)
717                 if(f->fid == fid)
718                         return f;
719                 else if(ff==nil && f->busy==FALSE)
720                         ff = f;
721         if(ff){
722                 ff->fid = fid;
723                 return ff;
724         }
725         f = emalloc(sizeof *f);
726         f->fid = fid;
727         f->next = *fh;
728         *fh = f;
729         return f;
730 }
731
732 uint
733 getclock()
734 {
735         char buf[32];
736
737         buf[0] = '\0';
738         pread(clockfd, buf, sizeof buf, 0);
739         return atoi(buf);
740 }
741
742 int
743 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
744 {
745         Dir d;
746
747         d.qid.path = QID(id, dir->qid);
748         d.qid.vers = 0;
749         d.qid.type = dir->type;
750         d.mode = dir->perm;
751         d.length = 0;   /* would be nice to do better */
752         d.name = dir->name;
753         d.uid = user;
754         d.gid = user;
755         d.muid = user;
756         d.atime = clock;
757         d.mtime = clock;
758         return convD2M(&d, buf, nbuf);
759 }