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
notifyf(void * a,char * s)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
ramfsmain(int argc,char * argv[])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*
rversion(Fid *)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*
rauth(Fid *)236 rauth(Fid*)
237 {
238 return "ramfs: no authentication required";
239 }
240
241 char*
rflush(Fid * f)242 rflush(Fid *f)
243 {
244 USED(f);
245 return 0;
246 }
247
248 char*
rattach(Fid * f)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*
clone(Fid * f,Fid ** nf)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*
rwalk(Fid * f)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 *
ropen(Fid * f)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 *
rcreate(Fid * f)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*
rread(Fid * f)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*
rwrite(Fid * f)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
realremove(Ram * r)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 *
rclunk(Fid * f)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 *
rremove(Fid * f)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 *
rstat(Fid * f)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 *
rwstat(Fid * f)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
ramstat(Ram * r,uchar * buf,uint nbuf)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 *
newfid(int 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
io(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
perm(Fid * f,Ram * r,int p)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 *
emalloc(ulong n)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 *
erealloc(void * p,ulong n)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 *
estrdup(char * q)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
ramfsusage(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*
taballoc(void)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*
xstrdup(char * s)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
hash(char * s)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*
atom(char * str)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