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 = 1, 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 ed = errdepth(-1); 317 for(;;){ 318 errdepth(ed); 319 qlock(&exq.qwait); 320 if(waserror()){ 321 qunlock(&exq.qwait); 322 nexterror(); 323 } 324 rendsleep(&exq.rwait, exwork, nil); 325 326 lock(&exq.l); 327 if(!exq.ref){ 328 unlock(&exq.l); 329 poperror(); 330 qunlock(&exq.qwait); 331 break; 332 } 333 q = exq.head; 334 if(q == nil){ 335 unlock(&exq.l); 336 poperror(); 337 qunlock(&exq.qwait); 338 continue; 339 } 340 exq.head = q->next; 341 if(exq.head == nil) 342 exq.tail = nil; 343 poperror(); 344 qunlock(&exq.qwait); 345 346 /* 347 * put the job on the work queue before it's 348 * visible as off of the head queue, so it's always 349 * findable for flushes and shutdown 350 */ 351 q->slave = up; 352 q->noresponse = 0; 353 q->responding = 0; 354 rendclearintr(); 355 fs = q->export; 356 lock(&fs->r); 357 q->next = fs->work; 358 fs->work = q; 359 unlock(&fs->r); 360 361 unlock(&exq.l); 362 363 if(exdebug > 1) 364 print("exslave dispatch %d %F\n", getpid(), &q->rpc); 365 366 if(waserror()){ 367 print("exslave err %r\n"); 368 err = up->error; 369 }else{ 370 if(q->rpc.type >= Tmax || !fcalls[q->rpc.type]) 371 err = "bad fcall type"; 372 else 373 err = (*fcalls[q->rpc.type])(fs, &q->rpc, &q->buf[IOHDRSZ]); 374 poperror(); 375 } 376 377 q->rpc.type++; 378 if(err){ 379 q->rpc.type = Rerror; 380 q->rpc.ename = err; 381 } 382 n = convS2M(&q->rpc, q->buf, Maxrpc); 383 384 if(exdebug) 385 print("exslave %d -> %F\n", getpid(), &q->rpc); 386 387 lock(&q->lk); 388 if(!q->noresponse){ 389 q->responding = 1; 390 unlock(&q->lk); 391 write(fs->io, q->buf, n); 392 }else 393 unlock(&q->lk); 394 395 /* 396 * exflush might set noresponse at this point, but 397 * setting noresponse means don't send a response now; 398 * it's okay that we sent a response already. 399 */ 400 if(exdebug > 1) 401 print("exslave %d written %d\n", getpid(), q->rpc.tag); 402 403 lock(&fs->r); 404 last = &fs->work; 405 for(t = fs->work; t != nil; t = t->next){ 406 if(t == q){ 407 *last = q->next; 408 break; 409 } 410 last = &t->next; 411 } 412 unlock(&fs->r); 413 414 exfree(q->export); 415 free(q); 416 417 lock(&exq.l); 418 exq.nwaiters++; 419 unlock(&exq.l); 420 } 421 if(exdebug) 422 fprint(2, "export slaveshutting down\n"); 423 kexit(); 424 } 425 426 Fid* 427 Exmkfid(Export *fs, int fid) 428 { 429 ulong h; 430 Fid *f, *nf; 431 432 nf = mallocz(sizeof(Fid), 1); 433 if(nf == nil) 434 return nil; 435 lock(&fs->fidlock); 436 h = fid % Nfidhash; 437 for(f = fs->fid[h]; f != nil; f = f->next){ 438 if(f->fid == fid){ 439 unlock(&fs->fidlock); 440 free(nf); 441 return nil; 442 } 443 } 444 445 nf->next = fs->fid[h]; 446 if(nf->next != nil) 447 nf->next->last = &nf->next; 448 nf->last = &fs->fid[h]; 449 fs->fid[h] = nf; 450 451 nf->fid = fid; 452 nf->ref = 1; 453 nf->attached = 1; 454 nf->offset = 0; 455 nf->chan = nil; 456 unlock(&fs->fidlock); 457 return nf; 458 } 459 460 Fid* 461 Exgetfid(Export *fs, int fid) 462 { 463 Fid *f; 464 ulong h; 465 466 lock(&fs->fidlock); 467 h = fid % Nfidhash; 468 for(f = fs->fid[h]; f; f = f->next) { 469 if(f->fid == fid){ 470 if(f->attached == 0) 471 break; 472 f->ref++; 473 unlock(&fs->fidlock); 474 return f; 475 } 476 } 477 unlock(&fs->fidlock); 478 return nil; 479 } 480 481 void 482 Exputfid(Export *fs, Fid *f) 483 { 484 lock(&fs->fidlock); 485 f->ref--; 486 if(f->ref == 0 && f->attached == 0){ 487 if(f->chan != nil) 488 cclose(f->chan); 489 f->chan = nil; 490 *f->last = f->next; 491 if(f->next != nil) 492 f->next->last = f->last; 493 unlock(&fs->fidlock); 494 free(f); 495 return; 496 } 497 unlock(&fs->fidlock); 498 } 499 500 static char* 501 Exversion(Export *fs, Fcall *rpc, uchar *) 502 { 503 if(rpc->msize > Maxrpc) 504 rpc->msize = Maxrpc; 505 if(strncmp(rpc->version, "9P", 2) != 0){ 506 rpc->version = "unknown"; 507 return nil; 508 } 509 510 fs->iounit = rpc->msize - IOHDRSZ; 511 rpc->version = "9P2000"; 512 return nil; 513 } 514 515 static char* 516 Exauth(Export *, Fcall *, uchar *) 517 { 518 return "vnc: authentication not required"; 519 } 520 521 static char* 522 Exattach(Export *fs, Fcall *rpc, uchar *) 523 { 524 Fid *f; 525 int w; 526 527 w = 0; 528 if(rpc->aname != nil) 529 w = strtol(rpc->aname, nil, 10); 530 if(w < 0 || w > fs->nroots) 531 error(Ebadspec); 532 f = Exmkfid(fs, rpc->fid); 533 if(f == nil) 534 return Einuse; 535 if(waserror()){ 536 f->attached = 0; 537 Exputfid(fs, f); 538 return up->error; 539 } 540 f->chan = cclone(fs->roots[w]); 541 poperror(); 542 rpc->qid = f->chan->qid; 543 Exputfid(fs, f); 544 return nil; 545 } 546 547 static char* 548 Exclunk(Export *fs, Fcall *rpc, uchar *) 549 { 550 Fid *f; 551 552 f = Exgetfid(fs, rpc->fid); 553 if(f != nil){ 554 f->attached = 0; 555 Exputfid(fs, f); 556 } 557 return nil; 558 } 559 560 static char* 561 Exwalk(Export *fs, Fcall *rpc, uchar *) 562 { 563 Fid *volatile f, *volatile nf; 564 Walkqid *wq; 565 Chan *c; 566 int i, nwname; 567 int volatile isnew; 568 569 f = Exgetfid(fs, rpc->fid); 570 if(f == nil) 571 return Enofid; 572 nf = nil; 573 if(waserror()){ 574 Exputfid(fs, f); 575 if(nf != nil) 576 Exputfid(fs, nf); 577 return up->error; 578 } 579 580 /* 581 * optional clone, but don't attach it until the walk succeeds. 582 */ 583 if(rpc->fid != rpc->newfid){ 584 nf = Exmkfid(fs, rpc->newfid); 585 if(nf == nil) 586 error(Einuse); 587 nf->attached = 0; 588 isnew = 1; 589 }else{ 590 nf = Exgetfid(fs, rpc->fid); 591 isnew = 0; 592 } 593 594 /* 595 * let the device do the work 596 */ 597 c = f->chan; 598 nwname = rpc->nwname; 599 wq = (*devtab[c->type]->walk)(c, nf->chan, rpc->wname, nwname); 600 if(wq == nil) 601 error(Enonexist); 602 603 poperror(); 604 605 /* 606 * copy qid array 607 */ 608 for(i = 0; i < wq->nqid; i++) 609 rpc->wqid[i] = wq->qid[i]; 610 rpc->nwqid = wq->nqid; 611 612 /* 613 * update the channel if everything walked correctly. 614 */ 615 if(isnew && wq->nqid == nwname){ 616 nf->chan = wq->clone; 617 nf->attached = 1; 618 } 619 620 free(wq); 621 Exputfid(fs, f); 622 Exputfid(fs, nf); 623 return nil; 624 } 625 626 static char* 627 Exopen(Export *fs, Fcall *rpc, uchar *) 628 { 629 Fid *volatile f; 630 Chan *c; 631 int iou; 632 633 f = Exgetfid(fs, rpc->fid); 634 if(f == nil) 635 return Enofid; 636 if(waserror()){ 637 Exputfid(fs, f); 638 return up->error; 639 } 640 c = f->chan; 641 c = (*devtab[c->type]->open)(c, rpc->mode); 642 poperror(); 643 644 f->chan = c; 645 f->offset = 0; 646 rpc->qid = f->chan->qid; 647 iou = f->chan->iounit; 648 if(iou > fs->iounit) 649 iou = fs->iounit; 650 rpc->iounit = iou; 651 Exputfid(fs, f); 652 return nil; 653 } 654 655 static char* 656 Excreate(Export *fs, Fcall *rpc, uchar *) 657 { 658 Fid *f; 659 Chan *c; 660 int iou; 661 662 f = Exgetfid(fs, rpc->fid); 663 if(f == nil) 664 return Enofid; 665 if(waserror()){ 666 Exputfid(fs, f); 667 return up->error; 668 } 669 c = f->chan; 670 (*devtab[c->type]->create)(c, rpc->name, rpc->mode, rpc->perm); 671 poperror(); 672 673 f->chan = c; 674 rpc->qid = f->chan->qid; 675 iou = f->chan->iounit; 676 if(iou > fs->iounit) 677 iou = fs->iounit; 678 rpc->iounit = iou; 679 Exputfid(fs, f); 680 return nil; 681 } 682 683 static char* 684 Exread(Export *fs, Fcall *rpc, uchar *buf) 685 { 686 Fid *f; 687 Chan *c; 688 long off; 689 690 f = Exgetfid(fs, rpc->fid); 691 if(f == nil) 692 return Enofid; 693 694 c = f->chan; 695 696 if(waserror()) { 697 Exputfid(fs, f); 698 return up->error; 699 } 700 701 rpc->data = (char*)buf; 702 off = rpc->offset; 703 c->offset = off; 704 rpc->count = (*devtab[c->type]->read)(c, rpc->data, rpc->count, off); 705 poperror(); 706 Exputfid(fs, f); 707 return nil; 708 } 709 710 static char* 711 Exwrite(Export *fs, Fcall *rpc, uchar *) 712 { 713 Fid *f; 714 Chan *c; 715 716 f = Exgetfid(fs, rpc->fid); 717 if(f == nil) 718 return Enofid; 719 if(waserror()){ 720 Exputfid(fs, f); 721 return up->error; 722 } 723 c = f->chan; 724 if(c->qid.type & QTDIR) 725 error(Eisdir); 726 rpc->count = (*devtab[c->type]->write)(c, rpc->data, rpc->count, rpc->offset); 727 poperror(); 728 Exputfid(fs, f); 729 return nil; 730 } 731 732 static char* 733 Exstat(Export *fs, Fcall *rpc, uchar *buf) 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->error; 744 } 745 c = f->chan; 746 rpc->stat = buf; 747 rpc->nstat = (*devtab[c->type]->stat)(c, rpc->stat, Maxrpc); 748 poperror(); 749 Exputfid(fs, f); 750 return nil; 751 } 752 753 static char* 754 Exwstat(Export *fs, Fcall *rpc, uchar *) 755 { 756 Fid *f; 757 Chan *c; 758 759 f = Exgetfid(fs, rpc->fid); 760 if(f == nil) 761 return Enofid; 762 if(waserror()){ 763 Exputfid(fs, f); 764 return up->error; 765 } 766 c = f->chan; 767 (*devtab[c->type]->wstat)(c, rpc->stat, rpc->nstat); 768 poperror(); 769 Exputfid(fs, f); 770 return nil; 771 } 772 773 static char* 774 Exremove(Export *fs, Fcall *rpc, uchar *) 775 { 776 Fid *f; 777 Chan *c; 778 779 f = Exgetfid(fs, rpc->fid); 780 if(f == nil) 781 return Enofid; 782 if(waserror()){ 783 Exputfid(fs, f); 784 return up->error; 785 } 786 c = f->chan; 787 (*devtab[c->type]->remove)(c); 788 poperror(); 789 790 /* 791 * chan is already clunked by remove. 792 * however, we need to recover the chan, 793 * and follow sysremove's lead in making to point to root. 794 */ 795 c->type = 0; 796 797 f->attached = 0; 798 Exputfid(fs, f); 799 return nil; 800 } 801