1 #include "stdinc.h" 2 #include <fcall.h> 3 #include "vac.h" 4 5 #define convM2Su(a, b, c, d) convM2S(a, b, c) 6 #define convS2Mu(a, b, c, d) convS2M(a, b, c) 7 #define convM2Du(a, b, c, d) convM2D(a, b, c) 8 #define convD2Mu(a, b, c, d) convD2M(a, b, c) 9 10 typedef struct Fid Fid; 11 12 enum 13 { 14 Stacksize = 320 * 1024, /* was 32K */ 15 OPERM = 0x3 /* mask of all permission types in open mode */ 16 }; 17 18 struct Fid 19 { 20 short busy; 21 short open; 22 int fid; 23 char *user; 24 Qid qid; 25 VacFile *file; 26 VacDirEnum *vde; 27 Fid *next; 28 }; 29 30 enum 31 { 32 Pexec = 1, 33 Pwrite = 2, 34 Pread = 4, 35 Pother = 1, 36 Pgroup = 8, 37 Powner = 64 38 }; 39 40 Fid *fids; 41 uchar *data; 42 int mfd[2]; 43 int srvfd = -1; 44 char *user; 45 uchar mdata[8192+IOHDRSZ]; 46 int messagesize = sizeof mdata; 47 Fcall rhdr; 48 Fcall thdr; 49 VacFs *fs; 50 VtConn *conn; 51 int noperm; 52 int dotu; 53 char *defmnt; 54 55 Fid * newfid(int); 56 void error(char*); 57 void io(void); 58 void vacshutdown(void); 59 void usage(void); 60 int perm(Fid*, int); 61 int permf(VacFile*, char*, int); 62 ulong getl(void *p); 63 void init(char*, char*, long, int); 64 int vacdirread(Fid *f, char *p, long off, long cnt); 65 int vacstat(VacFile *parent, VacDir *vd, uchar *p, int np); 66 void srv(void* a); 67 68 69 char *rflush(Fid*), *rversion(Fid*), 70 *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*), 71 *ropen(Fid*), *rcreate(Fid*), 72 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), 73 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*); 74 75 char *(*fcalls[Tmax])(Fid*); 76 77 void 78 initfcalls(void) 79 { 80 fcalls[Tflush]= rflush; 81 fcalls[Tversion]= rversion; 82 fcalls[Tattach]= rattach; 83 fcalls[Tauth]= rauth; 84 fcalls[Twalk]= rwalk; 85 fcalls[Topen]= ropen; 86 fcalls[Tcreate]= rcreate; 87 fcalls[Tread]= rread; 88 fcalls[Twrite]= rwrite; 89 fcalls[Tclunk]= rclunk; 90 fcalls[Tremove]= rremove; 91 fcalls[Tstat]= rstat; 92 fcalls[Twstat]= rwstat; 93 } 94 95 char Eperm[] = "permission denied"; 96 char Enotdir[] = "not a directory"; 97 char Enotexist[] = "file does not exist"; 98 char Einuse[] = "file in use"; 99 char Eexist[] = "file exists"; 100 char Enotowner[] = "not owner"; 101 char Eisopen[] = "file already open for I/O"; 102 char Excl[] = "exclusive use file already open"; 103 char Ename[] = "illegal name"; 104 char Erdonly[] = "read only file system"; 105 char Eio[] = "i/o error"; 106 char Eempty[] = "directory is not empty"; 107 char Emode[] = "illegal mode"; 108 109 int dflag; 110 111 void 112 notifyf(void *a, char *s) 113 { 114 USED(a); 115 if(strncmp(s, "interrupt", 9) == 0) 116 noted(NCONT); 117 noted(NDFLT); 118 } 119 120 void 121 threadmain(int argc, char *argv[]) 122 { 123 char *defsrv, *srvname; 124 int p[2], fd; 125 int stdio; 126 char *host = nil; 127 long ncache; 128 129 stdio = 0; 130 ncache = 256; 131 fmtinstall('H', encodefmt); 132 fmtinstall('V', vtscorefmt); 133 fmtinstall('F', vtfcallfmt); 134 135 defmnt = nil; 136 defsrv = nil; 137 ARGBEGIN{ 138 case 'd': 139 fmtinstall('F', fcallfmt); 140 dflag = 1; 141 break; 142 case 'c': 143 ncache = atoi(EARGF(usage())); 144 break; 145 case 'i': 146 defmnt = nil; 147 stdio = 1; 148 mfd[0] = 0; 149 mfd[1] = 1; 150 break; 151 case 'h': 152 host = EARGF(usage()); 153 break; 154 case 'S': 155 defsrv = EARGF(usage()); 156 break; 157 case 's': 158 defsrv = "vacfs"; 159 break; 160 case 'm': 161 defmnt = EARGF(usage()); 162 break; 163 case 'p': 164 noperm = 1; 165 break; 166 case 'V': 167 chattyventi = 1; 168 break; 169 default: 170 usage(); 171 }ARGEND 172 173 if(argc != 1) 174 usage(); 175 176 if(defsrv == nil && defmnt == nil && !stdio) 177 defmnt = "/n/vac"; 178 if(stdio && defmnt) 179 sysfatal("cannot use -m with -i"); 180 181 initfcalls(); 182 183 notify(notifyf); 184 user = getuser(); 185 186 conn = vtdial(host); 187 if(conn == nil) 188 sysfatal("could not connect to server: %r"); 189 190 if(vtconnect(conn) < 0) 191 sysfatal("vtconnect: %r"); 192 193 fs = vacfsopen(conn, argv[0], VtOREAD, ncache); 194 if(fs == nil) 195 sysfatal("vacfsopen: %r"); 196 197 if(!stdio){ 198 if(pipe(p) < 0) 199 sysfatal("pipe failed: %r"); 200 mfd[0] = p[0]; 201 mfd[1] = p[0]; 202 srvfd = p[1]; 203 } 204 205 procrfork(srv, 0, Stacksize, RFFDG|RFNAMEG|RFNOTEG); 206 207 if(!stdio){ 208 close(p[0]); 209 if(defsrv){ 210 srvname = smprint("/srv/%s", defsrv); 211 fd = create(srvname, OWRITE|ORCLOSE, 0666); 212 if(fd < 0) 213 sysfatal("create %s: %r", srvname); 214 if(fprint(fd, "%d", srvfd) < 0) 215 sysfatal("write %s: %r", srvname); 216 free(srvname); 217 } 218 if(defmnt){ 219 if(mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0) 220 sysfatal("mount %s: %r", defmnt); 221 } 222 } 223 threadexits(0); 224 } 225 226 void 227 srv(void *a) 228 { 229 USED(a); 230 io(); 231 vacshutdown(); 232 } 233 234 void 235 usage(void) 236 { 237 fprint(2, "usage: %s [-sd] [-h host] [-c ncache] [-m mountpoint] vacfile\n", argv0); 238 threadexitsall("usage"); 239 } 240 241 char* 242 rversion(Fid *unused) 243 { 244 Fid *f; 245 246 USED(unused); 247 248 for(f = fids; f; f = f->next) 249 if(f->busy) 250 rclunk(f); 251 252 if(rhdr.msize < 256) 253 return vtstrdup("version: message size too small"); 254 messagesize = rhdr.msize; 255 if(messagesize > sizeof mdata) 256 messagesize = sizeof mdata; 257 thdr.msize = messagesize; 258 if(strncmp(rhdr.version, "9P2000", 6) != 0) 259 return vtstrdup("unrecognized 9P version"); 260 thdr.version = "9P2000"; 261 if(strncmp(rhdr.version, "9P2000.u", 8) == 0){ 262 dotu = 1; 263 thdr.version = "9P2000.u"; 264 } 265 return nil; 266 } 267 268 char* 269 rflush(Fid *f) 270 { 271 USED(f); 272 return 0; 273 } 274 275 char* 276 rauth(Fid *f) 277 { 278 USED(f); 279 return vtstrdup("vacfs: authentication not required"); 280 } 281 282 char* 283 rattach(Fid *f) 284 { 285 /* no authentication for the momment */ 286 VacFile *file; 287 char err[80]; 288 289 file = vacfsgetroot(fs); 290 if(file == nil) { 291 rerrstr(err, sizeof err); 292 return vtstrdup(err); 293 } 294 295 f->busy = 1; 296 f->file = file; 297 f->qid.path = vacfilegetid(f->file); 298 f->qid.vers = 0; 299 f->qid.type = QTDIR; 300 thdr.qid = f->qid; 301 if(rhdr.uname[0]) 302 f->user = vtstrdup(rhdr.uname); 303 else 304 f->user = "none"; 305 return 0; 306 } 307 308 char* 309 rwalk(Fid *f) 310 { 311 VacFile *file, *nfile; 312 Fid *nf; 313 int nqid, nwname; 314 Qid qid; 315 char *err = nil; 316 317 if(f->busy == 0) 318 return Enotexist; 319 nf = nil; 320 if(rhdr.fid != rhdr.newfid){ 321 if(f->open) 322 return vtstrdup(Eisopen); 323 if(f->busy == 0) 324 return vtstrdup(Enotexist); 325 nf = newfid(rhdr.newfid); 326 if(nf->busy) 327 return vtstrdup(Eisopen); 328 nf->busy = 1; 329 nf->open = 0; 330 nf->qid = f->qid; 331 nf->file = vacfileincref(f->file); 332 nf->user = vtstrdup(f->user); 333 f = nf; 334 } 335 336 nwname = rhdr.nwname; 337 338 /* easy case */ 339 if(nwname == 0) { 340 thdr.nwqid = 0; 341 return 0; 342 } 343 344 file = f->file; 345 vacfileincref(file); 346 qid = f->qid; 347 348 for(nqid = 0; nqid < nwname; nqid++){ 349 if((qid.type & QTDIR) == 0){ 350 err = Enotdir; 351 break; 352 } 353 if(!permf(file, f->user, Pexec)) { 354 err = Eperm; 355 break; 356 } 357 nfile = vacfilewalk(file, rhdr.wname[nqid]); 358 if(nfile == nil) 359 break; 360 vacfiledecref(file); 361 file = nfile; 362 qid.type = QTFILE; 363 if(vacfileisdir(file)) 364 qid.type = QTDIR; 365 qid.vers = vacfilegetmcount(file); 366 qid.path = vacfilegetid(file); 367 thdr.wqid[nqid] = qid; 368 } 369 370 thdr.nwqid = nqid; 371 372 if(nqid == nwname){ 373 /* success */ 374 f->qid = thdr.wqid[nqid-1]; 375 vacfiledecref(f->file); 376 f->file = file; 377 return 0; 378 } 379 380 vacfiledecref(file); 381 if(nf != nil) 382 rclunk(nf); 383 384 /* only error on the first element */ 385 if(nqid == 0) 386 return vtstrdup(err); 387 388 return 0; 389 } 390 391 char * 392 ropen(Fid *f) 393 { 394 int mode, trunc; 395 396 if(f->open) 397 return vtstrdup(Eisopen); 398 if(!f->busy) 399 return vtstrdup(Enotexist); 400 401 mode = rhdr.mode; 402 thdr.iounit = messagesize - IOHDRSZ; 403 if(f->qid.type & QTDIR){ 404 if(mode != OREAD) 405 return vtstrdup(Eperm); 406 if(!perm(f, Pread)) 407 return vtstrdup(Eperm); 408 thdr.qid = f->qid; 409 f->vde = nil; 410 f->open = 1; 411 return 0; 412 } 413 if(mode & ORCLOSE) 414 return vtstrdup(Erdonly); 415 trunc = mode & OTRUNC; 416 mode &= OPERM; 417 if(mode==OWRITE || mode==ORDWR || trunc) 418 if(!perm(f, Pwrite)) 419 return vtstrdup(Eperm); 420 if(mode==OREAD || mode==ORDWR) 421 if(!perm(f, Pread)) 422 return vtstrdup(Eperm); 423 if(mode==OEXEC) 424 if(!perm(f, Pexec)) 425 return vtstrdup(Eperm); 426 thdr.qid = f->qid; 427 thdr.iounit = messagesize - IOHDRSZ; 428 f->open = 1; 429 return 0; 430 } 431 432 char* 433 rcreate(Fid* fid) 434 { 435 VacFile *vf; 436 ulong mode; 437 438 if(fid->open) 439 return vtstrdup(Eisopen); 440 if(!fid->busy) 441 return vtstrdup(Enotexist); 442 if(fs->mode & ModeSnapshot) 443 return vtstrdup(Erdonly); 444 vf = fid->file; 445 if(!vacfileisdir(vf)) 446 return vtstrdup(Enotdir); 447 if(!permf(vf, fid->user, Pwrite)) 448 return vtstrdup(Eperm); 449 450 mode = rhdr.perm & 0777; 451 452 if(rhdr.perm & DMDIR){ 453 if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND)) 454 return vtstrdup(Emode); 455 switch(rhdr.mode & OPERM){ 456 default: 457 return vtstrdup(Emode); 458 case OEXEC: 459 case OREAD: 460 break; 461 case OWRITE: 462 case ORDWR: 463 return vtstrdup(Eperm); 464 } 465 mode |= ModeDir; 466 } 467 vf = vacfilecreate(vf, rhdr.name, mode); 468 if(vf == nil) { 469 char err[80]; 470 rerrstr(err, sizeof err); 471 472 return vtstrdup(err); 473 } 474 475 vacfiledecref(fid->file); 476 477 fid->file = vf; 478 fid->qid.type = QTFILE; 479 if(vacfileisdir(vf)) 480 fid->qid.type = QTDIR; 481 fid->qid.vers = vacfilegetmcount(vf); 482 fid->qid.path = vacfilegetid(vf); 483 484 thdr.qid = fid->qid; 485 thdr.iounit = messagesize - IOHDRSZ; 486 487 return 0; 488 } 489 490 char* 491 rread(Fid *f) 492 { 493 char *buf; 494 vlong off; 495 int cnt; 496 VacFile *vf; 497 char err[80]; 498 int n; 499 500 if(!f->busy) 501 return vtstrdup(Enotexist); 502 vf = f->file; 503 thdr.count = 0; 504 off = rhdr.offset; 505 buf = thdr.data; 506 cnt = rhdr.count; 507 if(f->qid.type & QTDIR) 508 n = vacdirread(f, buf, off, cnt); 509 else if(vacfilegetmode(f->file)&ModeDevice) 510 return vtstrdup("device"); 511 else if(vacfilegetmode(f->file)&ModeLink) 512 return vtstrdup("symbolic link"); 513 else if(vacfilegetmode(f->file)&ModeNamedPipe) 514 return vtstrdup("named pipe"); 515 else 516 n = vacfileread(vf, buf, cnt, off); 517 if(n < 0) { 518 rerrstr(err, sizeof err); 519 return vtstrdup(err); 520 } 521 thdr.count = n; 522 return 0; 523 } 524 525 char* 526 rwrite(Fid *f) 527 { 528 USED(f); 529 return vtstrdup(Erdonly); 530 } 531 532 char * 533 rclunk(Fid *f) 534 { 535 f->busy = 0; 536 f->open = 0; 537 vtfree(f->user); 538 f->user = nil; 539 if(f->file) 540 vacfiledecref(f->file); 541 f->file = nil; 542 vdeclose(f->vde); 543 f->vde = nil; 544 return 0; 545 } 546 547 char * 548 rremove(Fid *f) 549 { 550 VacFile *vf, *vfp; 551 char errbuf[80]; 552 char *err = nil; 553 554 if(!f->busy) 555 return vtstrdup(Enotexist); 556 vf = f->file; 557 vfp = vacfilegetparent(vf); 558 559 if(!permf(vfp, f->user, Pwrite)) { 560 err = Eperm; 561 goto Exit; 562 } 563 564 if(!vacfileremove(vf)) { 565 rerrstr(errbuf, sizeof errbuf); 566 err = errbuf; 567 } 568 569 Exit: 570 vacfiledecref(vfp); 571 rclunk(f); 572 return vtstrdup(err); 573 } 574 575 char * 576 rstat(Fid *f) 577 { 578 VacDir dir; 579 static uchar statbuf[1024]; 580 VacFile *parent; 581 582 if(!f->busy) 583 return vtstrdup(Enotexist); 584 parent = vacfilegetparent(f->file); 585 vacfilegetdir(f->file, &dir); 586 thdr.stat = statbuf; 587 thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf); 588 vdcleanup(&dir); 589 vacfiledecref(parent); 590 return 0; 591 } 592 593 char * 594 rwstat(Fid *f) 595 { 596 if(!f->busy) 597 return vtstrdup(Enotexist); 598 return vtstrdup(Erdonly); 599 } 600 601 int 602 vacstat(VacFile *parent, VacDir *vd, uchar *p, int np) 603 { 604 int ret; 605 Dir dir; 606 607 memset(&dir, 0, sizeof(dir)); 608 609 dir.qid.path = vd->qid + vacfilegetqidoffset(parent); 610 if(vd->qidspace) 611 dir.qid.path += vd->qidoffset; 612 dir.qid.vers = vd->mcount; 613 dir.mode = vd->mode & 0777; 614 if(vd->mode & ModeAppend){ 615 dir.qid.type |= QTAPPEND; 616 dir.mode |= DMAPPEND; 617 } 618 if(vd->mode & ModeExclusive){ 619 dir.qid.type |= QTEXCL; 620 dir.mode |= DMEXCL; 621 } 622 if(vd->mode & ModeDir){ 623 dir.qid.type |= QTDIR; 624 dir.mode |= DMDIR; 625 } 626 627 628 dir.atime = vd->atime; 629 dir.mtime = vd->mtime; 630 dir.length = vd->size; 631 632 dir.name = vd->elem; 633 dir.uid = vd->uid; 634 dir.gid = vd->gid; 635 dir.muid = vd->mid; 636 637 ret = convD2Mu(&dir, p, np, dotu); 638 return ret; 639 } 640 641 int 642 vacdirread(Fid *f, char *p, long off, long cnt) 643 { 644 int i, n, nb; 645 VacDir vd; 646 647 /* 648 * special case of rewinding a directory 649 * otherwise ignore the offset 650 */ 651 if(off == 0 && f->vde){ 652 vdeclose(f->vde); 653 f->vde = nil; 654 } 655 656 if(f->vde == nil){ 657 f->vde = vdeopen(f->file); 658 if(f->vde == nil) 659 return -1; 660 } 661 662 for(nb = 0; nb < cnt; nb += n) { 663 i = vderead(f->vde, &vd); 664 if(i < 0) 665 return -1; 666 if(i == 0) 667 break; 668 n = vacstat(f->file, &vd, (uchar*)p, cnt-nb); 669 if(n <= BIT16SZ) { 670 vdeunread(f->vde); 671 break; 672 } 673 vdcleanup(&vd); 674 p += n; 675 } 676 return nb; 677 } 678 679 Fid * 680 newfid(int fid) 681 { 682 Fid *f, *ff; 683 684 ff = 0; 685 for(f = fids; f; f = f->next) 686 if(f->fid == fid) 687 return f; 688 else if(!ff && !f->busy) 689 ff = f; 690 if(ff){ 691 ff->fid = fid; 692 return ff; 693 } 694 f = vtmallocz(sizeof *f); 695 f->fid = fid; 696 f->next = fids; 697 fids = f; 698 return f; 699 } 700 701 void 702 io(void) 703 { 704 char *err; 705 int n; 706 707 for(;;){ 708 n = read9pmsg(mfd[0], mdata, sizeof mdata); 709 if(n <= 0) 710 break; 711 if(convM2Su(mdata, n, &rhdr, dotu) != n) 712 sysfatal("convM2S conversion error"); 713 714 if(dflag) 715 fprint(2, "vacfs:<-%F\n", &rhdr); 716 717 thdr.data = (char*)mdata + IOHDRSZ; 718 if(!fcalls[rhdr.type]) 719 err = "bad fcall type"; 720 else 721 err = (*fcalls[rhdr.type])(newfid(rhdr.fid)); 722 if(err){ 723 thdr.type = Rerror; 724 thdr.ename = err; 725 }else{ 726 thdr.type = rhdr.type + 1; 727 thdr.fid = rhdr.fid; 728 } 729 thdr.tag = rhdr.tag; 730 if(dflag) 731 fprint(2, "vacfs:->%F\n", &thdr); 732 n = convS2Mu(&thdr, mdata, messagesize, dotu); 733 if(n <= BIT16SZ) 734 sysfatal("convS2Mu conversion error"); 735 if(err) 736 vtfree(err); 737 738 if(write(mfd[1], mdata, n) != n) 739 sysfatal("mount write: %r"); 740 } 741 } 742 743 int 744 permf(VacFile *vf, char *user, int p) 745 { 746 VacDir dir; 747 ulong perm; 748 749 if(vacfilegetdir(vf, &dir)) 750 return 0; 751 perm = dir.mode & 0777; 752 753 if(noperm) 754 goto Good; 755 if((p*Pother) & perm) 756 goto Good; 757 if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm)) 758 goto Good; 759 if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm)) 760 goto Good; 761 vdcleanup(&dir); 762 return 0; 763 Good: 764 vdcleanup(&dir); 765 return 1; 766 } 767 768 int 769 perm(Fid *f, int p) 770 { 771 return permf(f->file, f->user, p); 772 } 773 774 void 775 vacshutdown(void) 776 { 777 Fid *f; 778 779 for(f = fids; f; f = f->next) { 780 if(!f->busy) 781 continue; 782 rclunk(f); 783 } 784 785 vacfsclose(fs); 786 vthangup(conn); 787 } 788 789