1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 6 /* 7 * Rather than reading /adm/users, which is a lot of work for 8 * a toy program, we assume all groups have the form 9 * NNN:user:user: 10 * meaning that each user is the leader of his own group. 11 */ 12 13 enum 14 { 15 OPERM = 0x3, /* mask of all permission types in open mode */ 16 Nram = 2048, 17 Maxsize = 512*1024*1024, 18 Maxfdata = 8192, 19 }; 20 21 typedef struct Fid Fid; 22 typedef struct Ram Ram; 23 24 struct Fid 25 { 26 short busy; 27 short open; 28 short rclose; 29 int fid; 30 Fid *next; 31 char *user; 32 Ram *ram; 33 }; 34 35 struct Ram 36 { 37 short busy; 38 short open; 39 long parent; /* index in Ram array */ 40 Qid qid; 41 long perm; 42 char *name; 43 ulong atime; 44 ulong mtime; 45 char *user; 46 char *group; 47 char *muid; 48 char *data; 49 long ndata; 50 }; 51 52 enum 53 { 54 Pexec = 1, 55 Pwrite = 2, 56 Pread = 4, 57 Pother = 1, 58 Pgroup = 8, 59 Powner = 64, 60 }; 61 62 ulong path; /* incremented for each new file */ 63 Fid *fids; 64 Ram ram[Nram]; 65 int nram; 66 int mfd[2]; 67 char *user; 68 uchar mdata[IOHDRSZ+Maxfdata]; 69 uchar rdata[Maxfdata]; /* buffer for data in reply */ 70 uchar statbuf[STATMAX]; 71 Fcall thdr; 72 Fcall rhdr; 73 int messagesize = sizeof mdata; 74 75 Fid * newfid(int); 76 uint ramstat(Ram*, uchar*, uint); 77 void error(char*); 78 void io(void); 79 void *erealloc(void*, ulong); 80 void *emalloc(ulong); 81 char *estrdup(char*); 82 void usage(void); 83 int perm(Fid*, Ram*, int); 84 85 char *rflush(Fid*), *rversion(Fid*), *rauth(Fid*), 86 *rattach(Fid*), *rwalk(Fid*), 87 *ropen(Fid*), *rcreate(Fid*), 88 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), 89 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*); 90 91 char *(*fcalls[])(Fid*) = { 92 [Tversion] rversion, 93 [Tflush] rflush, 94 [Tauth] rauth, 95 [Tattach] rattach, 96 [Twalk] rwalk, 97 [Topen] ropen, 98 [Tcreate] rcreate, 99 [Tread] rread, 100 [Twrite] rwrite, 101 [Tclunk] rclunk, 102 [Tremove] rremove, 103 [Tstat] rstat, 104 [Twstat] rwstat, 105 }; 106 107 char Eperm[] = "permission denied"; 108 char Enotdir[] = "not a directory"; 109 char Enoauth[] = "ramfs: authentication not required"; 110 char Enotexist[] = "file does not exist"; 111 char Einuse[] = "file in use"; 112 char Eexist[] = "file exists"; 113 char Eisdir[] = "file is a directory"; 114 char Enotowner[] = "not owner"; 115 char Eisopen[] = "file already open for I/O"; 116 char Excl[] = "exclusive use file already open"; 117 char Ename[] = "illegal name"; 118 char Eversion[] = "unknown 9P version"; 119 120 int debug; 121 int private; 122 123 void 124 notifyf(void *a, char *s) 125 { 126 USED(a); 127 if(strncmp(s, "interrupt", 9) == 0) 128 noted(NCONT); 129 noted(NDFLT); 130 } 131 132 void 133 main(int argc, char *argv[]) 134 { 135 Ram *r; 136 char *defmnt; 137 int p[2]; 138 int fd; 139 int stdio = 0; 140 char *service; 141 142 service = "ramfs"; 143 defmnt = "/tmp"; 144 ARGBEGIN{ 145 case 'D': 146 debug = 1; 147 break; 148 case 'i': 149 defmnt = 0; 150 stdio = 1; 151 mfd[0] = 0; 152 mfd[1] = 1; 153 break; 154 case 's': 155 defmnt = 0; 156 break; 157 case 'm': 158 defmnt = ARGF(); 159 break; 160 case 'p': 161 private++; 162 break; 163 case 'S': 164 defmnt = 0; 165 service = EARGF(usage()); 166 break; 167 default: 168 usage(); 169 }ARGEND 170 171 if(pipe(p) < 0) 172 error("pipe failed"); 173 if(!stdio){ 174 mfd[0] = p[0]; 175 mfd[1] = p[0]; 176 if(defmnt == 0){ 177 char buf[64]; 178 snprint(buf, sizeof buf, "#s/%s", service); 179 fd = create(buf, OWRITE, 0666); 180 if(fd < 0) 181 error("create failed"); 182 sprint(buf, "%d", p[1]); 183 if(write(fd, buf, strlen(buf)) < 0) 184 error("writing service file"); 185 } 186 } 187 188 user = getuser(); 189 notify(notifyf); 190 nram = 1; 191 r = &ram[0]; 192 r->busy = 1; 193 r->data = 0; 194 r->ndata = 0; 195 r->perm = DMDIR | 0775; 196 r->qid.type = QTDIR; 197 r->qid.path = 0LL; 198 r->qid.vers = 0; 199 r->parent = 0; 200 r->user = user; 201 r->group = user; 202 r->muid = user; 203 r->atime = time(0); 204 r->mtime = r->atime; 205 r->name = estrdup("."); 206 207 if(debug) 208 fmtinstall('F', fcallfmt); 209 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ 210 case -1: 211 error("fork"); 212 case 0: 213 close(p[1]); 214 io(); 215 break; 216 default: 217 close(p[0]); /* don't deadlock if child fails */ 218 if(defmnt && mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0) 219 error("mount failed"); 220 } 221 exits(0); 222 } 223 224 char* 225 rversion(Fid*) 226 { 227 Fid *f; 228 229 for(f = fids; f; f = f->next) 230 if(f->busy) 231 rclunk(f); 232 if(thdr.msize > sizeof mdata) 233 rhdr.msize = sizeof mdata; 234 else 235 rhdr.msize = thdr.msize; 236 messagesize = rhdr.msize; 237 if(strncmp(thdr.version, "9P2000", 6) != 0) 238 return Eversion; 239 rhdr.version = "9P2000"; 240 return 0; 241 } 242 243 char* 244 rauth(Fid*) 245 { 246 return "ramfs: no authentication required"; 247 } 248 249 char* 250 rflush(Fid *f) 251 { 252 USED(f); 253 return 0; 254 } 255 256 char* 257 rattach(Fid *f) 258 { 259 /* no authentication! */ 260 f->busy = 1; 261 f->rclose = 0; 262 f->ram = &ram[0]; 263 rhdr.qid = f->ram->qid; 264 if(thdr.uname[0]) 265 f->user = estrdup(thdr.uname); 266 else 267 f->user = "none"; 268 if(strcmp(user, "none") == 0) 269 user = f->user; 270 return 0; 271 } 272 273 char* 274 clone(Fid *f, Fid **nf) 275 { 276 if(f->open) 277 return Eisopen; 278 if(f->ram->busy == 0) 279 return Enotexist; 280 *nf = newfid(thdr.newfid); 281 (*nf)->busy = 1; 282 (*nf)->open = 0; 283 (*nf)->rclose = 0; 284 (*nf)->ram = f->ram; 285 (*nf)->user = f->user; /* no ref count; the leakage is minor */ 286 return 0; 287 } 288 289 char* 290 rwalk(Fid *f) 291 { 292 Ram *r, *fram; 293 char *name; 294 Ram *parent; 295 Fid *nf; 296 char *err; 297 ulong t; 298 int i; 299 300 err = nil; 301 nf = nil; 302 rhdr.nwqid = 0; 303 if(rhdr.newfid != rhdr.fid){ 304 err = clone(f, &nf); 305 if(err) 306 return err; 307 f = nf; /* walk the new fid */ 308 } 309 fram = f->ram; 310 if(thdr.nwname > 0){ 311 t = time(0); 312 for(i=0; i<thdr.nwname && i<MAXWELEM; i++){ 313 if((fram->qid.type & QTDIR) == 0){ 314 err = Enotdir; 315 break; 316 } 317 if(fram->busy == 0){ 318 err = Enotexist; 319 break; 320 } 321 fram->atime = t; 322 name = thdr.wname[i]; 323 if(strcmp(name, ".") == 0){ 324 Found: 325 rhdr.nwqid++; 326 rhdr.wqid[i] = fram->qid; 327 continue; 328 } 329 parent = &ram[fram->parent]; 330 if(!perm(f, parent, Pexec)){ 331 err = Eperm; 332 break; 333 } 334 if(strcmp(name, "..") == 0){ 335 fram = parent; 336 goto Found; 337 } 338 for(r=ram; r < &ram[nram]; r++) 339 if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){ 340 fram = r; 341 goto Found; 342 } 343 break; 344 } 345 if(i==0 && err == nil) 346 err = Enotexist; 347 } 348 if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){ 349 /* clunk the new fid, which is the one we walked */ 350 f->busy = 0; 351 f->ram = nil; 352 } 353 if(rhdr.nwqid == thdr.nwname) /* update the fid after a successful walk */ 354 f->ram = fram; 355 return err; 356 } 357 358 char * 359 ropen(Fid *f) 360 { 361 Ram *r; 362 int mode, trunc; 363 364 if(f->open) 365 return Eisopen; 366 r = f->ram; 367 if(r->busy == 0) 368 return Enotexist; 369 if(r->perm & DMEXCL) 370 if(r->open) 371 return Excl; 372 mode = thdr.mode; 373 if(r->qid.type & QTDIR){ 374 if(mode != OREAD) 375 return Eperm; 376 rhdr.qid = r->qid; 377 return 0; 378 } 379 if(mode & ORCLOSE){ 380 /* can't remove root; must be able to write parent */ 381 if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite)) 382 return Eperm; 383 f->rclose = 1; 384 } 385 trunc = mode & OTRUNC; 386 mode &= OPERM; 387 if(mode==OWRITE || mode==ORDWR || trunc) 388 if(!perm(f, r, Pwrite)) 389 return Eperm; 390 if(mode==OREAD || mode==ORDWR) 391 if(!perm(f, r, Pread)) 392 return Eperm; 393 if(mode==OEXEC) 394 if(!perm(f, r, Pexec)) 395 return Eperm; 396 if(trunc && (r->perm&DMAPPEND)==0){ 397 r->ndata = 0; 398 if(r->data) 399 free(r->data); 400 r->data = 0; 401 r->qid.vers++; 402 } 403 rhdr.qid = r->qid; 404 rhdr.iounit = messagesize-IOHDRSZ; 405 f->open = 1; 406 r->open++; 407 return 0; 408 } 409 410 char * 411 rcreate(Fid *f) 412 { 413 Ram *r; 414 char *name; 415 long parent, prm; 416 417 if(f->open) 418 return Eisopen; 419 if(f->ram->busy == 0) 420 return Enotexist; 421 parent = f->ram - ram; 422 if((f->ram->qid.type&QTDIR) == 0) 423 return Enotdir; 424 /* must be able to write parent */ 425 if(!perm(f, f->ram, Pwrite)) 426 return Eperm; 427 prm = thdr.perm; 428 name = thdr.name; 429 if(strcmp(name, ".")==0 || strcmp(name, "..")==0) 430 return Ename; 431 for(r=ram; r<&ram[nram]; r++) 432 if(r->busy && parent==r->parent) 433 if(strcmp((char*)name, r->name)==0) 434 return Einuse; 435 for(r=ram; r->busy; r++) 436 if(r == &ram[Nram-1]) 437 return "no free ram resources"; 438 r->busy = 1; 439 r->qid.path = ++path; 440 r->qid.vers = 0; 441 if(prm & DMDIR) 442 r->qid.type |= QTDIR; 443 r->parent = parent; 444 free(r->name); 445 r->name = estrdup(name); 446 r->user = f->user; 447 r->group = f->ram->group; 448 r->muid = f->ram->muid; 449 if(prm & DMDIR) 450 prm = (prm&~0777) | (f->ram->perm&prm&0777); 451 else 452 prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666); 453 r->perm = prm; 454 r->ndata = 0; 455 if(r-ram >= nram) 456 nram = r - ram + 1; 457 r->atime = time(0); 458 r->mtime = r->atime; 459 f->ram->mtime = r->atime; 460 f->ram = r; 461 rhdr.qid = r->qid; 462 rhdr.iounit = messagesize-IOHDRSZ; 463 f->open = 1; 464 if(thdr.mode & ORCLOSE) 465 f->rclose = 1; 466 r->open++; 467 return 0; 468 } 469 470 char* 471 rread(Fid *f) 472 { 473 Ram *r; 474 uchar *buf; 475 long off; 476 int n, m, cnt; 477 478 if(f->ram->busy == 0) 479 return Enotexist; 480 n = 0; 481 rhdr.count = 0; 482 off = thdr.offset; 483 buf = rdata; 484 cnt = thdr.count; 485 if(cnt > messagesize) /* shouldn't happen, anyway */ 486 cnt = messagesize; 487 if(f->ram->qid.type & QTDIR){ 488 for(r=ram+1; off > 0; r++){ 489 if(r->busy && r->parent==f->ram-ram) 490 off -= ramstat(r, statbuf, sizeof statbuf); 491 if(r == &ram[nram-1]) 492 return 0; 493 } 494 for(; r<&ram[nram] && n < cnt; r++){ 495 if(!r->busy || r->parent!=f->ram-ram) 496 continue; 497 m = ramstat(r, buf+n, cnt-n); 498 if(m == 0) 499 break; 500 n += m; 501 } 502 rhdr.data = (char*)rdata; 503 rhdr.count = n; 504 return 0; 505 } 506 r = f->ram; 507 if(off >= r->ndata) 508 return 0; 509 r->atime = time(0); 510 n = cnt; 511 if(off+n > r->ndata) 512 n = r->ndata - off; 513 rhdr.data = r->data+off; 514 rhdr.count = n; 515 return 0; 516 } 517 518 char* 519 rwrite(Fid *f) 520 { 521 Ram *r; 522 ulong off; 523 int cnt; 524 525 r = f->ram; 526 if(r->busy == 0) 527 return Enotexist; 528 off = thdr.offset; 529 if(r->perm & DMAPPEND) 530 off = r->ndata; 531 cnt = thdr.count; 532 if(r->qid.type & QTDIR) 533 return Eisdir; 534 if(off+cnt >= Maxsize) /* sanity check */ 535 return "write too big"; 536 if(off+cnt > r->ndata) 537 r->data = erealloc(r->data, off+cnt); 538 if(off > r->ndata) 539 memset(r->data+r->ndata, 0, off-r->ndata); 540 if(off+cnt > r->ndata) 541 r->ndata = off+cnt; 542 memmove(r->data+off, thdr.data, cnt); 543 r->qid.vers++; 544 r->mtime = time(0); 545 rhdr.count = cnt; 546 return 0; 547 } 548 549 void 550 realremove(Ram *r) 551 { 552 r->ndata = 0; 553 if(r->data) 554 free(r->data); 555 r->data = 0; 556 r->parent = 0; 557 memset(&r->qid, 0, sizeof r->qid); 558 free(r->name); 559 r->name = nil; 560 r->busy = 0; 561 } 562 563 char * 564 rclunk(Fid *f) 565 { 566 if(f->open) 567 f->ram->open--; 568 if(f->rclose) 569 realremove(f->ram); 570 f->busy = 0; 571 f->open = 0; 572 f->ram = 0; 573 return 0; 574 } 575 576 char * 577 rremove(Fid *f) 578 { 579 Ram *r; 580 581 if(f->open) 582 f->ram->open--; 583 f->busy = 0; 584 f->open = 0; 585 r = f->ram; 586 f->ram = 0; 587 if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite)) 588 return Eperm; 589 ram[r->parent].mtime = time(0); 590 realremove(r); 591 return 0; 592 } 593 594 char * 595 rstat(Fid *f) 596 { 597 if(f->ram->busy == 0) 598 return Enotexist; 599 rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf); 600 rhdr.stat = statbuf; 601 return 0; 602 } 603 604 char * 605 rwstat(Fid *f) 606 { 607 Ram *r, *s; 608 Dir dir; 609 610 if(f->ram->busy == 0) 611 return Enotexist; 612 convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf); 613 r = f->ram; 614 615 /* 616 * To change length, must have write permission on file. 617 */ 618 if(dir.length!=~0 && dir.length!=r->ndata){ 619 if(!perm(f, r, Pwrite)) 620 return Eperm; 621 } 622 623 /* 624 * To change name, must have write permission in parent 625 * and name must be unique. 626 */ 627 if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){ 628 if(!perm(f, &ram[r->parent], Pwrite)) 629 return Eperm; 630 for(s=ram; s<&ram[nram]; s++) 631 if(s->busy && s->parent==r->parent) 632 if(strcmp(dir.name, s->name)==0) 633 return Eexist; 634 } 635 636 /* 637 * To change mode, must be owner or group leader. 638 * Because of lack of users file, leader=>group itself. 639 */ 640 if(dir.mode!=~0 && r->perm!=dir.mode){ 641 if(strcmp(f->user, r->user) != 0) 642 if(strcmp(f->user, r->group) != 0) 643 return Enotowner; 644 } 645 646 /* 647 * To change group, must be owner and member of new group, 648 * or leader of current group and leader of new group. 649 * Second case cannot happen, but we check anyway. 650 */ 651 if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){ 652 if(strcmp(f->user, r->user) == 0) 653 if(strcmp(f->user, dir.gid) == 0) 654 goto ok; 655 if(strcmp(f->user, r->group) == 0) 656 if(strcmp(f->user, dir.gid) == 0) 657 goto ok; 658 return Enotowner; 659 ok:; 660 } 661 662 /* all ok; do it */ 663 if(dir.mode != ~0){ 664 dir.mode &= ~DMDIR; /* cannot change dir bit */ 665 dir.mode |= r->perm&DMDIR; 666 r->perm = dir.mode; 667 } 668 if(dir.name[0] != '\0'){ 669 free(r->name); 670 r->name = estrdup(dir.name); 671 } 672 if(dir.gid[0] != '\0') 673 r->group = estrdup(dir.gid); 674 if(dir.length!=~0 && dir.length!=r->ndata){ 675 r->data = erealloc(r->data, dir.length); 676 if(r->ndata < dir.length) 677 memset(r->data+r->ndata, 0, dir.length-r->ndata); 678 r->ndata = dir.length; 679 } 680 ram[r->parent].mtime = time(0); 681 return 0; 682 } 683 684 uint 685 ramstat(Ram *r, uchar *buf, uint nbuf) 686 { 687 int n; 688 Dir dir; 689 690 dir.name = r->name; 691 dir.qid = r->qid; 692 dir.mode = r->perm; 693 dir.length = r->ndata; 694 dir.uid = r->user; 695 dir.gid = r->group; 696 dir.muid = r->muid; 697 dir.atime = r->atime; 698 dir.mtime = r->mtime; 699 n = convD2M(&dir, buf, nbuf); 700 if(n > 2) 701 return n; 702 return 0; 703 } 704 705 Fid * 706 newfid(int fid) 707 { 708 Fid *f, *ff; 709 710 ff = 0; 711 for(f = fids; f; f = f->next) 712 if(f->fid == fid) 713 return f; 714 else if(!ff && !f->busy) 715 ff = f; 716 if(ff){ 717 ff->fid = fid; 718 return ff; 719 } 720 f = emalloc(sizeof *f); 721 f->ram = nil; 722 f->fid = fid; 723 f->next = fids; 724 fids = f; 725 return f; 726 } 727 728 void 729 io(void) 730 { 731 char *err, buf[20]; 732 int n, pid, ctl; 733 734 pid = getpid(); 735 if(private){ 736 snprint(buf, sizeof buf, "/proc/%d/ctl", pid); 737 ctl = open(buf, OWRITE); 738 if(ctl < 0){ 739 fprint(2, "can't protect ramfs\n"); 740 }else{ 741 fprint(ctl, "noswap\n"); 742 fprint(ctl, "private\n"); 743 close(ctl); 744 } 745 } 746 747 for(;;){ 748 /* 749 * reading from a pipe or a network device 750 * will give an error after a few eof reads. 751 * however, we cannot tell the difference 752 * between a zero-length read and an interrupt 753 * on the processes writing to us, 754 * so we wait for the error. 755 */ 756 n = read9pmsg(mfd[0], mdata, messagesize); 757 if(n < 0) 758 error("mount read"); 759 if(n == 0) 760 continue; 761 if(convM2S(mdata, n, &thdr) == 0) 762 continue; 763 764 if(debug) 765 fprint(2, "ramfs %d:<-%F\n", pid, &thdr); 766 767 if(!fcalls[thdr.type]) 768 err = "bad fcall type"; 769 else 770 err = (*fcalls[thdr.type])(newfid(thdr.fid)); 771 if(err){ 772 rhdr.type = Rerror; 773 rhdr.ename = err; 774 }else{ 775 rhdr.type = thdr.type + 1; 776 rhdr.fid = thdr.fid; 777 } 778 rhdr.tag = thdr.tag; 779 if(debug) 780 fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/ 781 n = convS2M(&rhdr, mdata, messagesize); 782 if(n == 0) 783 error("convS2M error on write"); 784 if(write(mfd[1], mdata, n) != n) 785 error("mount write"); 786 } 787 } 788 789 int 790 perm(Fid *f, Ram *r, int p) 791 { 792 if((p*Pother) & r->perm) 793 return 1; 794 if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm)) 795 return 1; 796 if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm)) 797 return 1; 798 return 0; 799 } 800 801 void 802 error(char *s) 803 { 804 fprint(2, "%s: %s: %r\n", argv0, s); 805 exits(s); 806 } 807 808 void * 809 emalloc(ulong n) 810 { 811 void *p; 812 813 p = malloc(n); 814 if(!p) 815 error("out of memory"); 816 memset(p, 0, n); 817 return p; 818 } 819 820 void * 821 erealloc(void *p, ulong n) 822 { 823 p = realloc(p, n); 824 if(!p) 825 error("out of memory"); 826 return p; 827 } 828 829 char * 830 estrdup(char *q) 831 { 832 char *p; 833 int n; 834 835 n = strlen(q)+1; 836 p = malloc(n); 837 if(!p) 838 error("out of memory"); 839 memmove(p, q, n); 840 return p; 841 } 842 843 void 844 usage(void) 845 { 846 fprint(2, "usage: %s [-is] [-m mountpoint]\n", argv0); 847 exits("usage"); 848 } 849