xref: /plan9/sys/src/cmd/ramfs.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
3*219b2ee8SDavid du Colombier #include <auth.h>
43e12c5d1SDavid du Colombier #include <fcall.h>
53e12c5d1SDavid du Colombier 
63e12c5d1SDavid du Colombier /*
73e12c5d1SDavid du Colombier  * Rather than reading /adm/users, which is a lot of work for
83e12c5d1SDavid du Colombier  * a toy program, we assume all groups have the form
93e12c5d1SDavid du Colombier  *	NNN:user:user:
103e12c5d1SDavid du Colombier  * meaning that each user is the leader of his own group.
113e12c5d1SDavid du Colombier  */
123e12c5d1SDavid du Colombier 
133e12c5d1SDavid du Colombier enum
143e12c5d1SDavid du Colombier {
153e12c5d1SDavid du Colombier 	OPERM	= 0x3,		/* mask of all permission types in open mode */
163e12c5d1SDavid du Colombier 	Nram	= 512,
173e12c5d1SDavid du Colombier };
183e12c5d1SDavid du Colombier 
193e12c5d1SDavid du Colombier typedef struct Fid Fid;
203e12c5d1SDavid du Colombier typedef struct Ram Ram;
213e12c5d1SDavid du Colombier 
223e12c5d1SDavid du Colombier struct Fid
233e12c5d1SDavid du Colombier {
243e12c5d1SDavid du Colombier 	short	busy;
253e12c5d1SDavid du Colombier 	short	open;
263e12c5d1SDavid du Colombier 	short	rclose;
273e12c5d1SDavid du Colombier 	int	fid;
283e12c5d1SDavid du Colombier 	Fid	*next;
293e12c5d1SDavid du Colombier 	char	*user;
303e12c5d1SDavid du Colombier 	Ram	*ram;
313e12c5d1SDavid du Colombier };
323e12c5d1SDavid du Colombier 
333e12c5d1SDavid du Colombier struct Ram
343e12c5d1SDavid du Colombier {
353e12c5d1SDavid du Colombier 	short	busy;
363e12c5d1SDavid du Colombier 	short	open;
373e12c5d1SDavid du Colombier 	long	parent;		/* index in Ram array */
383e12c5d1SDavid du Colombier 	Qid	qid;
393e12c5d1SDavid du Colombier 	long	perm;
403e12c5d1SDavid du Colombier 	char	name[NAMELEN];
413e12c5d1SDavid du Colombier 	ulong	atime;
423e12c5d1SDavid du Colombier 	ulong	mtime;
433e12c5d1SDavid du Colombier 	char	*user;
443e12c5d1SDavid du Colombier 	char	*group;
453e12c5d1SDavid du Colombier 	char	*data;
463e12c5d1SDavid du Colombier 	long	ndata;
473e12c5d1SDavid du Colombier };
483e12c5d1SDavid du Colombier 
493e12c5d1SDavid du Colombier enum
503e12c5d1SDavid du Colombier {
513e12c5d1SDavid du Colombier 	Pexec =		1,
523e12c5d1SDavid du Colombier 	Pwrite = 	2,
533e12c5d1SDavid du Colombier 	Pread = 	4,
543e12c5d1SDavid du Colombier 	Pother = 	1,
553e12c5d1SDavid du Colombier 	Pgroup = 	8,
563e12c5d1SDavid du Colombier 	Powner =	64,
573e12c5d1SDavid du Colombier };
583e12c5d1SDavid du Colombier 
593e12c5d1SDavid du Colombier ulong	path;		/* incremented for each new file */
603e12c5d1SDavid du Colombier Fid	*fids;
613e12c5d1SDavid du Colombier Ram	ram[Nram];
623e12c5d1SDavid du Colombier int	nram;
633e12c5d1SDavid du Colombier int	mfd[2];
643e12c5d1SDavid du Colombier char	user[NAMELEN];
653e12c5d1SDavid du Colombier char	mdata[MAXMSG+MAXFDATA];
663e12c5d1SDavid du Colombier Fcall	rhdr;
673e12c5d1SDavid du Colombier Fcall	thdr;
683e12c5d1SDavid du Colombier 
693e12c5d1SDavid du Colombier Fid *	newfid(int);
703e12c5d1SDavid du Colombier void	ramstat(Ram*, char*);
713e12c5d1SDavid du Colombier void	error(char*);
723e12c5d1SDavid du Colombier void	io(void);
733e12c5d1SDavid du Colombier void	*erealloc(void*, ulong);
743e12c5d1SDavid du Colombier void	*emalloc(ulong);
753e12c5d1SDavid du Colombier void	usage(void);
763e12c5d1SDavid du Colombier int	perm(Fid*, Ram*, int);
773e12c5d1SDavid du Colombier 
783e12c5d1SDavid du Colombier char	*rflush(Fid*), *rnop(Fid*), *rsession(Fid*),
793e12c5d1SDavid du Colombier 	*rattach(Fid*), *rclone(Fid*), *rwalk(Fid*),
803e12c5d1SDavid du Colombier 	*rclwalk(Fid*), *ropen(Fid*), *rcreate(Fid*),
813e12c5d1SDavid du Colombier 	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
82*219b2ee8SDavid du Colombier 	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
833e12c5d1SDavid du Colombier 
843e12c5d1SDavid du Colombier char 	*(*fcalls[])(Fid*) = {
853e12c5d1SDavid du Colombier 	[Tflush]	rflush,
863e12c5d1SDavid du Colombier 	[Tsession]	rsession,
873e12c5d1SDavid du Colombier 	[Tnop]		rnop,
883e12c5d1SDavid du Colombier 	[Tattach]	rattach,
893e12c5d1SDavid du Colombier 	[Tclone]	rclone,
903e12c5d1SDavid du Colombier 	[Twalk]		rwalk,
913e12c5d1SDavid du Colombier 	[Tclwalk]	rclwalk,
923e12c5d1SDavid du Colombier 	[Topen]		ropen,
933e12c5d1SDavid du Colombier 	[Tcreate]	rcreate,
943e12c5d1SDavid du Colombier 	[Tread]		rread,
953e12c5d1SDavid du Colombier 	[Twrite]	rwrite,
963e12c5d1SDavid du Colombier 	[Tclunk]	rclunk,
973e12c5d1SDavid du Colombier 	[Tremove]	rremove,
983e12c5d1SDavid du Colombier 	[Tstat]		rstat,
993e12c5d1SDavid du Colombier 	[Twstat]	rwstat,
1003e12c5d1SDavid du Colombier };
1013e12c5d1SDavid du Colombier 
1023e12c5d1SDavid du Colombier char	Eperm[] =	"permission denied";
1033e12c5d1SDavid du Colombier char	Enotdir[] =	"not a directory";
1043e12c5d1SDavid du Colombier char	Enoauth[] =	"no authentication in ramfs";
1053e12c5d1SDavid du Colombier char	Enotexist[] =	"file does not exist";
1063e12c5d1SDavid du Colombier char	Einuse[] =	"file in use";
1073e12c5d1SDavid du Colombier char	Eexist[] =	"file exists";
1083e12c5d1SDavid du Colombier char	Enotowner[] =	"not owner";
1093e12c5d1SDavid du Colombier char	Eisopen[] = 	"file already open for I/O";
1103e12c5d1SDavid du Colombier char	Excl[] = 	"exclusive use file already open";
1113e12c5d1SDavid du Colombier char	Ename[] = 	"illegal name";
1123e12c5d1SDavid du Colombier 
113*219b2ee8SDavid du Colombier int debug;
114*219b2ee8SDavid du Colombier 
1153e12c5d1SDavid du Colombier void
1163e12c5d1SDavid du Colombier notifyf(void *a, char *s)
1173e12c5d1SDavid du Colombier {
1183e12c5d1SDavid du Colombier 	USED(a);
1193e12c5d1SDavid du Colombier 	if(strncmp(s, "interrupt", 9) == 0)
1203e12c5d1SDavid du Colombier 		noted(NCONT);
1213e12c5d1SDavid du Colombier 	noted(NDFLT);
1223e12c5d1SDavid du Colombier }
1233e12c5d1SDavid du Colombier 
1243e12c5d1SDavid du Colombier void
1253e12c5d1SDavid du Colombier main(int argc, char *argv[])
1263e12c5d1SDavid du Colombier {
1273e12c5d1SDavid du Colombier 	Ram *r;
1283e12c5d1SDavid du Colombier 	char *defmnt;
1293e12c5d1SDavid du Colombier 	int p[2];
1303e12c5d1SDavid du Colombier 	char buf[12];
1313e12c5d1SDavid du Colombier 	int fd;
1323e12c5d1SDavid du Colombier 	int stdio = 0;
1333e12c5d1SDavid du Colombier 
1343e12c5d1SDavid du Colombier 	defmnt = "/tmp";
1353e12c5d1SDavid du Colombier 	ARGBEGIN{
136*219b2ee8SDavid du Colombier 	case 'd':
137*219b2ee8SDavid du Colombier 		debug = 1;
138*219b2ee8SDavid du Colombier 		break;
1393e12c5d1SDavid du Colombier 	case 'i':
1403e12c5d1SDavid du Colombier 		defmnt = 0;
1413e12c5d1SDavid du Colombier 		stdio = 1;
1423e12c5d1SDavid du Colombier 		mfd[0] = 0;
1433e12c5d1SDavid du Colombier 		mfd[1] = 1;
1443e12c5d1SDavid du Colombier 		break;
1453e12c5d1SDavid du Colombier 	case 's':
1463e12c5d1SDavid du Colombier 		defmnt = 0;
1473e12c5d1SDavid du Colombier 		break;
1483e12c5d1SDavid du Colombier 	case 'm':
1493e12c5d1SDavid du Colombier 		defmnt = ARGF();
1503e12c5d1SDavid du Colombier 		break;
1513e12c5d1SDavid du Colombier 	default:
1523e12c5d1SDavid du Colombier 		usage();
1533e12c5d1SDavid du Colombier 	}ARGEND
1543e12c5d1SDavid du Colombier 
1553e12c5d1SDavid du Colombier 	if(pipe(p) < 0)
1563e12c5d1SDavid du Colombier 		error("pipe failed");
1573e12c5d1SDavid du Colombier 	if(!stdio){
1583e12c5d1SDavid du Colombier 		mfd[0] = p[0];
1593e12c5d1SDavid du Colombier 		mfd[1] = p[0];
1603e12c5d1SDavid du Colombier 		if(defmnt == 0){
1613e12c5d1SDavid du Colombier 			fd = create("#s/ramfs", OWRITE, 0666);
1623e12c5d1SDavid du Colombier 			if(fd < 0)
1633e12c5d1SDavid du Colombier 				error("create of /srv/ramfs failed");
1643e12c5d1SDavid du Colombier 			sprint(buf, "%d", p[1]);
1653e12c5d1SDavid du Colombier 			if(write(fd, buf, strlen(buf)) < 0)
1663e12c5d1SDavid du Colombier 				error("writing /srv/ramfs");
1673e12c5d1SDavid du Colombier 		}
1683e12c5d1SDavid du Colombier 	}
1693e12c5d1SDavid du Colombier 
1703e12c5d1SDavid du Colombier 	notify(notifyf);
1713e12c5d1SDavid du Colombier 	nram = 1;
1723e12c5d1SDavid du Colombier 	r = &ram[0];
1733e12c5d1SDavid du Colombier 	r->busy = 1;
1743e12c5d1SDavid du Colombier 	r->data = 0;
1753e12c5d1SDavid du Colombier 	r->ndata = 0;
1763e12c5d1SDavid du Colombier 	r->perm = CHDIR | 0775;
1773e12c5d1SDavid du Colombier 	r->qid.path = CHDIR;
1783e12c5d1SDavid du Colombier 	r->qid.vers = 0;
1793e12c5d1SDavid du Colombier 	r->parent = 0;
1803e12c5d1SDavid du Colombier 	r->user = user;
1813e12c5d1SDavid du Colombier 	r->group = user;
1823e12c5d1SDavid du Colombier 	r->atime = time(0);
1833e12c5d1SDavid du Colombier 	r->mtime = r->atime;
1843e12c5d1SDavid du Colombier 	strcpy(r->name, ".");
1853e12c5d1SDavid du Colombier 	strcpy(user, getuser());
1863e12c5d1SDavid du Colombier 
187*219b2ee8SDavid du Colombier 	if(debug)
188*219b2ee8SDavid du Colombier 		fmtinstall('F', fcallconv);
1893e12c5d1SDavid du Colombier 	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
1903e12c5d1SDavid du Colombier 	case -1:
1913e12c5d1SDavid du Colombier 		error("fork");
1923e12c5d1SDavid du Colombier 	case 0:
1933e12c5d1SDavid du Colombier 		close(p[1]);
1943e12c5d1SDavid du Colombier 		io();
1953e12c5d1SDavid du Colombier 		break;
1963e12c5d1SDavid du Colombier 	default:
1973e12c5d1SDavid du Colombier 		close(p[0]);	/* don't deadlock if child fails */
198*219b2ee8SDavid du Colombier 		if(defmnt && mount(p[1], defmnt, MREPL|MCREATE, "") < 0)
1993e12c5d1SDavid du Colombier 			error("mount failed");
2003e12c5d1SDavid du Colombier 	}
2013e12c5d1SDavid du Colombier 	exits(0);
2023e12c5d1SDavid du Colombier }
2033e12c5d1SDavid du Colombier 
2043e12c5d1SDavid du Colombier char*
2053e12c5d1SDavid du Colombier rnop(Fid *f)
2063e12c5d1SDavid du Colombier {
2073e12c5d1SDavid du Colombier 	USED(f);
2083e12c5d1SDavid du Colombier 	return 0;
2093e12c5d1SDavid du Colombier }
2103e12c5d1SDavid du Colombier 
2113e12c5d1SDavid du Colombier char*
2123e12c5d1SDavid du Colombier rsession(Fid *unused)
2133e12c5d1SDavid du Colombier {
2143e12c5d1SDavid du Colombier 	Fid *f;
2153e12c5d1SDavid du Colombier 
2163e12c5d1SDavid du Colombier 	USED(unused);
2173e12c5d1SDavid du Colombier 
2183e12c5d1SDavid du Colombier 	for(f = fids; f; f = f->next)
2193e12c5d1SDavid du Colombier 		if(f->busy)
2203e12c5d1SDavid du Colombier 			rclunk(f);
221*219b2ee8SDavid du Colombier 	memset(thdr.authid, 0, sizeof(thdr.authid));
222*219b2ee8SDavid du Colombier 	memset(thdr.authdom, 0, sizeof(thdr.authdom));
223*219b2ee8SDavid du Colombier 	memset(thdr.chal, 0, sizeof(thdr.chal));
2243e12c5d1SDavid du Colombier 	return 0;
2253e12c5d1SDavid du Colombier }
2263e12c5d1SDavid du Colombier 
2273e12c5d1SDavid du Colombier char*
2283e12c5d1SDavid du Colombier rflush(Fid *f)
2293e12c5d1SDavid du Colombier {
2303e12c5d1SDavid du Colombier 	USED(f);
2313e12c5d1SDavid du Colombier 	return 0;
2323e12c5d1SDavid du Colombier }
2333e12c5d1SDavid du Colombier 
2343e12c5d1SDavid du Colombier char*
2353e12c5d1SDavid du Colombier rattach(Fid *f)
2363e12c5d1SDavid du Colombier {
2373e12c5d1SDavid du Colombier 	/* no authentication! */
2383e12c5d1SDavid du Colombier 	f->busy = 1;
2393e12c5d1SDavid du Colombier 	f->rclose = 0;
2403e12c5d1SDavid du Colombier 	f->ram = &ram[0];
2413e12c5d1SDavid du Colombier 	thdr.qid = f->ram->qid;
2423e12c5d1SDavid du Colombier 	if(rhdr.uname[0])
2433e12c5d1SDavid du Colombier 		f->user = strdup(rhdr.uname);
2443e12c5d1SDavid du Colombier 	else
2453e12c5d1SDavid du Colombier 		f->user = "none";
2463e12c5d1SDavid du Colombier 	return 0;
2473e12c5d1SDavid du Colombier }
2483e12c5d1SDavid du Colombier 
2493e12c5d1SDavid du Colombier char*
2503e12c5d1SDavid du Colombier rclone(Fid *f)
2513e12c5d1SDavid du Colombier {
2523e12c5d1SDavid du Colombier 	Fid *nf;
2533e12c5d1SDavid du Colombier 
2543e12c5d1SDavid du Colombier 	if(f->open)
2553e12c5d1SDavid du Colombier 		return Eisopen;
2563e12c5d1SDavid du Colombier 	if(f->ram->busy == 0)
2573e12c5d1SDavid du Colombier 		return Enotexist;
2583e12c5d1SDavid du Colombier 	nf = newfid(rhdr.newfid);
2593e12c5d1SDavid du Colombier 	nf->busy = 1;
2603e12c5d1SDavid du Colombier 	nf->open = 0;
2613e12c5d1SDavid du Colombier 	nf->rclose = 0;
2623e12c5d1SDavid du Colombier 	nf->ram = f->ram;
2633e12c5d1SDavid du Colombier 	nf->user = f->user;	/* no ref count; the leakage is minor */
2643e12c5d1SDavid du Colombier 	return 0;
2653e12c5d1SDavid du Colombier }
2663e12c5d1SDavid du Colombier 
2673e12c5d1SDavid du Colombier char*
2683e12c5d1SDavid du Colombier rwalk(Fid *f)
2693e12c5d1SDavid du Colombier {
2703e12c5d1SDavid du Colombier 	Ram *r;
2713e12c5d1SDavid du Colombier 	char *name;
2723e12c5d1SDavid du Colombier 	Ram *parent;
2733e12c5d1SDavid du Colombier 
2743e12c5d1SDavid du Colombier 	if((f->ram->qid.path & CHDIR) == 0)
2753e12c5d1SDavid du Colombier 		return Enotdir;
2763e12c5d1SDavid du Colombier 	if(f->ram->busy == 0)
2773e12c5d1SDavid du Colombier 		return Enotexist;
2783e12c5d1SDavid du Colombier 	f->ram->atime = time(0);
2793e12c5d1SDavid du Colombier 	name = rhdr.name;
2803e12c5d1SDavid du Colombier 	if(strcmp(name, ".") == 0){
2813e12c5d1SDavid du Colombier 		thdr.qid = f->ram->qid;
2823e12c5d1SDavid du Colombier 		return 0;
2833e12c5d1SDavid du Colombier 	}
2843e12c5d1SDavid du Colombier 	parent = &ram[f->ram->parent];
2853e12c5d1SDavid du Colombier 	if(!perm(f, parent, Pexec))
2863e12c5d1SDavid du Colombier 		return Eperm;
2873e12c5d1SDavid du Colombier 	if(strcmp(name, "..") == 0){
2883e12c5d1SDavid du Colombier 		f->ram = parent;
2893e12c5d1SDavid du Colombier 		thdr.qid = f->ram->qid;
2903e12c5d1SDavid du Colombier 		return 0;
2913e12c5d1SDavid du Colombier 	}
2923e12c5d1SDavid du Colombier 	for(r=ram; r < &ram[nram]; r++)
2933e12c5d1SDavid du Colombier 		if(r->busy && r->parent==f->ram-ram && strcmp(name, r->name)==0){
2943e12c5d1SDavid du Colombier 			thdr.qid = r->qid;
2953e12c5d1SDavid du Colombier 			f->ram = r;
2963e12c5d1SDavid du Colombier 			return 0;
2973e12c5d1SDavid du Colombier 		}
2983e12c5d1SDavid du Colombier 	return Enotexist;
2993e12c5d1SDavid du Colombier }
3003e12c5d1SDavid du Colombier 
3013e12c5d1SDavid du Colombier char *
3023e12c5d1SDavid du Colombier rclwalk(Fid *f)
3033e12c5d1SDavid du Colombier {
3043e12c5d1SDavid du Colombier 	Fid *nf;
3053e12c5d1SDavid du Colombier 	char *err;
3063e12c5d1SDavid du Colombier 
3073e12c5d1SDavid du Colombier 	nf = newfid(rhdr.newfid);
3083e12c5d1SDavid du Colombier 	nf->busy = 1;
3093e12c5d1SDavid du Colombier 	nf->rclose = 0;
3103e12c5d1SDavid du Colombier 	nf->ram = f->ram;
3113e12c5d1SDavid du Colombier 	nf->user = f->user;
3123e12c5d1SDavid du Colombier 	if(err = rwalk(nf))
3133e12c5d1SDavid du Colombier 		rclunk(nf);
3143e12c5d1SDavid du Colombier 	return err;
3153e12c5d1SDavid du Colombier }
3163e12c5d1SDavid du Colombier 
3173e12c5d1SDavid du Colombier char *
3183e12c5d1SDavid du Colombier ropen(Fid *f)
3193e12c5d1SDavid du Colombier {
3203e12c5d1SDavid du Colombier 	Ram *r;
3213e12c5d1SDavid du Colombier 	int mode, trunc;
3223e12c5d1SDavid du Colombier 
3233e12c5d1SDavid du Colombier 	if(f->open)
3243e12c5d1SDavid du Colombier 		return Eisopen;
3253e12c5d1SDavid du Colombier 	r = f->ram;
3263e12c5d1SDavid du Colombier 	if(r->busy == 0)
3273e12c5d1SDavid du Colombier 		return Enotexist;
3283e12c5d1SDavid du Colombier 	if(r->perm & CHEXCL)
3293e12c5d1SDavid du Colombier 		if(r->open)
3303e12c5d1SDavid du Colombier 			return Excl;
3313e12c5d1SDavid du Colombier 	mode = rhdr.mode;
3323e12c5d1SDavid du Colombier 	if(r->qid.path & CHDIR){
3333e12c5d1SDavid du Colombier 		if(mode != OREAD)
3343e12c5d1SDavid du Colombier 			return Eperm;
3353e12c5d1SDavid du Colombier 		thdr.qid = r->qid;
3363e12c5d1SDavid du Colombier 		return 0;
3373e12c5d1SDavid du Colombier 	}
3383e12c5d1SDavid du Colombier 	if(mode & ORCLOSE){
3393e12c5d1SDavid du Colombier 		/* must be able to write parent */
3403e12c5d1SDavid du Colombier 		if(!perm(f, &ram[r->parent], Pwrite))
3413e12c5d1SDavid du Colombier 			return Eperm;
3423e12c5d1SDavid du Colombier 		f->rclose = 1;
3433e12c5d1SDavid du Colombier 	}
3443e12c5d1SDavid du Colombier 	trunc = mode & OTRUNC;
3453e12c5d1SDavid du Colombier 	mode &= OPERM;
3463e12c5d1SDavid du Colombier 	if(mode==OWRITE || mode==ORDWR || trunc)
3473e12c5d1SDavid du Colombier 		if(!perm(f, r, Pwrite))
3483e12c5d1SDavid du Colombier 			return Eperm;
3493e12c5d1SDavid du Colombier 	if(mode==OREAD || mode==ORDWR)
3503e12c5d1SDavid du Colombier 		if(!perm(f, r, Pread))
3513e12c5d1SDavid du Colombier 			return Eperm;
3523e12c5d1SDavid du Colombier 	if(mode==OEXEC)
3533e12c5d1SDavid du Colombier 		if(!perm(f, r, Pexec))
3543e12c5d1SDavid du Colombier 			return Eperm;
3553e12c5d1SDavid du Colombier 	if(trunc && (r->perm&CHAPPEND)==0){
3563e12c5d1SDavid du Colombier 		r->ndata = 0;
3573e12c5d1SDavid du Colombier 		if(r->data)
3583e12c5d1SDavid du Colombier 			free(r->data);
3593e12c5d1SDavid du Colombier 		r->data = 0;
3603e12c5d1SDavid du Colombier 		r->qid.vers++;
3613e12c5d1SDavid du Colombier 	}
3623e12c5d1SDavid du Colombier 	thdr.qid = r->qid;
3633e12c5d1SDavid du Colombier 	f->open = 1;
3643e12c5d1SDavid du Colombier 	r->open++;
3653e12c5d1SDavid du Colombier 	return 0;
3663e12c5d1SDavid du Colombier }
3673e12c5d1SDavid du Colombier 
3683e12c5d1SDavid du Colombier char *
3693e12c5d1SDavid du Colombier rcreate(Fid *f)
3703e12c5d1SDavid du Colombier {
3713e12c5d1SDavid du Colombier 	Ram *r;
3723e12c5d1SDavid du Colombier 	char *name;
3733e12c5d1SDavid du Colombier 	long parent, prm;
3743e12c5d1SDavid du Colombier 
3753e12c5d1SDavid du Colombier 	if(f->open)
3763e12c5d1SDavid du Colombier 		return Eisopen;
3773e12c5d1SDavid du Colombier 	if(f->ram->busy == 0)
3783e12c5d1SDavid du Colombier 		return Enotexist;
3793e12c5d1SDavid du Colombier 	parent = f->ram - ram;
3803e12c5d1SDavid du Colombier 	if((f->ram->qid.path&CHDIR) == 0)
3813e12c5d1SDavid du Colombier 		return Enotdir;
3823e12c5d1SDavid du Colombier 	/* must be able to write parent */
3833e12c5d1SDavid du Colombier 	if(!perm(f, f->ram, Pwrite))
3843e12c5d1SDavid du Colombier 		return Eperm;
3853e12c5d1SDavid du Colombier 	prm = rhdr.perm;
3863e12c5d1SDavid du Colombier 	name = rhdr.name;
3873e12c5d1SDavid du Colombier 	if(strcmp(name, ".")==0 || strcmp(name, "..")==0)
3883e12c5d1SDavid du Colombier 		return Ename;
3893e12c5d1SDavid du Colombier 	for(r=ram; r<&ram[nram]; r++)
3903e12c5d1SDavid du Colombier 		if(r->busy && parent==r->parent)
3913e12c5d1SDavid du Colombier 		if(strcmp((char*)name, r->name)==0)
3923e12c5d1SDavid du Colombier 			return Einuse;
3933e12c5d1SDavid du Colombier 	for(r=ram; r->busy; r++)
3943e12c5d1SDavid du Colombier 		if(r == &ram[Nram-1])
3953e12c5d1SDavid du Colombier 			return "no free ram resources";
3963e12c5d1SDavid du Colombier 	r->busy = 1;
3973e12c5d1SDavid du Colombier 	r->qid.path = ++path;
3983e12c5d1SDavid du Colombier 	r->qid.vers = 0;
3993e12c5d1SDavid du Colombier 	if(prm & CHDIR)
4003e12c5d1SDavid du Colombier 		r->qid.path |= CHDIR;
4013e12c5d1SDavid du Colombier 	r->parent = parent;
4023e12c5d1SDavid du Colombier 	strcpy(r->name, (char*)name);
4033e12c5d1SDavid du Colombier 	r->user = f->user;
4043e12c5d1SDavid du Colombier 	r->group = f->ram->group;
4053e12c5d1SDavid du Colombier 	if(prm & CHDIR)
4063e12c5d1SDavid du Colombier 		prm = (prm&~0777) | (f->ram->perm&prm&0777);
4073e12c5d1SDavid du Colombier 	else
4083e12c5d1SDavid du Colombier 		prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666);
4093e12c5d1SDavid du Colombier 	r->perm = prm;
4103e12c5d1SDavid du Colombier 	r->ndata = 0;
4113e12c5d1SDavid du Colombier 	if(r-ram >= nram)
4123e12c5d1SDavid du Colombier 		nram = r - ram + 1;
4133e12c5d1SDavid du Colombier 	r->atime = time(0);
4143e12c5d1SDavid du Colombier 	r->mtime = r->atime;
4153e12c5d1SDavid du Colombier 	f->ram->mtime = r->atime;
4163e12c5d1SDavid du Colombier 	f->ram = r;
4173e12c5d1SDavid du Colombier 	thdr.qid = r->qid;
4183e12c5d1SDavid du Colombier 	f->open = 1;
4193e12c5d1SDavid du Colombier 	r->open++;
4203e12c5d1SDavid du Colombier 	return 0;
4213e12c5d1SDavid du Colombier }
4223e12c5d1SDavid du Colombier 
4233e12c5d1SDavid du Colombier char*
4243e12c5d1SDavid du Colombier rread(Fid *f)
4253e12c5d1SDavid du Colombier {
4263e12c5d1SDavid du Colombier 	Ram *r;
4273e12c5d1SDavid du Colombier 	char *buf;
4283e12c5d1SDavid du Colombier 	long off;
4293e12c5d1SDavid du Colombier 	int n, cnt;
4303e12c5d1SDavid du Colombier 
4313e12c5d1SDavid du Colombier 	if(f->ram->busy == 0)
4323e12c5d1SDavid du Colombier 		return Enotexist;
4333e12c5d1SDavid du Colombier 	n = 0;
4343e12c5d1SDavid du Colombier 	thdr.count = 0;
4353e12c5d1SDavid du Colombier 	off = rhdr.offset;
4363e12c5d1SDavid du Colombier 	buf = thdr.data;
437*219b2ee8SDavid du Colombier 	cnt = rhdr.count;
4383e12c5d1SDavid du Colombier 	if(f->ram->qid.path & CHDIR){
439*219b2ee8SDavid du Colombier 		cnt = (rhdr.count/DIRLEN)*DIRLEN;
440*219b2ee8SDavid du Colombier 		if(off%DIRLEN)
4413e12c5d1SDavid du Colombier 			return "i/o error";
4423e12c5d1SDavid du Colombier 		for(r=ram+1; off; r++){
4433e12c5d1SDavid du Colombier 			if(r->busy && r->parent==f->ram-ram)
4443e12c5d1SDavid du Colombier 				off -= DIRLEN;
4453e12c5d1SDavid du Colombier 			if(r == &ram[nram-1])
4463e12c5d1SDavid du Colombier 				return 0;
4473e12c5d1SDavid du Colombier 		}
4483e12c5d1SDavid du Colombier 		for(; r<&ram[nram] && n < cnt; r++){
4493e12c5d1SDavid du Colombier 			if(!r->busy || r->parent!=f->ram-ram)
4503e12c5d1SDavid du Colombier 				continue;
4513e12c5d1SDavid du Colombier 			ramstat(r, buf+n);
4523e12c5d1SDavid du Colombier 			n += DIRLEN;
4533e12c5d1SDavid du Colombier 		}
4543e12c5d1SDavid du Colombier 		thdr.count = n;
4553e12c5d1SDavid du Colombier 		return 0;
4563e12c5d1SDavid du Colombier 	}
4573e12c5d1SDavid du Colombier 	r = f->ram;
4583e12c5d1SDavid du Colombier 	if(off >= r->ndata)
4593e12c5d1SDavid du Colombier 		return 0;
4603e12c5d1SDavid du Colombier 	r->atime = time(0);
4613e12c5d1SDavid du Colombier 	n = cnt;
4623e12c5d1SDavid du Colombier 	if(off+n > r->ndata)
4633e12c5d1SDavid du Colombier 		n = r->ndata - off;
4643e12c5d1SDavid du Colombier 	thdr.data = r->data+off;
4653e12c5d1SDavid du Colombier 	thdr.count = n;
4663e12c5d1SDavid du Colombier 	return 0;
4673e12c5d1SDavid du Colombier }
4683e12c5d1SDavid du Colombier 
4693e12c5d1SDavid du Colombier char*
4703e12c5d1SDavid du Colombier rwrite(Fid *f)
4713e12c5d1SDavid du Colombier {
4723e12c5d1SDavid du Colombier 	Ram *r;
4733e12c5d1SDavid du Colombier 	ulong off;
4743e12c5d1SDavid du Colombier 	int cnt;
4753e12c5d1SDavid du Colombier 
4763e12c5d1SDavid du Colombier 	r = f->ram;
4773e12c5d1SDavid du Colombier 	if(r->busy == 0)
4783e12c5d1SDavid du Colombier 		return Enotexist;
4793e12c5d1SDavid du Colombier 	off = rhdr.offset;
4803e12c5d1SDavid du Colombier 	if(r->perm & CHAPPEND)
4813e12c5d1SDavid du Colombier 		off = r->ndata;
4823e12c5d1SDavid du Colombier 	cnt = rhdr.count;
4833e12c5d1SDavid du Colombier 	if(r->qid.path & CHDIR)
4843e12c5d1SDavid du Colombier 		return "file is a directory";
4853e12c5d1SDavid du Colombier 	if(off > 100*1024*1024)		/* sanity check */
4863e12c5d1SDavid du Colombier 		return "write too big";
4873e12c5d1SDavid du Colombier 	if(off+cnt > r->ndata)
4883e12c5d1SDavid du Colombier 		r->data = erealloc(r->data, off+cnt);
4893e12c5d1SDavid du Colombier 	if(off > r->ndata)
4903e12c5d1SDavid du Colombier 		memset(r->data+r->ndata, 0, off-r->ndata);
4913e12c5d1SDavid du Colombier 	if(off+cnt > r->ndata)
4923e12c5d1SDavid du Colombier 		r->ndata = off+cnt;
4933e12c5d1SDavid du Colombier 	memmove(r->data+off, rhdr.data, cnt);
4943e12c5d1SDavid du Colombier 	r->qid.vers++;
4953e12c5d1SDavid du Colombier 	r->mtime = time(0);
4963e12c5d1SDavid du Colombier 	thdr.count = cnt;
4973e12c5d1SDavid du Colombier 	return 0;
4983e12c5d1SDavid du Colombier }
4993e12c5d1SDavid du Colombier 
5003e12c5d1SDavid du Colombier void
5013e12c5d1SDavid du Colombier realremove(Ram *r)
5023e12c5d1SDavid du Colombier {
5033e12c5d1SDavid du Colombier 	r->ndata = 0;
5043e12c5d1SDavid du Colombier 	if(r->data)
5053e12c5d1SDavid du Colombier 		free(r->data);
5063e12c5d1SDavid du Colombier 	r->data = 0;
5073e12c5d1SDavid du Colombier 	r->parent = 0;
5083e12c5d1SDavid du Colombier 	r->qid = (Qid){0, 0};
5093e12c5d1SDavid du Colombier 	r->name[0] = 0;
5103e12c5d1SDavid du Colombier 	r->busy = 0;
5113e12c5d1SDavid du Colombier }
5123e12c5d1SDavid du Colombier 
5133e12c5d1SDavid du Colombier char *
5143e12c5d1SDavid du Colombier rclunk(Fid *f)
5153e12c5d1SDavid du Colombier {
5163e12c5d1SDavid du Colombier 	if(f->open)
5173e12c5d1SDavid du Colombier 		f->ram->open--;
5183e12c5d1SDavid du Colombier 	if(f->rclose)
5193e12c5d1SDavid du Colombier 		realremove(f->ram);
5203e12c5d1SDavid du Colombier 	f->busy = 0;
5213e12c5d1SDavid du Colombier 	f->open = 0;
5223e12c5d1SDavid du Colombier 	f->ram = 0;
5233e12c5d1SDavid du Colombier 	return 0;
5243e12c5d1SDavid du Colombier }
5253e12c5d1SDavid du Colombier 
5263e12c5d1SDavid du Colombier char *
5273e12c5d1SDavid du Colombier rremove(Fid *f)
5283e12c5d1SDavid du Colombier {
5293e12c5d1SDavid du Colombier 	Ram *r;
5303e12c5d1SDavid du Colombier 
5313e12c5d1SDavid du Colombier 	if(f->open)
5323e12c5d1SDavid du Colombier 		f->ram->open--;
5333e12c5d1SDavid du Colombier 	f->busy = 0;
5343e12c5d1SDavid du Colombier 	f->open = 0;
5353e12c5d1SDavid du Colombier 	r = f->ram;
5363e12c5d1SDavid du Colombier 	f->ram = 0;
5373e12c5d1SDavid du Colombier 	if(!perm(f, &ram[r->parent], Pwrite))
5383e12c5d1SDavid du Colombier 		return Eperm;
5393e12c5d1SDavid du Colombier 	ram[r->parent].mtime = time(0);
5403e12c5d1SDavid du Colombier 	realremove(r);
5413e12c5d1SDavid du Colombier 	return 0;
5423e12c5d1SDavid du Colombier }
5433e12c5d1SDavid du Colombier 
5443e12c5d1SDavid du Colombier char *
5453e12c5d1SDavid du Colombier rstat(Fid *f)
5463e12c5d1SDavid du Colombier {
5473e12c5d1SDavid du Colombier 	if(f->ram->busy == 0)
5483e12c5d1SDavid du Colombier 		return Enotexist;
5493e12c5d1SDavid du Colombier 	ramstat(f->ram, thdr.stat);
5503e12c5d1SDavid du Colombier 	return 0;
5513e12c5d1SDavid du Colombier }
5523e12c5d1SDavid du Colombier 
5533e12c5d1SDavid du Colombier char *
5543e12c5d1SDavid du Colombier rwstat(Fid *f)
5553e12c5d1SDavid du Colombier {
5563e12c5d1SDavid du Colombier 	Ram *r, *s;
5573e12c5d1SDavid du Colombier 	Dir dir;
5583e12c5d1SDavid du Colombier 
5593e12c5d1SDavid du Colombier 	if(f->ram->busy == 0)
5603e12c5d1SDavid du Colombier 		return Enotexist;
5613e12c5d1SDavid du Colombier 	convM2D(rhdr.stat, &dir);
5623e12c5d1SDavid du Colombier 	r = f->ram;
5633e12c5d1SDavid du Colombier 
5643e12c5d1SDavid du Colombier 	/*
5653e12c5d1SDavid du Colombier 	 * To change name, must have write permission in parent
5663e12c5d1SDavid du Colombier 	 * and name must be unique.
5673e12c5d1SDavid du Colombier 	 */
5683e12c5d1SDavid du Colombier 	if(strcmp(dir.name, r->name) != 0){
5693e12c5d1SDavid du Colombier 	 	if(!perm(f, &ram[r->parent], Pwrite))
5703e12c5d1SDavid du Colombier 			return Eperm;
5713e12c5d1SDavid du Colombier 		for(s=ram; s<&ram[nram]; s++)
5723e12c5d1SDavid du Colombier 			if(s->busy && s->parent==r->parent)
5733e12c5d1SDavid du Colombier 			if(strcmp(dir.name, s->name)==0)
5743e12c5d1SDavid du Colombier 				return Eexist;
5753e12c5d1SDavid du Colombier 	}
5763e12c5d1SDavid du Colombier 
5773e12c5d1SDavid du Colombier 	/*
5783e12c5d1SDavid du Colombier 	 * To change mode, must be owner or group leader to change mode.
5793e12c5d1SDavid du Colombier 	 * Because of lack of users file, leader=>group itself.
5803e12c5d1SDavid du Colombier 	 */
5813e12c5d1SDavid du Colombier 	if(r->perm != dir.mode){
5823e12c5d1SDavid du Colombier 		if(strcmp(f->user, r->user) != 0)
5833e12c5d1SDavid du Colombier 		if(strcmp(f->user, r->group) != 0)
5843e12c5d1SDavid du Colombier 			return Enotowner;
5853e12c5d1SDavid du Colombier 	}
5863e12c5d1SDavid du Colombier 
5873e12c5d1SDavid du Colombier 	/*
5883e12c5d1SDavid du Colombier 	 * To change group, must be owner and member of new group,
5893e12c5d1SDavid du Colombier 	 * or leader of current group and leader of new group.
5903e12c5d1SDavid du Colombier 	 * Second case cannot happen, but we check anyway.
5913e12c5d1SDavid du Colombier 	 */
5923e12c5d1SDavid du Colombier 	if(strcmp(r->group, dir.gid) != 0){
5933e12c5d1SDavid du Colombier 		if(strcmp(f->user, r->user) == 0)
5943e12c5d1SDavid du Colombier 		if(strcmp(f->user, dir.gid) == 0)
5953e12c5d1SDavid du Colombier 			goto ok;
5963e12c5d1SDavid du Colombier 		if(strcmp(f->user, r->group) == 0)
5973e12c5d1SDavid du Colombier 		if(strcmp(f->user, dir.gid) == 0)
5983e12c5d1SDavid du Colombier 			goto ok;
5993e12c5d1SDavid du Colombier 		return Enotowner;
6003e12c5d1SDavid du Colombier 		ok:;
6013e12c5d1SDavid du Colombier 	}
6023e12c5d1SDavid du Colombier 
6033e12c5d1SDavid du Colombier 	/* all ok; do it */
6043e12c5d1SDavid du Colombier 	dir.mode &= ~CHDIR;	/* cannot change dir bit */
6053e12c5d1SDavid du Colombier 	dir.mode |= r->perm&CHDIR;
6063e12c5d1SDavid du Colombier 	r->perm = dir.mode;
6073e12c5d1SDavid du Colombier 	memmove(r->name, dir.name, NAMELEN);
6083e12c5d1SDavid du Colombier 	r->group = strdup(dir.gid);
6093e12c5d1SDavid du Colombier 	ram[r->parent].mtime = time(0);
6103e12c5d1SDavid du Colombier 	return 0;
6113e12c5d1SDavid du Colombier }
6123e12c5d1SDavid du Colombier 
6133e12c5d1SDavid du Colombier void
6143e12c5d1SDavid du Colombier ramstat(Ram *r, char *buf)
6153e12c5d1SDavid du Colombier {
6163e12c5d1SDavid du Colombier 	Dir dir;
6173e12c5d1SDavid du Colombier 
6183e12c5d1SDavid du Colombier 	memmove(dir.name, r->name, NAMELEN);
6193e12c5d1SDavid du Colombier 	dir.qid = r->qid;
6203e12c5d1SDavid du Colombier 	dir.mode = r->perm;
6213e12c5d1SDavid du Colombier 	dir.length = r->ndata;
6223e12c5d1SDavid du Colombier 	dir.hlength = 0;
6233e12c5d1SDavid du Colombier 	strcpy(dir.uid, r->user);
6243e12c5d1SDavid du Colombier 	strcpy(dir.gid, r->group);
6253e12c5d1SDavid du Colombier 	dir.atime = r->atime;
6263e12c5d1SDavid du Colombier 	dir.mtime = r->mtime;
6273e12c5d1SDavid du Colombier 	convD2M(&dir, buf);
6283e12c5d1SDavid du Colombier }
6293e12c5d1SDavid du Colombier 
6303e12c5d1SDavid du Colombier Fid *
6313e12c5d1SDavid du Colombier newfid(int fid)
6323e12c5d1SDavid du Colombier {
6333e12c5d1SDavid du Colombier 	Fid *f, *ff;
6343e12c5d1SDavid du Colombier 
6353e12c5d1SDavid du Colombier 	ff = 0;
6363e12c5d1SDavid du Colombier 	for(f = fids; f; f = f->next)
6373e12c5d1SDavid du Colombier 		if(f->fid == fid)
6383e12c5d1SDavid du Colombier 			return f;
6393e12c5d1SDavid du Colombier 		else if(!ff && !f->busy)
6403e12c5d1SDavid du Colombier 			ff = f;
6413e12c5d1SDavid du Colombier 	if(ff){
6423e12c5d1SDavid du Colombier 		ff->fid = fid;
6433e12c5d1SDavid du Colombier 		return ff;
6443e12c5d1SDavid du Colombier 	}
6453e12c5d1SDavid du Colombier 	f = emalloc(sizeof *f);
6463e12c5d1SDavid du Colombier 	f->ram = 0;
6473e12c5d1SDavid du Colombier 	f->fid = fid;
6483e12c5d1SDavid du Colombier 	f->next = fids;
6493e12c5d1SDavid du Colombier 	fids = f;
6503e12c5d1SDavid du Colombier 	return f;
6513e12c5d1SDavid du Colombier }
6523e12c5d1SDavid du Colombier 
6533e12c5d1SDavid du Colombier void
6543e12c5d1SDavid du Colombier io(void)
6553e12c5d1SDavid du Colombier {
6563e12c5d1SDavid du Colombier 	char *err;
6573e12c5d1SDavid du Colombier 	int n;
6583e12c5d1SDavid du Colombier 
6593e12c5d1SDavid du Colombier 	for(;;){
6603e12c5d1SDavid du Colombier 		/*
6613e12c5d1SDavid du Colombier 		 * reading from a pipe or a network device
6623e12c5d1SDavid du Colombier 		 * will give an error after a few eof reads
6633e12c5d1SDavid du Colombier 		 * however, we cannot tell the difference
6643e12c5d1SDavid du Colombier 		 * between a zero-length read and an interrupt
6653e12c5d1SDavid du Colombier 		 * on the processes writing to us,
6663e12c5d1SDavid du Colombier 		 * so we wait for the error
6673e12c5d1SDavid du Colombier 		 */
6683e12c5d1SDavid du Colombier 		n = read(mfd[0], mdata, sizeof mdata);
6693e12c5d1SDavid du Colombier 		if(n == 0)
6703e12c5d1SDavid du Colombier 			continue;
6713e12c5d1SDavid du Colombier 		if(n < 0)
6723e12c5d1SDavid du Colombier 			error("mount read");
6733e12c5d1SDavid du Colombier 		if(convM2S(mdata, &rhdr, n) == 0)
6743e12c5d1SDavid du Colombier 			continue;
6753e12c5d1SDavid du Colombier 
676*219b2ee8SDavid du Colombier 		if(debug)
677*219b2ee8SDavid du Colombier 			fprint(2, "ramfs:<-%F\n", &rhdr);
6783e12c5d1SDavid du Colombier 
6793e12c5d1SDavid du Colombier 		thdr.data = mdata + MAXMSG;
6803e12c5d1SDavid du Colombier 		if(!fcalls[rhdr.type])
6813e12c5d1SDavid du Colombier 			err = "bad fcall type";
6823e12c5d1SDavid du Colombier 		else
6833e12c5d1SDavid du Colombier 			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
6843e12c5d1SDavid du Colombier 		if(err){
6853e12c5d1SDavid du Colombier 			thdr.type = Rerror;
6863e12c5d1SDavid du Colombier 			strncpy(thdr.ename, err, ERRLEN);
6873e12c5d1SDavid du Colombier 		}else{
6883e12c5d1SDavid du Colombier 			thdr.type = rhdr.type + 1;
6893e12c5d1SDavid du Colombier 			thdr.fid = rhdr.fid;
6903e12c5d1SDavid du Colombier 		}
6913e12c5d1SDavid du Colombier 		thdr.tag = rhdr.tag;
692*219b2ee8SDavid du Colombier 		if(debug)
693*219b2ee8SDavid du Colombier 			fprint(2, "ramfs:->%F\n", &thdr);/**/
6943e12c5d1SDavid du Colombier 		n = convS2M(&thdr, mdata);
6953e12c5d1SDavid du Colombier 		if(write(mfd[1], mdata, n) != n)
6963e12c5d1SDavid du Colombier 			error("mount write");
6973e12c5d1SDavid du Colombier 	}
6983e12c5d1SDavid du Colombier }
6993e12c5d1SDavid du Colombier 
7003e12c5d1SDavid du Colombier int
7013e12c5d1SDavid du Colombier perm(Fid *f, Ram *r, int p)
7023e12c5d1SDavid du Colombier {
7033e12c5d1SDavid du Colombier 	if((p*Pother) & r->perm)
7043e12c5d1SDavid du Colombier 		return 1;
7053e12c5d1SDavid du Colombier 	if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
7063e12c5d1SDavid du Colombier 		return 1;
7073e12c5d1SDavid du Colombier 	if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
7083e12c5d1SDavid du Colombier 		return 1;
7093e12c5d1SDavid du Colombier 	return 0;
7103e12c5d1SDavid du Colombier }
7113e12c5d1SDavid du Colombier 
7123e12c5d1SDavid du Colombier void
7133e12c5d1SDavid du Colombier error(char *s)
7143e12c5d1SDavid du Colombier {
715*219b2ee8SDavid du Colombier 	fprint(2, "%s: %s: %r\n", argv0, s);
7163e12c5d1SDavid du Colombier 	exits(s);
7173e12c5d1SDavid du Colombier }
7183e12c5d1SDavid du Colombier 
7193e12c5d1SDavid du Colombier void *
7203e12c5d1SDavid du Colombier emalloc(ulong n)
7213e12c5d1SDavid du Colombier {
7223e12c5d1SDavid du Colombier 	void *p;
7233e12c5d1SDavid du Colombier 
7243e12c5d1SDavid du Colombier 	p = malloc(n);
7253e12c5d1SDavid du Colombier 	if(!p)
7263e12c5d1SDavid du Colombier 		error("out of memory");
7273e12c5d1SDavid du Colombier 	return p;
7283e12c5d1SDavid du Colombier }
7293e12c5d1SDavid du Colombier 
7303e12c5d1SDavid du Colombier void *
7313e12c5d1SDavid du Colombier erealloc(void *p, ulong n)
7323e12c5d1SDavid du Colombier {
7333e12c5d1SDavid du Colombier 	p = realloc(p, n);
7343e12c5d1SDavid du Colombier 	if(!p)
7353e12c5d1SDavid du Colombier 		error("out of memory");
7363e12c5d1SDavid du Colombier 	return p;
7373e12c5d1SDavid du Colombier }
7383e12c5d1SDavid du Colombier 
7393e12c5d1SDavid du Colombier void
7403e12c5d1SDavid du Colombier usage(void)
7413e12c5d1SDavid du Colombier {
7423e12c5d1SDavid du Colombier 	fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);
7433e12c5d1SDavid du Colombier 	exits("usage");
7443e12c5d1SDavid du Colombier }
745