plan9port

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

vacfs.c (14671B)


      1 #include "stdinc.h"
      2 #include <fcall.h>
      3 #include "vac.h"
      4 
      5 typedef struct Fid Fid;
      6 
      7 enum
      8 {
      9 	OPERM	= 0x3		/* mask of all permission types in open mode */
     10 };
     11 
     12 struct Fid
     13 {
     14 	short busy;
     15 	short open;
     16 	int fid;
     17 	char *user;
     18 	Qid qid;
     19 	VacFile *file;
     20 	VacDirEnum *vde;
     21 	Fid	*next;
     22 };
     23 
     24 enum
     25 {
     26 	Pexec =		1,
     27 	Pwrite = 	2,
     28 	Pread = 	4,
     29 	Pother = 	1,
     30 	Pgroup = 	8,
     31 	Powner =	64
     32 };
     33 
     34 Fid	*fids;
     35 uchar	*data;
     36 int	mfd[2];
     37 int	srvfd = -1;
     38 char	*user;
     39 uchar	mdata[8192+IOHDRSZ];
     40 int messagesize = sizeof mdata;
     41 Fcall	rhdr;
     42 Fcall	thdr;
     43 VacFs	*fs;
     44 VtConn  *conn;
     45 int	noperm;
     46 char *defmnt;
     47 
     48 Fid *	newfid(int);
     49 void	error(char*);
     50 void	io(void);
     51 void	vacshutdown(void);
     52 void	usage(void);
     53 int	perm(Fid*, int);
     54 int	permf(VacFile*, char*, int);
     55 ulong	getl(void *p);
     56 void	init(char*, char*, long, int);
     57 int	vacdirread(Fid *f, char *p, long off, long cnt);
     58 int	vacstat(VacFile *parent, VacDir *vd, uchar *p, int np);
     59 void 	srv(void* a);
     60 
     61 
     62 char	*rflush(Fid*), *rversion(Fid*),
     63 	*rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
     64 	*ropen(Fid*), *rcreate(Fid*),
     65 	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
     66 	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
     67 
     68 char 	*(*fcalls[Tmax])(Fid*);
     69 
     70 void
     71 initfcalls(void)
     72 {
     73 	fcalls[Tflush]=	rflush;
     74 	fcalls[Tversion]=	rversion;
     75 	fcalls[Tattach]=	rattach;
     76 	fcalls[Tauth]=		rauth;
     77 	fcalls[Twalk]=		rwalk;
     78 	fcalls[Topen]=		ropen;
     79 	fcalls[Tcreate]=	rcreate;
     80 	fcalls[Tread]=		rread;
     81 	fcalls[Twrite]=	rwrite;
     82 	fcalls[Tclunk]=	rclunk;
     83 	fcalls[Tremove]=	rremove;
     84 	fcalls[Tstat]=		rstat;
     85 	fcalls[Twstat]=	rwstat;
     86 }
     87 
     88 char	Eperm[] =	"permission denied";
     89 char	Enotdir[] =	"not a directory";
     90 char	Enotexist[] =	"file does not exist";
     91 char	Einuse[] =	"file in use";
     92 char	Eexist[] =	"file exists";
     93 char	Enotowner[] =	"not owner";
     94 char	Eisopen[] = 	"file already open for I/O";
     95 char	Excl[] = 	"exclusive use file already open";
     96 char	Ename[] = 	"illegal name";
     97 char	Erdonly[] = 	"read only file system";
     98 char	Eio[] = 	"i/o error";
     99 char	Eempty[] = 	"directory is not empty";
    100 char	Emode[] =	"illegal mode";
    101 
    102 int dflag;
    103 
    104 void
    105 notifyf(void *a, char *s)
    106 {
    107 	USED(a);
    108 	if(strncmp(s, "interrupt", 9) == 0)
    109 		noted(NCONT);
    110 	noted(NDFLT);
    111 }
    112 
    113 #define TWID64 ~(u64int)0
    114 static u64int
    115 unittoull(char *s)
    116 {
    117 	char *es;
    118 	u64int n;
    119 
    120 	if(s == nil)
    121 		return TWID64;
    122 	n = strtoul(s, &es, 0);
    123 	if(*es == 'k' || *es == 'K'){
    124 		n *= 1024;
    125 		es++;
    126 	}else if(*es == 'm' || *es == 'M'){
    127 		n *= 1024*1024;
    128 		es++;
    129 	}else if(*es == 'g' || *es == 'G'){
    130 		n *= 1024*1024*1024;
    131 		es++;
    132 	}
    133 	if(*es != '\0')
    134 		return TWID64;
    135 	return n;
    136 }
    137 
    138 void
    139 threadmain(int argc, char *argv[])
    140 {
    141 	char *defsrv, *srvname;
    142 	int p[2], fd;
    143 	int stdio;
    144 	char *host = nil;
    145 	ulong mem;
    146 
    147 	mem = 16<<20;
    148 	stdio = 0;
    149 	fmtinstall('H', encodefmt);
    150 	fmtinstall('V', vtscorefmt);
    151 	fmtinstall('F', vtfcallfmt);
    152 
    153 	defmnt = nil;
    154 	defsrv = nil;
    155 	ARGBEGIN{
    156 	case 'd':
    157 		fmtinstall('F', fcallfmt);
    158 		dflag = 1;
    159 		break;
    160 	case 'i':
    161 		defmnt = nil;
    162 		stdio = 1;
    163 		mfd[0] = 0;
    164 		mfd[1] = 1;
    165 		break;
    166 	case 'h':
    167 		host = EARGF(usage());
    168 		break;
    169 	case 'S':
    170 		defsrv = EARGF(usage());
    171 		break;
    172 	case 's':
    173 		defsrv = "vacfs";
    174 		break;
    175 	case 'M':
    176 		mem = unittoull(EARGF(usage()));
    177 		break;
    178 	case 'm':
    179 		defmnt = EARGF(usage());
    180 		break;
    181 	case 'p':
    182 		noperm = 1;
    183 		break;
    184 	case 'V':
    185 		chattyventi = 1;
    186 		break;
    187 	default:
    188 		usage();
    189 	}ARGEND
    190 
    191 	if(argc != 1)
    192 		usage();
    193 
    194 #ifdef PLAN9PORT
    195 	if(defsrv == nil && defmnt == nil && !stdio){
    196 		srvname = strchr(argv[0], '/');
    197 		if(srvname)
    198 			srvname++;
    199 		else
    200 			srvname = argv[0];
    201 		defsrv = vtmalloc(6+strlen(srvname)+1);
    202 		strcpy(defsrv, "vacfs.");
    203 		strcat(defsrv, srvname);
    204 		if(strcmp(defsrv+strlen(defsrv)-4, ".vac") == 0)
    205 			defsrv[strlen(defsrv)-4] = 0;
    206 	}
    207 #else
    208 	if(defsrv == nil && defmnt == nil && !stdio)
    209 		defmnt = "/n/vac";
    210 #endif
    211 	if(stdio && defmnt)
    212 		sysfatal("cannot use -m with -i");
    213 
    214 	initfcalls();
    215 
    216 	notify(notifyf);
    217 	user = getuser();
    218 
    219 	conn = vtdial(host);
    220 	if(conn == nil)
    221 		sysfatal("could not connect to server: %r");
    222 
    223 	if(vtconnect(conn) < 0)
    224 		sysfatal("vtconnect: %r");
    225 
    226 	fs = vacfsopen(conn, argv[0], VtOREAD, mem);
    227 	if(fs == nil)
    228 		sysfatal("vacfsopen: %r");
    229 
    230 	if(!stdio){
    231 		if(pipe(p) < 0)
    232 			sysfatal("pipe failed: %r");
    233 		mfd[0] = p[0];
    234 		mfd[1] = p[0];
    235 		srvfd = p[1];
    236 #ifndef PLAN9PORT
    237 		if(defsrv){
    238 			srvname = smprint("/srv/%s", defsrv);
    239 			fd = create(srvname, OWRITE|ORCLOSE, 0666);
    240 			if(fd < 0)
    241 				sysfatal("create %s: %r", srvname);
    242 			if(fprint(fd, "%d", srvfd) < 0)
    243 				sysfatal("write %s: %r", srvname);
    244 			free(srvname);
    245 		}
    246 #endif
    247 	}
    248 
    249 #ifdef PLAN9PORT
    250 	USED(fd);
    251 	proccreate(srv, 0, 32 * 1024);
    252 	if(!stdio && post9pservice(p[1], defsrv, defmnt) < 0)
    253 		sysfatal("post9pservice");
    254 #else
    255 	procrfork(srv, 0, 32 * 1024, RFFDG|RFNAMEG|RFNOTEG);
    256 
    257 	if(!stdio){
    258 		close(p[0]);
    259 		if(defmnt){
    260 			if(mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
    261 				sysfatal("mount %s: %r", defmnt);
    262 		}
    263 	}
    264 #endif
    265 	threadexits(0);
    266 }
    267 
    268 void
    269 srv(void *a)
    270 {
    271 	USED(a);
    272 	io();
    273 	vacshutdown();
    274 }
    275 
    276 void
    277 usage(void)
    278 {
    279 	fprint(2, "usage: %s [-sd] [-h host] [-m mountpoint] [-M mem] vacfile\n", argv0);
    280 	threadexitsall("usage");
    281 }
    282 
    283 char*
    284 rversion(Fid *unused)
    285 {
    286 	Fid *f;
    287 
    288 	USED(unused);
    289 
    290 	for(f = fids; f; f = f->next)
    291 		if(f->busy)
    292 			rclunk(f);
    293 
    294 	if(rhdr.msize < 256)
    295 		return vtstrdup("version: message size too small");
    296 	messagesize = rhdr.msize;
    297 	if(messagesize > sizeof mdata)
    298 		messagesize = sizeof mdata;
    299 	thdr.msize = messagesize;
    300 	if(strncmp(rhdr.version, "9P2000", 6) != 0)
    301 		return vtstrdup("unrecognized 9P version");
    302 	thdr.version = "9P2000";
    303 	return nil;
    304 }
    305 
    306 char*
    307 rflush(Fid *f)
    308 {
    309 	USED(f);
    310 	return 0;
    311 }
    312 
    313 char*
    314 rauth(Fid *f)
    315 {
    316 	USED(f);
    317 	return vtstrdup("vacfs: authentication not required");
    318 }
    319 
    320 char*
    321 rattach(Fid *f)
    322 {
    323 	/* no authentication for the momment */
    324 	VacFile *file;
    325 	char err[80];
    326 
    327 	file = vacfsgetroot(fs);
    328 	if(file == nil) {
    329 		rerrstr(err, sizeof err);
    330 		return vtstrdup(err);
    331 	}
    332 
    333 	f->busy = 1;
    334 	f->file = file;
    335 	f->qid.path = vacfilegetid(f->file);
    336 	f->qid.vers = 0;
    337 	f->qid.type = QTDIR;
    338 	thdr.qid = f->qid;
    339 	if(rhdr.uname[0])
    340 		f->user = vtstrdup(rhdr.uname);
    341 	else
    342 		f->user = "none";
    343 	return 0;
    344 }
    345 
    346 char*
    347 rwalk(Fid *f)
    348 {
    349 	VacFile *file, *nfile;
    350 	Fid *nf;
    351 	int nqid, nwname;
    352 	Qid qid;
    353 	char *err = nil;
    354 
    355 	if(f->busy == 0)
    356 		return Enotexist;
    357 	nf = nil;
    358 	if(rhdr.fid != rhdr.newfid){
    359 		if(f->open)
    360 			return vtstrdup(Eisopen);
    361 		if(f->busy == 0)
    362 			return vtstrdup(Enotexist);
    363 		nf = newfid(rhdr.newfid);
    364 		if(nf->busy)
    365 			return vtstrdup(Eisopen);
    366 		nf->busy = 1;
    367 		nf->open = 0;
    368 		nf->qid = f->qid;
    369 		nf->file = vacfileincref(f->file);
    370 		nf->user = vtstrdup(f->user);
    371 		f = nf;
    372 	}
    373 
    374 	nwname = rhdr.nwname;
    375 
    376 	/* easy case */
    377 	if(nwname == 0) {
    378 		thdr.nwqid = 0;
    379 		return 0;
    380 	}
    381 
    382 	file = f->file;
    383 	vacfileincref(file);
    384 	qid = f->qid;
    385 
    386 	for(nqid = 0; nqid < nwname; nqid++){
    387 		if((qid.type & QTDIR) == 0){
    388 			err = Enotdir;
    389 			break;
    390 		}
    391 		if(!permf(file, f->user, Pexec)) {
    392 			err = Eperm;
    393 			break;
    394 		}
    395 		nfile = vacfilewalk(file, rhdr.wname[nqid]);
    396 		if(nfile == nil)
    397 			break;
    398 		vacfiledecref(file);
    399 		file = nfile;
    400 		qid.type = QTFILE;
    401 		if(vacfileisdir(file))
    402 			qid.type = QTDIR;
    403 #ifdef PLAN9PORT
    404 		if(vacfilegetmode(file)&ModeLink)
    405 			qid.type = QTSYMLINK;
    406 #endif
    407 		qid.vers = vacfilegetmcount(file);
    408 		qid.path = vacfilegetid(file);
    409 		thdr.wqid[nqid] = qid;
    410 	}
    411 
    412 	thdr.nwqid = nqid;
    413 
    414 	if(nqid == nwname){
    415 		/* success */
    416 		f->qid = thdr.wqid[nqid-1];
    417 		vacfiledecref(f->file);
    418 		f->file = file;
    419 		return 0;
    420 	}
    421 
    422 	vacfiledecref(file);
    423 	if(nf != nil)
    424 		rclunk(nf);
    425 
    426 	/* only error on the first element */
    427 	if(nqid == 0)
    428 		return vtstrdup(err);
    429 
    430 	return 0;
    431 }
    432 
    433 char *
    434 ropen(Fid *f)
    435 {
    436 	int mode, trunc;
    437 
    438 	if(f->open)
    439 		return vtstrdup(Eisopen);
    440 	if(!f->busy)
    441 		return vtstrdup(Enotexist);
    442 
    443 	mode = rhdr.mode;
    444 	thdr.iounit = messagesize - IOHDRSZ;
    445 	if(f->qid.type & QTDIR){
    446 		if(mode != OREAD)
    447 			return vtstrdup(Eperm);
    448 		if(!perm(f, Pread))
    449 			return vtstrdup(Eperm);
    450 		thdr.qid = f->qid;
    451 		f->vde = nil;
    452 		f->open = 1;
    453 		return 0;
    454 	}
    455 	if(mode & ORCLOSE)
    456 		return vtstrdup(Erdonly);
    457 	trunc = mode & OTRUNC;
    458 	mode &= OPERM;
    459 	if(mode==OWRITE || mode==ORDWR || trunc)
    460 		if(!perm(f, Pwrite))
    461 			return vtstrdup(Eperm);
    462 	if(mode==OREAD || mode==ORDWR)
    463 		if(!perm(f, Pread))
    464 			return vtstrdup(Eperm);
    465 	if(mode==OEXEC)
    466 		if(!perm(f, Pexec))
    467 			return vtstrdup(Eperm);
    468 	thdr.qid = f->qid;
    469 	thdr.iounit = messagesize - IOHDRSZ;
    470 	f->open = 1;
    471 	return 0;
    472 }
    473 
    474 char*
    475 rcreate(Fid* fid)
    476 {
    477 	VacFile *vf;
    478 	ulong mode;
    479 
    480 	if(fid->open)
    481 		return vtstrdup(Eisopen);
    482 	if(!fid->busy)
    483 		return vtstrdup(Enotexist);
    484 	if(fs->mode & ModeSnapshot)
    485 		return vtstrdup(Erdonly);
    486 	vf = fid->file;
    487 	if(!vacfileisdir(vf))
    488 		return vtstrdup(Enotdir);
    489 	if(!permf(vf, fid->user, Pwrite))
    490 		return vtstrdup(Eperm);
    491 
    492 	mode = rhdr.perm & 0777;
    493 
    494 	if(rhdr.perm & DMDIR){
    495 		if((rhdr.mode & OTRUNC) || (rhdr.perm & DMAPPEND))
    496 			return vtstrdup(Emode);
    497 		switch(rhdr.mode & OPERM){
    498 		default:
    499 			return vtstrdup(Emode);
    500 		case OEXEC:
    501 		case OREAD:
    502 			break;
    503 		case OWRITE:
    504 		case ORDWR:
    505 			return vtstrdup(Eperm);
    506 		}
    507 		mode |= ModeDir;
    508 	}
    509 	vf = vacfilecreate(vf, rhdr.name, mode);
    510 	if(vf == nil) {
    511 		char err[80];
    512 		rerrstr(err, sizeof err);
    513 
    514 		return vtstrdup(err);
    515 	}
    516 
    517 	vacfiledecref(fid->file);
    518 
    519 	fid->file = vf;
    520 	fid->qid.type = QTFILE;
    521 	if(vacfileisdir(vf))
    522 		fid->qid.type = QTDIR;
    523 	fid->qid.vers = vacfilegetmcount(vf);
    524 	fid->qid.path = vacfilegetid(vf);
    525 
    526 	thdr.qid = fid->qid;
    527 	thdr.iounit = messagesize - IOHDRSZ;
    528 
    529 	return 0;
    530 }
    531 
    532 char*
    533 rread(Fid *f)
    534 {
    535 	char *buf;
    536 	vlong off;
    537 	int cnt;
    538 	VacFile *vf;
    539 	char err[80];
    540 	int n;
    541 
    542 	if(!f->busy)
    543 		return vtstrdup(Enotexist);
    544 	vf = f->file;
    545 	thdr.count = 0;
    546 	off = rhdr.offset;
    547 	buf = thdr.data;
    548 	cnt = rhdr.count;
    549 	if(f->qid.type & QTDIR)
    550 		n = vacdirread(f, buf, off, cnt);
    551 	else if(vacfilegetmode(f->file)&ModeDevice)
    552 		return vtstrdup("device");
    553 	else if(vacfilegetmode(f->file)&ModeLink)
    554 		return vtstrdup("symbolic link");
    555 	else if(vacfilegetmode(f->file)&ModeNamedPipe)
    556 		return vtstrdup("named pipe");
    557 	else
    558 		n = vacfileread(vf, buf, cnt, off);
    559 	if(n < 0) {
    560 		rerrstr(err, sizeof err);
    561 		return vtstrdup(err);
    562 	}
    563 	thdr.count = n;
    564 	return 0;
    565 }
    566 
    567 char*
    568 rwrite(Fid *f)
    569 {
    570 	USED(f);
    571 	return vtstrdup(Erdonly);
    572 }
    573 
    574 char *
    575 rclunk(Fid *f)
    576 {
    577 	f->busy = 0;
    578 	f->open = 0;
    579 	vtfree(f->user);
    580 	f->user = nil;
    581 	if(f->file)
    582 		vacfiledecref(f->file);
    583 	f->file = nil;
    584 	vdeclose(f->vde);
    585 	f->vde = nil;
    586 	return 0;
    587 }
    588 
    589 char *
    590 rremove(Fid *f)
    591 {
    592 	VacFile *vf, *vfp;
    593 	char errbuf[80];
    594 	char *err = nil;
    595 
    596 	if(!f->busy)
    597 		return vtstrdup(Enotexist);
    598 	vf = f->file;
    599 	vfp = vacfilegetparent(vf);
    600 
    601 	if(!permf(vfp, f->user, Pwrite)) {
    602 		err = Eperm;
    603 		goto Exit;
    604 	}
    605 
    606 	if(!vacfileremove(vf)) {
    607 		rerrstr(errbuf, sizeof errbuf);
    608 		err = errbuf;
    609 	}
    610 
    611 Exit:
    612 	vacfiledecref(vfp);
    613 	rclunk(f);
    614 	return vtstrdup(err);
    615 }
    616 
    617 char *
    618 rstat(Fid *f)
    619 {
    620 	VacDir dir;
    621 	static uchar statbuf[1024];
    622 	VacFile *parent;
    623 
    624 	if(!f->busy)
    625 		return vtstrdup(Enotexist);
    626 	parent = vacfilegetparent(f->file);
    627 	vacfilegetdir(f->file, &dir);
    628 	thdr.stat = statbuf;
    629 	thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
    630 	vdcleanup(&dir);
    631 	vacfiledecref(parent);
    632 	return 0;
    633 }
    634 
    635 char *
    636 rwstat(Fid *f)
    637 {
    638 	if(!f->busy)
    639 		return vtstrdup(Enotexist);
    640 	return vtstrdup(Erdonly);
    641 }
    642 
    643 int
    644 vacstat(VacFile *parent, VacDir *vd, uchar *p, int np)
    645 {
    646 	int ret;
    647 	Dir dir;
    648 #ifdef PLAN9PORT
    649 	int n;
    650 	VacFile *vf;
    651 	uvlong size;
    652 	char *ext = nil;
    653 #endif
    654 
    655 	memset(&dir, 0, sizeof(dir));
    656 
    657 	dir.qid.path = vd->qid + vacfilegetqidoffset(parent);
    658 	if(vd->qidspace)
    659 		dir.qid.path += vd->qidoffset;
    660 	dir.qid.vers = vd->mcount;
    661 	dir.mode = vd->mode & 0777;
    662 	if(vd->mode & ModeAppend){
    663 		dir.qid.type |= QTAPPEND;
    664 		dir.mode |= DMAPPEND;
    665 	}
    666 	if(vd->mode & ModeExclusive){
    667 		dir.qid.type |= QTEXCL;
    668 		dir.mode |= DMEXCL;
    669 	}
    670 	if(vd->mode & ModeDir){
    671 		dir.qid.type |= QTDIR;
    672 		dir.mode |= DMDIR;
    673 	}
    674 
    675 #ifdef PLAN9PORT
    676 	if(vd->mode & (ModeLink|ModeDevice|ModeNamedPipe)){
    677 		vf = vacfilewalk(parent, vd->elem);
    678 		if(vf == nil)
    679 			return 0;
    680 		vacfilegetsize(vf, &size);
    681 		ext = malloc(size+1);
    682 		if(ext == nil)
    683 			return 0;
    684 		n = vacfileread(vf, ext, size, 0);
    685 		USED(n);
    686 		ext[size] = 0;
    687 		vacfiledecref(vf);
    688 		if(vd->mode & ModeLink){
    689 			dir.qid.type |= QTSYMLINK;
    690 			dir.mode |= DMSYMLINK;
    691 		}
    692 		if(vd->mode & ModeDevice)
    693 			dir.mode |= DMDEVICE;
    694 		if(vd->mode & ModeNamedPipe)
    695 			dir.mode |= DMNAMEDPIPE;
    696 	}
    697 #endif
    698 
    699 	dir.atime = vd->atime;
    700 	dir.mtime = vd->mtime;
    701 	dir.length = vd->size;
    702 
    703 	dir.name = vd->elem;
    704 	dir.uid = vd->uid;
    705 	dir.gid = vd->gid;
    706 	dir.muid = vd->mid;
    707 
    708 	ret = convD2M(&dir, p, np);
    709 #ifdef PLAN9PORT
    710 	free(ext);
    711 #endif
    712 	return ret;
    713 }
    714 
    715 int
    716 vacdirread(Fid *f, char *p, long off, long cnt)
    717 {
    718 	int i, n, nb;
    719 	VacDir vd;
    720 
    721 	/*
    722 	 * special case of rewinding a directory
    723 	 * otherwise ignore the offset
    724 	 */
    725 	if(off == 0 && f->vde){
    726 		vdeclose(f->vde);
    727 		f->vde = nil;
    728 	}
    729 
    730 	if(f->vde == nil){
    731 		f->vde = vdeopen(f->file);
    732 		if(f->vde == nil)
    733 			return -1;
    734 	}
    735 
    736 	for(nb = 0; nb < cnt; nb += n) {
    737 		i = vderead(f->vde, &vd);
    738 		if(i < 0)
    739 			return -1;
    740 		if(i == 0)
    741 			break;
    742 		n = vacstat(f->file, &vd, (uchar*)p, cnt-nb);
    743 		if(n <= BIT16SZ) {
    744 			vdeunread(f->vde);
    745 			break;
    746 		}
    747 		vdcleanup(&vd);
    748 		p += n;
    749 	}
    750 	return nb;
    751 }
    752 
    753 Fid *
    754 newfid(int fid)
    755 {
    756 	Fid *f, *ff;
    757 
    758 	ff = 0;
    759 	for(f = fids; f; f = f->next)
    760 		if(f->fid == fid)
    761 			return f;
    762 		else if(!ff && !f->busy)
    763 			ff = f;
    764 	if(ff){
    765 		ff->fid = fid;
    766 		return ff;
    767 	}
    768 	f = vtmallocz(sizeof *f);
    769 	f->fid = fid;
    770 	f->next = fids;
    771 	fids = f;
    772 	return f;
    773 }
    774 
    775 void
    776 io(void)
    777 {
    778 	char *err;
    779 	int n;
    780 
    781 	for(;;){
    782 		n = read9pmsg(mfd[0], mdata, sizeof mdata);
    783 		if(n <= 0)
    784 			break;
    785 		if(convM2S(mdata, n, &rhdr) != n)
    786 			sysfatal("convM2S conversion error");
    787 
    788 		if(dflag)
    789 			fprint(2, "vacfs:<-%F\n", &rhdr);
    790 
    791 		thdr.data = (char*)mdata + IOHDRSZ;
    792 		if(!fcalls[rhdr.type])
    793 			err = "bad fcall type";
    794 		else
    795 			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
    796 		if(err){
    797 			thdr.type = Rerror;
    798 			thdr.ename = err;
    799 #ifdef PLAN9PORT
    800 			thdr.errornum = 0;
    801 #endif
    802 		}else{
    803 			thdr.type = rhdr.type + 1;
    804 			thdr.fid = rhdr.fid;
    805 		}
    806 		thdr.tag = rhdr.tag;
    807 		if(dflag)
    808 			fprint(2, "vacfs:->%F\n", &thdr);
    809 		n = convS2M(&thdr, mdata, messagesize);
    810 		if(n <= BIT16SZ)
    811 			sysfatal("convS2M conversion error");
    812 		if(err)
    813 			vtfree(err);
    814 
    815 		if(write(mfd[1], mdata, n) != n)
    816 			sysfatal("mount write: %r");
    817 	}
    818 }
    819 
    820 int
    821 permf(VacFile *vf, char *user, int p)
    822 {
    823 	VacDir dir;
    824 	ulong perm;
    825 
    826 	if(vacfilegetdir(vf, &dir))
    827 		return 0;
    828 	perm = dir.mode & 0777;
    829 
    830 	if(noperm)
    831 		goto Good;
    832 	if((p*Pother) & perm)
    833 		goto Good;
    834 	if(strcmp(user, dir.gid)==0 && ((p*Pgroup) & perm))
    835 		goto Good;
    836 	if(strcmp(user, dir.uid)==0 && ((p*Powner) & perm))
    837 		goto Good;
    838 	vdcleanup(&dir);
    839 	return 0;
    840 Good:
    841 	vdcleanup(&dir);
    842 	return 1;
    843 }
    844 
    845 int
    846 perm(Fid *f, int p)
    847 {
    848 	return permf(f->file, f->user, p);
    849 }
    850 
    851 void
    852 vacshutdown(void)
    853 {
    854 	Fid *f;
    855 
    856 	for(f = fids; f; f = f->next) {
    857 		if(!f->busy)
    858 			continue;
    859 		rclunk(f);
    860 	}
    861 
    862 	vacfsclose(fs);
    863 	vthangup(conn);
    864 }