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 #include "kernel.h" 8 9 typedef struct Fid Fid; 10 typedef struct Export Export; 11 typedef struct Exq Exq; 12 typedef struct Uqid Uqid; 13 14 enum 15 { 16 Nfidhash = 32, 17 Nqidhash = 32, 18 QIDMASK = ((vlong)1<<48)-1, 19 MAXFDATA = 8192, 20 MAXRPCDEF = IOHDRSZ+MAXFDATA, /* initial/default */ 21 MAXRPCMAX = IOHDRSZ+64*1024, /* most every allowed */ 22 MSGHDRSZ = BIT32SZ+BIT8SZ+BIT16SZ 23 }; 24 25 struct Export 26 { 27 Lock; 28 Ref r; 29 Exq* work; 30 Lock fidlock; 31 Fid* fid[Nfidhash]; 32 QLock qidlock; 33 Uqid* qids[Nqidhash]; 34 ulong pathgen; 35 Chan* io; 36 Chan* root; 37 Pgrp* pgrp; 38 Egrp* egrp; 39 Fgrp* fgrp; 40 int async; 41 int readonly; 42 int msize; 43 char* user; 44 }; 45 46 struct Fid 47 { 48 Fid* next; 49 Fid** last; 50 Chan* chan; 51 int fid; 52 int ref; /* fcalls using the fid; locked by Export.Lock */ 53 vlong offset; /* last offset used (within directory) */ 54 int attached; /* fid attached or cloned but not clunked */ 55 Uqid* qid; /* generated qid */ 56 }; 57 58 struct Uqid 59 { 60 Ref; 61 int type; 62 int dev; 63 vlong oldpath; 64 vlong newpath; 65 Uqid* next; 66 }; 67 68 struct Exq 69 { 70 Lock; 71 int busy; /* fcall in progress */ 72 int finished; /* will do no more work on this request or flushes */ 73 Exq* next; 74 int shut; /* has been noted for shutdown */ 75 Exq* flush; /* queued flush requests */ 76 Exq* flusht; /* tail of flush queue */ 77 Export* export; 78 Proc* slave; 79 Fcall in, out; 80 uchar* buf; 81 int bsize; 82 }; 83 84 struct 85 { 86 Lock l; 87 QLock qwait; 88 Rendez rwait; 89 Exq *head; /* work waiting for a slave */ 90 Exq *tail; 91 }exq; 92 93 static void exshutdown(Export*); 94 static int exflushed(Export*, Exq*); 95 static void exslave(void*); 96 static void exfree(Export*); 97 static void exfreeq(Exq*); 98 static void exportproc(void*); 99 static void exreply(Exq*, char*); 100 static int exisroot(Export*, Chan*); 101 static Uqid* uqidalloc(Export*, Chan*); 102 static void freeuqid(Export*, Uqid*); 103 104 static char* Exversion(Export*, Fcall*, Fcall*); 105 static char* Exauth(Export*, Fcall*, Fcall*); 106 static char* Exattach(Export*, Fcall*, Fcall*); 107 static char* Exclunk(Export*, Fcall*, Fcall*); 108 static char* Excreate(Export*, Fcall*, Fcall*); 109 static char* Exopen(Export*, Fcall*, Fcall*); 110 static char* Exread(Export*, Fcall*, Fcall*); 111 static char* Exremove(Export*, Fcall*, Fcall*); 112 static char* Exstat(Export*, Fcall*, Fcall*); 113 static char* Exwalk(Export*, Fcall*, Fcall*); 114 static char* Exwrite(Export*, Fcall*, Fcall*); 115 static char* Exwstat(Export*, Fcall*, Fcall*); 116 117 static char *(*fcalls[Tmax])(Export*, Fcall*, Fcall*); 118 119 static char Enofid[] = "no such fid"; 120 static char Eseekdir[] = "can't seek on a directory"; 121 static char Eopen[] = "walk of open fid"; 122 static char Emode[] = "open/create -- unknown mode"; 123 static char Edupfid[] = "fid in use"; 124 static char Eaccess[] = "read/write -- not open in suitable mode"; 125 static char Ecount[] = "read/write -- count too big"; 126 int exdebug = 0; 127 128 int 129 export(int fd, char *dir, int async) 130 { 131 Chan *c, *dc; 132 Pgrp *pg; 133 Egrp *eg; 134 Export *fs; 135 136 if(waserror()) 137 return -1; 138 c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1); 139 poperror(); 140 141 if(waserror()){ 142 cclose(c); 143 return -1; 144 } 145 dc = namec(dir, Atodir, 0, 0); 146 poperror(); 147 148 fs = malloc(sizeof(Export)); 149 if(fs == nil){ 150 cclose(c); 151 cclose(dc); 152 error(Enomem); 153 } 154 155 fs->r.ref = 1; 156 pg = up->env->pgrp; 157 fs->pgrp = pg; 158 incref(pg); 159 eg = up->env->egrp; 160 fs->egrp = eg; 161 if(eg != nil) 162 incref(eg); 163 fs->fgrp = newfgrp(nil); 164 kstrdup(&fs->user, up->env->user); 165 fs->root = dc; 166 fs->io = c; 167 fs->pathgen = 0; 168 fs->msize = 0; 169 c->flag |= CMSG; 170 fs->async = async; 171 172 if(async){ 173 if(waserror()) 174 return -1; 175 kproc("exportfs", exportproc, fs, 0); 176 poperror(); 177 }else 178 exportproc(fs); 179 180 return 0; 181 } 182 183 static void 184 exportinit(void) 185 { 186 lock(&exq.l); 187 if(fcalls[Tversion] != nil) { 188 unlock(&exq.l); 189 return; 190 } 191 fcalls[Tversion] = Exversion; 192 fcalls[Tauth] = Exauth; 193 fcalls[Tattach] = Exattach; 194 fcalls[Twalk] = Exwalk; 195 fcalls[Topen] = Exopen; 196 fcalls[Tcreate] = Excreate; 197 fcalls[Tread] = Exread; 198 fcalls[Twrite] = Exwrite; 199 fcalls[Tclunk] = Exclunk; 200 fcalls[Tremove] = Exremove; 201 fcalls[Tstat] = Exstat; 202 fcalls[Twstat] = Exwstat; 203 unlock(&exq.l); 204 } 205 206 static int 207 exisroot(Export *fs, Chan *c) 208 { 209 return eqchan(fs->root, c, 1); 210 } 211 212 static int 213 exreadn(Chan *c, void *buf, int n) 214 { 215 int nr, t; 216 217 if(waserror()) 218 return -1; 219 for(nr = 0; nr < n;){ 220 t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0); 221 if(t <= 0) 222 break; 223 nr += t; 224 } 225 poperror(); 226 return nr; 227 } 228 229 static int 230 exreadmsg(Chan *c, void *a, uint n) 231 { 232 int m, len; 233 uchar *buf; 234 235 buf = a; 236 m = exreadn(c, buf, BIT32SZ); 237 if(m < BIT32SZ){ 238 if(m < 0) 239 return -1; 240 return 0; 241 } 242 len = GBIT32(buf); 243 if(len <= BIT32SZ || len > n){ 244 kwerrstr("bad length in Styx message header"); 245 return -1; 246 } 247 len -= BIT32SZ; 248 m = exreadn(c, buf+BIT32SZ, len); 249 if(m < len){ 250 if(m < 0) 251 return -1; 252 return 0; 253 } 254 return BIT32SZ+m; 255 } 256 257 static void 258 exportproc(void *a) 259 { 260 Exq *q; 261 int async, msize; 262 int n, type; 263 Export *fs = a; 264 265 exportinit(); 266 267 for(;;){ 268 269 msize = fs->msize; 270 if(msize == 0) 271 msize = MAXRPCDEF; 272 for(n=0;; n++){ /* we don't use smalloc, to avoid memset */ 273 q = mallocz(sizeof(*q)+msize, 0); 274 if(q != nil || n > 6000) 275 break; 276 if(n%600 == 0) 277 print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize); 278 tsleep(&up->sleep, return0, nil, 100); 279 } 280 if(q == nil){ 281 kwerrstr("out of memory: read request"); 282 n = -1; 283 break; 284 } 285 memset(q, 0, sizeof(*q)); 286 q->buf = (uchar*)q + sizeof(*q); 287 q->bsize = msize; 288 289 n = exreadmsg(fs->io, q->buf, msize); /* TO DO: avoid copy */ 290 if(n <= 0) 291 break; 292 if(convM2S(q->buf, n, &q->in) != n){ 293 kwerrstr("bad T-message"); 294 n = -1; 295 break; 296 } 297 type = q->in.type; 298 if(type < Tversion || type >= Tmax || type&1 || type == Terror){ 299 kwerrstr("invalid T-message type %d", type); 300 n = -1; 301 break; 302 } 303 304 if(exdebug) 305 print("export %ld <- %F\n", up->pid, &q->in); 306 307 q->out.type = type+1; 308 q->out.tag = q->in.tag; 309 310 q->export = fs; 311 incref(&fs->r); 312 313 if(fs->readonly){ 314 switch(type){ 315 case Topen: 316 if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD) 317 break; 318 /* FALL THROUGH */ 319 case Tcreate: 320 case Twrite: 321 case Tremove: 322 case Twstat: 323 q->out.type = Rerror; 324 q->out.ename = "file system read only"; 325 exreply(q, "exportproc"); 326 exfreeq(q); 327 continue; 328 } 329 } 330 331 if(q->in.type == Tflush){ 332 if(exflushed(fs, q)){ 333 /* not yet started or not found (flush arrived after reply); we reply */ 334 if(exdebug) 335 print("export: flush %d\n", q->in.oldtag); 336 exreply(q, "exportproc"); 337 exfreeq(q); 338 } 339 continue; 340 } 341 342 lock(&exq.l); 343 if(exq.head == nil) 344 exq.head = q; 345 else 346 exq.tail->next = q; 347 q->next = nil; 348 exq.tail = q; 349 unlock(&exq.l); 350 if(exq.qwait.head == nil) 351 kproc("exslave", exslave, nil, 0); 352 wakeup(&exq.rwait); 353 } 354 355 if(exdebug){ 356 if(n < 0) 357 print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr); 358 else 359 print("exportproc %ld shut down\n", up->pid); 360 } 361 362 free(q); 363 exshutdown(fs); 364 async = fs->async; 365 exfree(fs); 366 367 if(async) 368 pexit("mount shut down", 0); 369 } 370 371 static int 372 exflushed(Export *fs, Exq *fq) 373 { 374 Exq *q, **last; 375 ulong pid; 376 377 /* not yet started? */ 378 lock(&exq.l); 379 for(last = &exq.head; (q = *last) != nil; last = &q->next) 380 if(q->export == fs && q->in.tag == fq->in.oldtag){ 381 *last = q->next; 382 unlock(&exq.l); 383 /* not yet started: discard, and Rflush */ 384 exfreeq(q); 385 return 1; 386 } 387 unlock(&exq.l); 388 389 /* tricky case: in progress */ 390 lock(fs); 391 for(q = fs->work; q != nil; q = q->next) 392 if(q->in.tag == fq->in.oldtag){ 393 pid = 0; 394 lock(q); 395 if(q->finished){ 396 /* slave replied and emptied its flush queue; we can Rflush now */ 397 unlock(q); 398 return 1; 399 } 400 /* append to slave's flush queue */ 401 fq->next = nil; 402 if(q->flush != nil) 403 q->flusht->next = fq; 404 else 405 q->flush = fq; 406 q->flusht = fq; 407 if(q->busy){ 408 pid = q->slave->pid; 409 swiproc(q->slave, 0); 410 } 411 unlock(q); 412 unlock(fs); 413 if(exdebug && pid) 414 print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag); 415 return 0; 416 } 417 unlock(fs); 418 419 /* not found */ 420 return 1; 421 } 422 423 static void 424 exfreeq(Exq *q) 425 { 426 Exq *fq; 427 428 while((fq = q->flush) != nil){ 429 q->flush = fq->next; 430 exfree(fq->export); 431 free(fq); 432 } 433 exfree(q->export); 434 free(q); 435 } 436 437 static void 438 exshutdown(Export *fs) 439 { 440 Exq *q, **last; 441 442 /* work not started */ 443 lock(&exq.l); 444 for(last = &exq.head; (q = *last) != nil;) 445 if(q->export == fs){ 446 *last = q->next; 447 exfreeq(q); 448 }else 449 last = &q->next; 450 unlock(&exq.l); 451 452 /* tell slaves to abandon work in progress */ 453 lock(fs); 454 while((q = fs->work) != nil){ 455 fs->work = q->next; 456 lock(q); 457 q->shut = 1; 458 swiproc(q->slave, 0); /* whether busy or not */ 459 unlock(q); 460 } 461 unlock(fs); 462 } 463 464 static void 465 exfreefids(Export *fs) 466 { 467 Fid *f, *n; 468 int i; 469 470 for(i = 0; i < Nfidhash; i++){ 471 for(f = fs->fid[i]; f != nil; f = n){ 472 n = f->next; 473 f->attached = 0; 474 if(f->ref == 0) { 475 if(f->chan != nil) 476 cclose(f->chan); 477 freeuqid(fs, f->qid); 478 free(f); 479 } else 480 print("exfreefids: busy fid\n"); 481 } 482 } 483 } 484 485 static void 486 exfree(Export *fs) 487 { 488 if(exdebug) 489 print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref); 490 if(decref(&fs->r) != 0) 491 return; 492 closepgrp(fs->pgrp); 493 closeegrp(fs->egrp); 494 closefgrp(fs->fgrp); 495 cclose(fs->root); 496 cclose(fs->io); 497 exfreefids(fs); 498 free(fs->user); 499 free(fs); 500 } 501 502 static int 503 exwork(void*) 504 { 505 return exq.head != nil; 506 } 507 508 static void 509 exslave(void*) 510 { 511 Export *fs; 512 Exq *q, *t, *fq, **last; 513 char *err; 514 int nstat; 515 516 for(;;){ 517 qlock(&exq.qwait); 518 if(waserror()){ 519 qunlock(&exq.qwait); 520 continue; 521 } 522 sleep(&exq.rwait, exwork, nil); 523 poperror(); 524 525 lock(&exq.l); 526 q = exq.head; 527 if(q == nil) { 528 unlock(&exq.l); 529 qunlock(&exq.qwait); 530 continue; 531 } 532 exq.head = q->next; 533 534 qunlock(&exq.qwait); 535 536 /* 537 * put the job on the work queue before it's 538 * visible as off of the head queue, so it's always 539 * findable for flushes and shutdown 540 */ 541 notkilled(); 542 q->slave = up; 543 q->busy = 1; /* fcall in progress: interruptible */ 544 fs = q->export; 545 lock(fs); 546 q->next = fs->work; 547 fs->work = q; 548 unlock(fs); 549 unlock(&exq.l); 550 551 up->env->pgrp = q->export->pgrp; 552 up->env->egrp = q->export->egrp; 553 up->env->fgrp = q->export->fgrp; 554 kstrdup(&up->env->user, q->export->user); 555 556 if(exdebug > 1) 557 print("exslave %ld dispatch %F\n", up->pid, &q->in); 558 559 if(waserror()){ 560 print("exslave %ld err %s\n", up->pid, up->env->errstr); /* shouldn't happen */ 561 err = up->env->errstr; 562 }else{ 563 if(q->in.type >= Tmax || !fcalls[q->in.type]){ 564 snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in); 565 err = up->genbuf; 566 }else{ 567 switch(q->in.type){ 568 case Tread: 569 q->out.data = (char*)q->buf + IOHDRSZ; 570 break; 571 case Tstat: 572 q->out.stat = q->buf + MSGHDRSZ + BIT16SZ; /* leaves it just where we want it */ 573 nstat = q->bsize; 574 if(nstat > STATMAX) 575 nstat = STATMAX; 576 nstat -= MSGHDRSZ+BIT16SZ; 577 q->out.nstat = nstat; 578 break; 579 } 580 err = (*fcalls[q->in.type])(fs, &q->in, &q->out); 581 } 582 poperror(); 583 } 584 585 /* 586 * if the fcall completed without error we must reply, 587 * even if a flush is pending (because the underlying server 588 * might have changed state), unless the export has shut down completely. 589 * must also reply to each flush in order, and only after the original reply (if sent). 590 */ 591 lock(q); 592 notkilled(); 593 q->busy = 0; /* operation complete */ 594 if(!q->shut){ 595 if(q->flush == nil || err == nil){ 596 unlock(q); 597 q->out.type = q->in.type+1; 598 q->out.tag = q->in.tag; 599 if(err){ 600 q->out.type = Rerror; 601 q->out.ename = err; 602 } 603 exreply(q, "exslave"); 604 lock(q); 605 } 606 while((fq = q->flush) != nil && !q->shut){ 607 q->flush = fq->next; 608 unlock(q); 609 exreply(fq, "exslave"); 610 exfreeq(fq); 611 lock(q); 612 } 613 } 614 q->finished = 1; /* promise not to send any more */ 615 unlock(q); 616 617 lock(fs); 618 for(last = &fs->work; (t = *last) != nil; last = &t->next) 619 if(t == q){ 620 *last = q->next; 621 break; 622 } 623 unlock(fs); 624 625 notkilled(); 626 exfreeq(q); 627 } 628 print("exslave %ld shut down", up->pid); /* not reached */ 629 pexit("exslave shut down", 0); 630 } 631 632 static void 633 exreply(Exq *q, char *who) 634 { 635 Export *fs; 636 Fcall *r; 637 int n; 638 639 fs = q->export; 640 r = &q->out; 641 642 n = convS2M(r, q->buf, q->bsize); 643 if(n == 0){ 644 r->type = Rerror; 645 if(fs->msize == 0) 646 r->ename = "Tversion not seen"; 647 else 648 r->ename = "failed to convert R-message"; 649 n = convS2M(r, q->buf, q->bsize); 650 } 651 652 if(exdebug) 653 print("%s %ld -> %F\n", who, up->pid, r); 654 655 if(!waserror()){ 656 devtab[fs->io->type]->write(fs->io, q->buf, n, 0); 657 poperror(); 658 } 659 } 660 661 static int 662 exiounit(Export *fs, Chan *c) 663 { 664 int iounit; 665 666 iounit = fs->msize-IOHDRSZ; 667 if(c->iounit != 0 && c->iounit < fs->msize) 668 iounit = c->iounit; 669 return iounit; 670 } 671 672 static Qid 673 Exrmtqid(Chan *c, Uqid *qid) 674 { 675 Qid q; 676 677 q.path = qid->newpath; 678 q.vers = c->qid.vers; 679 q.type = c->qid.type; 680 return q; 681 } 682 683 static Fid* 684 Exmkfid(Export *fs, ulong fid) 685 { 686 ulong h; 687 Fid *f, *nf; 688 689 nf = malloc(sizeof(Fid)); 690 if(nf == nil) 691 return nil; 692 lock(&fs->fidlock); 693 h = fid % Nfidhash; 694 for(f = fs->fid[h]; f != nil; f = f->next){ 695 if(f->fid == fid){ 696 unlock(&fs->fidlock); 697 free(nf); 698 return nil; 699 } 700 } 701 702 nf->next = fs->fid[h]; 703 if(nf->next != nil) 704 nf->next->last = &nf->next; 705 nf->last = &fs->fid[h]; 706 fs->fid[h] = nf; 707 708 nf->fid = fid; 709 nf->ref = 1; 710 nf->attached = 1; 711 nf->offset = 0; 712 nf->chan = nil; 713 nf->qid = nil; 714 unlock(&fs->fidlock); 715 return nf; 716 } 717 718 static Fid* 719 Exgetfid(Export *fs, ulong fid) 720 { 721 Fid *f; 722 ulong h; 723 724 lock(&fs->fidlock); 725 h = fid % Nfidhash; 726 for(f = fs->fid[h]; f; f = f->next) { 727 if(f->fid == fid){ 728 if(f->attached == 0) 729 break; 730 f->ref++; 731 unlock(&fs->fidlock); 732 return f; 733 } 734 } 735 unlock(&fs->fidlock); 736 return nil; 737 } 738 739 static void 740 Exputfid(Export *fs, Fid *f) 741 { 742 Chan *c; 743 744 lock(&fs->fidlock); 745 f->ref--; 746 if(f->ref == 0 && f->attached == 0){ 747 c = f->chan; 748 f->chan = nil; 749 *f->last = f->next; 750 if(f->next != nil) 751 f->next->last = f->last; 752 unlock(&fs->fidlock); 753 if(c != nil) 754 cclose(c); 755 freeuqid(fs, f->qid); 756 free(f); 757 return; 758 } 759 unlock(&fs->fidlock); 760 } 761 762 static Chan* 763 exmount(Chan *c, Mhead **mp, int doname) 764 { 765 Chan *nc; 766 Cname *oname; 767 768 nc = nil; 769 if((c->flag & COPEN) == 0 && findmount(&nc, mp, c->type, c->dev, c->qid)){ 770 if(waserror()){ 771 cclose(nc); 772 nexterror(); 773 } 774 nc = cunique(nc); 775 poperror(); 776 if(doname){ 777 oname = c->name; 778 incref(oname); 779 cnameclose(nc->name); 780 nc->name = oname; 781 } 782 return nc; 783 } 784 incref(c); 785 return c; 786 } 787 788 static char* 789 Exversion(Export *fs, Fcall *t, Fcall *r) 790 { 791 char *p; 792 static char version[] = VERSION9P; 793 int iounit; 794 795 r->msize = t->msize; 796 if(r->msize > MAXRPCMAX) 797 r->msize = MAXRPCMAX; 798 iounit = fs->io->iounit; 799 if(iounit != 0 && iounit > 64 && iounit < r->msize) 800 r->msize = iounit; 801 if(r->msize < 64) 802 return "message size too small"; 803 if((p = strchr(t->version, '.')) != nil) 804 *p = 0; 805 if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){ 806 r->version = version; 807 fs->msize = r->msize; 808 }else 809 r->version = "unknown"; 810 return nil; 811 } 812 813 static char* 814 Exauth(Export *fs, Fcall *t, Fcall *r) 815 { 816 USED(fs); 817 USED(t); 818 USED(r); 819 return "authentication not required"; 820 } 821 822 static char* 823 Exattach(Export *fs, Fcall *t, Fcall *r) 824 { 825 Fid *f; 826 827 f = Exmkfid(fs, t->fid); 828 if(f == nil) 829 return Edupfid; 830 if(waserror()){ 831 f->attached = 0; 832 Exputfid(fs, f); 833 return up->env->errstr; 834 } 835 f->chan = cclone(fs->root); 836 f->qid = uqidalloc(fs, f->chan); 837 poperror(); 838 r->qid = Exrmtqid(f->chan, f->qid); 839 Exputfid(fs, f); 840 return nil; 841 } 842 843 static char* 844 Exclunk(Export *fs, Fcall *t, Fcall *r) 845 { 846 Fid *f; 847 848 USED(r); 849 f = Exgetfid(fs, t->fid); 850 if(f == nil) 851 return Enofid; 852 f->attached = 0; 853 Exputfid(fs, f); 854 return nil; 855 } 856 857 static int 858 safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror) 859 { 860 int r; 861 862 /* walk can raise error */ 863 if(waserror()) 864 return -1; 865 r = walk(cp, names, nnames, nomount, nerror); 866 poperror(); 867 return r; 868 } 869 870 static char* 871 Exwalk(Export *fs, Fcall *t, Fcall *r) 872 { 873 Fid *f, *nf; 874 Chan *c; 875 char *name; 876 Uqid *qid; 877 int i; 878 879 f = Exgetfid(fs, t->fid); 880 if(f == nil) 881 return Enofid; 882 if(f->chan->flag & COPEN){ 883 Exputfid(fs, f); 884 return Eopen; 885 } 886 887 if(waserror()) 888 return up->env->errstr; 889 c = cclone(f->chan); 890 poperror(); 891 qid = f->qid; 892 incref(qid); 893 r->nwqid = 0; 894 if(t->nwname > 0){ 895 for(i=0; i<t->nwname; i++){ 896 name = t->wname[i]; 897 if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){ 898 if(safewalk(&c, &name, 1, 0, nil) < 0){ 899 /* leave the original state on error */ 900 cclose(c); 901 freeuqid(fs, qid); 902 Exputfid(fs, f); 903 if(i == 0) 904 return up->env->errstr; 905 return nil; 906 } 907 freeuqid(fs, qid); 908 qid = uqidalloc(fs, c); 909 } 910 r->wqid[r->nwqid++] = Exrmtqid(c, qid); 911 } 912 } 913 914 if(t->newfid != t->fid){ 915 nf = Exmkfid(fs, t->newfid); 916 if(nf == nil){ 917 cclose(c); 918 freeuqid(fs, qid); 919 Exputfid(fs, f); 920 return Edupfid; 921 } 922 nf->chan = c; 923 nf->qid = qid; 924 Exputfid(fs, nf); 925 }else{ 926 cclose(f->chan); 927 f->chan = c; 928 freeuqid(fs, f->qid); 929 f->qid = qid; 930 } 931 Exputfid(fs, f); 932 return nil; 933 } 934 935 static char* 936 Exopen(Export *fs, Fcall *t, Fcall *r) 937 { 938 Fid *f; 939 Chan *c; 940 Uqid *qid; 941 Mhead *m; 942 943 f = Exgetfid(fs, t->fid); 944 if(f == nil) 945 return Enofid; 946 if(f->chan->flag & COPEN){ 947 Exputfid(fs, f); 948 return Emode; 949 } 950 m = nil; 951 c = exmount(f->chan, &m, 1); 952 if(waserror()){ 953 cclose(c); 954 Exputfid(fs, f); 955 return up->env->errstr; 956 } 957 958 /* only save the mount head if it's a multiple element union */ 959 if(m && m->mount && m->mount->next) 960 c->umh = m; 961 else 962 putmhead(m); 963 964 c = devtab[c->type]->open(c, t->mode); 965 if(t->mode & ORCLOSE) 966 c->flag |= CRCLOSE; 967 968 qid = uqidalloc(fs, c); 969 poperror(); 970 freeuqid(fs, f->qid); 971 cclose(f->chan); 972 f->chan = c; 973 f->qid = qid; 974 f->offset = 0; 975 r->qid = Exrmtqid(c, f->qid); 976 r->iounit = exiounit(fs, c); 977 Exputfid(fs, f); 978 return nil; 979 } 980 981 static char* 982 Excreate(Export *fs, Fcall *t, Fcall *r) 983 { 984 Fid *f; 985 volatile struct {Chan *c;} c, dc; 986 Cname *oname; 987 Uqid *qid; 988 Mhead *m; 989 990 f = Exgetfid(fs, t->fid); 991 if(f == nil) 992 return Enofid; 993 if(f->chan->flag & COPEN){ 994 Exputfid(fs, f); 995 return Emode; 996 } 997 if(waserror()){ 998 Exputfid(fs, f); 999 return up->env->errstr; 1000 } 1001 validname(t->name, 0); 1002 if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0')) 1003 error(Efilename); /* underlying server should check, but stop it here */ 1004 1005 m = nil; 1006 c.c = exmount(f->chan, &m, 1); 1007 if(waserror()){ 1008 cclose(c.c); 1009 if(m != nil) 1010 putmhead(m); 1011 nexterror(); 1012 } 1013 if(m != nil){ 1014 oname = c.c->name; 1015 incref(oname); 1016 if(waserror()){ 1017 cnameclose(oname); 1018 nexterror(); 1019 } 1020 dc.c = createdir(c.c, m); 1021 if(waserror()){ 1022 cclose(dc.c); 1023 nexterror(); 1024 } 1025 c.c = cunique(dc.c); 1026 poperror(); 1027 cnameclose(c.c->name); 1028 poperror(); 1029 c.c->name = oname; 1030 } 1031 devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm); 1032 c.c->name = addelem(c.c->name, t->name); 1033 if(t->mode & ORCLOSE) 1034 c.c->flag |= CRCLOSE; 1035 qid = uqidalloc(fs, c.c); 1036 poperror(); 1037 if(m != nil) 1038 putmhead(m); 1039 1040 poperror(); 1041 cclose(f->chan); 1042 f->chan = c.c; 1043 freeuqid(fs, f->qid); 1044 f->qid = qid; 1045 r->qid = Exrmtqid(c.c, f->qid); 1046 r->iounit = exiounit(fs, c.c); 1047 Exputfid(fs, f); 1048 return nil; 1049 } 1050 1051 static char* 1052 Exread(Export *fs, Fcall *t, Fcall *r) 1053 { 1054 Fid *f; 1055 Chan *c; 1056 long off; 1057 int dir, n, seek; 1058 1059 f = Exgetfid(fs, t->fid); 1060 if(f == nil) 1061 return Enofid; 1062 1063 if(waserror()) { 1064 Exputfid(fs, f); 1065 return up->env->errstr; 1066 } 1067 c = f->chan; 1068 if((c->flag & COPEN) == 0) 1069 error(Emode); 1070 if(c->mode != OREAD && c->mode != ORDWR) 1071 error(Eaccess); 1072 if(t->count < 0 || t->count > fs->msize-IOHDRSZ) 1073 error(Ecount); 1074 if(t->offset < 0) 1075 error(Enegoff); 1076 dir = c->qid.type & QTDIR; 1077 if(dir && t->offset != f->offset){ 1078 if(t->offset != 0) 1079 error(Eseekdir); 1080 f->offset = 0; 1081 c->uri = 0; 1082 c->dri = 0; 1083 } 1084 1085 for(;;){ 1086 n = t->count; 1087 seek = 0; 1088 off = t->offset; 1089 if(dir && f->offset != off){ 1090 off = f->offset; 1091 n = t->offset - off; 1092 if(n > MAXFDATA) 1093 n = MAXFDATA; 1094 seek = 1; 1095 } 1096 if(dir && c->umh != nil){ 1097 if(0) 1098 print("union read %d uri %d dri %d\n", seek, c->uri, c->dri); 1099 n = unionread(c, r->data, n); 1100 } 1101 else{ 1102 c->offset = off; 1103 n = devtab[c->type]->read(c, r->data, n, off); 1104 lock(c); 1105 c->offset += n; 1106 unlock(c); 1107 } 1108 f->offset = off + n; 1109 if(n == 0 || !seek) 1110 break; 1111 } 1112 r->count = n; 1113 1114 poperror(); 1115 Exputfid(fs, f); 1116 return nil; 1117 } 1118 1119 static char* 1120 Exwrite(Export *fs, Fcall *t, Fcall *r) 1121 { 1122 Fid *f; 1123 Chan *c; 1124 1125 f = Exgetfid(fs, t->fid); 1126 if(f == nil) 1127 return Enofid; 1128 if(waserror()){ 1129 Exputfid(fs, f); 1130 return up->env->errstr; 1131 } 1132 c = f->chan; 1133 if((c->flag & COPEN) == 0) 1134 error(Emode); 1135 if(c->mode != OWRITE && c->mode != ORDWR) 1136 error(Eaccess); 1137 if(c->qid.type & QTDIR) 1138 error(Eisdir); 1139 if(t->count < 0 || t->count > fs->msize-IOHDRSZ) 1140 error(Ecount); 1141 if(t->offset < 0) 1142 error(Enegoff); 1143 r->count = devtab[c->type]->write(c, t->data, t->count, t->offset); 1144 poperror(); 1145 Exputfid(fs, f); 1146 return nil; 1147 } 1148 1149 static char* 1150 Exstat(Export *fs, Fcall *t, Fcall *r) 1151 { 1152 Fid *f; 1153 Chan *c; 1154 int n; 1155 1156 f = Exgetfid(fs, t->fid); 1157 if(f == nil) 1158 return Enofid; 1159 c = exmount(f->chan, nil, 1); 1160 if(waserror()){ 1161 cclose(c); 1162 Exputfid(fs, f); 1163 return up->env->errstr; 1164 } 1165 n = devtab[c->type]->stat(c, r->stat, r->nstat); 1166 if(n <= BIT16SZ) 1167 error(Eshortstat); 1168 r->nstat = n; 1169 poperror(); 1170 cclose(c); 1171 Exputfid(fs, f); 1172 return nil; 1173 } 1174 1175 static char* 1176 Exwstat(Export *fs, Fcall *t, Fcall *r) 1177 { 1178 Fid *f; 1179 Chan *c; 1180 1181 USED(r); 1182 f = Exgetfid(fs, t->fid); 1183 if(f == nil) 1184 return Enofid; 1185 if(waserror()){ 1186 Exputfid(fs, f); 1187 return up->env->errstr; 1188 } 1189 validstat(t->stat, t->nstat); /* check name */ 1190 1191 c = exmount(f->chan, nil, 0); 1192 if(waserror()){ 1193 cclose(c); 1194 nexterror(); 1195 } 1196 devtab[c->type]->wstat(c, t->stat, t->nstat); 1197 poperror(); 1198 1199 cclose(c); 1200 poperror(); 1201 Exputfid(fs, f); 1202 return nil; 1203 } 1204 1205 static char* 1206 Exremove(Export *fs, Fcall *t, Fcall *r) 1207 { 1208 Fid *f; 1209 Chan *c; 1210 1211 USED(r); 1212 f = Exgetfid(fs, t->fid); 1213 if(f == nil) 1214 return Enofid; 1215 if(waserror()){ 1216 f->attached = 0; 1217 Exputfid(fs, f); 1218 return up->env->errstr; 1219 } 1220 c = exmount(f->chan, nil, 0); 1221 if(waserror()){ 1222 c->type = 0; /* see below */ 1223 cclose(c); 1224 nexterror(); 1225 } 1226 devtab[c->type]->remove(c); 1227 poperror(); 1228 poperror(); 1229 1230 /* 1231 * chan is already clunked by remove. 1232 * however, we need to recover the chan, 1233 * and follow sysremove's lead in making it point to root. 1234 */ 1235 c->type = 0; 1236 1237 cclose(c); 1238 f->attached = 0; 1239 Exputfid(fs, f); 1240 return nil; 1241 } 1242 1243 /* 1244 * unique path generation 1245 */ 1246 1247 static int 1248 uqidhash(vlong path) 1249 { 1250 ulong p; 1251 p = (ulong)path; 1252 return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1); 1253 } 1254 1255 static Uqid ** 1256 uqidlook(Uqid **tab, Chan *c, vlong path) 1257 { 1258 Uqid **hp, *q; 1259 1260 for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next) 1261 if(q->type == c->type && q->dev == c->dev && q->oldpath == path) 1262 break; 1263 return hp; 1264 } 1265 1266 static int 1267 uqidexists(Uqid **tab, vlong path) 1268 { 1269 int i; 1270 Uqid *q; 1271 1272 for(i=0; i<Nqidhash; i++) 1273 for(q = tab[i]; q != nil; q = q->next) 1274 if(q->newpath == path) 1275 return 1; 1276 return 0; 1277 } 1278 1279 static Uqid * 1280 uqidalloc(Export *fs, Chan *c) 1281 { 1282 Uqid **hp, *q; 1283 1284 qlock(&fs->qidlock); 1285 hp = uqidlook(fs->qids, c, c->qid.path); 1286 if((q = *hp) != nil){ 1287 incref(q); 1288 qunlock(&fs->qidlock); 1289 return q; 1290 } 1291 q = mallocz(sizeof(*q), 1); 1292 if(q == nil){ 1293 qunlock(&fs->qidlock); 1294 error(Enomem); 1295 } 1296 q->ref = 1; 1297 q->type = c->type; 1298 q->dev = c->dev; 1299 q->oldpath = c->qid.path; 1300 q->newpath = c->qid.path; 1301 while(uqidexists(fs->qids, q->newpath)){ 1302 if(++fs->pathgen >= (1<<16)) 1303 fs->pathgen = 1; 1304 q->newpath = ((vlong)fs->pathgen<<48) | (q->newpath & QIDMASK); 1305 } 1306 q->next = nil; 1307 *hp = q; 1308 qunlock(&fs->qidlock); 1309 return q; 1310 } 1311 1312 static void 1313 freeuqid(Export *fs, Uqid *q) 1314 { 1315 Uqid **hp; 1316 1317 if(q == nil) 1318 return; 1319 qlock(&fs->qidlock); 1320 if(decref(q) == 0){ 1321 hp = &fs->qids[uqidhash(q->oldpath)]; 1322 for(; *hp != nil; hp = &(*hp)->next) 1323 if(*hp == q){ 1324 *hp = q->next; 1325 free(q); 1326 break; 1327 } 1328 } 1329 qunlock(&fs->qidlock); 1330 } 1331