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