1203 lines · 20.8 KB · C
RawDownload
#include	"dat.h"#include	"fns.h"#include	"error.h" /* * References are managed as follows: * The channel to the server - a network connection or pipe - has one * reference for every Chan open on the server.  The server channel has * c->mux set to the Mnt used for muxing control to that server.  Mnts * have no reference count; they go away when c goes away. * Each channel derived from the mount point has mchan set to c, * and increfs/decrefs mchan to manage references on the server * connection. */ #define MAXRPC (IOHDRSZ+8192) struct Mntrpc{	Chan*	c;		/* Channel for whom we are working */	Mntrpc*	list;		/* Free/pending list */	Fcall	request;	/* Outgoing file system protocol message */	Fcall 	reply;		/* Incoming reply */	Mnt*	m;		/* Mount device during rpc */	Rendez	r;		/* Place to hang out */	uchar*	rpc;		/* I/O Data buffer */	uint		rpclen;	/* len of buffer */	Block	*b;		/* reply blocks */	char	done;		/* Rpc completed */	uvlong	stime;		/* start time for mnt statistics */	ulong	reqlen;		/* request length for mnt statistics */	ulong	replen;		/* reply length for mnt statistics */	Mntrpc*	flushed;	/* message this one flushes */}; enum{	TAGSHIFT = 5,			/* ulong has to be 32 bits */	TAGMASK = (1<<TAGSHIFT)-1,	NMASK = (64*1024)>>TAGSHIFT,}; struct Mntalloc{	Lock	l;	Mnt*	list;		/* Mount devices in use */	Mnt*	mntfree;	/* Free list */	Mntrpc*	rpcfree;	int	nrpcfree;	int	nrpcused;	ulong	id;	ulong	tagmask[NMASK];}mntalloc; void	mattach(Mnt*, Chan*, char*);Mnt*	mntchk(Chan*);void	mntdirfix(uchar*, Chan*);Mntrpc*	mntflushalloc(Mntrpc*, ulong);void	mntflushfree(Mnt*, Mntrpc*);void	mntfree(Mntrpc*);void	mntgate(Mnt*);void	mntpntfree(Mnt*);void	mntqrm(Mnt*, Mntrpc*);Mntrpc*	mntralloc(Chan*, ulong);long	mntrdwr(int, Chan*, void*, long, vlong);int	mntrpcread(Mnt*, Mntrpc*);void	mountio(Mnt*, Mntrpc*);void	mountmux(Mnt*, Mntrpc*);void	mountrpc(Mnt*, Mntrpc*);int	rpcattn(void*);Chan*	mntchan(void); char	Esbadstat[] = "invalid directory entry received from server";char Enoversion[] = "version not established for mount channel";  void (*mntstats)(int, Chan*, uvlong, ulong); static voidmntinit(void){	mntalloc.id = 1;	mntalloc.tagmask[0] = 1;			/* don't allow 0 as a tag */	mntalloc.tagmask[NMASK-1] = 0x80000000UL;	/* don't allow NOTAG */	fmtinstall('F', fcallfmt);/*	fmtinstall('D', dirfmt); *//*	fmtinstall('M', dirmodefmt);  */ 	cinit();} /* * Version is not multiplexed: message sent only once per connection. */longmntversion(Chan *c, char *version, int msize, int returnlen){	Fcall f;	uchar *msg;	Mnt *m;	char *v;	long k, l;	uvlong oo;	char buf[128]; 	qlock(&c->umqlock);	/* make sure no one else does this until we've established ourselves */	if(waserror()){		qunlock(&c->umqlock);		nexterror();	} 	/* defaults */	if(msize == 0)		msize = MAXRPC;	if(msize > c->iounit && c->iounit != 0)		msize = c->iounit;	v = version;	if(v == nil || v[0] == '\0')		v = VERSION9P; 	/* validity */	if(msize < 0)		error("bad iounit in version call");	if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)		error("bad 9P version specification"); 	m = c->mux; 	if(m != nil){		qunlock(&c->umqlock);		poperror(); 		strecpy(buf, buf+sizeof buf, m->version);		k = strlen(buf);		if(strncmp(buf, v, k) != 0){			snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v);			error(buf);		}		if(returnlen > 0){			if(returnlen < k)				error(Eshort);			memmove(version, buf, k);		}		return k;	} 	f.type = Tversion;	f.tag = NOTAG;	f.msize = msize;	f.version = v;	msg = malloc(8192+IOHDRSZ);	if(msg == nil)		exhausted("version memory");	if(waserror()){		free(msg);		nexterror();	}	k = convS2M(&f, msg, 8192+IOHDRSZ);	if(k == 0)		error("bad fversion conversion on send"); 	lock(&c->l);	oo = c->offset;	c->offset += k;	unlock(&c->l); 	l = devtab[c->type]->write(c, msg, k, oo); 	if(l < k){		lock(&c->l);		c->offset -= k - l;		unlock(&c->l);		error("short write in fversion");	} 	/* message sent; receive and decode reply */	k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);	if(k <= 0)		error("EOF receiving fversion reply"); 	lock(&c->l);	c->offset += k;	unlock(&c->l); 	l = convM2S(msg, k, &f);	if(l != k)		error("bad fversion conversion on reply");	if(f.type != Rversion){		if(f.type == Rerror)			error(f.ename);		error("unexpected reply type in fversion");	}	if(f.msize > msize)		error("server tries to increase msize in fversion");	if(f.msize<256 || f.msize>1024*1024)		error("nonsense value of msize in fversion");	if(strncmp(f.version, v, strlen(f.version)) != 0)		error("bad 9P version returned from server"); 	/* now build Mnt associated with this connection */	lock(&mntalloc.l);	m = mntalloc.mntfree;	if(m != 0)		mntalloc.mntfree = m->list;	else {		m = malloc(sizeof(Mnt));		if(m == 0) {			unlock(&mntalloc.l);			exhausted("mount devices");		}	}	m->list = mntalloc.list;	mntalloc.list = m;	m->version = nil;	kstrdup(&m->version, f.version);	m->id = mntalloc.id++;	m->q = qopen(10*MAXRPC, 0, nil, nil);	m->msize = f.msize;	unlock(&mntalloc.l); 	poperror();	/* msg */	free(msg); 	lock(&m->l);	m->queue = 0;	m->rip = 0; 	c->flag |= CMSG;	c->mux = m;	m->c = c;	unlock(&m->l); 	poperror();	/* c */	qunlock(&c->umqlock); 	k = strlen(f.version);	if(returnlen > 0){		if(returnlen < k)			error(Eshort);		memmove(version, f.version, k);	} 	return k;} Chan*mntauth(Chan *c, char *spec){	Mnt *m;	Mntrpc *r; 	m = c->mux; 	if(m == nil){		mntversion(c, VERSION9P, MAXRPC, 0);		m = c->mux;		if(m == nil)			error(Enoversion);	} 	c = mntchan();	if(waserror()) {		/* Close must not be called since it will		 * call mnt recursively		 */		chanfree(c);		nexterror();	} 	r = mntralloc(0, m->msize); 	if(waserror()) {		mntfree(r);		nexterror();	} 	r->request.type = Tauth;	r->request.afid = c->fid;	r->request.uname = up->env->user;	r->request.aname = spec;	mountrpc(m, r); 	c->qid = r->reply.aqid;	c->mchan = m->c;	incref(&m->c->r);	c->mqid = c->qid;	c->mode = ORDWR; 	poperror();	/* r */	mntfree(r); 	poperror();	/* c */ 	return c; } static Chan*mntattach(char *muxattach){	Mnt *m;	Chan *c;	Mntrpc *r;	struct bogus{		Chan	*chan;		Chan	*authchan;		char	*spec;		int	flags;	}bogus; 	bogus = *((struct bogus *)muxattach);	c = bogus.chan; 	m = c->mux; 	if(m == nil){		mntversion(c, nil, 0, 0);		m = c->mux;		if(m == nil)			error(Enoversion);	} 	c = mntchan();	if(waserror()) {		/* Close must not be called since it will		 * call mnt recursively		 */		chanfree(c);		nexterror();	} 	r = mntralloc(0, m->msize); 	if(waserror()) {		mntfree(r);		nexterror();	} 	r->request.type = Tattach;	r->request.fid = c->fid;	if(bogus.authchan == nil)		r->request.afid = NOFID;	else		r->request.afid = bogus.authchan->fid;	r->request.uname = up->env->user;	r->request.aname = bogus.spec;	mountrpc(m, r); 	c->qid = r->reply.qid;	c->mchan = m->c;	incref(&m->c->r);	c->mqid = c->qid; 	poperror();	/* r */	mntfree(r); 	poperror();	/* c */ 	if(bogus.flags&MCACHE)		c->flag |= CCACHE;	return c;} Chan*mntchan(void){	Chan *c; 	c = devattach('M', 0);	lock(&mntalloc.l);	c->dev = mntalloc.id++;	unlock(&mntalloc.l); 	if(c->mchan)		panic("mntchan non-zero %p", c->mchan);	return c;} static Walkqid*mntwalk(Chan *c, Chan *nc, char **name, int nname){	volatile int alloc;	int i;	Mnt *m;	Mntrpc *r;	Walkqid *wq; 	if(nc != nil)		print("mntwalk: nc != nil\n");	if(nname > MAXWELEM)		error("devmnt: too many name elements");	alloc = 0;	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));	if(waserror()){		if(alloc && wq->clone!=nil)			cclose(wq->clone);		free(wq);		return nil;	} 	alloc = 0;	m = mntchk(c);	r = mntralloc(c, m->msize);	if(nc == nil){		nc = devclone(c);		/*		 * Until the other side accepts this fid, we can't mntclose it.		 * Therefore set type to 0 for now; rootclose is known to be safe.		 */		nc->type = 0;		alloc = 1;	}	wq->clone = nc; 	if(waserror()) {		mntfree(r);		nexterror();	}	r->request.type = Twalk;	r->request.fid = c->fid;	r->request.newfid = nc->fid;	r->request.nwname = nname;	memmove(r->request.wname, name, nname*sizeof(char*)); 	mountrpc(m, r); 	if(r->reply.nwqid > nname)		error("too many QIDs returned by walk");	if(r->reply.nwqid < nname){		if(alloc)			cclose(nc);		wq->clone = nil;		if(r->reply.nwqid == 0){			free(wq);			wq = nil;			goto Return;		}	} 	/* move new fid onto mnt device and update its qid */	if(wq->clone != nil){		if(wq->clone != c){			wq->clone->type = c->type;			wq->clone->mchan = c->mchan;			incref(&c->mchan->r);		}		if(r->reply.nwqid > 0)			wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];	}	wq->nqid = r->reply.nwqid;	for(i=0; i<wq->nqid; i++)		wq->qid[i] = r->reply.wqid[i];     Return:	poperror();	mntfree(r);	poperror();	return wq;} static intmntstat(Chan *c, uchar *dp, int n){	Mnt *m;	Mntrpc *r; 	if(n < BIT16SZ)		error(Eshortstat);	m = mntchk(c);	r = mntralloc(c, m->msize);	if(waserror()) {		mntfree(r);		nexterror();	}	r->request.type = Tstat;	r->request.fid = c->fid;	mountrpc(m, r); 	if(r->reply.nstat > n){		/* doesn't fit; just patch the count and return */		PBIT16((uchar*)dp, r->reply.nstat);		n = BIT16SZ;	}else{		n = r->reply.nstat;		memmove(dp, r->reply.stat, n);		validstat(dp, n);		mntdirfix(dp, c);	}	poperror();	mntfree(r);	return n;} static Chan*mntopencreate(int type, Chan *c, char *name, int omode, ulong perm){	Mnt *m;	Mntrpc *r; 	m = mntchk(c);

Lines 1–500 of 1203.