URI:
       tfsys.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tfsys.c (18312B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <regexp.h>
            5 #include <thread.h>
            6 #include <fcall.h>
            7 #include <plumb.h>
            8 #include "plumber.h"
            9 
           10 enum
           11 {
           12         Stack = 32*1024
           13 };
           14 
           15 typedef struct Dirtab Dirtab;
           16 typedef struct Fid Fid;
           17 typedef struct Holdq Holdq;
           18 typedef struct Readreq Readreq;
           19 typedef struct Sendreq Sendreq;
           20 
           21 struct Dirtab
           22 {
           23         char                *name;
           24         uchar        type;
           25         uint                qid;
           26         uint                perm;
           27         int                nopen;                /* #fids open on this port */
           28         Fid                *fopen;
           29         Holdq        *holdq;
           30         Readreq        *readq;
           31         Sendreq        *sendq;
           32 };
           33 
           34 struct Fid
           35 {
           36         int                fid;
           37         int                busy;
           38         int                open;
           39         int                mode;
           40         Qid                qid;
           41         Dirtab        *dir;
           42         long                offset;                /* zeroed at beginning of each message, read or write */
           43         char                *writebuf;                /* partial message written so far; offset tells how much */
           44         Fid                *next;
           45         Fid                *nextopen;
           46 };
           47 
           48 struct Readreq
           49 {
           50         Fid                *fid;
           51         Fcall                *fcall;
           52         uchar        *buf;
           53         Readreq        *next;
           54 };
           55 
           56 struct Sendreq
           57 {
           58         int                        nfid;                /* number of fids that should receive this message */
           59         int                        nleft;                /* number left that haven't received it */
           60         Fid                        **fid;        /* fid[nfid] */
           61         Plumbmsg        *msg;
           62         char                        *pack;        /* plumbpack()ed message */
           63         int                        npack;        /* length of pack */
           64         Sendreq                *next;
           65 };
           66 
           67 struct Holdq
           68 {
           69         Plumbmsg        *msg;
           70         Holdq                *next;
           71 };
           72 
           73 struct        /* needed because incref() doesn't return value */
           74 {
           75         Lock        lk;
           76         int        ref;
           77 } rulesref;
           78 
           79 enum
           80 {
           81         NDIR        = 50,
           82         Nhash        = 16,
           83 
           84         Qdir                = 0,
           85         Qrules        = 1,
           86         Qsend        = 2,
           87         Qport        = 3,
           88         NQID        = Qport
           89 };
           90 
           91 static Dirtab dir[NDIR] =
           92 {
           93         { ".",                        QTDIR,        Qdir,                        0500|DMDIR },
           94         { "rules",                QTFILE,        Qrules,                0600 },
           95         { "send",                QTFILE,        Qsend,                0200 }
           96 };
           97 static int        ndir = NQID;
           98 
           99 static int                srvfd;
          100 #define clock plumbclock        /* SunOS name clash */
          101 static int                clock;
          102 static Fid                *fids[Nhash];
          103 static QLock        readlock;
          104 static QLock        queue;
          105 static int                messagesize = 8192+IOHDRSZ;        /* good start */
          106 
          107 static void        fsysproc(void*);
          108 static void fsysrespond(Fcall*, uchar*, char*);
          109 static Fid*        newfid(int);
          110 
          111 static Fcall* fsysflush(Fcall*, uchar*, Fid*);
          112 static Fcall* fsysversion(Fcall*, uchar*, Fid*);
          113 static Fcall* fsysauth(Fcall*, uchar*, Fid*);
          114 static Fcall* fsysattach(Fcall*, uchar*, Fid*);
          115 static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
          116 static Fcall* fsysopen(Fcall*, uchar*, Fid*);
          117 static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
          118 static Fcall* fsysread(Fcall*, uchar*, Fid*);
          119 static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
          120 static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
          121 static Fcall* fsysremove(Fcall*, uchar*, Fid*);
          122 static Fcall* fsysstat(Fcall*, uchar*, Fid*);
          123 static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
          124 
          125 Fcall*         (*fcall[Tmax])(Fcall*, uchar*, Fid*);
          126 
          127 static void
          128 initfcall(void)
          129 {
          130         fcall[Tflush]        = fsysflush;
          131         fcall[Tversion]        = fsysversion;
          132         fcall[Tauth]        = fsysauth;
          133         fcall[Tattach]        = fsysattach;
          134         fcall[Twalk]        = fsyswalk;
          135         fcall[Topen]        = fsysopen;
          136         fcall[Tcreate]        = fsyscreate;
          137         fcall[Tread]        = fsysread;
          138         fcall[Twrite]        = fsyswrite;
          139         fcall[Tclunk]        = fsysclunk;
          140         fcall[Tremove]= fsysremove;
          141         fcall[Tstat]        = fsysstat;
          142         fcall[Twstat]        = fsyswstat;
          143 }
          144 
          145 char        Ebadfcall[] =        "bad fcall type";
          146 char        Eperm[] =         "permission denied";
          147 char        Enomem[] =        "malloc failed for buffer";
          148 char        Enotdir[] =        "not a directory";
          149 char        Enoexist[] =        "plumb file does not exist";
          150 char        Eisdir[] =                "file is a directory";
          151 char        Ebadmsg[] =        "bad plumb message format";
          152 char Enosuchport[] ="no such plumb port";
          153 char Enoport[] =        "couldn't find destination for message";
          154 char        Einuse[] =         "file already open";
          155 
          156 /*
          157  * Add new port.  A no-op if port already exists or is the null string
          158  */
          159 void
          160 addport(char *port)
          161 {
          162         int i;
          163 
          164         if(port == nil)
          165                 return;
          166         for(i=NQID; i<ndir; i++)
          167                 if(strcmp(port, dir[i].name) == 0)
          168                         return;
          169         if(i == NDIR){
          170                 fprint(2, "plumb: too many ports; max %d\n", NDIR);
          171                 return;
          172         }
          173         ndir++;
          174         dir[i].name = estrdup(port);
          175         dir[i].qid = i;
          176         dir[i].perm = 0400;
          177         nports++;
          178         ports = erealloc(ports, nports*sizeof(char*));
          179         ports[nports-1] = dir[i].name;
          180 }
          181 
          182 static ulong
          183 getclock(void)
          184 {
          185         return time(0);
          186 }
          187 
          188 void
          189 startfsys(int foreground)
          190 {
          191         int p[2];
          192 
          193         fmtinstall('F', fcallfmt);
          194         clock = getclock();
          195         if(pipe(p) < 0)
          196                 error("can't create pipe: %r");
          197         /* 0 will be server end, 1 will be client end */
          198         srvfd = p[0];
          199         if(post9pservice(p[1], "plumb", nil) < 0)
          200                 sysfatal("post9pservice plumb: %r");
          201         close(p[1]);
          202         if(foreground)
          203                 fsysproc(nil);
          204         else
          205                 proccreate(fsysproc, nil, Stack);
          206 }
          207 
          208 static void
          209 fsysproc(void *v)
          210 {
          211         int n;
          212         Fcall *t;
          213         Fid *f;
          214         uchar *buf;
          215 
          216         USED(v);
          217         initfcall();
          218         t = nil;
          219         for(;;){
          220                 buf = malloc(messagesize);        /* avoid memset of emalloc */
          221                 if(buf == nil)
          222                         error("malloc failed: %r");
          223                 qlock(&readlock);
          224                 n = read9pmsg(srvfd, buf, messagesize);
          225                 if(n <= 0){
          226                         if(n < 0)
          227                                 error("i/o error on server channel");
          228                         threadexitsall("unmounted");
          229                 }
          230                 /*
          231                  * can give false positive (create an extra fsysproc) once in a while,
          232                  * but no false negatives, so good enough.  once we have one extra
          233                  * we'll never have more.
          234                  */
          235                 if(readlock.waiting.head == nil)        /* no other processes waiting to read; start one */
          236                         proccreate(fsysproc, nil, Stack);
          237                 qunlock(&readlock);
          238                 if(t == nil)
          239                         t = emalloc(sizeof(Fcall));
          240                 if(convM2S(buf, n, t) != n)
          241                         error("convert error in convM2S");
          242                 if(debug)
          243                         fprint(2, "<= %F\n", t);
          244                 if(fcall[t->type] == 0)
          245                         fsysrespond(t, buf, Ebadfcall);
          246                 else{
          247                         if(t->type==Tversion || t->type==Tauth)
          248                                 f = nil;
          249                         else
          250                                 f = newfid(t->fid);
          251                         t = (*fcall[t->type])(t, buf, f);
          252                 }
          253         }
          254 }
          255 
          256 static void
          257 fsysrespond(Fcall *t, uchar *buf, char *err)
          258 {
          259         int n;
          260 
          261         if(err){
          262                 t->type = Rerror;
          263                 t->ename = err;
          264         }else
          265                 t->type++;
          266         if(buf == nil)
          267                 buf = emalloc(messagesize);
          268         n = convS2M(t, buf, messagesize);
          269         if(n < 0)
          270                 error("convert error in convS2M");
          271         if(write(srvfd, buf, n) != n)
          272                 error("write error in respond");
          273         if(debug)
          274                 fprint(2, "=> %F\n", t);
          275         free(buf);
          276 }
          277 
          278 static
          279 Fid*
          280 newfid(int fid)
          281 {
          282         Fid *f, *ff, **fh;
          283 
          284         qlock(&queue);
          285         ff = nil;
          286         fh = &fids[fid&(Nhash-1)];
          287         for(f=*fh; f; f=f->next)
          288                 if(f->fid == fid)
          289                         goto Return;
          290                 else if(ff==nil && !f->busy)
          291                         ff = f;
          292         if(ff){
          293                 ff->fid = fid;
          294                 f = ff;
          295                 goto Return;
          296         }
          297         f = emalloc(sizeof *f);
          298         f->fid = fid;
          299         f->next = *fh;
          300         *fh = f;
          301     Return:
          302         qunlock(&queue);
          303         return f;
          304 }
          305 
          306 static uint
          307 dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
          308 {
          309         Dir d;
          310 
          311         d.qid.type = dir->type;
          312         d.qid.path = dir->qid;
          313         d.qid.vers = 0;
          314         d.mode = dir->perm;
          315         d.length = 0;        /* would be nice to do better */
          316         d.name = dir->name;
          317         d.uid = user;
          318         d.gid = user;
          319         d.muid = user;
          320         d.atime = clock;
          321         d.mtime = clock;
          322         return convD2M(&d, buf, nbuf);
          323 }
          324 
          325 static void
          326 queuesend(Dirtab *d, Plumbmsg *m)
          327 {
          328         Sendreq *s, *t;
          329         Fid *f;
          330         int i;
          331 
          332         s = emalloc(sizeof(Sendreq));
          333         s->nfid = d->nopen;
          334         s->nleft = s->nfid;
          335         s->fid = emalloc(s->nfid*sizeof(Fid*));
          336         i = 0;
          337         /* build array of fids open on this channel */
          338         for(f=d->fopen; f!=nil; f=f->nextopen)
          339                 s->fid[i++] = f;
          340         s->msg = m;
          341         s->next = nil;
          342         /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
          343         for(t=d->sendq; t!=nil; t=t->next)
          344                 if(t->next == nil)
          345                         break;
          346         if(t == nil)
          347                 d->sendq = s;
          348         else
          349                 t->next = s;
          350 }
          351 
          352 static void
          353 queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
          354 {
          355         Readreq *r;
          356 
          357         r = emalloc(sizeof(Readreq));
          358         r->fcall = t;
          359         r->buf = buf;
          360         r->fid = f;
          361         r->next = d->readq;
          362         d->readq = r;
          363 }
          364 
          365 static void
          366 drainqueue(Dirtab *d)
          367 {
          368         Readreq *r, *nextr, *prevr;
          369         Sendreq *s, *nexts, *prevs;
          370         int i, n;
          371 
          372         prevs = nil;
          373         for(s=d->sendq; s!=nil; s=nexts){
          374                 nexts = s->next;
          375                 for(i=0; i<s->nfid; i++){
          376                         prevr = nil;
          377                         for(r=d->readq; r!=nil; r=nextr){
          378                                 nextr = r->next;
          379                                 if(r->fid == s->fid[i]){
          380                                         /* pack the message if necessary */
          381                                         if(s->pack == nil)
          382                                                 s->pack = plumbpack(s->msg, &s->npack);
          383                                         /* exchange the stuff... */
          384                                         r->fcall->data = s->pack+r->fid->offset;
          385                                         n = s->npack - r->fid->offset;
          386                                         if(n > messagesize-IOHDRSZ)
          387                                                 n = messagesize-IOHDRSZ;
          388                                         if(n > r->fcall->count)
          389                                                 n = r->fcall->count;
          390                                         r->fcall->count = n;
          391                                         fsysrespond(r->fcall, r->buf, nil);
          392                                         r->fid->offset += n;
          393                                         if(r->fid->offset >= s->npack){
          394                                                 /* message transferred; delete this fid from send queue */
          395                                                 r->fid->offset = 0;
          396                                                 s->fid[i] = nil;
          397                                                 s->nleft--;
          398                                         }
          399                                         /* delete read request from queue */
          400                                         if(prevr)
          401                                                 prevr->next = r->next;
          402                                         else
          403                                                 d->readq = r->next;
          404                                         free(r->fcall);
          405                                         free(r);
          406                                         break;
          407                                 }else
          408                                         prevr = r;
          409                         }
          410                 }
          411                 /* if no fids left, delete this send from queue */
          412                 if(s->nleft == 0){
          413                         free(s->fid);
          414                         plumbfree(s->msg);
          415                         free(s->pack);
          416                         if(prevs)
          417                                 prevs->next = s->next;
          418                         else
          419                                 d->sendq = s->next;
          420                         free(s);
          421                 }else
          422                         prevs = s;
          423         }
          424 }
          425 
          426 /* can't flush a send because they are always answered synchronously */
          427 static void
          428 flushqueue(Dirtab *d, int oldtag)
          429 {
          430         Readreq *r, *prevr;
          431 
          432         prevr = nil;
          433         for(r=d->readq; r!=nil; r=r->next){
          434                 if(oldtag == r->fcall->tag){
          435                         /* delete read request from queue */
          436                         if(prevr)
          437                                 prevr->next = r->next;
          438                         else
          439                                 d->readq = r->next;
          440                         free(r->fcall);
          441                         free(r->buf);
          442                         free(r);
          443                         return;
          444                 }
          445                 prevr = r;
          446         }
          447 }
          448 
          449 /* remove messages awaiting delivery to now-closing fid */
          450 static void
          451 removesenders(Dirtab *d, Fid *fid)
          452 {
          453         Sendreq *s, *nexts, *prevs;
          454         int i;
          455 
          456         prevs = nil;
          457         for(s=d->sendq; s!=nil; s=nexts){
          458                 nexts = s->next;
          459                 for(i=0; i<s->nfid; i++)
          460                         if(fid == s->fid[i]){
          461                                 /* delete this fid from send queue */
          462                                 s->fid[i] = nil;
          463                                 s->nleft--;
          464                                 break;
          465                         }
          466                 /* if no fids left, delete this send from queue */
          467                 if(s->nleft == 0){
          468                         free(s->fid);
          469                         plumbfree(s->msg);
          470                         free(s->pack);
          471                         if(prevs)
          472                                 prevs->next = s->next;
          473                         else
          474                                 d->sendq = s->next;
          475                         free(s);
          476                 }else
          477                         prevs = s;
          478         }
          479 }
          480 
          481 static void
          482 hold(Plumbmsg *m, Dirtab *d)
          483 {
          484         Holdq *h, *q;
          485 
          486         h = emalloc(sizeof(Holdq));
          487         h->msg = m;
          488         /* add to end of queue */
          489         if(d->holdq == nil)
          490                 d->holdq = h;
          491         else{
          492                 for(q=d->holdq; q->next!=nil; q=q->next)
          493                         ;
          494                 q->next = h;
          495         }
          496 }
          497 
          498 static void
          499 queueheld(Dirtab *d)
          500 {
          501         Holdq *h;
          502 
          503         while(d->holdq != nil){
          504                 h = d->holdq;
          505                 d->holdq = h->next;
          506                 queuesend(d, h->msg);
          507                 /* no need to drain queue because we know no-one is reading yet */
          508                 free(h);
          509         }
          510 }
          511 
          512 static void
          513 dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
          514 {
          515         int i;
          516         char *err;
          517 
          518         qlock(&queue);
          519         err = nil;
          520         if(m->dst==nil || m->dst[0]=='\0'){
          521                 err = Enoport;
          522                 if(rs != nil)
          523                         err = startup(rs, e);
          524                 plumbfree(m);
          525         }else
          526                 for(i=NQID; i<ndir; i++)
          527                         if(strcmp(m->dst, dir[i].name) == 0){
          528                                 if(dir[i].nopen == 0){
          529                                         err = startup(rs, e);
          530                                         if(e!=nil && e->holdforclient)
          531                                                 hold(m, &dir[i]);
          532                                         else
          533                                                 plumbfree(m);
          534                                 }else{
          535                                         queuesend(&dir[i], m);
          536                                         drainqueue(&dir[i]);
          537                                 }
          538                                 break;
          539                         }
          540         freeexec(e);
          541         qunlock(&queue);
          542         fsysrespond(t, buf, err);
          543         free(t);
          544 }
          545 
          546 static Fcall*
          547 fsysversion(Fcall *t, uchar *buf, Fid *fid)
          548 {
          549         USED(fid);
          550 
          551         if(t->msize < 256){
          552                 fsysrespond(t, buf, "version: message size too small");
          553                 return t;
          554         }
          555         if(t->msize < messagesize)
          556                 messagesize = t->msize;
          557         t->msize = messagesize;
          558         if(strncmp(t->version, "9P2000", 6) != 0){
          559                 fsysrespond(t, buf, "unrecognized 9P version");
          560                 return t;
          561         }
          562         t->version = "9P2000";
          563         fsysrespond(t, buf, nil);
          564         return t;
          565 }
          566 
          567 static Fcall*
          568 fsysauth(Fcall *t, uchar *buf, Fid *fid)
          569 {
          570         USED(fid);
          571         fsysrespond(t, buf, "plumber: authentication not required");
          572         return t;
          573 }
          574 
          575 static Fcall*
          576 fsysattach(Fcall *t, uchar *buf, Fid *f)
          577 {
          578         Fcall out;
          579 
          580 /*
          581         if(strcmp(t->uname, user) != 0){
          582                 fsysrespond(&out, buf, Eperm);
          583                 return t;
          584         }
          585 */
          586         f->busy = 1;
          587         f->open = 0;
          588         f->qid.type = QTDIR;
          589         f->qid.path = Qdir;
          590         f->qid.vers = 0;
          591         f->dir = dir;
          592         memset(&out, 0, sizeof(Fcall));
          593         out.type = t->type;
          594         out.tag = t->tag;
          595         out.fid = f->fid;
          596         out.qid = f->qid;
          597         fsysrespond(&out, buf, nil);
          598         return t;
          599 }
          600 
          601 static Fcall*
          602 fsysflush(Fcall *t, uchar *buf, Fid *fid)
          603 {
          604         int i;
          605 
          606         USED(fid);
          607         qlock(&queue);
          608         for(i=NQID; i<ndir; i++)
          609                 flushqueue(&dir[i], t->oldtag);
          610         qunlock(&queue);
          611         fsysrespond(t, buf, nil);
          612         return t;
          613 }
          614 
          615 static Fcall*
          616 fsyswalk(Fcall *t, uchar *buf, Fid *f)
          617 {
          618         Fcall out;
          619         Fid *nf;
          620         ulong path;
          621         Dirtab *d, *dir;
          622         Qid q;
          623         int i;
          624         uchar type;
          625         char *err;
          626 
          627         if(f->open){
          628                 fsysrespond(t, buf, "clone of an open fid");
          629                 return t;
          630         }
          631 
          632         nf = nil;
          633         if(t->fid  != t->newfid){
          634                 nf = newfid(t->newfid);
          635                 if(nf->busy){
          636                         fsysrespond(t, buf, "clone to a busy fid");
          637                         return t;
          638                 }
          639                 nf->busy = 1;
          640                 nf->open = 0;
          641                 nf->dir = f->dir;
          642                 nf->qid = f->qid;
          643                 f = nf;        /* walk f */
          644         }
          645 
          646         out.nwqid = 0;
          647         err = nil;
          648         dir = f->dir;
          649         q = f->qid;
          650 
          651         if(t->nwname > 0){
          652                 for(i=0; i<t->nwname; i++){
          653                         if((q.type & QTDIR) == 0){
          654                                 err = Enotdir;
          655                                 break;
          656                         }
          657                         if(strcmp(t->wname[i], "..") == 0){
          658                                 type = QTDIR;
          659                                 path = Qdir;
          660         Accept:
          661                                 q.type = type;
          662                                 q.vers = 0;
          663                                 q.path = path;
          664                                 out.wqid[out.nwqid++] = q;
          665                                 continue;
          666                         }
          667                         d = dir;
          668                         d++;        /* skip '.' */
          669                         for(; d->name; d++)
          670                                 if(strcmp(t->wname[i], d->name) == 0){
          671                                         type = d->type;
          672                                         path = d->qid;
          673                                         dir = d;
          674                                         goto Accept;
          675                                 }
          676                         err = Enoexist;
          677                         break;
          678                 }
          679         }
          680 
          681         out.type = t->type;
          682         out.tag = t->tag;
          683         if(err!=nil || out.nwqid<t->nwname){
          684                 if(nf)
          685                         nf->busy = 0;
          686         }else if(out.nwqid == t->nwname){
          687                 f->qid = q;
          688                 f->dir = dir;
          689         }
          690 
          691         fsysrespond(&out, buf, err);
          692         return t;
          693 }
          694 
          695 static Fcall*
          696 fsysopen(Fcall *t, uchar *buf, Fid *f)
          697 {
          698         int m, clearrules, mode;
          699 
          700         clearrules = 0;
          701         if(t->mode & OTRUNC){
          702                 if(f->qid.path != Qrules)
          703                         goto Deny;
          704                 clearrules = 1;
          705         }
          706         /* can't truncate anything, so just disregard */
          707         mode = t->mode & ~(OTRUNC|OCEXEC);
          708         /* can't execute or remove anything */
          709         if(mode==OEXEC || (mode&ORCLOSE))
          710                 goto Deny;
          711         switch(mode){
          712         default:
          713                 goto Deny;
          714         case OREAD:
          715                 m = 0400;
          716                 break;
          717         case OWRITE:
          718                 m = 0200;
          719                 break;
          720         case ORDWR:
          721                 m = 0600;
          722                 break;
          723         }
          724         if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
          725                 goto Deny;
          726         if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
          727                 lock(&rulesref.lk);
          728                 if(rulesref.ref++ != 0){
          729                         rulesref.ref--;
          730                         unlock(&rulesref.lk);
          731                         fsysrespond(t, buf, Einuse);
          732                         return t;
          733                 }
          734                 unlock(&rulesref.lk);
          735         }
          736         if(clearrules){
          737                 writerules(nil, 0);
          738                 rules[0] = nil;
          739         }
          740         t->qid = f->qid;
          741         t->iounit = 0;
          742         qlock(&queue);
          743         f->mode = mode;
          744         f->open = 1;
          745         f->dir->nopen++;
          746         f->nextopen = f->dir->fopen;
          747         f->dir->fopen = f;
          748         queueheld(f->dir);
          749         qunlock(&queue);
          750         fsysrespond(t, buf, nil);
          751         return t;
          752 
          753     Deny:
          754         fsysrespond(t, buf, Eperm);
          755         return t;
          756 }
          757 
          758 static Fcall*
          759 fsyscreate(Fcall *t, uchar *buf, Fid *fid)
          760 {
          761         USED(fid);
          762         fsysrespond(t, buf, Eperm);
          763         return t;
          764 }
          765 
          766 static Fcall*
          767 fsysreadrules(Fcall *t, uchar *buf)
          768 {
          769         char *p;
          770         int n;
          771 
          772         p = printrules();
          773         n = strlen(p);
          774         t->data = p;
          775         if(t->offset >= n)
          776                 t->count = 0;
          777         else{
          778                 t->data = p+t->offset;
          779                 if(t->offset+t->count > n)
          780                         t->count = n-t->offset;
          781         }
          782         fsysrespond(t, buf, nil);
          783         free(p);
          784         return t;
          785 }
          786 
          787 static Fcall*
          788 fsysread(Fcall *t, uchar *buf, Fid *f)
          789 {
          790         uchar *b;
          791         int i, n, o, e;
          792         uint len;
          793         Dirtab *d;
          794         uint clock;
          795 
          796         if(f->qid.path != Qdir){
          797                 if(f->qid.path == Qrules)
          798                         return fsysreadrules(t, buf);
          799                 /* read from port */
          800                 if(f->qid.path < NQID){
          801                         fsysrespond(t, buf, "internal error: unknown read port");
          802                         return t;
          803                 }
          804                 qlock(&queue);
          805                 queueread(f->dir, t, buf, f);
          806                 drainqueue(f->dir);
          807                 qunlock(&queue);
          808                 return nil;
          809         }
          810         o = t->offset;
          811         e = t->offset+t->count;
          812         clock = getclock();
          813         b = malloc(messagesize-IOHDRSZ);
          814         if(b == nil){
          815                 fsysrespond(t, buf, Enomem);
          816                 return t;
          817         }
          818         n = 0;
          819         d = dir;
          820         d++;        /* first entry is '.' */
          821         for(i=0; d->name!=nil && i<e; i+=len){
          822                 len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
          823                 if(len <= BIT16SZ)
          824                         break;
          825                 if(i >= o)
          826                         n += len;
          827                 d++;
          828         }
          829         t->data = (char*)b;
          830         t->count = n;
          831         fsysrespond(t, buf, nil);
          832         free(b);
          833         return t;
          834 }
          835 
          836 static Fcall*
          837 fsyswrite(Fcall *t, uchar *buf, Fid *f)
          838 {
          839         Plumbmsg *m;
          840         int i, n;
          841         long count;
          842         char *data;
          843         Exec *e;
          844 
          845         switch((int)f->qid.path){
          846         case Qdir:
          847                 fsysrespond(t, buf, Eisdir);
          848                 return t;
          849         case Qrules:
          850                 clock = getclock();
          851                 fsysrespond(t, buf, writerules(t->data, t->count));
          852                 return t;
          853         case Qsend:
          854                 if(f->offset == 0){
          855                         data = t->data;
          856                         count = t->count;
          857                 }else{
          858                         /* partial message already assembled */
          859                         f->writebuf = erealloc(f->writebuf, f->offset + t->count);
          860                         memmove(f->writebuf+f->offset, t->data, t->count);
          861                         data = f->writebuf;
          862                         count = f->offset+t->count;
          863                 }
          864                 m = plumbunpackpartial(data, count, &n);
          865                 if(m == nil){
          866                         if(n == 0){
          867                                 f->offset = 0;
          868                                 free(f->writebuf);
          869                                 f->writebuf = nil;
          870                                 fsysrespond(t, buf, Ebadmsg);
          871                                 return t;
          872                         }
          873                         /* can read more... */
          874                         if(f->offset == 0){
          875                                 f->writebuf = emalloc(t->count);
          876                                 memmove(f->writebuf, t->data, t->count);
          877                         }
          878                         /* else buffer has already been grown */
          879                         f->offset += t->count;
          880                         fsysrespond(t, buf, nil);
          881                         return t;
          882                 }
          883                 /* release partial buffer */
          884                 f->offset = 0;
          885                 free(f->writebuf);
          886                 f->writebuf = nil;
          887                 for(i=0; rules[i]; i++)
          888                         if((e=matchruleset(m, rules[i])) != nil){
          889                                 dispose(t, buf, m, rules[i], e);
          890                                 return nil;
          891                         }
          892                 if(m->dst != nil){
          893                         dispose(t, buf, m, nil, nil);
          894                         return nil;
          895                 }
          896                 fsysrespond(t, buf, "no matching plumb rule");
          897                 return t;
          898         }
          899         fsysrespond(t, buf, "internal error: write to unknown file");
          900         return t;
          901 }
          902 
          903 static Fcall*
          904 fsysstat(Fcall *t, uchar *buf, Fid *f)
          905 {
          906         t->stat = emalloc(messagesize-IOHDRSZ);
          907         t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
          908         fsysrespond(t, buf, nil);
          909         free(t->stat);
          910         t->stat = nil;
          911         return t;
          912 }
          913 
          914 static Fcall*
          915 fsyswstat(Fcall *t, uchar *buf, Fid *fid)
          916 {
          917         USED(fid);
          918         fsysrespond(t, buf, Eperm);
          919         return t;
          920 }
          921 
          922 static Fcall*
          923 fsysremove(Fcall *t, uchar *buf, Fid *fid)
          924 {
          925         USED(fid);
          926         fsysrespond(t, buf, Eperm);
          927         return t;
          928 }
          929 
          930 static Fcall*
          931 fsysclunk(Fcall *t, uchar *buf, Fid *f)
          932 {
          933         Fid *prev, *p;
          934         Dirtab *d;
          935 
          936         qlock(&queue);
          937         if(f->open){
          938                 d = f->dir;
          939                 d->nopen--;
          940                 if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
          941                         /*
          942                          * just to be sure last rule is parsed; error messages will be lost, though,
          943                          * unless last write ended with a blank line
          944                          */
          945                         writerules(nil, 0);
          946                         lock(&rulesref.lk);
          947                         rulesref.ref--;
          948                         unlock(&rulesref.lk);
          949                 }
          950                 prev = nil;
          951                 for(p=d->fopen; p; p=p->nextopen){
          952                         if(p == f){
          953                                 if(prev)
          954                                         prev->nextopen = f->nextopen;
          955                                 else
          956                                         d->fopen = f->nextopen;
          957                                 removesenders(d, f);
          958                                 break;
          959                         }
          960                         prev = p;
          961                 }
          962         }
          963         f->busy = 0;
          964         f->open = 0;
          965         f->offset = 0;
          966         if(f->writebuf != nil){
          967                 free(f->writebuf);
          968                 f->writebuf = nil;
          969         }
          970         qunlock(&queue);
          971         fsysrespond(t, buf, nil);
          972         return t;
          973 }