text: #include <u.h>
  #include <libc.h>
  #include <auth.h>
  #include <fcall.h>
  #include <errno.h>
  #include "dat.h"
  #include "fns.h"
  
  enum
  {
  	Maxfdata	= 8192,
  	Maxiosize	= IOHDRSZ+Maxfdata,
  };
  
  void io(int);
  void rversion(void);
  void	rattach(void);
  void	rauth(void);
  void	rclunk(void);
  void	rcreate(void);
  void	rflush(void);
  void	ropen(void);
  void	rread(void);
  void	rremove(void);
  void	rsession(void);
  void	rstat(void);
  void	rwalk(void);
  void	rwrite(void);
  void	rwstat(void);
  
  static int	openflags(int);
  static void	usage(void);
  
  #define Reqsize (sizeof(Fcall)+Maxfdata)
  
  Fcall *req;
  Fcall *rep;
  
  uchar mdata[Maxiosize];
  char fdata[Maxfdata];
  uchar statbuf[STATMAX];
  
  
  extern Xfsub	*xsublist[];
  extern int	nclust;
  
  jmp_buf	err_lab[16];
  int	nerr_lab;
  char	err_msg[ERRMAX];
  
  int	chatty;
  int	nojoliet;
  int	noplan9;
  int norock;
  
  void    (*fcalls[Tmax])(void);
  
  static void
  initfcalls(void)
  {
  	fcalls[Tversion]=	rversion;
  	fcalls[Tflush]=	rflush;
  	fcalls[Tauth]=	rauth;
  	fcalls[Tattach]=	rattach;
  	fcalls[Twalk]=		rwalk;
  	fcalls[Topen]=		ropen;
  	fcalls[Tcreate]=	rcreate;
  	fcalls[Tread]=		rread;
  	fcalls[Twrite]=	rwrite;
  	fcalls[Tclunk]=	rclunk;
  	fcalls[Tremove]=	rremove;
  	fcalls[Tstat]=		rstat;
  	fcalls[Twstat]=	rwstat;
  }
  
  void
  main(int argc, char **argv)
  {
  	int srvfd, pipefd[2], stdio;
  	Xfsub **xs;
  	char *mtpt;
  
  	initfcalls();
  	stdio = 0;
  	mtpt = nil;
  	ARGBEGIN {
  	case '9':
  		noplan9 = 1;
  		break;
  	case 'c':
  		nclust = atoi(EARGF(usage()));
  		if (nclust <= 0)
  			sysfatal("nclust %d non-positive", nclust);
  		break;
  	case 'f':
  		deffile = EARGF(usage());
  		break;
  	case 'r':
  		norock = 1;
  		break;
  	case 's':
  		stdio = 1;
  		break;
  	case 'v':
  		chatty = 1;
  		break;
  	case 'J':
  		nojoliet = 1;
  		break;
  	case 'm':
  		mtpt = EARGF(usage());
  		break;
  	default:
  		usage();
  	} ARGEND
  
  	switch(argc) {
  	case 0:
  		break;
  	case 1:
  		srvname = argv[0];
  		break;
  	default:
  		usage();
  	}
  
  	iobuf_init();
  	for(xs=xsublist; *xs; xs++)
  		(*(*xs)->reset)();
  
  	if(stdio) {
  		pipefd[0] = 0;
  		pipefd[1] = 1;
  	} else {
  		close(0);
  		close(1);
  		open("/dev/null", OREAD);
  		open("/dev/null", OWRITE);
  		if(pipe(pipefd) < 0)
  			panic(1, "pipe");
  		
  		if(post9pservice(pipefd[0], srvname, mtpt) < 0)
  			sysfatal("post9pservice: %r");
  		close(pipefd[0]);
  	}
  	srvfd = pipefd[1];
  	
  	switch(rfork(RFNOWAIT|RFNOTEG|RFFDG|RFPROC)){
  	case -1:
  		panic(1, "fork");
  	default:
  		_exits(0);
  	case 0:
  		break;
  	}
  
  	io(srvfd);
  	exits(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");
  }
  
  static void
  usage(void)
  {
  	fprint(2, "usage: %s [-v] [-9Jr] [-s] [-f devicefile] [srvname]\n", argv0);
  	exits("usage");
  }
  
  void
  error(char *p)
  {
  	strecpy(err_msg, err_msg+sizeof err_msg, p);
  	nexterror();
  }
  
  void
  nexterror(void)
  {
  	longjmp(err_lab[--nerr_lab], 1);
  }
  
  void*
  ealloc(long n)
  {
  	void *p;
  
  	p = malloc(n);
  	if(p == 0)
  		error("no memory");
  	return p;
  }
  
  void
  setnames(Dir *d, char *n)
  {
  	d->name = n;
  	d->uid = n+Maxname;
  	d->gid = n+Maxname*2;
  	d->muid = n+Maxname*3;
  
  	d->name[0] = '\0';
  	d->uid[0] = '\0';
  	d->gid[0] = '\0';
  	d->muid[0] = '\0';
  }
  
  void
  rversion(void)
  {
  	if(req->msize > Maxiosize)
  		rep->msize = Maxiosize;
  	else
  		rep->msize = req->msize;
  	rep->version = "9P2000";
  }
  
  void
  rauth(void)
  {
  	error("9660srv: authentication not required");
  }
  
  void
  rflush(void)
  {
  }
  
  void
  rattach(void)
  {
  	Xfs *xf;
  	Xfile *root;
  	Xfsub **xs;
  
  	chat("attach(fid=%d,uname=\"%s\",aname=\"%s\")...",
  		req->fid, req->uname, req->aname);
  
  	if(waserror()){
  		xfile(req->fid, Clunk);
  		nexterror();
  	}
  	root = xfile(req->fid, Clean);
  	root->qid = (Qid){0, 0, QTDIR};
  	root->xf = xf = ealloc(sizeof(Xfs));
  	memset(xf, 0, sizeof(Xfs));
  	xf->ref = 1;
  	xf->d = getxdata(req->aname);
  
  	for(xs=xsublist; *xs; xs++)
  		if((*(*xs)->attach)(root) >= 0){
  			poperror();
  			xf->s = *xs;
  			xf->rootqid = root->qid;
  			rep->qid = root->qid;
  			return;
  		}
  	error("unknown format");
  }
  
  Xfile*
  doclone(Xfile *of, int newfid)
  {
  	Xfile *nf, *next;
  
  	nf = xfile(newfid, Clean);
  	if(waserror()){
  		xfile(newfid, Clunk);
  		nexterror();
  	}
  	next = nf->next;
  	*nf = *of;
  	nf->next = next;
  	nf->fid = newfid;
  	refxfs(nf->xf, 1);
  	if(nf->len){
  		nf->ptr = ealloc(nf->len);
  		memmove(nf->ptr, of->ptr, nf->len);
  	}else
  		nf->ptr = of->ptr;
  	(*of->xf->s->clone)(of, nf);
  	poperror();
  	return nf;
  }
  
  void
  rwalk(void)
  {
  	Xfile *f, *nf;
  	Isofile *oldptr;
  	int oldlen;
  	Qid oldqid;
  
  	rep->nwqid = 0;
  	nf = nil;
  	f = xfile(req->fid, Asis);
  	if(req->fid != req->newfid)
  		f = nf = doclone(f, req->newfid);
  
  	/* save old state in case of error */
  	oldqid = f->qid;
  	oldlen = f->len;
  	oldptr = f->ptr;
  	if(oldlen){
  		oldptr = ealloc(oldlen);
  		memmove(oldptr, f->ptr, oldlen);
  	}
  
  	if(waserror()){
  		if(nf != nil)
  			xfile(req->newfid, Clunk);
  		if(rep->nwqid == req->nwname){
  			if(oldlen)
  				free(oldptr);
  		}else{
  			/* restore previous state */
  			f->qid = oldqid;
  			if(f->len)
  				free(f->ptr);
  			f->ptr = oldptr;
  			f->len = oldlen;
  		}
  		if(rep->nwqid==req->nwname || rep->nwqid > 0){
  			err_msg[0] = '\0';
  			return;
  		}
  		nexterror();
  	}
  
  	for(rep->nwqid=0; rep->nwqid < req->nwname && rep->nwqid < MAXWELEM; rep->nwqid++){
  		chat("\twalking %s\n", req->wname[rep->nwqid]);
  		if(!(f->qid.type & QTDIR)){
  			chat("\tnot dir: type=%#x\n", f->qid.type);
  			error("walk in non-directory");
  		}
  
  		if(strcmp(req->wname[rep->nwqid], "..")==0){
  			if(f->qid.path != f->xf->rootqid.path)
  				(*f->xf->s->walkup)(f);
  		}else
  			(*f->xf->s->walk)(f, req->wname[rep->nwqid]);
  		rep->wqid[rep->nwqid] = f->qid;
  	}
  	poperror();
  	if(oldlen)
  		free(oldptr);
  }
  
  void
  ropen(void)
  {
  	Xfile *f;
  
  	f = xfile(req->fid, Asis);
  	if(f->flags&Omodes)
  		error("open on open file");
  	if(req->mode&ORCLOSE)
  		error("no removes");
  	(*f->xf->s->open)(f, req->mode);
  	f->flags = openflags(req->mode);
  	rep->qid = f->qid;
  	rep->iounit = 0;
  }
  
  void
  rcreate(void)
  {
  	error("no creates");
  /*
  	Xfile *f;
  
  	if(strcmp(req->name, ".") == 0 || strcmp(req->name, "..") == 0)
  		error("create . or ..");
  	f = xfile(req->fid, Asis);
  	if(f->flags&Omodes)
  		error("create on open file");
  	if(!(f->qid.path&CHDIR))
  		error("create in non-directory");
  	(*f->xf->s->create)(f, req->name, req->perm, req->mode);
  	chat("f->qid=0x%8.8lux...", f->qid.path);
  	f->flags = openflags(req->mode);
  	rep->qid = f->qid;
  */
  }
  
  void
  rread(void)
  {
  	Xfile *f;
  
  	f=xfile(req->fid, Asis);
  	if (!(f->flags&Oread))
  		error("file not opened for reading");
  	if(f->qid.type & QTDIR)
  		rep->count = (*f->xf->s->readdir)(f, (uchar*)fdata, req->offset, req->count);
  	else
  		rep->count = (*f->xf->s->read)(f, fdata, req->offset, req->count);
  	rep->data = fdata;
  }
  
  void
  rwrite(void)
  {
  	Xfile *f;
  
  	f=xfile(req->fid, Asis);
  	if(!(f->flags&Owrite))
  		error("file not opened for writing");
  	rep->count = (*f->xf->s->write)(f, req->data, req->offset, req->count);
  }
  
  void
  rclunk(void)
  {
  	Xfile *f;
  
  	if(!waserror()){
  		f = xfile(req->fid, Asis);
  		(*f->xf->s->clunk)(f);
  		poperror();
  	}
  	xfile(req->fid, Clunk);
  }
  
  void
  rremove(void)
  {
  	error("no removes");
  }
  
  void
  rstat(void)
  {
  	Xfile *f;
  	Dir dir;
  
  	chat("stat(fid=%d)...", req->fid);
  	f=xfile(req->fid, Asis);
  	setnames(&dir, fdata);
  	(*f->xf->s->stat)(f, &dir);
  	if(chatty)
  		showdir(2, &dir);
  	rep->nstat = convD2M(&dir, statbuf, sizeof statbuf);
  	rep->stat = statbuf;
  }
  
  void
  rwstat(void)
  {
  	error("no wstat");
  }
  
  static int
  openflags(int mode)
  {
  	int flags = 0;
  
  	switch(mode & ~(OTRUNC|OCEXEC|ORCLOSE)){
  	case OREAD:
  	case OEXEC:
  		flags = Oread; break;
  	case OWRITE:
  		flags = Owrite; break;
  	case ORDWR:
  		flags = Oread|Owrite; break;
  	}
  	if(mode & ORCLOSE)
  		flags |= Orclose;
  	return flags;
  }
  
  void
  showdir(int fd, Dir *s)
  {
  	char a_time[32], m_time[32];
  	char *p;
  
  	strcpy(a_time, ctime(s->atime));
  	if(p=strchr(a_time, '\n'))	/* assign = */
  		*p = 0;
  	strcpy(m_time, ctime(s->mtime));
  	if(p=strchr(m_time, '\n'))	/* assign = */
  		*p = 0;
  	fprint(fd, "name=\"%s\" qid=(0x%llux,%lud) type=%d dev=%d \
  mode=0x%8.8lux=0%luo atime=%s mtime=%s length=%lld uid=\"%s\" gid=\"%s\"...",
  		s->name, s->qid.path, s->qid.vers, s->type, s->dev,
  		s->mode, s->mode,
  		a_time, m_time, s->length, s->uid, s->gid);
  }
  
  #define	SIZE	1024
  
  void
  chat(char *fmt, ...)
  {
  	va_list arg;
  
  	if(chatty){
  		va_start(arg, fmt);
  		vfprint(2, fmt, arg);
  		va_end(arg);
  	}
  }
  
  void
  panic(int rflag, char *fmt, ...)
  {
  	va_list arg;
  	char buf[SIZE]; int n;
  
  	n = sprint(buf, "%s %d: ", argv0, getpid());
  	va_start(arg, fmt);
  	vseprint(buf+n, buf+SIZE, fmt, arg);
  	va_end(arg);
  	fprint(2, (rflag ? "%s: %r\n" : "%s\n"), buf);
  	if(chatty){
  		fprint(2, "abort\n");
  		abort();
  	}
  	exits("panic");
  }
  