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