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