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