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