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