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 } 804