1134 lines · 19.8 KB · C
RawDownload
#include	"u.h"#include	"lib.h"#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*	z;		/* Place to hang out */	Block*	b;		/* reply blocks */	Mntrpc*	flushed;	/* message this one flushes */	char	done;		/* Rpc completed */}; enum{	TAGSHIFT = 5,	TAGMASK = (1<<TAGSHIFT)-1,	NMASK = (64*1024)>>TAGSHIFT,}; static struct Mntalloc{	Lock	lk;	Mnt*	list;		/* Mount devices in use */	Mnt*	mntfree;	/* Free list */	Mntrpc*	rpcfree;	ulong	nrpcfree;	ulong	nrpcused;	ulong	id;	u32int	tagmask[NMASK];} mntalloc; static Chan*	mntchan(void);static Mnt*	mntchk(Chan*);static void	mntdirfix(uchar*, Chan*);static Mntrpc*	mntflushalloc(Mntrpc*);static Mntrpc*	mntflushfree(Mnt*, Mntrpc*);static void	mntfree(Mntrpc*);static void	mntgate(Mnt*);static void	mntqrm(Mnt*, Mntrpc*);static Mntrpc*	mntralloc(Chan*);static long	mntrdwr(int, Chan*, void*, long, vlong);static int	mntrpcread(Mnt*, Mntrpc*);static void	mountio(Mnt*, Mntrpc*);static void	mountmux(Mnt*, Mntrpc*);static void	mountrpc(Mnt*, Mntrpc*);static int	rpcattn(void*); char	Esbadstat[] = "invalid directory entry received from server";char	Enoversion[] = "version not established for mount channel";  static voidmntreset(void){	mntalloc.id = 1;	mntalloc.tagmask[0] = 1;			/* don't allow 0 as a tag */	mntalloc.tagmask[NMASK-1] = 0x80000000;		/* don't allow NOTAG */	fmtinstall('F', fcallfmt);	fmtinstall('D', dirfmt);/* We can't install %M since eipfmt does and is used in the kernel [sape] */} /* * Version is not multiplexed: message sent only once per connection. */intmntversion(Chan *c, char *version, int msize, int returnlen){	Fcall f;	uchar *msg;	Mnt *m;	char *v;	Queue *q;	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->ref.lk);	oo = c->offset;	c->offset += k;	unlock(&c->ref.lk); 	l = devtab[c->type]->write(c, msg, k, oo);	if(l < k){		lock(&c->ref.lk);		c->offset -= k - l;		unlock(&c->ref.lk);		error("short write in fversion");	} 	/* message sent; receive and decode reply */	for(k = 0; k < BIT32SZ || (k < GBIT32(msg) && k < 8192+IOHDRSZ); k += l){		l = devtab[c->type]->read(c, msg+k, 8192+IOHDRSZ-k, c->offset);		if(l <= 0)			error("EOF receiving fversion reply");		lock(&c->ref.lk);		c->offset += l;		unlock(&c->ref.lk);	} 	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");	k = strlen(f.version);	if(strncmp(f.version, v, k) != 0)		error("bad 9P version returned from server");	if(returnlen > 0 && returnlen < k)		error(Eshort); 	v = nil;	kstrdup(&v, f.version);	q = qopen(10*MAXRPC, 0, nil, nil);	if(q == nil){		free(v);		exhausted("mount queues");	} 	/* now build Mnt associated with this connection */	lock(&mntalloc.lk);	m = mntalloc.mntfree;	if(m != nil)		mntalloc.mntfree = m->list;	else {		unlock(&mntalloc.lk);		m = malloc(sizeof(Mnt));		if(m == nil) {			qfree(q);			free(v);			exhausted("mount devices");		}		lock(&mntalloc.lk);	}	m->list = mntalloc.list;	mntalloc.list = m;	m->version = v;	m->id = mntalloc.id++;	m->q = q;	m->msize = f.msize;	unlock(&mntalloc.lk); 	if(returnlen > 0)		memmove(version, f.version, k);	/* length was checked above */ 	poperror();	/* msg */	free(msg); 	lock(&m->lk);	m->queue = nil;	m->rip = nil; 	c->flag |= CMSG;	c->mux = m;	m->c = c;	unlock(&m->lk); 	poperror();	/* c */	qunlock(&c->umqlock); 	return k;} Chan*mntauth(Chan *c, char *spec){	Mnt *m;	Mntrpc *r; 	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(c);	if(waserror()) {		mntfree(r);		nexterror();	} 	r->request.type = Tauth;	r->request.afid = c->fid;	r->request.uname = up->user;	r->request.aname = spec;	mountrpc(m, r); 	c->qid = r->reply.aqid;	c->mchan = m->c;	incref(&m->c->ref);	c->mqid = c->qid;	c->mode = ORDWR;	c->iounit = m->msize-IOHDRSZ; 	poperror();	/* r */	mntfree(r); 	poperror();	/* c */ 	return c; } Chan*mntattach(Chan *c, Chan *ac, char *spec, int flags){	Mnt *m;	Mntrpc *r; 	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(c);	if(waserror()) {		mntfree(r);		nexterror();	}	r->request.type = Tattach;	r->request.fid = c->fid;	if(ac == nil)		r->request.afid = NOFID;	else		r->request.afid = ac->fid;	r->request.uname = up->user;	r->request.aname = spec;	mountrpc(m, r); 	c->qid = r->reply.qid;	c->mchan = m->c;	incref(&m->c->ref);	c->mqid = c->qid; 	poperror();	/* r */	mntfree(r); 	poperror();	/* c */ 	if(flags&MCACHE)		c->flag |= CCACHE;	return c;} static Chan*noattach(char *s){	USED(s);	error(Enoattach);	return nil;} static Chan*mntchan(void){	Chan *c; 	c = devattach('M', 0);	lock(&mntalloc.lk);	c->dev = mntalloc.id++;	unlock(&mntalloc.lk); 	if(c->mchan != nil)		panic("mntchan non-zero %p", c->mchan);	return c;} static Walkqid*mntwalk(Chan *c, Chan *nc, char **name, int nname){	int i, alloc;	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);	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;		nc->flag |= (c->flag & CCACHE);		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->ref);		}		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);	if(waserror()) {		mntfree(r);		nexterror();	}	r->request.type = Tstat;	r->request.fid = c->fid;	mountrpc(m, r); 	if(r->reply.nstat > n){		n = BIT16SZ;		PBIT16((uchar*)dp, r->reply.nstat-2);	}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);	r = mntralloc(c);	if(waserror()) {		mntfree(r);		nexterror();	}	r->request.type = type;

Lines 1–500 of 1134.