1 /* 2 * exportfs - Export a plan 9 name space across a network 3 */ 4 #include <u.h> 5 #include <libc.h> 6 #include <auth.h> 7 #include <fcall.h> 8 #include <libsec.h> 9 #define Extern 10 #include "exportfs.h" 11 12 #define QIDPATH ((1LL<<48)-1) 13 vlong newqid = 0; 14 15 enum { 16 Encnone, 17 Encssl, 18 Enctls, 19 }; 20 21 void (*fcalls[])(Fsrpc*) = 22 { 23 [Tversion] Xversion, 24 [Tauth] Xauth, 25 [Tflush] Xflush, 26 [Tattach] Xattach, 27 [Twalk] Xwalk, 28 [Topen] slave, 29 [Tcreate] Xcreate, 30 [Tclunk] Xclunk, 31 [Tread] slave, 32 [Twrite] slave, 33 [Tremove] Xremove, 34 [Tstat] Xstat, 35 [Twstat] Xwstat, 36 }; 37 38 /* accounting and debugging counters */ 39 int filecnt; 40 int freecnt; 41 int qidcnt; 42 int qfreecnt; 43 int ncollision; 44 45 int netfd; 46 int srvfd = -1; 47 int nonone = 1; 48 char *filterp; 49 char *ealgs = "rc4_256 sha1"; 50 char *aanfilter = "/bin/aan"; 51 int encproto = Encnone; 52 53 static void mksecret(char *, uchar *); 54 static int localread9pmsg(int, void *, uint, ulong *); 55 static char *anstring = "tcp!*!0"; 56 int filter(int, char *); 57 58 void 59 usage(void) 60 { 61 fprint(2, "usage: %s [-ads] [-f dbgfile] [-m msize] [-r root] [-S srvfile] [-e 'crypt hash'] [-A announce-string]\n", argv0); 62 fatal("usage"); 63 } 64 65 void 66 main(int argc, char **argv) 67 { 68 char buf[ERRMAX], ebuf[ERRMAX]; 69 Fsrpc *r; 70 int n; 71 char *dbfile, *srv, *file; 72 AuthInfo *ai; 73 ulong initial; 74 75 dbfile = "/tmp/exportdb"; 76 srv = nil; 77 srvfd = -1; 78 79 ai = nil; 80 ARGBEGIN{ 81 case 'a': 82 /* 83 * We use p9any so we don't have to visit this code again, with the 84 * cost that this code is incompatible with the old world, which 85 * requires p9sk2. (The two differ in who talks first, so compatibility 86 * is awkward.) 87 */ 88 ai = auth_proxy(0, auth_getkey, "proto=p9any role=server"); 89 if(ai == nil) 90 fatal("auth_proxy: %r"); 91 if(nonone && strcmp(ai->cuid, "none") == 0) 92 fatal("exportfs by none disallowed"); 93 if(auth_chuid(ai, nil) < 0) 94 fatal("auth_chuid: %r"); 95 if(newns(ai->cuid, 0) < 0) 96 fatal("newns"); 97 putenv("service", "exportfs"); 98 break; 99 100 case 'e': 101 ealgs = ARGF(); 102 if(ealgs == nil) 103 usage(); 104 if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) 105 ealgs = nil; 106 break; 107 108 case 'S': 109 if(srvfd != -1) 110 usage(); 111 file = EARGF(usage()); 112 if((srvfd = open(file, ORDWR)) < 0) 113 sysfatal("open '%s': %r", file); 114 break; 115 116 case 'd': 117 dbg++; 118 break; 119 120 case 'f': 121 dbfile = EARGF(usage()); 122 break; 123 124 case 'F': 125 /* accepted but ignored, for backwards compatibility */ 126 break; 127 128 case 'm': 129 messagesize = strtoul(EARGF(usage()), nil, 0); 130 break; 131 132 case 'r': 133 srv = EARGF(usage()); 134 break; 135 136 case 's': 137 srv = "/"; 138 break; 139 140 case 'p': 141 patternfile = EARGF(usage()); 142 break; 143 144 case 'A': 145 anstring = EARGF(usage()); 146 break; 147 148 default: 149 usage(); 150 }ARGEND 151 USED(argc, argv); 152 153 exclusions(); 154 155 if(dbg) { 156 n = create(dbfile, OWRITE|OTRUNC, 0666); 157 dup(n, DFD); 158 close(n); 159 } 160 161 if(srvfd >= 0 && srv){ 162 fprint(2, "exportfs: -S cannot be used with -r or -s\n"); 163 usage(); 164 } 165 166 DEBUG(DFD, "exportfs: started\n"); 167 168 rfork(RFNOTEG); 169 170 if(messagesize == 0){ 171 messagesize = iounit(netfd); 172 if(messagesize == 0) 173 messagesize = 8192+IOHDRSZ; 174 } 175 176 Workq = emallocz(sizeof(Fsrpc)*Nr_workbufs); 177 // for(i=0; i<Nr_workbufs; i++) 178 // Workq[i].buf = emallocz(messagesize); 179 fhash = emallocz(sizeof(Fid*)*FHASHSIZE); 180 181 fmtinstall('F', fcallfmt); 182 183 /* 184 * Get tree to serve from network connection, 185 * check we can get there and ack the connection 186 */ 187 if(srvfd != -1) { 188 /* do nothing */ 189 } 190 else if(srv) { 191 chdir(srv); 192 DEBUG(DFD, "invoked as server for %s", srv); 193 strncpy(buf, srv, sizeof buf); 194 } 195 else { 196 buf[0] = 0; 197 n = read(0, buf, sizeof(buf)-1); 198 if(n < 0) { 199 errstr(buf, sizeof buf); 200 fprint(0, "read(0): %s", buf); 201 DEBUG(DFD, "read(0): %s", buf); 202 exits(buf); 203 } 204 buf[n] = 0; 205 if(chdir(buf) < 0) { 206 errstr(ebuf, sizeof ebuf); 207 fprint(0, "chdir(%d:\"%s\"): %s", n, buf, ebuf); 208 DEBUG(DFD, "chdir(%d:\"%s\"): %s", n, buf, ebuf); 209 exits(ebuf); 210 } 211 } 212 213 DEBUG(DFD, "initing root\n"); 214 initroot(); 215 216 DEBUG(DFD, "exportfs: %s\n", buf); 217 218 if(srv == nil && srvfd == -1 && write(0, "OK", 2) != 2) 219 fatal("open ack write"); 220 221 if (readn(netfd, &initial, sizeof(ulong)) < sizeof(ulong)) 222 fatal("can't read initial string: %r\n"); 223 224 if (!strncmp((char *)&initial, "impo", sizeof(ulong))) { 225 char buf[128], *p, *args[3]; 226 227 // New import. Read import's parameters... 228 initial = 0; 229 230 p = buf; 231 while (p - buf < sizeof buf) { 232 if ((n = read(netfd, p, 1)) < 0) 233 fatal("can't read impo arguments: %r\n"); 234 235 if (n == 0) 236 fatal("connection closed while reading arguments\n"); 237 238 if (*p == '\n') 239 *p = '\0'; 240 if (*p++ == '\0') 241 break; 242 } 243 244 if (tokenize(buf, args, nelem(args)) != 2) 245 fatal("impo arguments invalid: impo%s...\n", buf); 246 247 if (!strcmp(args[0], "aan")) 248 filterp = aanfilter; 249 else if (strcmp(args[0], "nofilter")) 250 fatal("import filter argument unsupported: %s\n", args[0]); 251 252 if (!strcmp(args[1], "ssl")) 253 encproto = Encssl; 254 else if (!strcmp(args[1], "tls")) 255 encproto = Enctls; 256 else if (strcmp(args[1], "clear")) 257 fatal("import encryption proto unsupported: %s\n", args[1]); 258 259 if (encproto == Enctls) 260 sysfatal("%s: tls has not yet been implemented\n", argv[0]); 261 } 262 263 if (encproto != Encnone && ealgs && ai) { 264 uchar key[16]; 265 uchar digest[SHA1dlen]; 266 char fromclientsecret[21]; 267 char fromserversecret[21]; 268 int i; 269 270 memmove(key+4, ai->secret, ai->nsecret); 271 272 /* exchange random numbers */ 273 srand(truerand()); 274 for(i = 0; i < 4; i++) 275 key[i+12] = rand(); 276 277 if (initial) 278 fatal("Protocol botch: old import\n"); 279 if(readn(netfd, key, 4) != 4) 280 fatal("can't read key part; %r\n"); 281 282 if(write(netfd, key+12, 4) != 4) 283 fatal("can't write key part; %r\n"); 284 285 /* scramble into two secrets */ 286 sha1(key, sizeof(key), digest, nil); 287 mksecret(fromclientsecret, digest); 288 mksecret(fromserversecret, digest+10); 289 290 if (filterp) 291 netfd = filter(netfd, filterp); 292 293 switch (encproto) { 294 case Encssl: 295 netfd = pushssl(netfd, ealgs, fromserversecret, 296 fromclientsecret, nil); 297 break; 298 case Enctls: 299 default: 300 fatal("Unsupported encryption protocol\n"); 301 } 302 303 if(netfd < 0) 304 fatal("can't establish ssl connection: %r"); 305 } 306 else if (filterp) { 307 if (initial) 308 fatal("Protocol botch: don't know how to deal with this\n"); 309 netfd = filter(netfd, filterp); 310 } 311 312 /* 313 * Start serving file requests from the network 314 */ 315 for(;;) { 316 r = getsbuf(); 317 if(r == 0) 318 fatal("Out of service buffers"); 319 320 n = localread9pmsg(netfd, r->buf, messagesize, &initial); 321 if(n <= 0) 322 fatal(nil); 323 324 if(convM2S(r->buf, n, &r->work) == 0) 325 fatal("convM2S format error"); 326 327 DEBUG(DFD, "%F\n", &r->work); 328 (fcalls[r->work.type])(r); 329 } 330 } 331 332 // WARNING: Replace this with the original version as soon as all 333 // _old_ imports have been replaced with negotiating imports. Also 334 // cpu relies on this (which needs to be fixed!) -- pb. 335 static int 336 localread9pmsg(int fd, void *abuf, uint n, ulong *initial) 337 { 338 int m, len; 339 uchar *buf; 340 341 buf = abuf; 342 343 /* read count */ 344 assert(BIT32SZ == sizeof(ulong)); 345 if (*initial) { 346 memcpy(buf, initial, BIT32SZ); 347 *initial = 0; 348 } 349 else { 350 m = readn(fd, buf, BIT32SZ); 351 if(m != BIT32SZ){ 352 if(m < 0) 353 return -1; 354 return 0; 355 } 356 } 357 358 len = GBIT32(buf); 359 if(len <= BIT32SZ || len > n){ 360 werrstr("bad length in 9P2000 message header"); 361 return -1; 362 } 363 len -= BIT32SZ; 364 m = readn(fd, buf+BIT32SZ, len); 365 if(m < len) 366 return 0; 367 return BIT32SZ+m; 368 } 369 void 370 reply(Fcall *r, Fcall *t, char *err) 371 { 372 uchar *data; 373 int n; 374 375 t->tag = r->tag; 376 t->fid = r->fid; 377 if(err) { 378 t->type = Rerror; 379 t->ename = err; 380 } 381 else 382 t->type = r->type + 1; 383 384 DEBUG(DFD, "\t%F\n", t); 385 386 data = malloc(messagesize); /* not mallocz; no need to clear */ 387 if(data == nil) 388 fatal(Enomem); 389 n = convS2M(t, data, messagesize); 390 if(write(netfd, data, n)!=n) 391 {syslog(0, "exportfs", "short write: %r"); 392 fatal("mount write"); 393 } 394 free(data); 395 } 396 397 Fid * 398 getfid(int nr) 399 { 400 Fid *f; 401 402 for(f = fidhash(nr); f; f = f->next) 403 if(f->nr == nr) 404 return f; 405 406 return 0; 407 } 408 409 int 410 freefid(int nr) 411 { 412 Fid *f, **l; 413 char buf[128]; 414 415 l = &fidhash(nr); 416 for(f = *l; f; f = f->next) { 417 if(f->nr == nr) { 418 if(f->mid) { 419 sprint(buf, "/mnt/exportfs/%d", f->mid); 420 unmount(0, buf); 421 psmap[f->mid] = 0; 422 } 423 if(f->f) { 424 freefile(f->f); 425 f->f = nil; 426 } 427 if(f->dir){ 428 free(f->dir); 429 f->dir = nil; 430 } 431 *l = f->next; 432 f->next = fidfree; 433 fidfree = f; 434 return 1; 435 } 436 l = &f->next; 437 } 438 439 return 0; 440 } 441 442 Fid * 443 newfid(int nr) 444 { 445 Fid *new, **l; 446 int i; 447 448 l = &fidhash(nr); 449 for(new = *l; new; new = new->next) 450 if(new->nr == nr) 451 return 0; 452 453 if(fidfree == 0) { 454 fidfree = emallocz(sizeof(Fid) * Fidchunk); 455 456 for(i = 0; i < Fidchunk-1; i++) 457 fidfree[i].next = &fidfree[i+1]; 458 459 fidfree[Fidchunk-1].next = 0; 460 } 461 462 new = fidfree; 463 fidfree = new->next; 464 465 memset(new, 0, sizeof(Fid)); 466 new->next = *l; 467 *l = new; 468 new->nr = nr; 469 new->fid = -1; 470 new->mid = 0; 471 472 return new; 473 } 474 475 Fsrpc * 476 getsbuf(void) 477 { 478 static int ap; 479 int look, rounds; 480 Fsrpc *wb; 481 int small_instead_of_fast = 1; 482 483 if(small_instead_of_fast) 484 ap = 0; /* so we always start looking at the beginning and reuse buffers */ 485 486 for(rounds = 0; rounds < 10; rounds++) { 487 for(look = 0; look < Nr_workbufs; look++) { 488 if(++ap == Nr_workbufs) 489 ap = 0; 490 if(Workq[ap].busy == 0) 491 break; 492 } 493 494 if(look == Nr_workbufs){ 495 sleep(10 * rounds); 496 continue; 497 } 498 499 wb = &Workq[ap]; 500 wb->pid = 0; 501 wb->canint = 0; 502 wb->flushtag = NOTAG; 503 wb->busy = 1; 504 if(wb->buf == nil) /* allocate buffers dynamically to keep size down */ 505 wb->buf = emallocz(messagesize); 506 return wb; 507 } 508 fatal("No more work buffers"); 509 return nil; 510 } 511 512 void 513 freefile(File *f) 514 { 515 File *parent, *child; 516 517 Loop: 518 f->ref--; 519 if(f->ref > 0) 520 return; 521 freecnt++; 522 if(f->ref < 0) abort(); 523 DEBUG(DFD, "free %s\n", f->name); 524 /* delete from parent */ 525 parent = f->parent; 526 if(parent->child == f) 527 parent->child = f->childlist; 528 else{ 529 for(child=parent->child; child->childlist!=f; child=child->childlist) 530 if(child->childlist == nil) 531 fatal("bad child list"); 532 child->childlist = f->childlist; 533 } 534 freeqid(f->qidt); 535 free(f->name); 536 f->name = nil; 537 free(f); 538 f = parent; 539 if(f != nil) 540 goto Loop; 541 } 542 543 File * 544 file(File *parent, char *name) 545 { 546 Dir *dir; 547 char *path; 548 File *f; 549 550 DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name); 551 552 path = makepath(parent, name); 553 if(patternfile != nil && excludefile(path)){ 554 free(path); 555 return nil; 556 } 557 dir = dirstat(path); 558 free(path); 559 if(dir == nil) 560 return nil; 561 562 for(f = parent->child; f; f = f->childlist) 563 if(strcmp(name, f->name) == 0) 564 break; 565 566 if(f == nil){ 567 f = emallocz(sizeof(File)); 568 f->name = estrdup(name); 569 570 f->parent = parent; 571 f->childlist = parent->child; 572 parent->child = f; 573 parent->ref++; 574 f->ref = 0; 575 filecnt++; 576 } 577 f->ref++; 578 f->qid.type = dir->qid.type; 579 f->qid.vers = dir->qid.vers; 580 f->qidt = uniqueqid(dir); 581 f->qid.path = f->qidt->uniqpath; 582 583 f->inval = 0; 584 585 free(dir); 586 587 return f; 588 } 589 590 void 591 initroot(void) 592 { 593 Dir *dir; 594 595 root = emallocz(sizeof(File)); 596 root->name = estrdup("."); 597 598 dir = dirstat(root->name); 599 if(dir == nil) 600 fatal("root stat"); 601 602 root->ref = 1; 603 root->qid.vers = dir->qid.vers; 604 root->qidt = uniqueqid(dir); 605 root->qid.path = root->qidt->uniqpath; 606 root->qid.type = QTDIR; 607 free(dir); 608 609 psmpt = emallocz(sizeof(File)); 610 psmpt->name = estrdup("/"); 611 612 dir = dirstat(psmpt->name); 613 if(dir == nil) 614 return; 615 616 psmpt->ref = 1; 617 psmpt->qid.vers = dir->qid.vers; 618 psmpt->qidt = uniqueqid(dir); 619 psmpt->qid.path = psmpt->qidt->uniqpath; 620 free(dir); 621 622 psmpt = file(psmpt, "mnt"); 623 if(psmpt == 0) 624 return; 625 psmpt = file(psmpt, "exportfs"); 626 } 627 628 char* 629 makepath(File *p, char *name) 630 { 631 int i, n; 632 char *c, *s, *path, *seg[256]; 633 634 seg[0] = name; 635 n = strlen(name)+2; 636 for(i = 1; i < 256 && p; i++, p = p->parent){ 637 seg[i] = p->name; 638 n += strlen(p->name)+1; 639 } 640 path = malloc(n); 641 if(path == nil) 642 fatal("out of memory"); 643 s = path; 644 645 while(i--) { 646 for(c = seg[i]; *c; c++) 647 *s++ = *c; 648 *s++ = '/'; 649 } 650 while(s[-1] == '/') 651 s--; 652 *s = '\0'; 653 654 return path; 655 } 656 657 int 658 qidhash(vlong path) 659 { 660 int h, n; 661 662 h = 0; 663 for(n=0; n<64; n+=Nqidbits){ 664 h ^= path; 665 path >>= Nqidbits; 666 } 667 return h & (Nqidtab-1); 668 } 669 670 void 671 freeqid(Qidtab *q) 672 { 673 ulong h; 674 Qidtab *l; 675 676 q->ref--; 677 if(q->ref > 0) 678 return; 679 qfreecnt++; 680 h = qidhash(q->path); 681 if(qidtab[h] == q) 682 qidtab[h] = q->next; 683 else{ 684 for(l=qidtab[h]; l->next!=q; l=l->next) 685 if(l->next == nil) 686 fatal("bad qid list"); 687 l->next = q->next; 688 } 689 free(q); 690 } 691 692 Qidtab* 693 qidlookup(Dir *d) 694 { 695 ulong h; 696 Qidtab *q; 697 698 h = qidhash(d->qid.path); 699 for(q=qidtab[h]; q!=nil; q=q->next) 700 if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path) 701 return q; 702 return nil; 703 } 704 705 int 706 qidexists(vlong path) 707 { 708 int h; 709 Qidtab *q; 710 711 for(h=0; h<Nqidtab; h++) 712 for(q=qidtab[h]; q!=nil; q=q->next) 713 if(q->uniqpath == path) 714 return 1; 715 return 0; 716 } 717 718 Qidtab* 719 uniqueqid(Dir *d) 720 { 721 ulong h; 722 vlong path; 723 Qidtab *q; 724 725 q = qidlookup(d); 726 if(q != nil){ 727 q->ref++; 728 return q; 729 } 730 path = d->qid.path; 731 while(qidexists(path)){ 732 DEBUG(DFD, "collision on %s\n", d->name); 733 /* collision: find a new one */ 734 ncollision++; 735 path &= QIDPATH; 736 ++newqid; 737 if(newqid >= (1<<16)){ 738 DEBUG(DFD, "collision wraparound\n"); 739 newqid = 1; 740 } 741 path |= newqid<<48; 742 DEBUG(DFD, "assign qid %.16llux\n", path); 743 } 744 q = mallocz(sizeof(Qidtab), 1); 745 if(q == nil) 746 fatal("no memory for qid table"); 747 qidcnt++; 748 q->ref = 1; 749 q->type = d->type; 750 q->dev = d->dev; 751 q->path = d->qid.path; 752 q->uniqpath = path; 753 h = qidhash(d->qid.path); 754 q->next = qidtab[h]; 755 qidtab[h] = q; 756 return q; 757 } 758 759 void 760 fatal(char *s, ...) 761 { 762 char buf[ERRMAX]; 763 va_list arg; 764 Proc *m; 765 766 if (s) { 767 va_start(arg, s); 768 vsnprint(buf, ERRMAX, s, arg); 769 va_end(arg); 770 } 771 772 /* Clear away the slave children */ 773 for(m = Proclist; m; m = m->next) 774 postnote(PNPROC, m->pid, "kill"); 775 776 DEBUG(DFD, "%s\n", buf); 777 if (s) 778 sysfatal(buf); 779 else 780 exits(nil); 781 } 782 783 void* 784 emallocz(uint n) 785 { 786 void *p; 787 788 p = mallocz(n, 1); 789 if(p == nil) 790 fatal(Enomem); 791 return p; 792 } 793 794 char* 795 estrdup(char *s) 796 { 797 char *t; 798 799 t = strdup(s); 800 if(t == nil) 801 fatal(Enomem); 802 return t; 803 } 804 805 /* Network on fd1, mount driver on fd0 */ 806 int 807 filter(int fd, char *cmd) 808 { 809 int p[2], lfd, len, nb, argc; 810 char newport[128], buf[128], devdir[40], *s, *file, *argv[16]; 811 812 // Get a free port and post it to the client. 813 if (announce(anstring, devdir) < 0) 814 sysfatal("filter: Cannot announce %s: %r\n", anstring); 815 816 snprint(buf, sizeof(buf), "%s/local", devdir); 817 buf[sizeof buf - 1] = '\0'; 818 if ((lfd = open(buf, OREAD)) < 0) 819 sysfatal("filter: Cannot open %s: %r\n", buf); 820 if ((len = read(lfd, newport, sizeof newport - 1)) < 0) 821 sysfatal("filter: Cannot read %s: %r\n", buf); 822 close(lfd); 823 newport[len] = '\0'; 824 825 if ((s = strchr(newport, '\n')) != nil) 826 *s = '\0'; 827 828 if ((nb = write(fd, newport, len)) < 0) 829 sysfatal("getport; cannot write port; %r"); 830 assert(nb == len); 831 832 argc = tokenize(cmd, argv, nelem(argv)-2); 833 if (argc == 0) 834 sysfatal("filter: empty command"); 835 argv[argc++] = buf; 836 argv[argc] = nil; 837 file = argv[0]; 838 if (s = strrchr(argv[0], '/')) 839 argv[0] = s+1; 840 841 if(pipe(p) < 0) 842 fatal("pipe"); 843 844 switch(rfork(RFNOWAIT|RFPROC|RFFDG)) { 845 case -1: 846 fatal("rfork record module"); 847 case 0: 848 if (dup(p[0], 1) < 0) 849 fatal("filter: Cannot dup to 1; %r\n"); 850 if (dup(p[0], 0) < 0) 851 fatal("filter: Cannot dup to 0; %r\n"); 852 close(p[0]); 853 close(p[1]); 854 exec(file, argv); 855 fatal("exec record module"); 856 default: 857 close(fd); 858 close(p[0]); 859 } 860 return p[1]; 861 } 862 863 static void 864 mksecret(char *t, uchar *f) 865 { 866 sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", 867 f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]); 868 } 869