Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}