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