803 lines · 13.3 KB · C
RawDownload
#include	<u.h>#include	<libc.h>#include	<fcall.h>#include	"compat.h"#include	"error.h" typedef	struct Fid	Fid;typedef	struct Export	Export;typedef	struct Exq	Exq;typedef	struct Exwork	Exwork; enum{	Nfidhash	= 32,	Maxfdata	= 8192,	Maxrpc		= IOHDRSZ + Maxfdata,}; struct Export{	Ref	r;	Exq*	work;	Lock	fidlock;	Fid*	fid[Nfidhash];	int	io;		/* fd to read/write */	int	iounit;	int	nroots;	Chan	**roots;}; struct Fid{	Fid*	next;	Fid**	last;	Chan*	chan;	long	offset;	int	fid;	int	ref;		/* fcalls using the fid; locked by Export.Lock */	int	attached;	/* fid attached or cloned but not clunked */}; struct Exq{	Lock	lk;	int	responding;	/* writing out reply message */	int	noresponse;	/* don't respond to this one */	Exq*	next;	int	shut;		/* has been noted for shutdown */	Export*	export;	void*	slave;	Fcall	rpc;	uchar	buf[Maxrpc];}; struct Exwork{	Lock	l; 	int	ref; 	int	nwaiters;	/* queue of slaves waiting for work */	QLock	qwait;	Rendez	rwait; 	Exq	*head;		/* work waiting for a slave */	Exq	*tail;}; Exwork exq; static void	exshutdown(Export*);static void	exflush(Export*, int, int);static void	exslave(void*);static void	exfree(Export*);static void	exportproc(Export*); static char*	Exattach(Export*, Fcall*, uchar*);static char*	Exauth(Export*, Fcall*, uchar*);static char*	Exclunk(Export*, Fcall*, uchar*);static char*	Excreate(Export*, Fcall*, uchar*);static char*	Exversion(Export*, Fcall*, uchar*);static char*	Exopen(Export*, Fcall*, uchar*);static char*	Exread(Export*, Fcall*, uchar*);static char*	Exremove(Export*, Fcall*, uchar*);static char*	Exsession(Export*, Fcall*, uchar*);static char*	Exstat(Export*, Fcall*, uchar*);static char*	Exwalk(Export*, Fcall*, uchar*);static char*	Exwrite(Export*, Fcall*, uchar*);static char*	Exwstat(Export*, Fcall*, uchar*); static char	*(*fcalls[Tmax])(Export*, Fcall*, uchar*); static char	Enofid[]   = "no such fid";static char	Eseekdir[] = "can't seek on a directory";static char	Ereaddir[] = "unaligned read of a directory";static int	exdebug = 0; intsysexport(int fd, Chan **roots, int nroots){	Export *fs; 	fs = smalloc(sizeof(Export));	fs->r.ref = 1;	fs->io = fd;	fs->roots = roots;	fs->nroots = nroots; 	exportproc(fs); 	return 0;} static voidexportinit(void){	lock(&exq.l);	exq.ref++;	if(fcalls[Tversion] != nil){		unlock(&exq.l);		return;	} 	fmtinstall('F', fcallfmt);	fcalls[Tversion] = Exversion;	fcalls[Tauth] = Exauth;	fcalls[Tattach] = Exattach;	fcalls[Twalk] = Exwalk;	fcalls[Topen] = Exopen;	fcalls[Tcreate] = Excreate;	fcalls[Tread] = Exread;	fcalls[Twrite] = Exwrite;	fcalls[Tclunk] = Exclunk;	fcalls[Tremove] = Exremove;	fcalls[Tstat] = Exstat;	fcalls[Twstat] = Exwstat;	unlock(&exq.l);} static voidexportproc(Export *fs){	Exq *q;	int n, ed; 	exportinit();	ed = errdepth(-1);	for(;;){		errdepth(ed);		q = smalloc(sizeof(Exq)); 		n = read9pmsg(fs->io, q->buf, Maxrpc);		if(n <= 0 || convM2S(q->buf, n, &q->rpc) != n)			goto bad; 		if(exdebug)			print("export %d <- %F\n", getpid(), &q->rpc); 		if(q->rpc.type == Tflush){			exflush(fs, q->rpc.tag, q->rpc.oldtag);			free(q);			continue;		} 		q->export = fs;		incref(&fs->r); 		lock(&exq.l);		if(exq.head == nil)			exq.head = q;		else			exq.tail->next = q;		q->next = nil;		exq.tail = q;		n = exq.nwaiters;		if(n)			exq.nwaiters = n - 1;		unlock(&exq.l);		if(!n)			kproc("exportfs", exslave, nil);		rendwakeup(&exq.rwait);	}bad:	free(q);	if(exdebug)		fprint(2, "export proc shutting down: %r\n");	exshutdown(fs);	exfree(fs);} static voidexflush(Export *fs, int flushtag, int tag){	Exq *q, **last;	Fcall fc;	uchar buf[Maxrpc];	int n; 	/* hasn't been started? */	lock(&exq.l);	last = &exq.head;	for(q = exq.head; q != nil; q = q->next){		if(q->export == fs && q->rpc.tag == tag){			*last = q->next;			unlock(&exq.l);			exfree(fs);			free(q);			goto Respond;		}		last = &q->next;	}	unlock(&exq.l); 	/* in progress? */	lock(&fs->r);	for(q = fs->work; q != nil; q = q->next){		if(q->rpc.tag == tag){			lock(&q->lk);			q->noresponse = 1;			if(!q->responding)				rendintr(q->slave);			unlock(&q->lk);			break;		}	}	unlock(&fs->r); Respond:	fc.type = Rflush;	fc.tag = flushtag; 	n = convS2M(&fc, buf, Maxrpc);	if(n == 0)		panic("convS2M error on write");	if(write(fs->io, buf, n) != n)		panic("mount write");} static voidexshutdown(Export *fs){	Exq *q, **last; 	lock(&exq.l);	last = &exq.head;	for(q = exq.head; q != nil; q = *last){		if(q->export == fs){			*last = q->next;			exfree(fs);			free(q);			continue;		}		last = &q->next;	} 	/*	 * cleanly shut down the slaves if this is the last fs around	 */	exq.ref--;	if(!exq.ref)		rendwakeup(&exq.rwait);	unlock(&exq.l); 	/*	 * kick any sleepers	 */	lock(&fs->r);	for(q = fs->work; q != nil; q = q->next){		lock(&q->lk);		q->noresponse = 1;		if(!q->responding)			rendintr(q->slave);		unlock(&q->lk);	}	unlock(&fs->r);} static voidexfree(Export *fs){	Fid *f, *n;	int i; 	if(decref(&fs->r) != 0)		return;	for(i = 0; i < Nfidhash; i++){		for(f = fs->fid[i]; f != nil; f = n){			if(f->chan != nil)				cclose(f->chan);			n = f->next;			free(f);		}	}	free(fs);} static intexwork(void *){	int work; 	lock(&exq.l);	work = exq.head != nil || !exq.ref;	unlock(&exq.l);	return work;} static voidexslave(void *){	Export *fs;	Exq *q, *t, **last;	char *volatile err;	int n, ed; 	while(waserror())		fprint(2, "exslave %d errored out of loop -- heading back in!\n", getpid());	ed = errdepth(-1);	for(;;){		errdepth(ed);		qlock(&exq.qwait);		if(waserror()){			qunlock(&exq.qwait);			nexterror();		}		rendsleep(&exq.rwait, exwork, nil); 		lock(&exq.l);		if(!exq.ref){			unlock(&exq.l);			poperror();			qunlock(&exq.qwait);			break;		}		q = exq.head;		if(q == nil){			unlock(&exq.l);			poperror();			qunlock(&exq.qwait);			continue;		}		exq.head = q->next;		if(exq.head == nil)			exq.tail = nil;		poperror();		qunlock(&exq.qwait); 		/*		 * put the job on the work queue before it's		 * visible as off of the head queue, so it's always		 * findable for flushes and shutdown		 */		q->slave = up;		q->noresponse = 0;		q->responding = 0;		rendclearintr();		fs = q->export;		lock(&fs->r);		q->next = fs->work;		fs->work = q;		unlock(&fs->r); 		unlock(&exq.l); 		if(exdebug > 1)			print("exslave dispatch %d %F\n", getpid(), &q->rpc); 		if(waserror()){			print("exslave err %r\n");			err = up->error;		}else{			if(q->rpc.type >= Tmax || !fcalls[q->rpc.type])				err = "bad fcall type";			else				err = (*fcalls[q->rpc.type])(fs, &q->rpc, &q->buf[IOHDRSZ]);			poperror();		} 		q->rpc.type++;		if(err){			q->rpc.type = Rerror;			q->rpc.ename = err;		}		n = convS2M(&q->rpc, q->buf, Maxrpc); 		if(exdebug)			print("exslave %d -> %F\n", getpid(), &q->rpc); 		lock(&q->lk);		if(!q->noresponse){			q->responding = 1;			unlock(&q->lk);			write(fs->io, q->buf, n);		}else			unlock(&q->lk); 		/*		 * exflush might set noresponse at this point, but		 * setting noresponse means don't send a response now;		 * it's okay that we sent a response already.		 */		if(exdebug > 1)			print("exslave %d written %d\n", getpid(), q->rpc.tag); 		lock(&fs->r);		last = &fs->work;		for(t = fs->work; t != nil; t = t->next){			if(t == q){				*last = q->next;				break;			}			last = &t->next;		}		unlock(&fs->r); 		exfree(q->export);		free(q); 		rendclearintr();		lock(&exq.l);		exq.nwaiters++;		unlock(&exq.l);	}	if(exdebug)		fprint(2, "export slaveshutting down\n");	kexit();} Fid*Exmkfid(Export *fs, int fid){	ulong h;	Fid *f, *nf; 	nf = mallocz(sizeof(Fid), 1);	if(nf == nil)		return nil;	lock(&fs->fidlock);	h = fid % Nfidhash;	for(f = fs->fid[h]; f != nil; f = f->next){		if(f->fid == fid){			unlock(&fs->fidlock);			free(nf);			return nil;		}	} 	nf->next = fs->fid[h];	if(nf->next != nil)		nf->next->last = &nf->next;	nf->last = &fs->fid[h];	fs->fid[h] = nf; 	nf->fid = fid;	nf->ref = 1;	nf->attached = 1;	nf->offset = 0;	nf->chan = nil;	unlock(&fs->fidlock);	return nf;} Fid*Exgetfid(Export *fs, int fid){	Fid *f;	ulong h; 	lock(&fs->fidlock);	h = fid % Nfidhash;	for(f = fs->fid[h]; f; f = f->next){		if(f->fid == fid){			if(f->attached == 0)				break;			f->ref++;			unlock(&fs->fidlock);			return f;		}	}	unlock(&fs->fidlock);	return nil;} voidExputfid(Export *fs, Fid *f){	lock(&fs->fidlock);	f->ref--;	if(f->ref == 0 && f->attached == 0){		if(f->chan != nil)			cclose(f->chan);		f->chan = nil;		*f->last = f->next;		if(f->next != nil)			f->next->last = f->last;		unlock(&fs->fidlock);		free(f);		return;	}	unlock(&fs->fidlock);

Lines 1–500 of 803.