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