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