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
sysexport(int fd,Chan ** roots,int nroots)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
exportinit(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
exportproc(Export * fs)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
exflush(Export * fs,int flushtag,int tag)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
exshutdown(Export * fs)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
exfree(Export * fs)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
exwork(void *)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
exslave(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*
Exmkfid(Export * fs,int 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*
Exgetfid(Export * fs,int 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
Exputfid(Export * fs,Fid * f)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*
Exversion(Export * fs,Fcall * rpc,uchar *)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*
Exauth(Export *,Fcall *,uchar *)519 Exauth(Export *, Fcall *, uchar *)
520 {
521 return "vnc: authentication not required";
522 }
523
524 static char*
Exattach(Export * fs,Fcall * rpc,uchar *)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*
Exclunk(Export * fs,Fcall * rpc,uchar *)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*
Exwalk(Export * fs,Fcall * rpc,uchar *)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*
Exopen(Export * fs,Fcall * rpc,uchar *)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*
Excreate(Export * fs,Fcall * rpc,uchar *)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*
Exread(Export * fs,Fcall * rpc,uchar * buf)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*
Exwrite(Export * fs,Fcall * rpc,uchar *)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*
Exstat(Export * fs,Fcall * rpc,uchar * buf)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*
Exwstat(Export * fs,Fcall * rpc,uchar *)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*
Exremove(Export * fs,Fcall * rpc,uchar *)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 }
804