]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vnc/exportfs.c
kernel: keep segment locked for data2txt
[plan9front.git] / sys / src / cmd / vnc / exportfs.c
1 #include        <u.h>
2 #include        <libc.h>
3 #include        <fcall.h>
4 #include        "compat.h"
5 #include        "error.h"
6
7 typedef struct Fid      Fid;
8 typedef struct Export   Export;
9 typedef struct Exq      Exq;
10 typedef struct Exwork   Exwork;
11
12 enum
13 {
14         Nfidhash        = 32,
15         Maxfdata        = 8192,
16         Maxrpc          = IOHDRSZ + Maxfdata,
17 };
18
19 struct Export
20 {
21         Ref     r;
22         Exq*    work;
23         Lock    fidlock;
24         Fid*    fid[Nfidhash];
25         int     io;             /* fd to read/write */
26         int     iounit;
27         int     nroots;
28         Chan    **roots;
29 };
30
31 struct Fid
32 {
33         Fid*    next;
34         Fid**   last;
35         Chan*   chan;
36         long    offset;
37         int     fid;
38         int     ref;            /* fcalls using the fid; locked by Export.Lock */
39         int     attached;       /* fid attached or cloned but not clunked */
40 };
41
42 struct Exq
43 {
44         Lock    lk;
45         int     responding;     /* writing out reply message */
46         int     noresponse;     /* don't respond to this one */
47         Exq*    next;
48         int     shut;           /* has been noted for shutdown */
49         Export* export;
50         void*   slave;
51         Fcall   rpc;
52         uchar   buf[Maxrpc];
53 };
54
55 struct Exwork
56 {
57         Lock    l;
58
59         int     ref;
60
61         int     nwaiters;       /* queue of slaves waiting for work */
62         QLock   qwait;
63         Rendez  rwait;
64
65         Exq     *head;          /* work waiting for a slave */
66         Exq     *tail;
67 };
68
69 Exwork exq;
70
71 static void     exshutdown(Export*);
72 static void     exflush(Export*, int, int);
73 static void     exslave(void*);
74 static void     exfree(Export*);
75 static void     exportproc(Export*);
76
77 static char*    Exattach(Export*, Fcall*, uchar*);
78 static char*    Exauth(Export*, Fcall*, uchar*);
79 static char*    Exclunk(Export*, Fcall*, uchar*);
80 static char*    Excreate(Export*, Fcall*, uchar*);
81 static char*    Exversion(Export*, Fcall*, uchar*);
82 static char*    Exopen(Export*, Fcall*, uchar*);
83 static char*    Exread(Export*, Fcall*, uchar*);
84 static char*    Exremove(Export*, Fcall*, uchar*);
85 static char*    Exsession(Export*, Fcall*, uchar*);
86 static char*    Exstat(Export*, Fcall*, uchar*);
87 static char*    Exwalk(Export*, Fcall*, uchar*);
88 static char*    Exwrite(Export*, Fcall*, uchar*);
89 static char*    Exwstat(Export*, Fcall*, uchar*);
90
91 static char     *(*fcalls[Tmax])(Export*, Fcall*, uchar*);
92
93 static char     Enofid[]   = "no such fid";
94 static char     Eseekdir[] = "can't seek on a directory";
95 static char     Ereaddir[] = "unaligned read of a directory";
96 static int      exdebug = 0;
97
98 int
99 sysexport(int fd, Chan **roots, int nroots)
100 {
101         Export *fs;
102
103         fs = smalloc(sizeof(Export));
104         fs->r.ref = 1;
105         fs->io = fd;
106         fs->roots = roots;
107         fs->nroots = nroots;
108
109         exportproc(fs);
110
111         return 0;
112 }
113
114 static void
115 exportinit(void)
116 {
117         lock(&exq.l);
118         exq.ref++;
119         if(fcalls[Tversion] != nil){
120                 unlock(&exq.l);
121                 return;
122         }
123
124         fmtinstall('F', fcallfmt);
125         fcalls[Tversion] = Exversion;
126         fcalls[Tauth] = Exauth;
127         fcalls[Tattach] = Exattach;
128         fcalls[Twalk] = Exwalk;
129         fcalls[Topen] = Exopen;
130         fcalls[Tcreate] = Excreate;
131         fcalls[Tread] = Exread;
132         fcalls[Twrite] = Exwrite;
133         fcalls[Tclunk] = Exclunk;
134         fcalls[Tremove] = Exremove;
135         fcalls[Tstat] = Exstat;
136         fcalls[Twstat] = Exwstat;
137         unlock(&exq.l);
138 }
139
140 static void
141 exportproc(Export *fs)
142 {
143         Exq *q;
144         int n, ed;
145
146         exportinit();
147         ed = errdepth(-1);
148         for(;;){
149                 errdepth(ed);
150                 q = smalloc(sizeof(Exq));
151
152                 n = read9pmsg(fs->io, q->buf, Maxrpc);
153                 if(n <= 0 || convM2S(q->buf, n, &q->rpc) != n)
154                         goto bad;
155
156                 if(exdebug)
157                         print("export %d <- %F\n", getpid(), &q->rpc);
158
159                 if(q->rpc.type == Tflush){
160                         exflush(fs, q->rpc.tag, q->rpc.oldtag);
161                         free(q);
162                         continue;
163                 }
164
165                 q->export = fs;
166                 incref(&fs->r);
167
168                 lock(&exq.l);
169                 if(exq.head == nil)
170                         exq.head = q;
171                 else
172                         exq.tail->next = q;
173                 q->next = nil;
174                 exq.tail = q;
175                 n = exq.nwaiters;
176                 if(n)
177                         exq.nwaiters = n - 1;
178                 unlock(&exq.l);
179                 if(!n)
180                         kproc("exportfs", exslave, nil);
181                 rendwakeup(&exq.rwait);
182         }
183 bad:
184         free(q);
185         if(exdebug)
186                 fprint(2, "export proc shutting down: %r\n");
187         exshutdown(fs);
188         exfree(fs);
189 }
190
191 static void
192 exflush(Export *fs, int flushtag, int tag)
193 {
194         Exq *q, **last;
195         Fcall fc;
196         uchar buf[Maxrpc];
197         int n;
198
199         /* hasn't been started? */
200         lock(&exq.l);
201         last = &exq.head;
202         for(q = exq.head; q != nil; q = q->next){
203                 if(q->export == fs && q->rpc.tag == tag){
204                         *last = q->next;
205                         unlock(&exq.l);
206                         exfree(fs);
207                         free(q);
208                         goto Respond;
209                 }
210                 last = &q->next;
211         }
212         unlock(&exq.l);
213
214         /* in progress? */
215         lock(&fs->r);
216         for(q = fs->work; q != nil; q = q->next){
217                 if(q->rpc.tag == tag){
218                         lock(&q->lk);
219                         q->noresponse = 1;
220                         if(!q->responding)
221                                 rendintr(q->slave);
222                         unlock(&q->lk);
223                         break;
224                 }
225         }
226         unlock(&fs->r);
227
228 Respond:
229         fc.type = Rflush;
230         fc.tag = flushtag;
231
232         n = convS2M(&fc, buf, Maxrpc);
233         if(n == 0)
234                 panic("convS2M error on write");
235         if(write(fs->io, buf, n) != n)
236                 panic("mount write");
237 }
238
239 static void
240 exshutdown(Export *fs)
241 {
242         Exq *q, **last;
243
244         lock(&exq.l);
245         last = &exq.head;
246         for(q = exq.head; q != nil; q = *last){
247                 if(q->export == fs){
248                         *last = q->next;
249                         exfree(fs);
250                         free(q);
251                         continue;
252                 }
253                 last = &q->next;
254         }
255
256         /*
257          * cleanly shut down the slaves if this is the last fs around
258          */
259         exq.ref--;
260         if(!exq.ref)
261                 rendwakeup(&exq.rwait);
262         unlock(&exq.l);
263
264         /*
265          * kick any sleepers
266          */
267         lock(&fs->r);
268         for(q = fs->work; q != nil; q = q->next){
269                 lock(&q->lk);
270                 q->noresponse = 1;
271                 if(!q->responding)
272                         rendintr(q->slave);
273                 unlock(&q->lk);
274         }
275         unlock(&fs->r);
276 }
277
278 static void
279 exfree(Export *fs)
280 {
281         Fid *f, *n;
282         int i;
283
284         if(decref(&fs->r) != 0)
285                 return;
286         for(i = 0; i < Nfidhash; i++){
287                 for(f = fs->fid[i]; f != nil; f = n){
288                         if(f->chan != nil)
289                                 cclose(f->chan);
290                         n = f->next;
291                         free(f);
292                 }
293         }
294         free(fs);
295 }
296
297 static int
298 exwork(void *)
299 {
300         int work;
301
302         lock(&exq.l);
303         work = exq.head != nil || !exq.ref;
304         unlock(&exq.l);
305         return work;
306 }
307
308 static void
309 exslave(void *)
310 {
311         Export *fs;
312         Exq *q, *t, **last;
313         char *volatile err;
314         int n, ed;
315
316         while(waserror())
317                 fprint(2, "exslave %d errored out of loop -- heading back in!\n", getpid());
318         ed = errdepth(-1);
319         for(;;){
320                 errdepth(ed);
321                 qlock(&exq.qwait);
322                 if(waserror()){
323                         qunlock(&exq.qwait);
324                         nexterror();
325                 }
326                 rendsleep(&exq.rwait, exwork, nil);
327
328                 lock(&exq.l);
329                 if(!exq.ref){
330                         unlock(&exq.l);
331                         poperror();
332                         qunlock(&exq.qwait);
333                         break;
334                 }
335                 q = exq.head;
336                 if(q == nil){
337                         unlock(&exq.l);
338                         poperror();
339                         qunlock(&exq.qwait);
340                         continue;
341                 }
342                 exq.head = q->next;
343                 if(exq.head == nil)
344                         exq.tail = nil;
345                 poperror();
346                 qunlock(&exq.qwait);
347
348                 /*
349                  * put the job on the work queue before it's
350                  * visible as off of the head queue, so it's always
351                  * findable for flushes and shutdown
352                  */
353                 q->slave = up;
354                 q->noresponse = 0;
355                 q->responding = 0;
356                 rendclearintr();
357                 fs = q->export;
358                 lock(&fs->r);
359                 q->next = fs->work;
360                 fs->work = q;
361                 unlock(&fs->r);
362
363                 unlock(&exq.l);
364
365                 if(exdebug > 1)
366                         print("exslave dispatch %d %F\n", getpid(), &q->rpc);
367
368                 if(waserror()){
369                         print("exslave err %r\n");
370                         err = up->error;
371                 }else{
372                         if(q->rpc.type >= Tmax || !fcalls[q->rpc.type])
373                                 err = "bad fcall type";
374                         else
375                                 err = (*fcalls[q->rpc.type])(fs, &q->rpc, &q->buf[IOHDRSZ]);
376                         poperror();
377                 }
378
379                 q->rpc.type++;
380                 if(err){
381                         q->rpc.type = Rerror;
382                         q->rpc.ename = err;
383                 }
384                 n = convS2M(&q->rpc, q->buf, Maxrpc);
385
386                 if(exdebug)
387                         print("exslave %d -> %F\n", getpid(), &q->rpc);
388
389                 lock(&q->lk);
390                 if(!q->noresponse){
391                         q->responding = 1;
392                         unlock(&q->lk);
393                         write(fs->io, q->buf, n);
394                 }else
395                         unlock(&q->lk);
396
397                 /*
398                  * exflush might set noresponse at this point, but
399                  * setting noresponse means don't send a response now;
400                  * it's okay that we sent a response already.
401                  */
402                 if(exdebug > 1)
403                         print("exslave %d written %d\n", getpid(), q->rpc.tag);
404
405                 lock(&fs->r);
406                 last = &fs->work;
407                 for(t = fs->work; t != nil; t = t->next){
408                         if(t == q){
409                                 *last = q->next;
410                                 break;
411                         }
412                         last = &t->next;
413                 }
414                 unlock(&fs->r);
415
416                 exfree(q->export);
417                 free(q);
418
419                 rendclearintr();
420                 lock(&exq.l);
421                 exq.nwaiters++;
422                 unlock(&exq.l);
423         }
424         if(exdebug)
425                 fprint(2, "export slaveshutting down\n");
426         kexit();
427 }
428
429 Fid*
430 Exmkfid(Export *fs, int fid)
431 {
432         ulong h;
433         Fid *f, *nf;
434
435         nf = mallocz(sizeof(Fid), 1);
436         if(nf == nil)
437                 return nil;
438         lock(&fs->fidlock);
439         h = fid % Nfidhash;
440         for(f = fs->fid[h]; f != nil; f = f->next){
441                 if(f->fid == fid){
442                         unlock(&fs->fidlock);
443                         free(nf);
444                         return nil;
445                 }
446         }
447
448         nf->next = fs->fid[h];
449         if(nf->next != nil)
450                 nf->next->last = &nf->next;
451         nf->last = &fs->fid[h];
452         fs->fid[h] = nf;
453
454         nf->fid = fid;
455         nf->ref = 1;
456         nf->attached = 1;
457         nf->offset = 0;
458         nf->chan = nil;
459         unlock(&fs->fidlock);
460         return nf;
461 }
462
463 Fid*
464 Exgetfid(Export *fs, int fid)
465 {
466         Fid *f;
467         ulong h;
468
469         lock(&fs->fidlock);
470         h = fid % Nfidhash;
471         for(f = fs->fid[h]; f; f = f->next){
472                 if(f->fid == fid){
473                         if(f->attached == 0)
474                                 break;
475                         f->ref++;
476                         unlock(&fs->fidlock);
477                         return f;
478                 }
479         }
480         unlock(&fs->fidlock);
481         return nil;
482 }
483
484 void
485 Exputfid(Export *fs, Fid *f)
486 {
487         lock(&fs->fidlock);
488         f->ref--;
489         if(f->ref == 0 && f->attached == 0){
490                 if(f->chan != nil)
491                         cclose(f->chan);
492                 f->chan = nil;
493                 *f->last = f->next;
494                 if(f->next != nil)
495                         f->next->last = f->last;
496                 unlock(&fs->fidlock);
497                 free(f);
498                 return;
499         }
500         unlock(&fs->fidlock);
501 }
502
503 static char*
504 Exversion(Export *fs, Fcall *rpc, uchar *)
505 {
506         if(rpc->msize > Maxrpc)
507                 rpc->msize = Maxrpc;
508         if(strncmp(rpc->version, "9P", 2) != 0){
509                 rpc->version = "unknown";
510                 return nil;
511         }
512
513         fs->iounit = rpc->msize - IOHDRSZ;
514         rpc->version = "9P2000";
515         return nil;
516 }
517
518 static char*
519 Exauth(Export *, Fcall *, uchar *)
520 {
521         return "vnc: authentication not required";
522 }
523
524 static char*
525 Exattach(Export *fs, Fcall *rpc, uchar *)
526 {
527         Fid *f;
528         int w;
529
530         w = 0;
531         if(rpc->aname != nil)
532                 w = strtol(rpc->aname, nil, 10);
533         if(w < 0 || w > fs->nroots)
534                 error(Ebadspec);
535         f = Exmkfid(fs, rpc->fid);
536         if(f == nil)
537                 return Einuse;
538         if(waserror()){
539                 f->attached = 0;
540                 Exputfid(fs, f);
541                 return up->error;
542         }
543         f->chan = cclone(fs->roots[w]);
544         poperror();
545         rpc->qid = f->chan->qid;
546         Exputfid(fs, f);
547         return nil;
548 }
549
550 static char*
551 Exclunk(Export *fs, Fcall *rpc, uchar *)
552 {
553         Fid *f;
554
555         f = Exgetfid(fs, rpc->fid);
556         if(f != nil){
557                 f->attached = 0;
558                 Exputfid(fs, f);
559         }
560         return nil;
561 }
562
563 static char*
564 Exwalk(Export *fs, Fcall *rpc, uchar *)
565 {
566         Fid *volatile f, *volatile nf;
567         Walkqid *wq;
568         Chan *c;
569         int i, nwname;
570         int volatile isnew;
571
572         f = Exgetfid(fs, rpc->fid);
573         if(f == nil)
574                 return Enofid;
575         nf = nil;
576         if(waserror()){
577                 Exputfid(fs, f);
578                 if(nf != nil)
579                         Exputfid(fs, nf);
580                 return up->error;
581         }
582
583         /*
584          * optional clone, but don't attach it until the walk succeeds.
585          */
586         if(rpc->fid != rpc->newfid){
587                 nf = Exmkfid(fs, rpc->newfid);
588                 if(nf == nil)
589                         error(Einuse);
590                 nf->attached = 0;
591                 isnew = 1;
592         }else{
593                 nf = Exgetfid(fs, rpc->fid);
594                 isnew = 0;
595         }
596
597         /*
598          * let the device do the work
599          */
600         c = f->chan;
601         nwname = rpc->nwname;
602         wq = (*devtab[c->type]->walk)(c, nf->chan, rpc->wname, nwname);
603         if(wq == nil)
604                 error(Enonexist);
605
606         poperror();
607
608         /*
609          * copy qid array
610          */
611         for(i = 0; i < wq->nqid; i++)
612                 rpc->wqid[i] = wq->qid[i];
613         rpc->nwqid = wq->nqid;
614
615         /*
616          * update the channel if everything walked correctly.
617          */
618         if(isnew && wq->nqid == nwname){
619                 nf->chan = wq->clone;
620                 nf->attached = 1;
621         }
622
623         free(wq);
624         Exputfid(fs, f);
625         Exputfid(fs, nf);
626         return nil;
627 }
628
629 static char*
630 Exopen(Export *fs, Fcall *rpc, uchar *)
631 {
632         Fid *volatile f;
633         Chan *c;
634         int iou;
635
636         f = Exgetfid(fs, rpc->fid);
637         if(f == nil)
638                 return Enofid;
639         if(waserror()){
640                 Exputfid(fs, f);
641                 return up->error;
642         }
643         c = f->chan;
644         c = (*devtab[c->type]->open)(c, rpc->mode);
645         poperror();
646
647         f->chan = c;
648         f->offset = 0;
649         rpc->qid = f->chan->qid;
650         iou = f->chan->iounit;
651         if(iou > fs->iounit)
652                 iou = fs->iounit;
653         rpc->iounit = iou;
654         Exputfid(fs, f);
655         return nil;
656 }
657
658 static char*
659 Excreate(Export *fs, Fcall *rpc, uchar *)
660 {
661         Fid *f;
662         Chan *c;
663         int iou;
664
665         f = Exgetfid(fs, rpc->fid);
666         if(f == nil)
667                 return Enofid;
668         if(waserror()){
669                 Exputfid(fs, f);
670                 return up->error;
671         }
672         c = f->chan;
673         (*devtab[c->type]->create)(c, rpc->name, rpc->mode, rpc->perm);
674         poperror();
675
676         f->chan = c;
677         rpc->qid = f->chan->qid;
678         iou = f->chan->iounit;
679         if(iou > fs->iounit)
680                 iou = fs->iounit;
681         rpc->iounit = iou;
682         Exputfid(fs, f);
683         return nil;
684 }
685
686 static char*
687 Exread(Export *fs, Fcall *rpc, uchar *buf)
688 {
689         Fid *f;
690         Chan *c;
691         long off;
692
693         f = Exgetfid(fs, rpc->fid);
694         if(f == nil)
695                 return Enofid;
696
697         c = f->chan;
698
699         if(waserror()){
700                 Exputfid(fs, f);
701                 return up->error;
702         }
703
704         rpc->data = (char*)buf;
705         off = rpc->offset;
706         c->offset = off;
707         rpc->count = (*devtab[c->type]->read)(c, rpc->data, rpc->count, off);
708         poperror();
709         Exputfid(fs, f);
710         return nil;
711 }
712
713 static char*
714 Exwrite(Export *fs, Fcall *rpc, uchar *)
715 {
716         Fid *f;
717         Chan *c;
718
719         f = Exgetfid(fs, rpc->fid);
720         if(f == nil)
721                 return Enofid;
722         if(waserror()){
723                 Exputfid(fs, f);
724                 return up->error;
725         }
726         c = f->chan;
727         if(c->qid.type & QTDIR)
728                 error(Eisdir);
729         rpc->count = (*devtab[c->type]->write)(c, rpc->data, rpc->count, rpc->offset);
730         poperror();
731         Exputfid(fs, f);
732         return nil;
733 }
734
735 static char*
736 Exstat(Export *fs, Fcall *rpc, uchar *buf)
737 {
738         Fid *f;
739         Chan *c;
740
741         f = Exgetfid(fs, rpc->fid);
742         if(f == nil)
743                 return Enofid;
744         if(waserror()){
745                 Exputfid(fs, f);
746                 return up->error;
747         }
748         c = f->chan;
749         rpc->stat = buf;
750         rpc->nstat = (*devtab[c->type]->stat)(c, rpc->stat, Maxrpc);
751         poperror();
752         Exputfid(fs, f);
753         return nil;
754 }
755
756 static char*
757 Exwstat(Export *fs, Fcall *rpc, uchar *)
758 {
759         Fid *f;
760         Chan *c;
761
762         f = Exgetfid(fs, rpc->fid);
763         if(f == nil)
764                 return Enofid;
765         if(waserror()){
766                 Exputfid(fs, f);
767                 return up->error;
768         }
769         c = f->chan;
770         (*devtab[c->type]->wstat)(c, rpc->stat, rpc->nstat);
771         poperror();
772         Exputfid(fs, f);
773         return nil;
774 }
775
776 static char*
777 Exremove(Export *fs, Fcall *rpc, uchar *)
778 {
779         Fid *f;
780         Chan *c;
781
782         f = Exgetfid(fs, rpc->fid);
783         if(f == nil)
784                 return Enofid;
785         if(waserror()){
786                 Exputfid(fs, f);
787                 return up->error;
788         }
789         c = f->chan;
790         (*devtab[c->type]->remove)(c);
791         poperror();
792
793         /*
794          * chan is already clunked by remove.
795          * however, we need to recover the chan,
796          * and follow sysremove's lead in making to point to root.
797          */
798         c->type = 0;
799
800         f->attached = 0;
801         Exputfid(fs, f);
802         return nil;
803 }