1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #define Extern extern 6 #include "exportfs.h" 7 8 extern char *netdir, *local, *remote; 9 10 char Ebadfid[] = "Bad fid"; 11 char Enotdir[] = "Not a directory"; 12 char Edupfid[] = "Fid already in use"; 13 char Eopen[] = "Fid already opened"; 14 char Exmnt[] = "Cannot .. past mount point"; 15 char Emip[] = "Mount in progress"; 16 char Enopsmt[] = "Out of pseudo mount points"; 17 char Enomem[] = "No memory"; 18 char Eversion[] = "Bad 9P2000 version"; 19 char Ereadonly[] = "File system read only"; 20 21 ulong messagesize; 22 int readonly; 23 24 void 25 Xversion(Fsrpc *t) 26 { 27 Fcall rhdr; 28 29 if(t->work.msize > messagesize) 30 t->work.msize = messagesize; 31 messagesize = t->work.msize; 32 if(strncmp(t->work.version, "9P2000", 6) != 0){ 33 reply(&t->work, &rhdr, Eversion); 34 return; 35 } 36 rhdr.version = "9P2000"; 37 rhdr.msize = t->work.msize; 38 reply(&t->work, &rhdr, 0); 39 t->busy = 0; 40 } 41 42 void 43 Xauth(Fsrpc *t) 44 { 45 Fcall rhdr; 46 47 reply(&t->work, &rhdr, "exportfs: authentication not required"); 48 t->busy = 0; 49 } 50 51 void 52 Xflush(Fsrpc *t) 53 { 54 Fsrpc *w, *e; 55 Fcall rhdr; 56 57 e = &Workq[Nr_workbufs]; 58 59 for(w = Workq; w < e; w++) { 60 if(w->work.tag == t->work.oldtag) { 61 DEBUG(DFD, "\tQ busy %d pid %p can %d\n", w->busy, w->pid, w->canint); 62 if(w->busy && w->pid) { 63 w->flushtag = t->work.tag; 64 DEBUG(DFD, "\tset flushtag %d\n", t->work.tag); 65 if(w->canint) 66 postnote(PNPROC, w->pid, "flush"); 67 t->busy = 0; 68 return; 69 } 70 } 71 } 72 73 reply(&t->work, &rhdr, 0); 74 DEBUG(DFD, "\tflush reply\n"); 75 t->busy = 0; 76 } 77 78 void 79 Xattach(Fsrpc *t) 80 { 81 int i, nfd; 82 Fcall rhdr; 83 Fid *f; 84 char buf[128]; 85 86 f = newfid(t->work.fid); 87 if(f == 0) { 88 reply(&t->work, &rhdr, Ebadfid); 89 t->busy = 0; 90 return; 91 } 92 93 if(srvfd >= 0){ 94 if(psmpt == 0){ 95 Nomount: 96 reply(&t->work, &rhdr, Enopsmt); 97 t->busy = 0; 98 freefid(t->work.fid); 99 return; 100 } 101 for(i=0; i<Npsmpt; i++) 102 if(psmap[i] == 0) 103 break; 104 if(i >= Npsmpt) 105 goto Nomount; 106 sprint(buf, "%d", i); 107 f->f = file(psmpt, buf); 108 if(f->f == nil) 109 goto Nomount; 110 sprint(buf, "/mnt/exportfs/%d", i); 111 nfd = dup(srvfd, -1); 112 if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) < 0){ 113 errstr(buf, sizeof buf); 114 reply(&t->work, &rhdr, buf); 115 t->busy = 0; 116 freefid(t->work.fid); 117 close(nfd); 118 return; 119 } 120 psmap[i] = 1; 121 f->mid = i; 122 }else{ 123 f->f = root; 124 f->f->ref++; 125 } 126 127 rhdr.qid = f->f->qid; 128 reply(&t->work, &rhdr, 0); 129 t->busy = 0; 130 } 131 132 Fid* 133 clonefid(Fid *f, int new) 134 { 135 Fid *n; 136 137 n = newfid(new); 138 if(n == 0) { 139 n = getfid(new); 140 if(n == 0) 141 fatal("inconsistent fids"); 142 if(n->fid >= 0) 143 close(n->fid); 144 freefid(new); 145 n = newfid(new); 146 if(n == 0) 147 fatal("inconsistent fids2"); 148 } 149 n->f = f->f; 150 n->f->ref++; 151 return n; 152 } 153 154 void 155 Xwalk(Fsrpc *t) 156 { 157 char err[ERRMAX], *e; 158 Fcall rhdr; 159 Fid *f, *nf; 160 File *wf; 161 int i; 162 163 f = getfid(t->work.fid); 164 if(f == 0) { 165 reply(&t->work, &rhdr, Ebadfid); 166 t->busy = 0; 167 return; 168 } 169 170 nf = nil; 171 if(t->work.newfid != t->work.fid){ 172 nf = clonefid(f, t->work.newfid); 173 f = nf; 174 } 175 176 rhdr.nwqid = 0; 177 e = nil; 178 for(i=0; i<t->work.nwname; i++){ 179 if(i == MAXWELEM){ 180 e = "Too many path elements"; 181 break; 182 } 183 184 if(strcmp(t->work.wname[i], "..") == 0) { 185 if(f->f->parent == nil) { 186 e = Exmnt; 187 break; 188 } 189 wf = f->f->parent; 190 wf->ref++; 191 goto Accept; 192 } 193 194 wf = file(f->f, t->work.wname[i]); 195 if(wf == 0){ 196 errstr(err, sizeof err); 197 e = err; 198 break; 199 } 200 Accept: 201 freefile(f->f); 202 rhdr.wqid[rhdr.nwqid++] = wf->qid; 203 f->f = wf; 204 continue; 205 } 206 207 if(nf!=nil && (e!=nil || rhdr.nwqid!=t->work.nwname)) 208 freefid(t->work.newfid); 209 if(rhdr.nwqid > 0) 210 e = nil; 211 reply(&t->work, &rhdr, e); 212 t->busy = 0; 213 } 214 215 void 216 Xclunk(Fsrpc *t) 217 { 218 Fcall rhdr; 219 Fid *f; 220 221 f = getfid(t->work.fid); 222 if(f == 0) { 223 reply(&t->work, &rhdr, Ebadfid); 224 t->busy = 0; 225 return; 226 } 227 228 if(f->fid >= 0) 229 close(f->fid); 230 231 freefid(t->work.fid); 232 reply(&t->work, &rhdr, 0); 233 t->busy = 0; 234 } 235 236 void 237 Xstat(Fsrpc *t) 238 { 239 char err[ERRMAX], *path; 240 Fcall rhdr; 241 Fid *f; 242 Dir *d; 243 int s; 244 uchar *statbuf; 245 246 f = getfid(t->work.fid); 247 if(f == 0) { 248 reply(&t->work, &rhdr, Ebadfid); 249 t->busy = 0; 250 return; 251 } 252 if(f->fid >= 0) 253 d = dirfstat(f->fid); 254 else { 255 path = makepath(f->f, ""); 256 d = dirstat(path); 257 free(path); 258 } 259 260 if(d == nil) { 261 errstr(err, sizeof err); 262 reply(&t->work, &rhdr, err); 263 t->busy = 0; 264 return; 265 } 266 267 d->qid.path = f->f->qidt->uniqpath; 268 s = sizeD2M(d); 269 statbuf = emallocz(s); 270 s = convD2M(d, statbuf, s); 271 free(d); 272 rhdr.nstat = s; 273 rhdr.stat = statbuf; 274 reply(&t->work, &rhdr, 0); 275 free(statbuf); 276 t->busy = 0; 277 } 278 279 static int 280 getiounit(int fd) 281 { 282 int n; 283 284 n = iounit(fd); 285 if(n > messagesize-IOHDRSZ) 286 n = messagesize-IOHDRSZ; 287 return n; 288 } 289 290 void 291 Xcreate(Fsrpc *t) 292 { 293 char err[ERRMAX], *path; 294 Fcall rhdr; 295 Fid *f; 296 File *nf; 297 298 if(readonly) { 299 reply(&t->work, &rhdr, Ereadonly); 300 t->busy = 0; 301 return; 302 } 303 f = getfid(t->work.fid); 304 if(f == 0) { 305 reply(&t->work, &rhdr, Ebadfid); 306 t->busy = 0; 307 return; 308 } 309 310 311 path = makepath(f->f, t->work.name); 312 f->fid = create(path, t->work.mode, t->work.perm); 313 free(path); 314 if(f->fid < 0) { 315 errstr(err, sizeof err); 316 reply(&t->work, &rhdr, err); 317 t->busy = 0; 318 return; 319 } 320 321 nf = file(f->f, t->work.name); 322 if(nf == 0) { 323 errstr(err, sizeof err); 324 reply(&t->work, &rhdr, err); 325 t->busy = 0; 326 return; 327 } 328 329 f->mode = t->work.mode; 330 freefile(f->f); 331 f->f = nf; 332 rhdr.qid = f->f->qid; 333 rhdr.iounit = getiounit(f->fid); 334 reply(&t->work, &rhdr, 0); 335 t->busy = 0; 336 } 337 338 void 339 Xremove(Fsrpc *t) 340 { 341 char err[ERRMAX], *path; 342 Fcall rhdr; 343 Fid *f; 344 345 if(readonly) { 346 reply(&t->work, &rhdr, Ereadonly); 347 t->busy = 0; 348 return; 349 } 350 f = getfid(t->work.fid); 351 if(f == 0) { 352 reply(&t->work, &rhdr, Ebadfid); 353 t->busy = 0; 354 return; 355 } 356 357 path = makepath(f->f, ""); 358 DEBUG(DFD, "\tremove: %s\n", path); 359 if(remove(path) < 0) { 360 free(path); 361 errstr(err, sizeof err); 362 reply(&t->work, &rhdr, err); 363 t->busy = 0; 364 return; 365 } 366 free(path); 367 368 f->f->inval = 1; 369 if(f->fid >= 0) 370 close(f->fid); 371 freefid(t->work.fid); 372 373 reply(&t->work, &rhdr, 0); 374 t->busy = 0; 375 } 376 377 void 378 Xwstat(Fsrpc *t) 379 { 380 char err[ERRMAX], *path; 381 Fcall rhdr; 382 Fid *f; 383 int s; 384 char *strings; 385 Dir d; 386 387 if(readonly) { 388 reply(&t->work, &rhdr, Ereadonly); 389 t->busy = 0; 390 return; 391 } 392 f = getfid(t->work.fid); 393 if(f == 0) { 394 reply(&t->work, &rhdr, Ebadfid); 395 t->busy = 0; 396 return; 397 } 398 strings = emallocz(t->work.nstat); /* ample */ 399 if(convM2D(t->work.stat, t->work.nstat, &d, strings) <= BIT16SZ){ 400 rerrstr(err, sizeof err); 401 reply(&t->work, &rhdr, err); 402 t->busy = 0; 403 free(strings); 404 return; 405 } 406 407 if(f->fid >= 0) 408 s = dirfwstat(f->fid, &d); 409 else { 410 path = makepath(f->f, ""); 411 s = dirwstat(path, &d); 412 free(path); 413 } 414 if(s < 0) { 415 rerrstr(err, sizeof err); 416 reply(&t->work, &rhdr, err); 417 } 418 else { 419 /* wstat may really be rename */ 420 if(strcmp(d.name, f->f->name)!=0 && strcmp(d.name, "")!=0){ 421 free(f->f->name); 422 f->f->name = estrdup(d.name); 423 } 424 reply(&t->work, &rhdr, 0); 425 } 426 free(strings); 427 t->busy = 0; 428 } 429 430 /* 431 * based on libthread's threadsetname, but drags in less library code. 432 * actually just sets the arguments displayed. 433 */ 434 void 435 procsetname(char *fmt, ...) 436 { 437 int fd; 438 char *cmdname; 439 char buf[128]; 440 va_list arg; 441 442 va_start(arg, fmt); 443 cmdname = vsmprint(fmt, arg); 444 va_end(arg); 445 if (cmdname == nil) 446 return; 447 snprint(buf, sizeof buf, "#p/%d/args", getpid()); 448 if((fd = open(buf, OWRITE)) >= 0){ 449 write(fd, cmdname, strlen(cmdname)+1); 450 close(fd); 451 } 452 free(cmdname); 453 } 454 455 void 456 slave(Fsrpc *f) 457 { 458 Proc *p; 459 uintptr pid; 460 Fcall rhdr; 461 static int nproc; 462 463 if(readonly){ 464 switch(f->work.type){ 465 case Twrite: 466 reply(&f->work, &rhdr, Ereadonly); 467 f->busy = 0; 468 return; 469 case Topen: 470 if((f->work.mode&3) == OWRITE || (f->work.mode&OTRUNC)){ 471 reply(&f->work, &rhdr, Ereadonly); 472 f->busy = 0; 473 return; 474 } 475 } 476 } 477 for(;;) { 478 for(p = Proclist; p; p = p->next) { 479 if(p->busy == 0) { 480 f->pid = p->pid; 481 p->busy = 1; 482 pid = (uintptr)rendezvous((void*)p->pid, f); 483 if(pid != p->pid) 484 fatal("rendezvous sync fail"); 485 return; 486 } 487 } 488 489 if(++nproc > MAXPROC) 490 fatal("too many procs"); 491 492 pid = rfork(RFPROC|RFMEM); 493 switch(pid) { 494 case -1: 495 fatal("rfork"); 496 497 case 0: 498 if (local[0] != '\0') 499 if (netdir[0] != '\0') 500 procsetname("%s: %s -> %s", netdir, 501 local, remote); 502 else 503 procsetname("%s -> %s", local, remote); 504 blockingslave(); 505 fatal("slave"); 506 507 default: 508 p = malloc(sizeof(Proc)); 509 if(p == 0) 510 fatal("out of memory"); 511 512 p->busy = 0; 513 p->pid = pid; 514 p->next = Proclist; 515 Proclist = p; 516 517 rendezvous((void*)pid, p); 518 } 519 } 520 } 521 522 void 523 blockingslave(void) 524 { 525 Fsrpc *p; 526 Fcall rhdr; 527 Proc *m; 528 uintptr pid; 529 530 notify(flushaction); 531 532 pid = getpid(); 533 534 m = rendezvous((void*)pid, 0); 535 536 for(;;) { 537 p = rendezvous((void*)pid, (void*)pid); 538 if(p == (void*)~0) /* Interrupted */ 539 continue; 540 541 DEBUG(DFD, "\tslave: %p %F b %d p %p\n", pid, &p->work, p->busy, p->pid); 542 if(p->flushtag != NOTAG) 543 goto flushme; 544 545 switch(p->work.type) { 546 case Tread: 547 slaveread(p); 548 break; 549 550 case Twrite: 551 slavewrite(p); 552 break; 553 554 case Topen: 555 slaveopen(p); 556 break; 557 558 default: 559 reply(&p->work, &rhdr, "exportfs: slave type error"); 560 } 561 if(p->flushtag != NOTAG) { 562 flushme: 563 p->work.type = Tflush; 564 p->work.tag = p->flushtag; 565 reply(&p->work, &rhdr, 0); 566 } 567 p->busy = 0; 568 m->busy = 0; 569 } 570 } 571 572 int 573 openmount(int sfd) 574 { 575 int p[2]; 576 char *arg[10], fdbuf[20], mbuf[20]; 577 578 if(pipe(p) < 0) 579 return -1; 580 581 switch(rfork(RFPROC|RFMEM|RFNOWAIT|RFNAMEG|RFFDG)){ 582 case -1: 583 return -1; 584 585 default: 586 close(sfd); 587 close(p[0]); 588 return p[1]; 589 590 case 0: 591 break; 592 } 593 594 close(p[1]); 595 596 arg[0] = "exportfs"; 597 snprint(fdbuf, sizeof fdbuf, "-S/fd/%d", sfd); 598 arg[1] = fdbuf; 599 snprint(mbuf, sizeof mbuf, "-m%lud", messagesize-IOHDRSZ); 600 arg[2] = mbuf; 601 arg[3] = nil; 602 603 close(0); 604 close(1); 605 dup(p[0], 0); 606 dup(p[0], 1); 607 exec("/bin/exportfs", arg); 608 _exits("whoops: exec failed"); 609 return -1; 610 } 611 612 void 613 slaveopen(Fsrpc *p) 614 { 615 char err[ERRMAX], *path; 616 Fcall *work, rhdr; 617 Fid *f; 618 Dir *d; 619 620 work = &p->work; 621 622 f = getfid(work->fid); 623 if(f == 0) { 624 reply(work, &rhdr, Ebadfid); 625 return; 626 } 627 if(f->fid >= 0) { 628 close(f->fid); 629 f->fid = -1; 630 } 631 632 path = makepath(f->f, ""); 633 DEBUG(DFD, "\topen: %s %d\n", path, work->mode); 634 635 p->canint = 1; 636 if(p->flushtag != NOTAG){ 637 free(path); 638 return; 639 } 640 /* There is a race here I ignore because there are no locks */ 641 f->fid = open(path, work->mode); 642 free(path); 643 p->canint = 0; 644 if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) { 645 Error: 646 errstr(err, sizeof err); 647 reply(work, &rhdr, err); 648 return; 649 } 650 f->f->qid = d->qid; 651 free(d); 652 if(f->f->qid.type & QTMOUNT){ /* fork new exportfs for this */ 653 f->fid = openmount(f->fid); 654 if(f->fid < 0) 655 goto Error; 656 } 657 658 DEBUG(DFD, "\topen: fd %d\n", f->fid); 659 f->mode = work->mode; 660 f->offset = 0; 661 rhdr.iounit = getiounit(f->fid); 662 rhdr.qid = f->f->qid; 663 reply(work, &rhdr, 0); 664 } 665 666 void 667 slaveread(Fsrpc *p) 668 { 669 Fid *f; 670 int n, r; 671 Fcall *work, rhdr; 672 char *data, err[ERRMAX]; 673 674 work = &p->work; 675 676 f = getfid(work->fid); 677 if(f == 0) { 678 reply(work, &rhdr, Ebadfid); 679 return; 680 } 681 682 n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count; 683 p->canint = 1; 684 if(p->flushtag != NOTAG) 685 return; 686 data = malloc(n); 687 if(data == nil) 688 fatal(Enomem); 689 690 /* can't just call pread, since directories must update the offset */ 691 if(patternfile != nil && (f->f->qid.type&QTDIR)) 692 r = preaddir(f, (uchar*)data, n, work->offset); 693 else 694 r = pread(f->fid, data, n, work->offset); 695 p->canint = 0; 696 if(r < 0) { 697 free(data); 698 errstr(err, sizeof err); 699 reply(work, &rhdr, err); 700 return; 701 } 702 703 DEBUG(DFD, "\tread: fd=%d %d bytes\n", f->fid, r); 704 705 rhdr.data = data; 706 rhdr.count = r; 707 reply(work, &rhdr, 0); 708 free(data); 709 } 710 711 void 712 slavewrite(Fsrpc *p) 713 { 714 char err[ERRMAX]; 715 Fcall *work, rhdr; 716 Fid *f; 717 int n; 718 719 work = &p->work; 720 721 f = getfid(work->fid); 722 if(f == 0) { 723 reply(work, &rhdr, Ebadfid); 724 return; 725 } 726 727 n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count; 728 p->canint = 1; 729 if(p->flushtag != NOTAG) 730 return; 731 n = pwrite(f->fid, work->data, n, work->offset); 732 p->canint = 0; 733 if(n < 0) { 734 errstr(err, sizeof err); 735 reply(work, &rhdr, err); 736 return; 737 } 738 739 DEBUG(DFD, "\twrite: %d bytes fd=%d\n", n, f->fid); 740 741 rhdr.count = n; 742 reply(work, &rhdr, 0); 743 } 744 745 void 746 reopen(Fid *f) 747 { 748 USED(f); 749 fatal("reopen"); 750 } 751 752 void 753 flushaction(void *a, char *cause) 754 { 755 USED(a); 756 if(strncmp(cause, "sys:", 4) == 0 && !strstr(cause, "pipe")) { 757 fprint(2, "exportsrv: note: %s\n", cause); 758 exits("noted"); 759 } 760 if(strncmp(cause, "kill", 4) == 0) 761 noted(NDFLT); 762 763 noted(NCONT); 764 } 765