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 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 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 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 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 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 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 346 exwork(void *a) 347 { 348 return exq.head != nil; 349 } 350 351 void 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* 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* 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 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* 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* 535 Exauth(Export *e, Fcall *f) 536 { 537 return "authentication not required"; 538 } 539 540 char* 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* 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* 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* 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* 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* 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* 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* 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* 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* 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* 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