Exemplo n.º 1
0
/*
 *	Main protocol loop
 */
void
io(void)
{
    Fcall	rhdr;
    int n;

    for(;;) {
        n = read9pmsg(srvfd, rbuf, sizeof rbuf-1);
        if(n <= 0)
            fatal("mount read");
        if(convM2S(rbuf, n, &rhdr) == 0) {
            if(debugfd >= 0)
                fprint(2, "%s: malformed message\n", argv0);
            continue;
        }

        if(debugfd >= 0)
            fprint(debugfd, "<-%F\n", &rhdr);/**/

        if(!fcalls[rhdr.type])
            reply(&rhdr, "bad fcall type");
        else
            (*fcalls[rhdr.type])(&rhdr);
    }
}
Exemplo n.º 2
0
void
shuffle(void *arg)
{
	int *fd;
	char *buf, *tbuf;
	int n;
	Fcall *t;

	fd = (int*)arg;
	buf = emalloc(BLEN+1);
	t = nil;
	tbuf = nil;
	for(;;){
		n = read9pmsg(fd[0], buf, BLEN);
		if(n <= 0){
			if(debug)
				fprint(dfd, "%d->%d read returns %d: %r\n", fd[0], fd[1], n);
			break;
		}
		if(debug){
			if(t == nil)
				t = emalloc(sizeof(Fcall));
			if(tbuf == nil)
				tbuf = emalloc(BLEN+1);
			memmove(tbuf, buf, n);	/* because convM2S is destructive */
			if(convM2S((uchar*)tbuf, n, t) != n)
				fprint(dfd, "%d->%d convert error in convM2S", fd[0], fd[1]);
			else
				fprint(dfd, "%d->%d %F\n", fd[0], fd[1], t);
		}
		if(write(fd[1], buf, n) != n)
			break;
	}
	threadexitsall(0);
}
Exemplo n.º 3
0
Arquivo: fs.c Projeto: 00001/plan9port
void
io(void)
{
	char *err;
	int n, nerr;
	char buf[ERRMAX];

	errstr(buf, sizeof buf);
	for(nerr=0, buf[0]='\0'; nerr<100; nerr++){
		/*
		 * reading from a pipe or a network device
		 * will give an error after a few eof reads
		 * however, we cannot tell the difference
		 * between a zero-length read and an interrupt
		 * on the processes writing to us,
		 * so we wait for the error
		 */
		n = read9pmsg(mfd[0], mdata, sizeof mdata);
		if(n==0)
			continue;
		if(n < 0){
			if(buf[0]=='\0')
				errstr(buf, sizeof buf);
			continue;
		}
		nerr = 0;
		buf[0] = '\0';
		if(convM2S(mdata, n, &rhdr) != n)
			error("convert error in convM2S");

		if(verbose)
			fprint(2, "tapefs: <=%F\n", &rhdr);/**/

		thdr.data = (char*)mdata + IOHDRSZ;
		thdr.stat = mdata + IOHDRSZ;
		if(!fcalls[rhdr.type])
			err = "bad fcall type";
		else
			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
		if(err){
			thdr.type = Rerror;
			thdr.ename = err;
		}else{
			thdr.type = rhdr.type + 1;
			thdr.fid = rhdr.fid;
		}
		thdr.tag = rhdr.tag;
		n = convS2M(&thdr, mdata, messagesize);
		if(n <= 0)
			error("convert error in convS2M");
		if(verbose)
			fprint(2, "tapefs: =>%F\n", &thdr);/**/
		if(write(mfd[1], mdata, n) != n)
			error("mount write");
	}
	if(buf[0]=='\0' || strstr(buf, "hungup"))
		exits("");
	fprint(2, "%s: mount read: %s\n", argv0, buf);
	exits(buf);
}
Exemplo n.º 4
0
Arquivo: fsys.c Projeto: fr1tz/nadir
void
fsysproc(void *v)
{
	int n;
	Xfid *x;
	Fid *f;
	Fcall t;
	uchar *buf;

	threadsetname("fsysproc");

	USED(v);
	x = nil;
	for(;;){
		buf = emalloc(messagesize+UTFmax);	/* overflow for appending partial rune in xfidwrite */
		n = read9pmsg(sfd, buf, messagesize);
		if(n <= 0){
			if(closing)
				break;
			error("i/o error on server channel");
		}
		if(x == nil){
			sendp(cxfidalloc, nil);
			x = recvp(cxfidalloc);
		}
		x->buf = buf;
		if(convM2S(buf, n, &x->fcall) != n)
			error("convert error in convM2S");
		if(DEBUG)
			fprint(2, "%F\n", &x->fcall);
		if(fcall[x->fcall.type] == nil)
			x = respond(x, &t, "bad fcall type");
		else{
			switch(x->fcall.type){
			case Tversion:
			case Tauth:
			case Tflush:
				f = nil;
				break;
			case Tattach:
				f = newfid(x->fcall.fid);
				break;
			default:
				f = newfid(x->fcall.fid);
				if(!f->busy){
					x->f = f;
					x = respond(x, &t, "fid not in use");
					continue;
				}
				break;
			}
			x->f = f;
			x  = (*fcall[x->fcall.type])(x, f);
		}
	}
}
Exemplo n.º 5
0
Arquivo: srv.c Projeto: 99years/plan9
static Req*
getreq(Srv *s)
{
	long n;
	uchar *buf;
	Fcall f;
	Req *r;

	qlock(&s->rlock);
	if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
		qunlock(&s->rlock);
		return nil;
	}

	buf = emalloc9p(n);
	memmove(buf, s->rbuf, n);
	qunlock(&s->rlock);

	if(convM2S(buf, n, &f) != n){
		free(buf);
		return nil;
	}

	if((r=allocreq(s->rpool, f.tag)) == nil){	/* duplicate tag: cons up a fake Req */
		r = emalloc9p(sizeof *r);
		incref(&r->ref);
		r->tag = f.tag;
		r->ifcall = f;
		r->error = Eduptag;
		r->buf = buf;
		r->responded = 0;
		r->type = 0;
		r->srv = s;
		r->pool = nil;
if(chatty9p)
	fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
		return r;
	}

	r->srv = s;
	r->responded = 0;
	r->buf = buf;
	r->ifcall = f;
	memset(&r->ofcall, 0, sizeof r->ofcall);
	r->type = r->ifcall.type;

