⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 exportfs.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#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);}static char*Exversion(Export *fs, Fcall *rpc, uchar *){	if(rpc->msize > Maxrpc)		rpc->msize = Maxrpc;	if(strncmp(rpc->version, "9P", 2) != 0){		rpc->version = "unknown";		return nil;	}	fs->iounit = rpc->msize - IOHDRSZ;	rpc->version = "9P2000";	return nil;}static char*Exauth(Export *, Fcall *, uchar *){	return "vnc: authentication not required";}static char*Exattach(Export *fs, Fcall *rpc, uchar *){	Fid *f;	int w;	w = 0;	if(rpc->aname != nil)		w = strtol(rpc->aname, nil, 10);	if(w < 0 || w > fs->nroots)		error(Ebadspec);	f = Exmkfid(fs, rpc->fid);	if(f == nil)		return Einuse;	if(waserror()){		f->attached = 0;		Exputfid(fs, f);		return up->error;	}	f->chan = cclone(fs->roots[w]);	poperror();	rpc->qid = f->chan->qid;	Exputfid(fs, f);	return nil;}static char*Exclunk(Export *fs, Fcall *rpc, uchar *){	Fid *f;	f = Exgetfid(fs, rpc->fid);	if(f != nil){		f->attached = 0;		Exputfid(fs, f);	}	return nil;}static char*Exwalk(Export *fs, Fcall *rpc, uchar *){	Fid *volatile f, *volatile nf;	Walkqid *wq;	Chan *c;	int i, nwname;	int volatile isnew;	f = Exgetfid(fs, rpc->fid);	if(f == nil)		return Enofid;	nf = nil;	if(waserror()){		Exputfid(fs, f);		if(nf != nil)			Exputfid(fs, nf);		return up->error;	}	/*	 * optional clone, but don't attach it until the walk succeeds.	 */	if(rpc->fid != rpc->newfid){		nf = Exmkfid(fs, rpc->newfid);		if(nf == nil)			error(Einuse);		nf->attached = 0;		isnew = 1;	}else{		nf = Exgetfid(fs, rpc->fid);		isnew = 0;	}	/*	 * let the device do the work	 */	c = f->chan;	nwname = rpc->nwname;	wq = (*devtab[c->type]->walk)(c, nf->chan, rpc->wname, nwname);	if(wq == nil)		error(Enonexist);	poperror();	/*	 * copy qid array	 */	for(i = 0; i < wq->nqid; i++)		rpc->wqid[i] = wq->qid[i];	rpc->nwqid = wq->nqid;	/*	 * update the channel if everything walked correctly.	 */	if(isnew && wq->nqid == nwname){		nf->chan = wq->clone;		nf->attached = 1;	}	free(wq);	Exputfid(fs, f);	Exputfid(fs, nf);	return nil;}static char*Exopen(Export *fs, Fcall *rpc, uchar *){	Fid *volatile f;	Chan *c;	int iou;	f = Exgetfid(fs, rpc->fid);	if(f == nil)		return Enofid;	if(waserror()){		Exputfid(fs, f);		return up->error;	}	c = f->chan;	c = (*devtab[c->type]->open)(c, rpc->mode);	poperror();	f->chan = c;	f->offset = 0;	rpc->qid = f->chan->qid;	iou = f->chan->iounit;	if(iou > fs->iounit)		iou = fs->iounit;	rpc->iounit = iou;	Exputfid(fs, f);	return nil;}static char*Excreate(Export *fs, Fcall *rpc, uchar *){	Fid *f;	Chan *c;	int iou;	f = Exgetfid(fs, rpc->fid);	if(f == nil)		return Enofid;	if(waserror()){		Exputfid(fs, f);		return up->error;	}	c = f->chan;	(*devtab[c->type]->create)(c, rpc->name, rpc->mode, rpc->perm);	poperror();	f->chan = c;	rpc->qid = f->chan->qid;	iou = f->chan->iounit;	if(iou > fs->iounit)		iou = fs->iounit;	rpc->iounit = iou;	Exputfid(fs, f);	return nil;}static char*Exread(Export *fs, Fcall *rpc, uchar *buf){	Fid *f;	Chan *c;	long off;	f = Exgetfid(fs, rpc->fid);	if(f == nil)		return Enofid;	c = f->chan;	if(waserror()){		Exputfid(fs, f);		return up->error;	}	rpc->data = (char*)buf;	off = rpc->offset;	c->offset = off;	rpc->count = (*devtab[c->type]->read)(c, rpc->data, rpc->count, off);	poperror();	Exputfid(fs, f);	return nil;}static char*Exwrite(Export *fs, Fcall *rpc, uchar *){	Fid *f;	Chan *c;	f = Exgetfid(fs, rpc->fid);	if(f == nil)		return Enofid;	if(waserror()){		Exputfid(fs, f);		return up->error;	}	c = f->chan;	if(c->qid.type & QTDIR)		error(Eisdir);	rpc->count = (*devtab[c->type]->write)(c, rpc->data, rpc->count, rpc->offset);	poperror();	Exputfid(fs, f);	return nil;}static char*Exstat(Export *fs, Fcall *rpc, uchar *buf){	Fid *f;	Chan *c;	f = Exgetfid(fs, rpc->fid);	if(f == nil)		return Enofid;	if(waserror()){		Exputfid(fs, f);		return up->error;	}	c = f->chan;	rpc->stat = buf;	rpc->nstat = (*devtab[c->type]->stat)(c, rpc->stat, Maxrpc);	poperror();	Exputfid(fs, f);	return nil;}static char*Exwstat(Export *fs, Fcall *rpc, uchar *){	Fid *f;	Chan *c;	f = Exgetfid(fs, rpc->fid);	if(f == nil)		return Enofid;	if(waserror()){		Exputfid(fs, f);		return up->error;	}	c = f->chan;	(*devtab[c->type]->wstat)(c, rpc->stat, rpc->nstat);	poperror();	Exputfid(fs, f);	return nil;}static char*Exremove(Export *fs, Fcall *rpc, uchar *){	Fid *f;	Chan *c;	f = Exgetfid(fs, rpc->fid);	if(f == nil)		return Enofid;	if(waserror()){		Exputfid(fs, f);		return up->error;	}	c = f->chan;	(*devtab[c->type]->remove)(c);	poperror();	/*	 * chan is already clunked by remove.	 * however, we need to recover the chan,	 * and follow sysremove's lead in making to point to root.	 */	c->type = 0;	f->attached = 0;	Exputfid(fs, f);	return nil;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -