plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

ramfs.c (15918B)


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