if(chatty9p)
	if(r->error)
		fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
	else	
		fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);

	return r;
}
Exemplo n.º 6
0
static int
getfcall(int fd, Fcall* f)
{
	int r;

	r = read9pmsg(fd, rxbuf, sizeof(rxbuf));
	if (r <= 0)
		return 0;
	if (convM2S(rxbuf, sizeof(rxbuf), f) == 0)
		return -1;
	return 1;
}
Exemplo n.º 7
0
void
io(int srvfd)
{
	int n, pid;

	pid = getpid();

	fmtinstall('F', fcallfmt);
	for(;;){
		/*
		 * reading from a pipe or a network device
		 * will give an error after a few eof reads.
		 * however, we cannot tell the difference
		 * between a zero-length read and an interrupt
		 * on the processes writing to us,
		 * so we wait for the error.
		 */
		n = read9pmsg(srvfd, mdata, sizeof mdata);
		if(n < 0)
			break;
		if(n == 0)
			continue;
		if(convM2S(mdata, n, req) == 0)
			continue;

		if(chatty)
			fprint(2, "dossrv %d:<-%F\n", pid, req);

		errno = 0;
		if(!fcalls[req->type])
			errno = Ebadfcall;
		else
			(*fcalls[req->type])();
		if(errno){
			rep->type = Rerror;
			rep->ename = xerrstr(errno);
		}else{
			rep->type = req->type + 1;
			rep->fid = req->fid;
		}
		rep->tag = req->tag;
		if(chatty)
			fprint(2, "dossrv %d:->%F\n", pid, rep);
		n = convS2M(rep, mdata, sizeof mdata);
		if(n == 0)
			panic("convS2M error on write");
		if(write(srvfd, mdata, n) != n)
			panic("mount write");
	}
	chat("server shut down");
}
Exemplo n.º 8
0
Arquivo: 9p.c Projeto: 99years/plan9
int
xmesg(Session *s, int t)
{
	int n;

	if(chatty){
		if(0 <= t && t < nelem(tnames) && tnames[t])
			chat("T%s...", tnames[t]);
		else
			chat("T%d...", t);
	}
	s->f.type = t;
	s->f.tag = ++s->tag;
	if(p9debug)
		fprint(2, "xmseg\tsend %F\n", &s->f);
	n = convS2M(&s->f, s->data, messagesize);
	if(niwrite(s->fd, s->data, n) != n){
		clog("xmesg write error on %d: %r\n", s->fd);
		return -1;
	}
again:
	n = read9pmsg(s->fd, s->data, messagesize);
	if(n < 0){
		clog("xmesg read error: %r\n");
		return -1;
	}
	if(convM2S(s->data, n, &s->f) <= 0){
		clog("xmesg bad convM2S %d %.2x %.2x %.2x %.2x\n",
			n, ((uchar*)s->data)[0], ((uchar*)s->data)[1],
			((uchar*)s->data)[2], ((uchar*)s->data)[3]);
		return -1;
	}
	if(p9debug)
		fprint(2, "\trecv %F\n", &s->f);
	if(s->f.tag != s->tag){
		clog("xmesg tag %d for %d\n", s->f.tag, s->tag);
		goto again;
	}
	if(s->f.type == Rerror){
		if(t == Tclunk)
			clog("xmesg clunk: %s", s->f.ename);
		chat("xmesg %d error %s...", t, s->f.ename);
		return -1;
	}
	if(s->f.type != t+1){
		clog("xmesg type mismatch: %d, expected %d\n", s->f.type, t+1);
		return -1;
	}
	return 0;
}
Exemplo n.º 9
0
void
fsrun(void *a)
{
	struct Fsarg *fsarg;
	Fs* fs;
	char *root;
	int n, t;
	Request *r;
	Fid *f;
	Dir *d;

	fsarg = a;
	fs = fsarg->fs;
	fs->fd = fsarg->fd;
	root = fsarg->root;
	d = dirstat("/");
	if(d == nil)
		fatal("root %s inaccessible: %r", root);
	fs->rootqid = d->qid;
	free(d);

	for(;;){
		r = allocreq(messagesize);
		qlock(&iolock);
		n = read9pmsg(fs->fd, r->buf, messagesize);
		qunlock(&iolock);
		if(n <= 0)
			fatal("read9pmsg error: %r");

		if(convM2S(r->buf, n, &r->f) == 0){
			fprint(2, "can't convert %ux %ux %ux\n", r->buf[0],
				r->buf[1], r->buf[2]);
			free(r);
			continue;
		}

		f = fsgetfid(fs, r->f.fid);
		r->fid = f;
		if(debug)
			fprint(2, "%F path %llux\n", &r->f, f->qid.path);

		t = r->f.type;
		r->f.type++;
		(*fcall[t])(fs, r, f);
		fsputfid(fs, f);
	}

}
Exemplo n.º 10
0
void
mainproc(void *v)
{
	int n, nn;
	Fcall f;
	USED(v);

	atnotify(ignorepipe, 1);
	fmtinstall('D', dirfmt);
	fmtinstall('M', dirmodefmt);
	fmtinstall('F', fcallfmt);
	fmtinstall('H', encodefmt);

	outq = qalloc();
	inq = qalloc();

	if(!versioned){
		f.type = Tversion;
		f.version = "9P2000";
		f.msize = msize;
		f.tag = NOTAG;
		n = convS2M(&f, vbuf, sizeof vbuf);
		if(n <= BIT16SZ)
			sysfatal("convS2M conversion error");
		if(verbose > 1) fprint(2, "%T * <- %F\n", &f);
		nn = write(1, vbuf, n);
		if(n != nn)
			sysfatal("error writing Tversion: %r\n");
		n = read9pmsg(0, vbuf, sizeof vbuf);
		if(n < 0)
			sysfatal("read9pmsg failure");
		if(convM2S(vbuf, n, &f) != n)
			sysfatal("convM2S failure");
		if(f.msize < msize)
			msize = f.msize;
		if(verbose > 1) fprint(2, "%T * -> %F\n", &f);
	}

	threadcreate(inputthread, nil, STACK);
	threadcreate(outputthread, nil, STACK);

/*	if(rootfid) */
/*		dorootstat(); */
	
	threadcreate(listenthread, nil, STACK);
	threadexits(0);
}
Exemplo n.º 11
0
Arquivo: fsys.c Projeto: npe9/harvey
static
void
filsysproc(void *arg)
{
	int n;
	Xfid *x;
	Fid *f;
	Fcall t;
	uint8_t *buf;
	Filsys *fs;

	threadsetname("FILSYSPROC");
	fs = arg;
	fs->pid = getpid();
	x = nil;
	for(;;){
		buf = emalloc(messagesize+UTFmax);	/* UTFmax for appending partial rune in xfidwrite */
		n = read9pmsg(fs->sfd, buf, messagesize);
		if(n <= 0){
			yield();	/* if threadexitsall'ing, will not return */
			fprint(2, "rio: %d: read9pmsg: %d %r\n", getpid(), n);
			errorshouldabort = 0;
			error("eof or i/o error on server channel");
		}
		if(x == nil){
			send(fs->cxfidalloc, nil);
			recv(fs->cxfidalloc, &x);
			x->fs = fs;
		}
		x->buf = buf;
		if(convM2S(buf, n, x) != n)
			error("convert error in convM2S");
		if(DEBUG)
			fprint(2, "rio:<-%F\n", &x->Fcall);
		if(fcall[x->type] == nil)
			x = filsysrespond(fs, x, &t, Ebadfcall);
		else{
			if(x->type==Tversion || x->type==Tauth)
				f = nil;
			else
				f = newfid(fs, x->fid);
			x->f = f;
			x  = (*fcall[x->type])(fs, x, f);
		}
		firstmessage = 0;
	}
}
Exemplo n.º 12
0
void
io(void)
{
	char *err;
	int n;

	for(;;){
		n = read9pmsg(mfd[0], mdata, sizeof mdata);
		if(n <= 0)
			break;
		if(convM2S(mdata, n, &rhdr) != n)
			sysfatal("convM2S conversion error");

		if(dflag)
			fprint(2, "vacfs:<-%F\n", &rhdr);

		thdr.data = (char*)mdata + IOHDRSZ;
		if(!fcalls[rhdr.type])
			err = "bad fcall type";
		else
			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
		if(err){
			thdr.type = Rerror;
			thdr.ename = err;
#ifdef PLAN9PORT
			thdr.errornum = 0;
#endif
		}else{
			thdr.type = rhdr.type + 1;
			thdr.fid = rhdr.fid;
		}
		thdr.tag = rhdr.tag;
		if(dflag)
			fprint(2, "vacfs:->%F\n", &thdr);
		n = convS2M(&thdr, mdata, messagesize);
		if(n <= BIT16SZ)
			sysfatal("convS2M conversion error");
		if(err)
			vtfree(err);

		if(write(mfd[1], mdata, n) != n)
			sysfatal("mount write: %r");
	}
}
Exemplo n.º 13
0
Arquivo: cs.c Projeto: brho/akaros
static void io(void)
{
	long n;
	uint8_t mdata[IOHDRSZ + Maxfdata];
	struct job *job;
	pthread_attr_t pth_attr;

	/*
	 * each request is handled via a thread. Somewhat less efficient than
	 * the old cs but way cleaner.
	 */

	pthread_attr_init(&pth_attr);
	pthread_attr_setdetachstate(&pth_attr, PTHREAD_CREATE_DETACHED);
	for (;;) {
		n = read9pmsg(mfd[0], mdata, sizeof(mdata));
		if (n <= 0)
			error(1, 0, "%s: %r", "mount read");
		job = newjob();
		if (convM2S(mdata, n, &job->request) != n) {
			fprintf(stderr,
			        "convM2S went south: format error %ux %ux %ux %ux %ux",
				mdata[0], mdata[1], mdata[2], mdata[3],
				mdata[4]);
			error(1, 0, "format error %ux %ux %ux %ux %ux",
			      mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
			freejob(job);
			continue;
		}
		/* stash the thread in the job so we can join them all
		 * later if we want to.
		 */
		if (pthread_create(&job->thread, &pth_attr, &job_thread, job)) {
			error(1, 0, "%s: %r", "Failed to create job");
			continue;
		}
	}
}
Exemplo n.º 14
0
void
ioproc0(void *v)
{
	long n;
	Mfile *mf;
	uchar mdata[IOHDRSZ + Maxfdata];
	Request req;
	Job *job;

	USED(v);

	for(;;){
		n = read9pmsg(mfd[0], mdata, sizeof mdata);
		if(n <= 0){
			syslog(0, logfile, "error reading mntpt: %r");
			break;
		}
		job = newjob();
		if(convM2S(mdata, n, &job->request) != n){
			freejob(job);
			continue;
		}
		if(debug)
			syslog(0, logfile, "%F", &job->request);

		getactivity(&req);
		req.aborttime = now + 60;	/* don't spend more than 60 seconds */

		mf = nil;
		switch(job->request.type){
		case Tversion:
		case Tauth:
		case Tflush:
			break;
		case Tattach:
			mf = newfid(job->request.fid, 1);
			if(mf == nil){
				sendmsg(job, "fid in use");
				goto skip;
			}
			break;
		default:
			mf = newfid(job->request.fid, 0);
			if(mf == nil){
				sendmsg(job, "unknown fid");
				goto skip;
			}
			break;
		}

		switch(job->request.type){
		default:
			syslog(1, logfile, "unknown request type %d", job->request.type);
			break;
		case Tversion:
			rversion(job);
			break;
		case Tauth:
			rauth(job);
			break;
		case Tflush:
			rflush(job);
			break;
		case Tattach:
			rattach(job, mf);
			break;
		case Twalk:
			rwalk(job, mf);
			break;
		case Topen:
			ropen(job, mf);
			break;
		case Tcreate:
			rcreate(job, mf);
			break;
		case Tread:
			rread(job, mf);
			break;
		case Twrite:
			rwrite(job, mf, &req);
			break;
		case Tclunk:
			rclunk(job, mf);
			break;
		case Tremove:
			rremove(job, mf);
			break;
		case Tstat:
			rstat(job, mf);
			break;
		case Twstat:
			rwstat(job, mf);
			break;
		}
skip:
		freejob(job);
		putactivity();
	}
}
Exemplo n.º 15
0
void
io(void)
{
	volatile long n;
	volatile uchar mdata[IOHDRSZ + Maxfdata];
	Job *volatile job;
	Mfile *volatile mf;
	volatile Request req;

	memset(&req, 0, sizeof req);
	/*
	 *  a slave process is sometimes forked to wait for replies from other
	 *  servers.  The master process returns immediately via a longjmp
	 *  through 'mret'.
	 */
	if(setjmp(req.mret))
		putactivity(0);
	req.isslave = 0;
	stop = 0;
	while(!stop){
		procsetname("%d %s/dns Twrites of %d 9p rpcs read; %d alarms",
			stats.qrecvd9p, mntpt, stats.qrecvd9prpc, stats.alarms);
		n = read9pmsg(mfd[0], mdata, sizeof mdata);
		if(n<=0){
			dnslog("error reading 9P from %s: %r", mntpt);
			sleep(2000);	/* don't thrash after read error */
			return;
		}

		stats.qrecvd9prpc++;
		job = newjob();
		if(convM2S(mdata, n, &job->request) != n){
			freejob(job);
			continue;
		}
		mf = newfid(job->request.fid, 0);
		if(debug)
			dnslog("%F", &job->request);

		getactivity(&req, 0);
		req.aborttime = timems() + Maxreqtm;
		req.from = "9p";

		switch(job->request.type){
		default:
			warning("unknown request type %d", job->request.type);
			break;
		case Tversion:
			rversion(job);
			break;
		case Tauth:
			rauth(job);
			break;
		case Tflush:
			rflush(job);
			break;
		case Tattach:
			rattach(job, mf);
			break;
		case Twalk:
			rwalk(job, mf);
			break;
		case Topen:
			ropen(job, mf);
			break;
		case Tcreate:
			rcreate(job, mf);
			break;
		case Tread:
			rread(job, mf);
			break;
		case Twrite:
			/* &req is handed to dnresolve() */
			rwrite(job, mf, &req);
			break;
		case Tclunk:
			rclunk(job, mf);
			break;
		case Tremove:
			rremove(job, mf);
			break;
		case Tstat:
			rstat(job, mf);
			break;
		case Twstat:
			rwstat(job, mf);
			break;
		}

		freejob(job);

		/*
		 *  slave processes die after replying
		 */
		if(req.isslave){
			putactivity(0);
			_exits(0);
		}

		putactivity(0);
	}
	/* kill any udp server, notifier, etc. processes */
	postnote(PNGROUP, getpid(), "die");
	sleep(1000);
}
Exemplo n.º 16
0
void
io(int srvfd)
{
	int n, pid;
	Fcall xreq, xrep;

	req = &xreq;
	rep = &xrep;
	pid = getpid();
	fmtinstall('F', fcallfmt);

	for(;;){
		/*
		 * reading from a pipe or a network device
		 * will give an error after a few eof reads.
		 * however, we cannot tell the difference
		 * between a zero-length read and an interrupt
		 * on the processes writing to us,
		 * so we wait for the error.
		 */
		n = read9pmsg(srvfd, mdata, sizeof mdata);
		if(n < 0)
			break;
		if(n == 0)
			continue;
		if(convM2S(mdata, n, req) == 0)
			continue;

		if(chatty)
			fprint(2, "9660srv %d:<-%F\n", pid, req);

		errno = 0;
		if(!waserror()){
			err_msg[0] = 0;
			if(req->type >= nelem(fcalls) || !fcalls[req->type])
				error("bad fcall type");
			(*fcalls[req->type])();
			poperror();
		}

		if(err_msg[0]){
			rep->type = Rerror;
			rep->ename = err_msg;
		}else{
			rep->type = req->type + 1;
			rep->fid = req->fid;
		}
		rep->tag = req->tag;

		if(chatty)
			fprint(2, "9660srv %d:->%F\n", pid, rep);
		n = convS2M(rep, mdata, sizeof mdata);
		if(n == 0)
			panic(1, "convS2M error on write");
		if(write(srvfd, mdata, n) != n)
			panic(1, "mount write");
		if(nerr_lab != 0)
			panic(0, "err stack %d");
	}
	chat("server shut down");
}
Exemplo n.º 17
0
Arquivo: cpu.c Projeto: 99years/plan9
void
notefs(int fd)
{
	uchar buf[IOHDRSZ+Maxfdata];
	int i, n, ncpunote;
	Fcall f;
	Qid wqid[MAXWELEM];
	Fid *fid, *nfid;
	int doreply;

	rfork(RFNOTEG);
	fmtinstall('F', fcallfmt);

	for(n = 0; n < Nfid; n++){
		fids[n].file = -1;
		fids[n].omode = -1;
	}

	ncpunote = 0;
	for(;;){
		n = read9pmsg(fd, buf, sizeof(buf));
		if(n <= 0){
			if(dbg)
				fprint(2, "read9pmsg(%d) returns %d: %r\n", fd, n);
			break;
		}
		if(convM2S(buf, n, &f) <= BIT16SZ)
			break;
		if(dbg)
			fprint(2, "notefs: ->%F\n", &f);
		doreply = 1;
		fid = getfid(f.fid);
		if(fid == nil){
nofids:
			f.type = Rerror;
			f.ename = Enofile;
			fsreply(fd, &f);
			continue;
		}
		switch(f.type++){
		default:
			f.type = Rerror;
			f.ename = "unknown type";
			break;
		case Tflush:
			flushreq(f.oldtag);
			break;
		case Tversion:
			if(f.msize > IOHDRSZ+Maxfdata)
				f.msize = IOHDRSZ+Maxfdata;
			break;
		case Tauth:
			f.type = Rerror;
			f.ename = "authentication not required";
			break;
		case Tattach:
			f.qid = fstab[Qdir].qid;
			fid->file = Qdir;
			break;
		case Twalk:
			nfid = nil;
			if(f.newfid != f.fid){
				nfid = getfid(f.newfid);
				if(nfid == nil)
					goto nofids;
				nfid->file = fid->file;
				fid = nfid;
			}
			for(i=0; i<f.nwname && i<MAXWELEM; i++){
				if(fid->file != Qdir){
					f.type = Rerror;
					f.ename = Enotdir;
					break;
				}
				if(strcmp(f.wname[i], "..") == 0){
					wqid[i] = fstab[Qdir].qid;
					continue;
				}
				if(strcmp(f.wname[i], "cpunote") != 0){
					if(i == 0){
						f.type = Rerror;
						f.ename = "file does not exist";
					}
					break;
				}
				fid->file = Qcpunote;
				wqid[i] = fstab[Qcpunote].qid;
			}
			if(nfid != nil && (f.type == Rerror || i < f.nwname))
				nfid ->file = -1;
			if(f.type != Rerror){
				f.nwqid = i;
				for(i=0; i<f.nwqid; i++)
					f.wqid[i] = wqid[i];
			}
			break;
		case Topen:
			if(f.mode != OREAD){
				f.type = Rerror;
				f.ename = Eperm;
				break;
			}
			fid->omode = f.mode;
			if(fid->file == Qcpunote)
				ncpunote++;
			f.qid = fstab[fid->file].qid;
			f.iounit = 0;
			break;
		case Tread:
			if(fsread(fd, fid, &f) < 0)
				goto err;
			doreply = 0;
			break;
		case Tclunk:
			if(fid->omode != -1 && fid->file == Qcpunote){
				ncpunote--;
				if(ncpunote == 0)	/* remote side is done */
					goto err;
			}
			fid->file = -1;
			fid->omode = -1;
			break;
		case Tstat:
			if(fsstat(fd, fid, &f) < 0)
				goto err;
			doreply = 0;
			break;
		case Tcreate:
		case Twrite:
		case Tremove:
		case Twstat:
			f.type = Rerror;
			f.ename = Eperm;
			break;
		}
		if(doreply)
			if(fsreply(fd, &f) < 0)
				break;
	}
err:
	if(dbg)
		fprint(2, "notefs exiting: %r\n");
	werrstr("success");
	postnote(PNGROUP, exportpid, "kill");
	if(dbg)
		fprint(2, "postnote PNGROUP %d: %r\n", exportpid);
	close(fd);
}