xref: /plan9/sys/lib/dist/cmd/bzfs/oramfs.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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