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