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