]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vnc/exportfs.c
vnc: don't prompt for password on auth_respond() failure
[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                         break;
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         free(q);
184         if(exdebug)
185                 fprint(2, "export proc shutting down: %r\n");
186         exshutdown(fs);
187         exfree(fs);
188 }
189
190 static void
191 exflush(Export *fs, int flushtag, int tag)
192 {
193         Exq *q, **last;
194         Fcall fc;
195         uchar buf[Maxrpc];
196         int n;
197
198         /* hasn't been started? */
199         lock(&exq.l);
200         last = &exq.head;
201         for(q = exq.head; q != nil; q = q->next){
202                 if(q->export == fs && q->rpc.tag == tag){
203                         *last = q->next;
204                         unlock(&exq.l);
205                         exfree(fs);
206                         free(q);
207                         goto Respond;
208                 }
209                 last = &q->next;
210         }
211         unlock(&exq.l);
212
213         /* in progress? */
214         lock(&fs->r);
215         for(q = fs->work; q != nil; q = q->next){
216                 if(q->rpc.tag == tag){
217                         lock(&q->lk);
218                         q->noresponse = 1;
219                         if(!q->responding)
220                                 rendintr(q->slave);
221                         unlock(&q->lk);
222                         break;
223                 }
224         }
225         unlock(&fs->r);
226
227 Respond:
228         fc.type = Rflush;
229         fc.tag = flushtag;
230
231         n = convS2M(&fc, buf, Maxrpc);
232         if(n == 0)
233                 panic("convS2M error on write");
234         if(write(fs->io, buf, n) != n)
235                 panic("mount write");
236 }
237
238 static void
239 exshutdown(Export *fs)
240 {
241         Exq *q, **last;
242
243         lock(&exq.l);
244         last = &exq.head;
245         for(q = exq.head; q != nil; q = *last){
246                 if(q->export == fs){
247                         *last = q->next;
248                         exfree(fs);
249                         free(q);
250                         continue;
251                 }
252                 last = &q->next;
253         }
254
255         /*
256          * cleanly shut down the slaves if this is the last fs around
257          */
258         exq.ref--;
259         if(!exq.ref)
260                 rendwakeup(&exq.rwait);
261         unlock(&exq.l);
262
263         /*
264          * kick any sleepers
265          */
266         lock(&fs->r);
267         for(q = fs->work; q != nil; q = q->next){
268                 lock(&q->lk);
269                 q->noresponse = 1;
270                 if(!q->responding)
271                         rendintr(q->slave);
272                 unlock(&q->lk);
273         }
274         unlock(&fs->r);
275 }
276
277 static void
278 exfree(Export *fs)
279 {
280         Fid *f, *n;
281         int i;
282
283         if(decref(&fs->r) != 0)
284                 return;
285         for(i = 0; i < Nfidhash; i++){
286                 for(f = fs->fid[i]; f != nil; f = n){
287                         if(f->chan != nil)
288                                 cclose(f->chan);
289                         n = f->next;
290                         free(f);
291                 }
292         }
293         free(fs);
294 }
295
296 static int
297 exwork(void *)
298 {
299         int work;
300
301         lock(&exq.l);
302         work = exq.head != nil || !exq.ref;
303         unlock(&exq.l);
304         return work;
305 }
306
307 static void
308 exslave(void *)
309 {
310         Export *fs;
311         Exq *q, *t, **last;
312         char *volatile err;
313         int n, ed;
314
315         while(waserror())
316                 fprint(2, "exslave %d errored out of loop -- heading back in!\n", getpid());
317         ed = errdepth(-1);
318         for(;;){
319                 errdepth(ed);
320                 qlock(&exq.qwait);
321                 if(waserror()){
322                         qunlock(&exq.qwait);
323                         nexterror();
324                 }
325                 rendsleep(&exq.rwait, exwork, nil);
326
327                 lock(&exq.l);
328                 if(!exq.ref){
329                         unlock(&exq.l);
330                         poperror();
331                         qunlock(&exq.qwait);
332                         break;
333                 }
334                 q = exq.head;
335                 if(q == nil){
336                         unlock(&exq.l);
337                         poperror();
338                         qunlock(&exq.qwait);
339                         continue;
340                 }
341                 exq.head = q->next;
342                 if(exq.head == nil)
343                         exq.tail = nil;
344                 poperror();
345                 qunlock(&exq.qwait);
346
347                 /*
348                  * put the job on the work queue before it's
349                  * visible as off of the head queue, so it's always
350                  * findable for flushes and shutdown
351                  */
352                 q->slave = up;
353                 q->noresponse = 0;
354                 q->responding = 0;
355                 rendclearintr();
356                 fs = q->export;
357                 lock(&fs->r);
358                 q->next = fs->work;
359                 fs->work = q;
360                 unlock(&fs->r);
361
362                 unlock(&exq.l);
363
364                 if(exdebug > 1)
365                         print("exslave dispatch %d %F\n", getpid(), &q->rpc);
366
367                 if(waserror()){
368                         print("exslave err %r\n");
369                         err = up->error;
370                 }else{
371                         if(q->rpc.type >= Tmax || !fcalls[q->rpc.type])
372                                 err = "bad fcall type";
373                         else
374                                 err = (*fcalls[q->rpc.type])(fs, &q->rpc, &q->buf[IOHDRSZ]);
375                         poperror();
376                 }
377
378                 q->rpc.type++;
379                 if(err){
380                         q->rpc.type = Rerror;
381                         q->rpc.ename = err;
382                 }
383                 n = convS2M(&q->rpc, q->buf, Maxrpc);
384
385                 if(exdebug)
386                         print("exslave %d -> %F\n", getpid(), &q->rpc);
387
388                 lock(&q->lk);
389                 if(!q->noresponse){
390                         q->responding = 1;
391                         unlock(&q->lk);
392                         write(fs->io, q->buf, n);
393                 }else
394                         unlock(&q->lk);
395
396                 /*
397                  * exflush might set noresponse at this point, but
398                  * setting noresponse means don't send a response now;
399                  * it's okay that we sent a response already.
400                  */
401                 if(exdebug > 1)
402                         print("exslave %d written %d\n", getpid(), q->rpc.tag);
403
404                 lock(&fs->r);
405                 last = &fs->work;
406                 for(t = fs->work; t != nil; t = t->next){
407                         if(t == q){
408                                 *last = q->next;
409                                 break;
410                         }
411                         last = &t->next;
412                 }
413                 unlock(&fs->r);
414
415                 exfree(q->export);
416                 free(q);
417
418                 rendclearintr();
419                 lock(&exq.l);
420                 exq.nwaiters++;
421                 unlock(&exq.l);
422         }
423         if(exdebug)
424                 fprint(2, "export slaveshutting down\n");
425         kexit();
426 }
427
428 Fid*
429 Exmkfid(Export *fs, int fid)
430 {
431         ulong h;
432         Fid *f, *nf;
433
434         nf = mallocz(sizeof(Fid), 1);
435         if(nf == nil)
436                 return nil;
437         lock(&fs->fidlock);
438         h = fid % Nfidhash;
439         for(f = fs->fid[h]; f != nil; f = f->next){
440                 if(f->fid == fid){
441                         unlock(&fs->fidlock);
442                         free(nf);
443                         return nil;
444                 }
445         }
446
447         nf->next = fs->fid[h];
448         if(nf->next != nil)
449                 nf->next->last = &nf->next;
450         nf->last = &fs->fid[h];
451         fs->fid[h] = nf;
452
453         nf->fid = fid;
454         nf->ref = 1;
455         nf->attached = 1;
456         nf->offset = 0;
457         nf->chan = nil;
458         unlock(&fs->fidlock);
459         return nf;
460 }
461
462 Fid*
463 Exgetfid(Export *fs, int fid)
464 {
465         Fid *f;
466         ulong h;
467
468         lock(&fs->fidlock);
469         h = fid % Nfidhash;
470         for(f = fs->fid[h]; f; f = f->next){
471                 if(f->fid == fid){
472                         if(f->attached == 0)
473                                 break;
474                         f->ref++;
475                         unlock(&fs->fidlock);
476                         return f;
477                 }
478         }
479         unlock(&fs->fidlock);
480         return nil;
481 }
482
483 void
484 Exputfid(Export *fs, Fid *f)
485 {
486         lock(&fs->fidlock);
487         f->ref--;
488         if(f->ref == 0 && f->attached == 0){
489                 if(f->chan != nil)
490                         cclose(f->chan);
491                 f->chan = nil;
492                 *f->last = f->next;
493                 if(f->next != nil)
494                         f->next->last = f->last;
495                 unlock(&fs->fidlock);
496                 free(f);
497                 return;
498         }
499         unlock(&fs->fidlock);
500 }
501
502 static char*
503 Exversion(Export *fs, Fcall *rpc, uchar *)
504 {
505         if(rpc->msize < 256)
506                 return "version: message size too small";
507         if(rpc->msize > Maxrpc)
508                 rpc->msize = Maxrpc;
509         if(strncmp(rpc->version, "9P", 2) != 0){
510                 rpc->version = "unknown";
511                 return nil;
512         }
513
514         fs->iounit = rpc->msize - IOHDRSZ;
515         rpc->version = "9P2000";
516         return nil;
517 }
518
519 static char*
520 Exauth(Export *, Fcall *, uchar *)
521 {
522         return "vnc: authentication not required";
523 }
524
525 static char*
526 Exattach(Export *fs, Fcall *rpc, uchar *)
527 {
528         Fid *f;
529         int w;
530
531         w = 0;
532         if(rpc->aname != nil)
533                 w = strtol(rpc->aname, nil, 10);
534         if(w < 0 || w > fs->nroots)
535                 error(Ebadspec);
536         f = Exmkfid(fs, rpc->fid);
537         if(f == nil)
538                 return Einuse;
539         if(waserror()){
540                 f->attached = 0;
541                 Exputfid(fs, f);
542                 return up->error;
543         }
544         f->chan = cclone(fs->roots[w]);
545         poperror();
546         rpc->qid = f->chan->qid;
547         Exputfid(fs, f);
548         return nil;
549 }
550
551 static char*
552 Exclunk(Export *fs, Fcall *rpc, uchar *)
553 {
554         Fid *f;
555
556         f = Exgetfid(fs, rpc->fid);
557         if(f != nil){
558                 f->attached = 0;
559                 Exputfid(fs, f);
560         }
561         return nil;
562 }
563
564 static char*
565 Exwalk(Export *fs, Fcall *rpc, uchar *)
566 {
567         Fid *volatile f, *volatile nf;
568         Walkqid *wq;
569         Chan *c;
570         int i, nwname;
571         int volatile isnew;
572
573         f = Exgetfid(fs, rpc->fid);
574         if(f == nil)
575                 return Enofid;
576         nf = nil;
577         if(waserror()){
578                 Exputfid(fs, f);
579                 if(nf != nil)
580                         Exputfid(fs, nf);
581                 return up->error;
582         }
583
584         /*
585          * optional clone, but don't attach it until the walk succeeds.
586          */
587         if(rpc->fid != rpc->newfid){
588                 nf = Exmkfid(fs, rpc->newfid);
589                 if(nf == nil)
590                         error(Einuse);
591                 nf->attached = 0;
592                 isnew = 1;
593         }else{
594                 nf = Exgetfid(fs, rpc->fid);
595                 isnew = 0;
596         }
597
598         /*
599          * let the device do the work
600          */
601         c = f->chan;
602         nwname = rpc->nwname;
603         wq = (*devtab[c->type]->walk)(c, nf->chan, rpc->wname, nwname);
604         if(wq == nil)
605                 error(Enonexist);
606
607         poperror();
608
609         /*
610          * copy qid array
611          */
612         for(i = 0; i < wq->nqid; i++)
613                 rpc->wqid[i] = wq->qid[i];
614         rpc->nwqid = wq->nqid;
615
616         /*
617          * update the channel if everything walked correctly.
618          */
619         if(isnew && wq->nqid == nwname){
620                 nf->chan = wq->clone;
621                 nf->attached = 1;
622         }
623
624         free(wq);
625         Exputfid(fs, f);
626         Exputfid(fs, nf);
627         return nil;
628 }
629
630 static char*
631 Exopen(Export *fs, Fcall *rpc, uchar *)
632 {
633         Fid *volatile f;
634         Chan *c;
635         int iou;
636
637         f = Exgetfid(fs, rpc->fid);
638         if(f == nil)
639                 return Enofid;
640         if(waserror()){
641                 Exputfid(fs, f);
642                 return up->error;
643         }
644         c = f->chan;
645         c = (*devtab[c->type]->open)(c, rpc->mode);
646         poperror();
647
648         f->chan = c;
649         f->offset = 0;
650         rpc->qid = f->chan->qid;
651         iou = f->chan->iounit;
652         if(iou > fs->iounit)
653                 iou = fs->iounit;
654         rpc->iounit = iou;
655         Exputfid(fs, f);
656         return nil;
657 }
658
659 static char*
660 Excreate(Export *fs, Fcall *rpc, uchar *)
661 {
662         Fid *f;
663         Chan *c;
664         int iou;
665
666         f = Exgetfid(fs, rpc->fid);
667         if(f == nil)
668                 return Enofid;
669         if(waserror()){
670                 Exputfid(fs, f);
671                 return up->error;
672         }
673         c = f->chan;
674         (*devtab[c->type]->create)(c, rpc->name, rpc->mode, rpc->perm);
675         poperror();
676
677         f->chan = c;
678         rpc->qid = f->chan->qid;
679         iou = f->chan->iounit;
680         if(iou > fs->iounit)
681                 iou = fs->iounit;
682         rpc->iounit = iou;
683         Exputfid(fs, f);
684         return nil;
685 }
686
687 static char*
688 Exread(Export *fs, Fcall *rpc, uchar *buf)
689 {
690         Fid *f;
691         Chan *c;
692         long off;
693
694         f = Exgetfid(fs, rpc->fid);
695         if(f == nil)
696                 return Enofid;
697
698         c = f->chan;
699
700         if(waserror()){
701                 Exputfid(fs, f);
702                 return up->error;
703         }
704
705         rpc->data = (char*)buf;
706         off = rpc->offset;
707         c->offset = off;
708         rpc->count = (*devtab[c->type]->read)(c, rpc->data, rpc->count, off);
709         poperror();
710         Exputfid(fs, f);
711         return nil;
712 }
713
714 static char*
715 Exwrite(Export *fs, Fcall *rpc, uchar *)
716 {
717         Fid *f;
718         Chan *c;
719
720         f = Exgetfid(fs, rpc->fid);
721         if(f == nil)
722                 return Enofid;
723         if(waserror()){
724                 Exputfid(fs, f);
725                 return up->error;
726         }
727         c = f->chan;
728         if(c->qid.type & QTDIR)
729                 error(Eisdir);
730         rpc->count = (*devtab[c->type]->write)(c, rpc->data, rpc->count, rpc->offset);
731         poperror();
732         Exputfid(fs, f);
733         return nil;
734 }
735
736 static char*
737 Exstat(Export *fs, Fcall *rpc, uchar *buf)
738 {
739         Fid *f;
740         Chan *c;
741
742         f = Exgetfid(fs, rpc->fid);
743         if(f == nil)
744                 return Enofid;
745         if(waserror()){
746                 Exputfid(fs, f);
747                 return up->error;
748         }
749         c = f->chan;
750         rpc->stat = buf;
751         rpc->nstat = (*devtab[c->type]->stat)(c, rpc->stat, Maxrpc);
752         poperror();
753         Exputfid(fs, f);
754         return nil;
755 }
756
757 static char*
758 Exwstat(Export *fs, Fcall *rpc, uchar *)
759 {
760         Fid *f;
761         Chan *c;
762
763         f = Exgetfid(fs, rpc->fid);
764         if(f == nil)
765                 return Enofid;
766         if(waserror()){
767                 Exputfid(fs, f);
768                 return up->error;
769         }
770         c = f->chan;
771         (*devtab[c->type]->wstat)(c, rpc->stat, rpc->nstat);
772         poperror();
773         Exputfid(fs, f);
774         return nil;
775 }
776
777 static char*
778 Exremove(Export *fs, Fcall *rpc, uchar *)
779 {
780         Fid *f;
781         Chan *c;
782
783         f = Exgetfid(fs, rpc->fid);
784         if(f == nil)
785                 return Enofid;
786         if(waserror()){
787                 Exputfid(fs, f);
788                 return up->error;
789         }
790         c = f->chan;
791         (*devtab[c->type]->remove)(c);
792         poperror();
793
794         /*
795          * chan is already clunked by remove.
796          * however, we need to recover the chan,
797          * and follow sysremove's lead in making to point to root.
798          */
799         c->type = 0;
800
801         f->attached = 0;
802         Exputfid(fs, f);
803         return nil;
804 }