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