1 #include "u.h" 2 #include "lib.h" 3 #include "dat.h" 4 #include "fns.h" 5 #include "error.h" 6 7 /* 8 * References are managed as follows: 9 * The channel to the server - a network connection or pipe - has one 10 * reference for every Chan open on the server. The server channel has 11 * c->mux set to the Mnt used for muxing control to that server. Mnts 12 * have no reference count; they go away when c goes away. 13 * Each channel derived from the mount point has mchan set to c, 14 * and increfs/decrefs mchan to manage references on the server 15 * connection. 16 */ 17 18 #define MAXRPC (IOHDRSZ+8192) 19 20 struct Mntrpc 21 { 22 Chan* c; /* Channel for whom we are working */ 23 Mntrpc* list; /* Free/pending list */ 24 Fcall request; /* Outgoing file system protocol message */ 25 Fcall reply; /* Incoming reply */ 26 Mnt* m; /* Mount device during rpc */ 27 Rendez r; /* Place to hang out */ 28 uchar* rpc; /* I/O Data buffer */ 29 uint rpclen; /* len of buffer */ 30 Block *b; /* reply blocks */ 31 char done; /* Rpc completed */ 32 uvlong stime; /* start time for mnt statistics */ 33 ulong reqlen; /* request length for mnt statistics */ 34 ulong replen; /* reply length for mnt statistics */ 35 Mntrpc* flushed; /* message this one flushes */ 36 }; 37 38 enum 39 { 40 TAGSHIFT = 5, /* ulong has to be 32 bits */ 41 TAGMASK = (1<<TAGSHIFT)-1, 42 NMASK = (64*1024)>>TAGSHIFT, 43 }; 44 45 struct Mntalloc 46 { 47 Lock lk; 48 Mnt* list; /* Mount devices in use */ 49 Mnt* mntfree; /* Free list */ 50 Mntrpc* rpcfree; 51 int nrpcfree; 52 int nrpcused; 53 ulong id; 54 ulong tagmask[NMASK]; 55 }mntalloc; 56 57 void mattach(Mnt*, Chan*, char*); 58 Mnt* mntchk(Chan*); 59 void mntdirfix(uchar*, Chan*); 60 Mntrpc* mntflushalloc(Mntrpc*, ulong); 61 void mntflushfree(Mnt*, Mntrpc*); 62 void mntfree(Mntrpc*); 63 void mntgate(Mnt*); 64 void mntpntfree(Mnt*); 65 void mntqrm(Mnt*, Mntrpc*); 66 Mntrpc* mntralloc(Chan*, ulong); 67 long mntrdwr(int, Chan*, void*, long, vlong); 68 int mntrpcread(Mnt*, Mntrpc*); 69 void mountio(Mnt*, Mntrpc*); 70 void mountmux(Mnt*, Mntrpc*); 71 void mountrpc(Mnt*, Mntrpc*); 72 int rpcattn(void*); 73 Chan* mntchan(void); 74 75 char Esbadstat[] = "invalid directory entry received from server"; 76 char Enoversion[] = "version not established for mount channel"; 77 78 79 void (*mntstats)(int, Chan*, uvlong, ulong); 80 81 static void 82 mntreset(void) 83 { 84 mntalloc.id = 1; 85 mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ 86 mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ 87 fmtinstall('F', fcallfmt); 88 fmtinstall('D', dirfmt); 89 /* We can't install %M since eipfmt does and is used in the kernel [sape] */ 90 91 cinit(); 92 } 93 94 /* 95 * Version is not multiplexed: message sent only once per connection. 96 */ 97 long 98 mntversion(Chan *c, char *version, int msize, int returnlen) 99 { 100 Fcall f; 101 uchar *msg; 102 Mnt *m; 103 char *v; 104 long k, l; 105 uvlong oo; 106 char buf[128]; 107 108 qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ 109 if(waserror()){ 110 qunlock(&c->umqlock); 111 nexterror(); 112 } 113 114 /* defaults */ 115 if(msize == 0) 116 msize = MAXRPC; 117 if(msize > c->iounit && c->iounit != 0) 118 msize = c->iounit; 119 v = version; 120 if(v == nil || v[0] == '\0') 121 v = VERSION9P; 122 123 /* validity */ 124 if(msize < 0) 125 error("bad iounit in version call"); 126 if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) 127 error("bad 9P version specification"); 128 129 m = c->mux; 130 131 if(m != nil){ 132 qunlock(&c->umqlock); 133 poperror(); 134 135 strecpy(buf, buf+sizeof buf, m->version); 136 k = strlen(buf); 137 if(strncmp(buf, v, k) != 0){ 138 snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); 139 error(buf); 140 } 141 if(returnlen > 0){ 142 if(returnlen < k) 143 error(Eshort); 144 memmove(version, buf, k); 145 } 146 return k; 147 } 148 149 f.type = Tversion; 150 f.tag = NOTAG; 151 f.msize = msize; 152 f.version = v; 153 msg = malloc(8192+IOHDRSZ); 154 if(msg == nil) 155 exhausted("version memory"); 156 if(waserror()){ 157 free(msg); 158 nexterror(); 159 } 160 k = convS2M(&f, msg, 8192+IOHDRSZ); 161 if(k == 0) 162 error("bad fversion conversion on send"); 163 164 lock(&c->ref.lk); 165 oo = c->offset; 166 c->offset += k; 167 unlock(&c->ref.lk); 168 169 l = devtab[c->type]->write(c, msg, k, oo); 170 171 if(l < k){ 172 lock(&c->ref.lk); 173 c->offset -= k - l; 174 unlock(&c->ref.lk); 175 error("short write in fversion"); 176 } 177 178 /* message sent; receive and decode reply */ 179 k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); 180 if(k <= 0) 181 error("EOF receiving fversion reply"); 182 183 lock(&c->ref.lk); 184 c->offset += k; 185 unlock(&c->ref.lk); 186 187 l = convM2S(msg, k, &f); 188 if(l != k) 189 error("bad fversion conversion on reply"); 190 if(f.type != Rversion){ 191 if(f.type == Rerror) 192 error(f.ename); 193 error("unexpected reply type in fversion"); 194 } 195 if(f.msize > msize) 196 error("server tries to increase msize in fversion"); 197 if(f.msize<256 || f.msize>1024*1024) 198 error("nonsense value of msize in fversion"); 199 k = strlen(f.version); 200 if(strncmp(f.version, v, k) != 0) 201 error("bad 9P version returned from server"); 202 203 /* now build Mnt associated with this connection */ 204 lock(&mntalloc.lk); 205 m = mntalloc.mntfree; 206 if(m != 0) 207 mntalloc.mntfree = m->list; 208 else { 209 m = malloc(sizeof(Mnt)); 210 if(m == 0) { 211 unlock(&mntalloc.lk); 212 exhausted("mount devices"); 213 } 214 } 215 m->list = mntalloc.list; 216 mntalloc.list = m; 217 m->version = nil; 218 kstrdup(&m->version, f.version); 219 m->id = mntalloc.id++; 220 m->q = qopen(10*MAXRPC, 0, 0, nil); 221 m->msize = f.msize; 222 unlock(&mntalloc.lk); 223 224 if(returnlen > 0){ 225 if(returnlen < k) 226 error(Eshort); 227 memmove(version, f.version, k); 228 } 229 230 poperror(); /* msg */ 231 free(msg); 232 233 lock(&m->lk); 234 m->queue = 0; 235 m->rip = 0; 236 237 c->flag |= CMSG; 238 c->mux = m; 239 m->c = c; 240 unlock(&m->lk); 241 242 poperror(); /* c */ 243 qunlock(&c->umqlock); 244 245 return k; 246 } 247 248 Chan* 249 mntauth(Chan *c, char *spec) 250 { 251 Mnt *m; 252 Mntrpc *r; 253 254 m = c->mux; 255 256 if(m == nil){ 257 mntversion(c, VERSION9P, MAXRPC, 0); 258 m = c->mux; 259 if(m == nil) 260 error(Enoversion); 261 } 262 263 c = mntchan(); 264 if(waserror()) { 265 /* Close must not be called since it will 266 * call mnt recursively 267 */ 268 chanfree(c); 269 nexterror(); 270 } 271 272 r = mntralloc(0, m->msize); 273 274 if(waserror()) { 275 mntfree(r); 276 nexterror(); 277 } 278 279 r->request.type = Tauth; 280 r->request.afid = c->fid; 281 r->request.uname = up->user; 282 r->request.aname = spec; 283 mountrpc(m, r); 284 285 c->qid = r->reply.aqid; 286 c->mchan = m->c; 287 incref(&m->c->ref); 288 c->mqid = c->qid; 289 c->mode = ORDWR; 290 291 poperror(); /* r */ 292 mntfree(r); 293 294 poperror(); /* c */ 295 296 return c; 297 298 } 299 300 static Chan* 301 mntattach(char *muxattach) 302 { 303 Mnt *m; 304 Chan *c; 305 Mntrpc *r; 306 struct bogus{ 307 Chan *chan; 308 Chan *authchan; 309 char *spec; 310 int flags; 311 }bogus; 312 313 bogus = *((struct bogus *)muxattach); 314 c = bogus.chan; 315 316 m = c->mux; 317 318 if(m == nil){ 319 mntversion(c, nil, 0, 0); 320 m = c->mux; 321 if(m == nil) 322 error(Enoversion); 323 } 324 325 c = mntchan(); 326 if(waserror()) { 327 /* Close must not be called since it will 328 * call mnt recursively 329 */ 330 chanfree(c); 331 nexterror(); 332 } 333 334 r = mntralloc(0, m->msize); 335 336 if(waserror()) { 337 mntfree(r); 338 nexterror(); 339 } 340 341 r->request.type = Tattach; 342 r->request.fid = c->fid; 343 if(bogus.authchan == nil) 344 r->request.afid = NOFID; 345 else 346 r->request.afid = bogus.authchan->fid; 347 r->request.uname = up->user; 348 r->request.aname = bogus.spec; 349 mountrpc(m, r); 350 351 c->qid = r->reply.qid; 352 c->mchan = m->c; 353 incref(&m->c->ref); 354 c->mqid = c->qid; 355 356 poperror(); /* r */ 357 mntfree(r); 358 359 poperror(); /* c */ 360 361 if(bogus.flags&MCACHE) 362 c->flag |= CCACHE; 363 return c; 364 } 365 366 Chan* 367 mntchan(void) 368 { 369 Chan *c; 370 371 c = devattach('M', 0); 372 lock(&mntalloc.lk); 373 c->dev = mntalloc.id++; 374 unlock(&mntalloc.lk); 375 376 if(c->mchan) 377 panic("mntchan non-zero %p", c->mchan); 378 return c; 379 } 380 381 static Walkqid* 382 mntwalk(Chan *c, Chan *nc, char **name, int nname) 383 { 384 int i, alloc; 385 Mnt *m; 386 Mntrpc *r; 387 Walkqid *wq; 388 389 if(nc != nil) 390 print("mntwalk: nc != nil\n"); 391 if(nname > MAXWELEM) 392 error("devmnt: too many name elements"); 393 alloc = 0; 394 wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); 395 if(waserror()){ 396 if(alloc && wq->clone!=nil) 397 cclose(wq->clone); 398 free(wq); 399 return nil; 400 } 401 402 alloc = 0; 403 m = mntchk(c); 404 r = mntralloc(c, m->msize); 405 if(nc == nil){ 406 nc = devclone(c); 407 /* 408 * Until the other side accepts this fid, we can't mntclose it. 409 * Therefore set type to 0 for now; rootclose is known to be safe. 410 */ 411 nc->type = 0; 412 alloc = 1; 413 } 414 wq->clone = nc; 415 416 if(waserror()) { 417 mntfree(r); 418 nexterror(); 419 } 420 r->request.type = Twalk; 421 r->request.fid = c->fid; 422 r->request.newfid = nc->fid; 423 r->request.nwname = nname; 424 memmove(r->request.wname, name, nname*sizeof(char*)); 425 426 mountrpc(m, r); 427 428 if(r->reply.nwqid > nname) 429 error("too many QIDs returned by walk"); 430 if(r->reply.nwqid < nname){ 431 if(alloc) 432 cclose(nc); 433 wq->clone = nil; 434 if(r->reply.nwqid == 0){ 435 free(wq); 436 wq = nil; 437 goto Return; 438 } 439 } 440 441 /* move new fid onto mnt device and update its qid */ 442 if(wq->clone != nil){ 443 if(wq->clone != c){ 444 wq->clone->type = c->type; 445 wq->clone->mchan = c->mchan; 446 incref(&c->mchan->ref); 447 } 448 if(r->reply.nwqid > 0) 449 wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; 450 } 451 wq->nqid = r->reply.nwqid; 452 for(i=0; i<wq->nqid; i++) 453 wq->qid[i] = r->reply.wqid[i]; 454 455 Return: 456 poperror(); 457 mntfree(r); 458 poperror(); 459 return wq; 460 } 461 462 static int 463 mntstat(Chan *c, uchar *dp, int n) 464 { 465 Mnt *m; 466 Mntrpc *r; 467 468 if(n < BIT16SZ) 469 error(Eshortstat); 470 m = mntchk(c); 471 r = mntralloc(c, m->msize); 472 if(waserror()) { 473 mntfree(r); 474 nexterror(); 475 } 476 r->request.type = Tstat; 477 r->request.fid = c->fid; 478 mountrpc(m, r); 479 480 /* r->reply.nstat is 16 bits 481 if(r->reply.nstat >= 1<<16) 482 error("returned stat buffer count too large"); 483 */ 484 485 if(r->reply.nstat > n){ 486 /* 487 * 12/31/2002 RSC 488 * 489 * This should be nstat-2, which is the first two 490 * bytes of the stat buffer. But dirstat and dirfstat 491 * depended on getting the full nstat (they didn't 492 * add BIT16SZ themselves). I fixed dirstat and dirfstat 493 * but am leaving this unchanged for now. After a 494 * few months, once enough of the relevant binaries 495 * have been recompiled for other reasons, we can 496 * change this to nstat-2. Devstat gets this right 497 * (via convD2M). 498 */ 499 /* doesn't fit; just patch the count and return */ 500 PBIT16((uchar*)dp, r->reply.nstat); 501 n = BIT16SZ; 502 }else{ 503 n = r->reply.nstat; 504 memmove(dp, r->reply.stat, n); 505 validstat(dp, n); 506 mntdirfix(dp, c); 507 } 508 poperror(); 509 mntfree(r); 510 return n; 511 } 512 513 static Chan* 514 mntopencreate(int type, Chan *c, char *name, int omode, ulong perm) 515 { 516 Mnt *m; 517 Mntrpc *r; 518 519 m = mntchk(c); 520 r = mntralloc(c, m->msize); 521 if(waserror()) { 522 mntfree(r); 523 nexterror(); 524 } 525 r->request.type = type; 526 r->request.fid = c->fid; 527 r->request.mode = omode; 528 if(type == Tcreate){ 529 r->request.perm = perm; 530 r->request.name = name; 531 } 532 mountrpc(m, r); 533 534 c->qid = r->reply.qid; 535 c->offset = 0; 536 c->mode = openmode(omode); 537 c->iounit = r->reply.iounit; 538 if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ) 539 c->iounit = m->msize-IOHDRSZ; 540 c->flag |= COPEN; 541 poperror(); 542 mntfree(r); 543 544 if(c->flag & CCACHE) 545 copen(c); 546 547 return c; 548 } 549 550 static Chan* 551 mntopen(Chan *c, int omode) 552 { 553 return mntopencreate(Topen, c, nil, omode, 0); 554 } 555 556 static void 557 mntcreate(Chan *c, char *name, int omode, ulong perm) 558 { 559 mntopencreate(Tcreate, c, name, omode, perm); 560 } 561 562 static void 563 mntclunk(Chan *c, int t) 564 { 565 Mnt *m; 566 Mntrpc *r; 567 568 m = mntchk(c); 569 r = mntralloc(c, m->msize); 570 if(waserror()){ 571 mntfree(r); 572 nexterror(); 573 } 574 575 r->request.type = t; 576 r->request.fid = c->fid; 577 mountrpc(m, r); 578 mntfree(r); 579 poperror(); 580 } 581 582 void 583 muxclose(Mnt *m) 584 { 585 Mntrpc *q, *r; 586 587 for(q = m->queue; q; q = r) { 588 r = q->list; 589 mntfree(q); 590 } 591 m->id = 0; 592 free(m->version); 593 m->version = nil; 594 mntpntfree(m); 595 } 596 597 void 598 mntpntfree(Mnt *m) 599 { 600 Mnt *f, **l; 601 Queue *q; 602 603 lock(&mntalloc.lk); 604 l = &mntalloc.list; 605 for(f = *l; f; f = f->list) { 606 if(f == m) { 607 *l = m->list; 608 break; 609 } 610 l = &f->list; 611 } 612 m->list = mntalloc.mntfree; 613 mntalloc.mntfree = m; 614 q = m->q; 615 unlock(&mntalloc.lk); 616 617 qfree(q); 618 } 619 620 static void 621 mntclose(Chan *c) 622 { 623 mntclunk(c, Tclunk); 624 } 625 626 static void 627 mntremove(Chan *c) 628 { 629 mntclunk(c, Tremove); 630 } 631 632 static int 633 mntwstat(Chan *c, uchar *dp, int n) 634 { 635 Mnt *m; 636 Mntrpc *r; 637 638 m = mntchk(c); 639 r = mntralloc(c, m->msize); 640 if(waserror()) { 641 mntfree(r); 642 nexterror(); 643 } 644 r->request.type = Twstat; 645 r->request.fid = c->fid; 646 r->request.nstat = n; 647 r->request.stat = dp; 648 mountrpc(m, r); 649 poperror(); 650 mntfree(r); 651 return n; 652 } 653 654 static long 655 mntread(Chan *c, void *buf, long n, vlong off) 656 { 657 uchar *p, *e; 658 int nc, cache, isdir, dirlen; 659 660 isdir = 0; 661 cache = c->flag & CCACHE; 662 if(c->qid.type & QTDIR) { 663 cache = 0; 664 isdir = 1; 665 } 666 667 p = buf; 668 if(cache) { 669 nc = cread(c, buf, n, off); 670 if(nc > 0) { 671 n -= nc; 672 if(n == 0) 673 return nc; 674 p += nc; 675 off += nc; 676 } 677 n = mntrdwr(Tread, c, p, n, off); 678 cupdate(c, p, n, off); 679 return n + nc; 680 } 681 682 n = mntrdwr(Tread, c, buf, n, off); 683 if(isdir) { 684 for(e = &p[n]; p+BIT16SZ < e; p += dirlen){ 685 dirlen = BIT16SZ+GBIT16(p); 686 if(p+dirlen > e) 687 break; 688 validstat(p, dirlen); 689 mntdirfix(p, c); 690 } 691 if(p != e) 692 error(Esbadstat); 693 } 694 return n; 695 } 696 697 static long 698 mntwrite(Chan *c, void *buf, long n, vlong off) 699 { 700 return mntrdwr(Twrite, c, buf, n, off); 701 } 702 703 long 704 mntrdwr(int type, Chan *c, void *buf, long n, vlong off) 705 { 706 Mnt *m; 707 Mntrpc *r; 708 char *uba; 709 int cache; 710 ulong cnt, nr, nreq; 711 712 m = mntchk(c); 713 uba = buf; 714 cnt = 0; 715 cache = c->flag & CCACHE; 716 if(c->qid.type & QTDIR) 717 cache = 0; 718 for(;;) { 719 r = mntralloc(c, m->msize); 720 if(waserror()) { 721 mntfree(r); 722 nexterror(); 723 } 724 r->request.type = type; 725 r->request.fid = c->fid; 726 r->request.offset = off; 727 r->request.data = uba; 728 nr = n; 729 if(nr > m->msize-IOHDRSZ) 730 nr = m->msize-IOHDRSZ; 731 r->request.count = nr; 732 mountrpc(m, r); 733 nreq = r->request.count; 734 nr = r->reply.count; 735 if(nr > nreq) 736 nr = nreq; 737 738 if(type == Tread) 739 r->b = bl2mem((uchar*)uba, r->b, nr); 740 else if(cache) 741 cwrite(c, (uchar*)uba, nr, off); 742 743 poperror(); 744 mntfree(r); 745 off += nr; 746 uba += nr; 747 cnt += nr; 748 n -= nr; 749 if(nr != nreq || n == 0) 750 break; 751 } 752 return cnt; 753 } 754 755 void 756 mountrpc(Mnt *m, Mntrpc *r) 757 { 758 char *sn, *cn; 759 int t; 760 761 r->reply.tag = 0; 762 r->reply.type = Tmax; /* can't ever be a valid message type */ 763 764 mountio(m, r); 765 766 t = r->reply.type; 767 switch(t) { 768 case Rerror: 769 error(r->reply.ename); 770 case Rflush: 771 error(Eintr); 772 default: 773 if(t == r->request.type+1) 774 break; 775 sn = "?"; 776 if(m->c->name != nil) 777 sn = m->c->name->s; 778 cn = "?"; 779 if(r->c != nil && r->c->name != nil) 780 cn = r->c->name->s; 781 print("mnt: proc %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n", 782 up->pid, sn, cn, 783 r, r->request.tag, r->request.fid, r->request.type, 784 r->reply.type, r->reply.tag); 785 error(Emountrpc); 786 } 787 } 788 789 void 790 mountio(Mnt *m, Mntrpc *r) 791 { 792 int n; 793 794 while(waserror()) { 795 if(m->rip == up) 796 mntgate(m); 797 if(strcmp(up->errstr, Eintr) != 0){ 798 mntflushfree(m, r); 799 nexterror(); 800 } 801 r = mntflushalloc(r, m->msize); 802 } 803 804 lock(&m->lk); 805 r->m = m; 806 r->list = m->queue; 807 m->queue = r; 808 unlock(&m->lk); 809 810 /* Transmit a file system rpc */ 811 if(m->msize == 0) 812 panic("msize"); 813 n = convS2M(&r->request, r->rpc, m->msize); 814 if(n < 0) 815 panic("bad message type in mountio"); 816 if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n) 817 error(Emountrpc); 818 r->stime = fastticks(nil); 819 r->reqlen = n; 820 821 /* Gate readers onto the mount point one at a time */ 822 for(;;) { 823 lock(&m->lk); 824 if(m->rip == 0) 825 break; 826 unlock(&m->lk); 827 sleep(&r->r, rpcattn, r); 828 if(r->done){ 829 poperror(); 830 mntflushfree(m, r); 831 return; 832 } 833 } 834 m->rip = up; 835 unlock(&m->lk); 836 while(r->done == 0) { 837 if(mntrpcread(m, r) < 0) 838 error(Emountrpc); 839 mountmux(m, r); 840 } 841 mntgate(m); 842 poperror(); 843 mntflushfree(m, r); 844 } 845 846 static int 847 doread(Mnt *m, int len) 848 { 849 Block *b; 850 851 while(qlen(m->q) < len){ 852 b = devtab[m->c->type]->bread(m->c, m->msize, 0); 853 if(b == nil) 854 return -1; 855 if(BLEN(b) == 0){ 856 freeblist(b); 857 return -1; 858 } 859 qaddlist(m->q, b); 860 } 861 return 0; 862 } 863 864 int 865 mntrpcread(Mnt *m, Mntrpc *r) 866 { 867 int i, t, len, hlen; 868 Block *b, **l, *nb; 869 870 r->reply.type = 0; 871 r->reply.tag = 0; 872 873 /* read at least length, type, and tag and pullup to a single block */ 874 if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0) 875 return -1; 876 nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ); 877 878 /* read in the rest of the message, avoid rediculous (for now) message sizes */ 879 len = GBIT32(nb->rp); 880 if(len > m->msize){ 881 qdiscard(m->q, qlen(m->q)); 882 return -1; 883 } 884 if(doread(m, len) < 0) 885 return -1; 886 887 /* pullup the header (i.e. everything except data) */ 888 t = nb->rp[BIT32SZ]; 889 switch(t){ 890 case Rread: 891 hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ; 892 break; 893 default: 894 hlen = len; 895 break; 896 } 897 nb = pullupqueue(m->q, hlen); 898 899 if(convM2S(nb->rp, len, &r->reply) <= 0){ 900 /* bad message, dump it */ 901 print("mntrpcread: convM2S failed\n"); 902 qdiscard(m->q, len); 903 return -1; 904 } 905 906 /* hang the data off of the fcall struct */ 907 l = &r->b; 908 *l = nil; 909 do { 910 b = qremove(m->q); 911 if(hlen > 0){ 912 b->rp += hlen; 913 len -= hlen; 914 hlen = 0; 915 } 916 i = BLEN(b); 917 if(i <= len){ 918 len -= i; 919 *l = b; 920 l = &(b->next); 921 } else { 922 /* split block and put unused bit back */ 923 nb = allocb(i-len); 924 memmove(nb->wp, b->rp+len, i-len); 925 b->wp = b->rp+len; 926 nb->wp += i-len; 927 qputback(m->q, nb); 928 *l = b; 929 return 0; 930 } 931 }while(len > 0); 932 933 return 0; 934 } 935 936 void 937 mntgate(Mnt *m) 938 { 939 Mntrpc *q; 940 941 lock(&m->lk); 942 m->rip = 0; 943 for(q = m->queue; q; q = q->list) { 944 if(q->done == 0) 945 if(wakeup(&q->r)) 946 break; 947 } 948 unlock(&m->lk); 949 } 950 951 void 952 mountmux(Mnt *m, Mntrpc *r) 953 { 954 Mntrpc **l, *q; 955 956 lock(&m->lk); 957 l = &m->queue; 958 for(q = *l; q; q = q->list) { 959 /* look for a reply to a message */ 960 if(q->request.tag == r->reply.tag) { 961 *l = q->list; 962 if(q != r) { 963 /* 964 * Completed someone else. 965 * Trade pointers to receive buffer. 966 */ 967 q->reply = r->reply; 968 q->b = r->b; 969 r->b = nil; 970 } 971 q->done = 1; 972 unlock(&m->lk); 973 if(mntstats != 0) 974 (*mntstats)(q->request.type, 975 m->c, q->stime, 976 q->reqlen + r->replen); 977 if(q != r) 978 wakeup(&q->r); 979 return; 980 } 981 l = &q->list; 982 } 983 unlock(&m->lk); 984 print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type); 985 } 986 987 /* 988 * Create a new flush request and chain the previous 989 * requests from it 990 */ 991 Mntrpc* 992 mntflushalloc(Mntrpc *r, ulong iounit) 993 { 994 Mntrpc *fr; 995 996 fr = mntralloc(0, iounit); 997 998 fr->request.type = Tflush; 999 if(r->request.type == Tflush) 1000 fr->request.oldtag = r->request.oldtag; 1001 else 1002 fr->request.oldtag = r->request.tag; 1003 fr->flushed = r; 1004 1005 return fr; 1006 } 1007 1008 /* 1009 * Free a chain of flushes. Remove each unanswered 1010 * flush and the original message from the unanswered 1011 * request queue. Mark the original message as done 1012 * and if it hasn't been answered set the reply to to 1013 * Rflush. 1014 */ 1015 void 1016 mntflushfree(Mnt *m, Mntrpc *r) 1017 { 1018 Mntrpc *fr; 1019 1020 while(r){ 1021 fr = r->flushed; 1022 if(!r->done){ 1023 r->reply.type = Rflush; 1024 mntqrm(m, r); 1025 } 1026 if(fr) 1027 mntfree(r); 1028 r = fr; 1029 } 1030 } 1031 1032 int 1033 alloctag(void) 1034 { 1035 int i, j; 1036 ulong v; 1037 1038 for(i = 0; i < NMASK; i++){ 1039 v = mntalloc.tagmask[i]; 1040 if(v == ~0) 1041 continue; 1042 for(j = 0; j < 1<<TAGSHIFT; j++) 1043 if((v & (1<<j)) == 0){ 1044 mntalloc.tagmask[i] |= 1<<j; 1045 return (i<<TAGSHIFT) + j; 1046 } 1047 } 1048 panic("no friggin tags left"); 1049 return NOTAG; 1050 } 1051 1052 void 1053 freetag(int t) 1054 { 1055 mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK)); 1056 } 1057 1058 Mntrpc* 1059 mntralloc(Chan *c, ulong msize) 1060 { 1061 Mntrpc *new; 1062 1063 lock(&mntalloc.lk); 1064 new = mntalloc.rpcfree; 1065 if(new == nil){ 1066 new = malloc(sizeof(Mntrpc)); 1067 if(new == nil) { 1068 unlock(&mntalloc.lk); 1069 exhausted("mount rpc header"); 1070 } 1071 /* 1072 * The header is split from the data buffer as 1073 * mountmux may swap the buffer with another header. 1074 */ 1075 new->rpc = mallocz(msize, 0); 1076 if(new->rpc == nil){ 1077 free(new); 1078 unlock(&mntalloc.lk); 1079 exhausted("mount rpc buffer"); 1080 } 1081 new->rpclen = msize; 1082 new->request.tag = alloctag(); 1083 } 1084 else { 1085 mntalloc.rpcfree = new->list; 1086 mntalloc.nrpcfree--; 1087 if(new->rpclen < msize){ 1088 free(new->rpc); 1089 new->rpc = mallocz(msize, 0); 1090 if(new->rpc == nil){ 1091 free(new); 1092 mntalloc.nrpcused--; 1093 unlock(&mntalloc.lk); 1094 exhausted("mount rpc buffer"); 1095 } 1096 new->rpclen = msize; 1097 } 1098 } 1099 mntalloc.nrpcused++; 1100 unlock(&mntalloc.lk); 1101 new->c = c; 1102 new->done = 0; 1103 new->flushed = nil; 1104 new->b = nil; 1105 return new; 1106 } 1107 1108 void 1109 mntfree(Mntrpc *r) 1110 { 1111 if(r->b != nil) 1112 freeblist(r->b); 1113 lock(&mntalloc.lk); 1114 if(mntalloc.nrpcfree >= 10){ 1115 free(r->rpc); 1116 free(r); 1117 freetag(r->request.tag); 1118 } 1119 else{ 1120 r->list = mntalloc.rpcfree; 1121 mntalloc.rpcfree = r; 1122 mntalloc.nrpcfree++; 1123 } 1124 mntalloc.nrpcused--; 1125 unlock(&mntalloc.lk); 1126 } 1127 1128 void 1129 mntqrm(Mnt *m, Mntrpc *r) 1130 { 1131 Mntrpc **l, *f; 1132 1133 lock(&m->lk); 1134 r->done = 1; 1135 1136 l = &m->queue; 1137 for(f = *l; f; f = f->list) { 1138 if(f == r) { 1139 *l = r->list; 1140 break; 1141 } 1142 l = &f->list; 1143 } 1144 unlock(&m->lk); 1145 } 1146 1147 Mnt* 1148 mntchk(Chan *c) 1149 { 1150 Mnt *m; 1151 1152 /* This routine is mostly vestiges of prior lives; now it's just sanity checking */ 1153 1154 if(c->mchan == nil) 1155 panic("mntchk 1: nil mchan c %s\n", c2name(c)); 1156 1157 m = c->mchan->mux; 1158 1159 if(m == nil) 1160 print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan)); 1161 1162 /* 1163 * Was it closed and reused (was error(Eshutdown); now, it can't happen) 1164 */ 1165 if(m->id == 0 || m->id >= c->dev) 1166 panic("mntchk 3: can't happen"); 1167 1168 return m; 1169 } 1170 1171 /* 1172 * Rewrite channel type and dev for in-flight data to 1173 * reflect local values. These entries are known to be 1174 * the first two in the Dir encoding after the count. 1175 */ 1176 void 1177 mntdirfix(uchar *dirbuf, Chan *c) 1178 { 1179 uint r; 1180 1181 r = devtab[c->type]->dc; 1182 dirbuf += BIT16SZ; /* skip count */ 1183 PBIT16(dirbuf, r); 1184 dirbuf += BIT16SZ; 1185 PBIT32(dirbuf, c->dev); 1186 } 1187 1188 int 1189 rpcattn(void *v) 1190 { 1191 Mntrpc *r; 1192 1193 r = v; 1194 return r->done || r->m->rip == 0; 1195 } 1196 1197 Dev mntdevtab = { 1198 'M', 1199 "mnt", 1200 1201 mntreset, 1202 devinit, 1203 devshutdown, 1204 mntattach, 1205 mntwalk, 1206 mntstat, 1207 mntopen, 1208 mntcreate, 1209 mntclose, 1210 mntread, 1211 devbread, 1212 mntwrite, 1213 devbwrite, 1214 mntremove, 1215 mntwstat, 1216 }; 1217