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