1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include <fcall.h> 6 #include <String.h> 7 #include <libsec.h> 8 9 enum 10 { 11 OPERM = 0x3, /* mask of all permission types in open mode */ 12 Maxsize = 512*1024*1024, 13 Maxfdata = 8192, 14 NAMELEN = 28, 15 }; 16 17 typedef struct Fid Fid; 18 struct Fid 19 { 20 short busy; 21 int fid; 22 Fid *next; 23 char *user; 24 String *path; /* complete path */ 25 26 int fd; /* set on open or create */ 27 Qid qid; /* set on open or create */ 28 int attach; /* this is an attach fd */ 29 30 ulong diroff; /* directory offset */ 31 Dir *dir; /* directory entries */ 32 int ndir; /* number of directory entries */ 33 }; 34 35 Fid *fids; 36 int mfd[2]; 37 char *user; 38 uchar mdata[IOHDRSZ+Maxfdata]; 39 uchar rdata[Maxfdata]; /* buffer for data in reply */ 40 uchar statbuf[STATMAX]; 41 Fcall thdr; 42 Fcall rhdr; 43 int messagesize = sizeof mdata; 44 int readonly; 45 char *srvname; 46 int debug; 47 48 Fid * newfid(int); 49 void io(void); 50 void *erealloc(void*, ulong); 51 void *emalloc(ulong); 52 char *estrdup(char*); 53 void usage(void); 54 void fidqid(Fid*, Qid*); 55 char* short2long(char*); 56 char* long2short(char*, int); 57 void readnames(void); 58 void post(char*, int); 59 60 char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*), 61 *rattach(Fid*), *rwalk(Fid*), 62 *ropen(Fid*), *rcreate(Fid*), 63 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), 64 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*); 65 66 char *(*fcalls[])(Fid*) = { 67 [Tversion] rversion, 68 [Tflush] rflush, 69 [Tauth] rauth, 70 [Tattach] rattach, 71 [Twalk] rwalk, 72 [Topen] ropen, 73 [Tcreate] rcreate, 74 [Tread] rread, 75 [Twrite] rwrite, 76 [Tclunk] rclunk, 77 [Tremove] rremove, 78 [Tstat] rstat, 79 [Twstat] rwstat, 80 }; 81 82 char Eperm[] = "permission denied"; 83 char Enotdir[] = "not a directory"; 84 char Enoauth[] = "lnfs: authentication not required"; 85 char Enotexist[] = "file does not exist"; 86 char Einuse[] = "file in use"; 87 char Eexist[] = "file exists"; 88 char Eisdir[] = "file is a directory"; 89 char Enotowner[] = "not owner"; 90 char Eisopen[] = "file already open for I/O"; 91 char Excl[] = "exclusive use file already open"; 92 char Ename[] = "illegal name"; 93 char Eversion[] = "unknown 9P version"; 94 95 void 96 usage(void) 97 { 98 fprint(2, "usage: %s [-r] [-s srvname] mountpoint\n", argv0); 99 exits("usage"); 100 } 101 102 void 103 notifyf(void *a, char *s) 104 { 105 USED(a); 106 if(strncmp(s, "interrupt", 9) == 0) 107 noted(NCONT); 108 noted(NDFLT); 109 } 110 111 void 112 main(int argc, char *argv[]) 113 { 114 char *defmnt; 115 int p[2]; 116 Dir *d; 117 118 ARGBEGIN{ 119 case 'r': 120 readonly = 1; 121 break; 122 case 'd': 123 debug = 1; 124 break; 125 case 's': 126 srvname = ARGF(); 127 if(srvname == nil) 128 usage(); 129 break; 130 default: 131 usage(); 132 }ARGEND 133 134 if(argc < 1) 135 usage(); 136 defmnt = argv[0]; 137 d = dirstat(defmnt); 138 if(d == nil || !(d->qid.type & QTDIR)) 139 sysfatal("mountpoint must be an accessible directory"); 140 free(d); 141 142 if(pipe(p) < 0) 143 sysfatal("pipe failed"); 144 mfd[0] = p[0]; 145 mfd[1] = p[0]; 146 147 user = getuser(); 148 notify(notifyf); 149 150 if(srvname != nil) 151 post(srvname, p[1]); 152 153 if(debug) 154 fmtinstall('F', fcallfmt); 155 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ 156 case -1: 157 sysfatal("fork: %r"); 158 case 0: 159 close(p[1]); 160 chdir(defmnt); 161 io(); 162 break; 163 default: 164 close(p[0]); /* don't deadlock if child fails */ 165 if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0) 166 sysfatal("mount failed: %r"); 167 } 168 exits(0); 169 } 170 171 void 172 post(char *srvname, int pfd) 173 { 174 char name[128]; 175 int fd; 176 177 snprint(name, sizeof name, "#s/%s", srvname); 178 fd = create(name, OWRITE, 0666); 179 if(fd < 0) 180 sysfatal("create of %s failed: %r", srvname); 181 sprint(name, "%d", pfd); 182 if(write(fd, name, strlen(name)) < 0) 183 sysfatal("writing %s: %r", srvname); 184 } 185 char* 186 rversion(Fid*) 187 { 188 Fid *f; 189 190 for(f = fids; f; f = f->next) 191 if(f->busy) 192 rclunk(f); 193 if(thdr.msize > sizeof mdata) 194 rhdr.msize = sizeof mdata; 195 else 196 rhdr.msize = thdr.msize; 197 messagesize = rhdr.msize; 198 if(strncmp(thdr.version, "9P2000", 6) != 0) 199 return Eversion; 200 rhdr.version = "9P2000"; 201 return nil; 202 } 203 204 char* 205 rauth(Fid*) 206 { 207 return Enoauth; 208 } 209 210 char* 211 rflush(Fid *f) 212 { 213 USED(f); 214 return nil; 215 } 216 217 char* 218 rattach(Fid *f) 219 { 220 /* no authentication! */ 221 f->busy = 1; 222 if(thdr.uname[0]) 223 f->user = estrdup(thdr.uname); 224 else 225 f->user = "none"; 226 if(strcmp(user, "none") == 0) 227 user = f->user; 228 if(f->path) 229 s_free(f->path); 230 f->path = s_copy("."); 231 fidqid(f, &rhdr.qid); 232 f->attach = 1; 233 return nil; 234 } 235 236 char* 237 clone(Fid *f, Fid **nf) 238 { 239 if(f->fd >= 0) 240 return Eisopen; 241 *nf = newfid(thdr.newfid); 242 (*nf)->busy = 1; 243 if((*nf)->path) 244 s_free((*nf)->path); 245 (*nf)->path = s_clone(f->path); 246 (*nf)->fd = -1; 247 (*nf)->user = f->user; 248 (*nf)->attach = 0; 249 return nil; 250 } 251 252 char* 253 rwalk(Fid *f) 254 { 255 char *name; 256 Fid *nf; 257 char *err; 258 int i; 259 String *npath; 260 Dir *d; 261 char *cp; 262 Qid qid; 263 264 err = nil; 265 nf = nil; 266 rhdr.nwqid = 0; 267 if(rhdr.newfid != rhdr.fid){ 268 err = clone(f, &nf); 269 if(err) 270 return err; 271 f = nf; /* walk the new fid */ 272 } 273 readnames(); 274 npath = s_clone(f->path); 275 if(thdr.nwname > 0){ 276 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){ 277 name = long2short(thdr.wname[i], 0); 278 if(strcmp(name, ".") == 0){ 279 ; 280 } else if(strcmp(name, "..") == 0){ 281 cp = strrchr(s_to_c(npath), '/'); 282 if(cp != nil){ 283 *cp = 0; 284 npath->ptr = cp; 285 } 286 } else { 287 s_append(npath, "/"); 288 s_append(npath, name); 289 } 290 d = dirstat(s_to_c(npath)); 291 if(d == nil) 292 break; 293 rhdr.nwqid++; 294 qid = d->qid; 295 rhdr.wqid[i] = qid; 296 free(d); 297 } 298 if(i==0 && err == nil) 299 err = Enotexist; 300 } 301 302 /* if there was an error and we cloned, get rid of the new fid */ 303 if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){ 304 f->busy = 0; 305 s_free(npath); 306 } 307 308 /* update the fid after a successful walk */ 309 if(rhdr.nwqid == thdr.nwname){ 310 s_free(f->path); 311 f->path = npath; 312 } 313 return err; 314 } 315 316 static char* 317 passerror(void) 318 { 319 static char err[256]; 320 321 rerrstr(err, sizeof err); 322 return err; 323 } 324 325 char* 326 ropen(Fid *f) 327 { 328 if(readonly && (thdr.mode & 3)) 329 return Eperm; 330 if(f->fd >= 0) 331 return Eisopen; 332 f->fd = open(s_to_c(f->path), thdr.mode); 333 if(f->fd < 0) 334 return passerror(); 335 fidqid(f, &rhdr.qid); 336 f->qid = rhdr.qid; 337 rhdr.iounit = messagesize-IOHDRSZ; 338 return nil; 339 } 340 341 char* 342 rcreate(Fid *f) 343 { 344 char *name; 345 346 if(readonly) 347 return Eperm; 348 readnames(); 349 name = long2short(thdr.name, 1); 350 if(f->fd >= 0) 351 return Eisopen; 352 s_append(f->path, "/"); 353 s_append(f->path, name); 354 f->fd = create(s_to_c(f->path), thdr.mode, thdr.perm); 355 if(f->fd < 0) 356 return passerror(); 357 fidqid(f, &rhdr.qid); 358 f->qid = rhdr.qid; 359 rhdr.iounit = messagesize-IOHDRSZ; 360 return nil; 361 } 362 363 char* 364 rreaddir(Fid *f) 365 { 366 int i; 367 int n; 368 369 /* reread the directory */ 370 if(thdr.offset == 0){ 371 if(f->dir) 372 free(f->dir); 373 f->dir = nil; 374 if(f->diroff != 0) 375 seek(f->fd, 0, 0); 376 f->ndir = dirreadall(f->fd, &f->dir); 377 f->diroff = 0; 378 if(f->ndir < 0) 379 return passerror(); 380 readnames(); 381 for(i = 0; i < f->ndir; i++) 382 f->dir[i].name = short2long(f->dir[i].name); 383 } 384 385 /* copy in as many directory entries as possible */ 386 for(n = 0; f->diroff < f->ndir; n += i){ 387 i = convD2M(&f->dir[f->diroff], rdata+n, thdr.count - n); 388 if(i <= BIT16SZ) 389 break; 390 f->diroff++; 391 } 392 rhdr.data = (char*)rdata; 393 rhdr.count = n; 394 return nil; 395 } 396 397 char* 398 rread(Fid *f) 399 { 400 long n; 401 402 if(f->fd < 0) 403 return Enotexist; 404 if(thdr.count > messagesize-IOHDRSZ) 405 thdr.count = messagesize-IOHDRSZ; 406 if(f->qid.type & QTDIR) 407 return rreaddir(f); 408 n = pread(f->fd, rdata, thdr.count, thdr.offset); 409 if(n < 0) 410 return passerror(); 411 rhdr.data = (char*)rdata; 412 rhdr.count = n; 413 return nil; 414 } 415 416 char* 417 rwrite(Fid *f) 418 { 419 long n; 420 421 if(readonly || (f->qid.type & QTDIR)) 422 return Eperm; 423 if(f->fd < 0) 424 return Enotexist; 425 if(thdr.count > messagesize-IOHDRSZ) /* shouldn't happen, anyway */ 426 thdr.count = messagesize-IOHDRSZ; 427 n = pwrite(f->fd, thdr.data, thdr.count, thdr.offset); 428 if(n < 0) 429 return passerror(); 430 rhdr.count = n; 431 return nil; 432 } 433 434 char* 435 rclunk(Fid *f) 436 { 437 f->busy = 0; 438 close(f->fd); 439 f->fd = -1; 440 f->path = s_reset(f->path); 441 if(f->attach){ 442 free(f->user); 443 f->user = nil; 444 } 445 f->attach = 0; 446 if(f->dir != nil){ 447 free(f->dir); 448 f->dir = nil; 449 } 450 f->diroff = f->ndir = 0; 451 return nil; 452 } 453 454 char* 455 rremove(Fid *f) 456 { 457 if(remove(s_to_c(f->path)) < 0) 458 return passerror(); 459 return nil; 460 } 461 462 char* 463 rstat(Fid *f) 464 { 465 int n; 466 Dir *d; 467 468 d = dirstat(s_to_c(f->path)); 469 if(d == nil) 470 return passerror(); 471 d->name = short2long(d->name); 472 n = convD2M(d, statbuf, sizeof statbuf); 473 free(d); 474 if(n <= BIT16SZ) 475 return passerror(); 476 rhdr.nstat = n; 477 rhdr.stat = statbuf; 478 return nil; 479 } 480 481 char* 482 rwstat(Fid *f) 483 { 484 int n; 485 Dir d; 486 487 if(readonly) 488 return Eperm; 489 convM2D(thdr.stat, thdr.nstat, &d, (char*)rdata); 490 d.name = long2short(d.name, 1); 491 n = dirwstat(s_to_c(f->path), &d); 492 if(n < 0) 493 return passerror(); 494 return nil; 495 } 496 497 Fid * 498 newfid(int fid) 499 { 500 Fid *f, *ff; 501 502 ff = 0; 503 for(f = fids; f; f = f->next) 504 if(f->fid == fid) 505 return f; 506 else if(!ff && !f->busy) 507 ff = f; 508 if(ff){ 509 ff->fid = fid; 510 return ff; 511 } 512 f = emalloc(sizeof *f); 513 f->path = s_reset(f->path); 514 f->fd = -1; 515 f->fid = fid; 516 f->next = fids; 517 fids = f; 518 return f; 519 } 520 521 void 522 io(void) 523 { 524 char *err; 525 int n, pid; 526 527 pid = getpid(); 528 529 for(;;){ 530 /* 531 * reading from a pipe or a network device 532 * will give an error after a few eof reads. 533 * however, we cannot tell the difference 534 * between a zero-length read and an interrupt 535 * on the processes writing to us, 536 * so we wait for the error. 537 */ 538 n = read9pmsg(mfd[0], mdata, messagesize); 539 if(n < 0) 540 sysfatal("mount read"); 541 if(n == 0) 542 continue; 543 if(convM2S(mdata, n, &thdr) == 0) 544 continue; 545 546 if(debug) 547 fprint(2, "%s %d:<-%F\n", argv0, pid, &thdr); 548 549 if(!fcalls[thdr.type]) 550 err = "bad fcall type"; 551 else 552 err = (*fcalls[thdr.type])(newfid(thdr.fid)); 553 if(err){ 554 rhdr.type = Rerror; 555 rhdr.ename = err; 556 }else{ 557 rhdr.type = thdr.type + 1; 558 rhdr.fid = thdr.fid; 559 } 560 rhdr.tag = thdr.tag; 561 if(debug) 562 fprint(2, "%s %d:->%F\n", argv0, pid, &rhdr);/**/ 563 n = convS2M(&rhdr, mdata, messagesize); 564 if(n == 0) 565 sysfatal("convS2M error on write"); 566 if(write(mfd[1], mdata, n) != n) 567 sysfatal("mount write"); 568 } 569 } 570 571 void * 572 emalloc(ulong n) 573 { 574 void *p; 575 576 p = malloc(n); 577 if(!p) 578 sysfatal("out of memory"); 579 memset(p, 0, n); 580 return p; 581 } 582 583 void * 584 erealloc(void *p, ulong n) 585 { 586 p = realloc(p, n); 587 if(!p) 588 sysfatal("out of memory"); 589 return p; 590 } 591 592 char * 593 estrdup(char *q) 594 { 595 char *p; 596 int n; 597 598 n = strlen(q)+1; 599 p = malloc(n); 600 if(!p) 601 sysfatal("out of memory"); 602 memmove(p, q, n); 603 return p; 604 } 605 606 void 607 fidqid(Fid *f, Qid *q) 608 { 609 Dir *d; 610 611 d = dirstat(s_to_c(f->path)); 612 if(d == nil) 613 *q = (Qid){0, 0, QTFILE}; 614 else { 615 *q = d->qid; 616 free(d); 617 } 618 } 619 620 /* 621 * table of name translations 622 * 623 * the file ./.longnames contains all the known long names. 624 * the short name is the first NAMELEN-1 bytes of the base64 625 * encoding of the MD5 hash of the longname. 626 */ 627 628 typedef struct Name Name; 629 struct Name 630 { 631 Name *next; 632 char shortname[NAMELEN]; 633 char *longname; 634 }; 635 636 Dir *dbstat; /* last stat of the name file */ 637 char *namefile = "./.longnames"; 638 char *namebuf; 639 Name *names; 640 641 Name* 642 newname(char *longname, int writeflag) 643 { 644 Name *np; 645 int n; 646 uchar digest[MD5dlen]; 647 int fd; 648 649 /* chain in new name */ 650 n = strlen(longname); 651 np = emalloc(sizeof(*np)+n+1); 652 np->longname = (char*)&np[1]; 653 strcpy(np->longname, longname); 654 md5((uchar*)longname, n, digest, nil); 655 enc32(np->shortname, sizeof(np->shortname), digest, MD5dlen); 656 np->next = names; 657 names = np; 658 659 /* don't change namefile if we're read only */ 660 if(!writeflag) 661 return np; 662 663 /* add to namefile */ 664 fd = open(namefile, OWRITE); 665 if(fd >= 0){ 666 seek(fd, 0, 2); 667 fprint(fd, "%s\n", longname); 668 close(fd); 669 } 670 return np; 671 } 672 673 void 674 freenames(void) 675 { 676 Name *np, *next; 677 678 for(np = names; np != nil; np = next){ 679 next = np->next; 680 free(np); 681 } 682 names = nil; 683 } 684 685 /* 686 * reread the file if the qid.path has changed. 687 * 688 * read any new entries if length has changed. 689 */ 690 void 691 readnames(void) 692 { 693 Dir *d; 694 int fd; 695 vlong offset; 696 Biobuf *b; 697 char *p; 698 699 d = dirstat(namefile); 700 if(d == nil){ 701 if(readonly) 702 return; 703 704 /* create file if it doesn't exist */ 705 fd = create(namefile, OREAD, DMAPPEND|0666); 706 if(fd < 0) 707 return; 708 if(dbstat != nil) 709 free(dbstat); 710 dbstat = nil; 711 close(fd); 712 return; 713 } 714 715 /* up to date? */ 716 offset = 0; 717 if(dbstat != nil){ 718 if(d->qid.path == dbstat->qid.path){ 719 if(d->length <= dbstat->length){ 720 free(d); 721 return; 722 } 723 offset = dbstat->length; 724 } else { 725 freenames(); 726 } 727 free(dbstat); 728 dbstat = nil; 729 } 730 731 /* read file */ 732 b = Bopen(namefile, OREAD); 733 if(b == nil){ 734 free(d); 735 return; 736 } 737 Bseek(b, offset, 0); 738 while((p = Brdline(b, '\n')) != nil){ 739 p[Blinelen(b)-1] = 0; 740 newname(p, 0); 741 } 742 Bterm(b); 743 dbstat = d; 744 } 745 746 /* 747 * look up a long name, if it doesn't exist in the 748 * file, add an entry to the file if the writeflag is 749 * non-zero. Return a pointer to the short name. 750 */ 751 char* 752 long2short(char *longname, int writeflag) 753 { 754 Name *np; 755 756 if(strlen(longname) < NAMELEN-1 && strpbrk(longname, " ")==nil) 757 return longname; 758 759 for(np = names; np != nil; np = np->next) 760 if(strcmp(longname, np->longname) == 0) 761 return np->shortname; 762 if(!writeflag) 763 return longname; 764 np = newname(longname, !readonly); 765 return np->shortname; 766 } 767 768 /* 769 * look up a short name, if it doesn't exist, return the 770 * longname. 771 */ 772 char* 773 short2long(char *shortname) 774 { 775 Name *np; 776 777 for(np = names; np != nil; np = np->next) 778 if(strcmp(shortname, np->shortname) == 0) 779 return np->longname; 780 return shortname; 781 } 782