Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/gnot/chan.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname gnot/chan.c 1990/03091
## diff -e /dev/null /n/bootesdump/1990/03091/sys/src/9/68020/chan.c
0a
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"


struct{
	Lock;
	Chan	*free;
}chanalloc;

int
incref(Ref *r)
{
	lock(r);
	r->ref++;
	unlock(r);
	return r->ref;
}

int
decref(Ref *r)
{
	lock(r);
	r->ref--;
	unlock(r);
	return r->ref;
}

void
chaninit(void)
{
	int i;
	Chan *c;

	chanalloc.free = ialloc(conf.nchan*sizeof(Chan), 0);

	c = chanalloc.free;
	for(i=0; i<conf.nchan-1; i++,c++){
		c->fid = i;
		c->next = c+1;
	}
	c->next = 0;
}

void
chandevreset(void)
{
	int i;

	for(i=0; i<strlen(devchar); i++)
		(*devtab[i].reset)();
}

void
chandevinit(void)
{
	int i;

	for(i=0; i<strlen(devchar); i++)
		(*devtab[i].init)();
}

Chan*
newchan(void)
{
	Chan *c;

loop:
	lock(&chanalloc);
	if(c = chanalloc.free){		/* assign = */
		chanalloc.free = c->next;
		c->flag = 0;
		c->ref = 1;
		unlock(&chanalloc);
		c->offset = 0;
		c->mnt = 0;
		c->stream = 0;
		c->mchan = 0;
		c->mqid = 0;
		return c;
	}
	unlock(&chanalloc);
	print("no chans\n");
	if(u == 0)
		panic("newchan");
	u->p->state = Wakeme;
	alarm(1000, wakeme, u->p);
	sched();
	goto loop;
}

void
close(Chan *c)
{
	if(decref(c) == 0){
		if(!waserror()){
			(*devtab[c->type].close)(c);
			poperror();
		}
		lock(&chanalloc);
		c->next = chanalloc.free;
		chanalloc.free = c;
		unlock(&chanalloc);
	}
}

int
eqchan(Chan *a, Chan *b, long qmask)
{
	if((a->qid^b->qid) & qmask)
		return 0;
	if(a->type != b->type)
		return 0;
	if(a->dev != b->dev)
		return 0;
	return 1;
}

/*
 * omnt is locked.  return with nmnt locked.
 */
Mount*
mountsplit(Mount *omnt)
{
	Mount *nmnt;

	print("mount copy on write\n");
	nmnt = newmount();
	lock(nmnt);
	nmnt->term = omnt->term;
	nmnt->mountid = omnt->mountid;
	nmnt->next = omnt->next;
	if(nmnt->next)
		incref(nmnt);
	nmnt->c = omnt->c;
	incref(nmnt->c);
	omnt->ref--;
	unlock(omnt);
	return nmnt;
}

int
mount(Chan *new, Chan *old, int flag)
{
	int i;
	Mtab *mt, *mz;
	Mount *mnt, *omnt, *nmnt, *pmnt;
	Pgrp *pg;
	int isnew;

	if(CHDIR & (old->qid^new->qid))
		error(0, Emount);
	if((old->qid&CHDIR)==0 && (flag&MORDER)!=MREPL)
		error(0, Emount);

	mz = 0;
	isnew = 0;
	pg = u->p->pgrp;
	lock(pg);
	if(waserror()){
		unlock(pg);
		nexterror();
	}
	/*
	 * Is old already in mount table?
	 */
	mt = pg->mtab;
	for(i=0; i<pg->nmtab; i++,mt++){
		if(mt->c==0 && mz==0)
			mz = mt;
		else if(eqchan(mt->c, old, CHDIR|QPATH))
			goto Found;
	}
	isnew = 1;
	if(mz == 0){
		if(i == conf.nmtab)
			error(0, Enomount);
		mz = &pg->mtab[i];
		pg->nmtab++;
	}
	mz->mnt = 0;
	mz->c = old;
	mt = mz;

    Found:
	new->flag = CMOUNT;
	if(flag & MCREATE)
		new->flag |= CCREATE;
	mnt = newmount();
	mnt->c = new;

	switch(flag & MORDER){
	/*
	 * These two always go at head of list
	 */
	case MBEFORE:
		if(mt->mnt == 0)
			error(0, Enotunion);
		/* fall through */

	case MREPL:
		omnt = mt->mnt;
		if(omnt)
			incref(omnt);
		mnt->next = omnt;
		mt->mnt = mnt;
		mnt->term = 1;
		if((flag&MORDER) == MBEFORE)
			mnt->term = 0;
		break;

	/*
	 * This one never goes at head of list
	 */
	case MAFTER:
		if(mt->mnt == 0)
			error(0, Enotunion);
		omnt = mt->mnt;
		pmnt = 0;
		while(!omnt->term){
			lock(omnt);
			if(omnt->ref > 1){
				omnt = mountsplit(omnt);
				if(pmnt)
					pmnt->next = omnt;
				else
					mt->mnt = omnt;
			}
			unlock(omnt);
			nmnt = omnt->next;
			if(nmnt == 0)
				panic("MAFTER term");
			pmnt = omnt;
			omnt = nmnt;
		}
		mnt->next = omnt->next;
		omnt->next = mnt;
		mnt->term = 1;
		omnt->term = 0;
		break;
	}

	incref(new);
	if(isnew)
		incref(old);
	unlock(pg);
	poperror();
	return mnt->mountid;
}

Chan*
clone(Chan *c, Chan *nc)
{
	return (*devtab[c->type].clone)(c, nc);
}

Chan*
domount(Chan *c)
{
	int i;
	ulong mntid;
	Mtab *mt;
	Mount *mnt;
	Pgrp *pg;
	Chan *nc, *mc;

	pg = u->p->pgrp;
	/*
	 * Is c in in mount table?
	 */
	mt = pg->mtab;
	for(i=0; i<pg->nmtab; i++,mt++)
		if(mt->c && eqchan(mt->c, c, CHDIR|QPATH))
			goto Found;
	/*
	 * No; c is unaffected
	 */
	return c;

	/*
	 * Yes; move c through table
	 */
    Found:
	lock(pg);
	if(!eqchan(mt->c, c, CHDIR|QPATH)){	/* table changed underfoot */
		print("domount: changed underfoot?\n");
		unlock(pg);
		return c;
	}
	mnt = mt->mnt;
	mntid = mnt->mountid;
	mc = mnt->c;
	incref(mc);
	unlock(pg);
	if(waserror()){
		close(mc);
		nexterror();
	}
	nc = clone(mc, 0);
	close(mc);
	poperror();
	close(c);
	nc->mnt = mnt;
	nc->mountid = mntid;
	return nc;
}

Chan*
walk(Chan *ac, char *name, int domnt)
{
	Mount *mnt;
	int first = 1;
	Chan *c = ac;
	Chan *nc, *mc;
	Pgrp *pg = u->p->pgrp;

	/*
	 * name may be empty if the file name is "/", "#c" etc.
	 */
    Again:
	if(name[0] && (*devtab[c->type].walk)(c, name)==0){
		if(!(c->flag&CMOUNT))
			goto Notfound;
		mnt = c->mnt;
		if(mnt == 0)
			panic("walk");
		lock(pg);
		if(mnt->term){
			unlock(pg);
			goto Notfound;
		}
		if(c->mountid != mnt->mountid){
			print("walk: changed underfoot?\n");
			unlock(pg);
			goto Notfound;
		}
		mnt = mnt->next;
		mc = mnt->c;
		incref(mc);
		unlock(pg);
		if(waserror()){
			close(mc);
			nexterror();
		}
		if(mnt == 0)
			panic("walk 1");
		nc = clone(mc, 0);
		close(mc);
		poperror();
		if(!first)
			close(c);
		nc->mnt = mnt;
		c = nc;
		first = 0;
		goto Again;
	}

	if(name[0])			/* walk succeeded */
		c->flag &= ~CMOUNT;

	if(!first)
		close(ac);

	if(domnt)
		return domount(c);
	return c;

    Notfound:
	if(!first)
		close(c);
	return 0;
}

/*
 * c is a mounted non-creatable directory.  find a creatable one.
 */
Chan*
createdir(Chan *c)
{
	Mount *mnt;
	Pgrp *pg = u->p->pgrp;
	Chan *mc, *nc;

	lock(pg);
	if(waserror()){
		unlock(pg);
		nexterror();
	}
	mnt = c->mnt;
	if(c->mountid != mnt->mountid){
		print("createdir: changed underfoot?\n");
		error(0, Enocreate);
	}
	do{
		if(mnt->term)
			error(0, Enocreate);
		mnt = mnt->next;
	}while(!(mnt->c->flag&CCREATE));
	mc = mnt->c;
	incref(mc);
	unlock(pg);
	if(waserror()){
		close(mc);
		nexterror();
	}
	nc = clone(mc, 0);
	poperror();
	close(c);
	close(mc);
	nc->mnt = mnt;
	return nc;
}

/*
 * Turn a name into a channel.
 * &name[0] is known to be a valid address.  It may be a kernel address.
 */
Chan*
namec(char *name, int amode, int omode, ulong perm)
{
	Chan *c, *nc;
	int t;
	int mntok;
	char *elem = u->elem;

	if(name[0] == 0)
		error(0, Enonexist);
	mntok = 1;
	if(name[0] == '/'){
		c = clone(u->slash, 0);
		/*
		 * Skip leading slashes.
		 */
		name = skipslash(name);
	}else if(name[0] == '#'){
		mntok = 0;
		if(!((ulong)name & KZERO))
			validaddr((ulong)(name+1), 2, 0);
		if(name[1]=='|' || ('A'<=name[1] && name[1]<='Z'))
			error(0, Enonexist);
		t = devno(name[1], 1);
		if(t == -1)
			error(0, Ebadsharp);
		name += 2;
		if(*name == '/'){
			name = skipslash(name);
			elem[0]=0;
		}else
			name = nextelem(name, elem);
		c = (*devtab[t].attach)(elem);
	}else
		c = clone(u->dot, 0);

	if(waserror()){
		close(c);
		nexterror();
	}

	name = nextelem(name, elem);
	if(mntok)
	if(!(amode==Amount && elem[0]==0))	/* don't domount on dot or slash */
		c = domount(c);			/* see case Atodir below */

	/*
	 * How to treat the last element of the name depends on the operation.
	 * Therefore do all but the last element by the easy algorithm.
	 */
	while(*name){
		if((nc=walk(c, elem, mntok)) == 0)
			error(0, Enonexist);
		c = nc;
		name = nextelem(name, elem);
	}
	/*
	 * Last element; act according to type of access.
	 */

	switch(amode){
	case Aaccess:
		if((nc=walk(c, elem, mntok)) == 0)
			error(0, Enonexist);
		c = nc;
		break;

	case Atodir:
		/*
		 * Directories (e.g. for cd) are left before the mount point,
		 * so one may mount on / or . and see the effect.
		 */
		if((nc=walk(c, elem, 0)) == 0)
			error(0, Enonexist);
		c = nc;
		if(!(c->qid & CHDIR))
			error(0, Enotdir);
		break;

	case Aopen:
		if((nc=walk(c, elem, mntok)) == 0)
			error(0, Enonexist);
		c = nc;
	Open:
		c = (*devtab[c->type].open)(c, omode);
		break;

	case Amount:
		/*
		 * When mounting on an already mounted upon directory, one wants
		 * the second mount to be attached to the original directory, not
		 * the replacement.
		 */
		if((nc=walk(c, elem, 0)) == 0)
			error(0, Enonexist);
		c = nc;
		break;

	case Acreate:
		if((nc=walk(c, elem, 1)) != 0){
			c = nc;
			omode |= OTRUNC;
			goto Open;
		}
		if((c->flag&(CMOUNT|CCREATE)) == CMOUNT)
			c = createdir(c);
		(*devtab[c->type].create)(c, elem, omode, perm);
		break;

	default:
		panic("unknown namec access %d\n", amode);
	}
	poperror();
	return c;
}

/*
 * name[0] is addressable.
 */
char*
skipslash(char *name)
{
	while(*name == '/'){
		if(((ulong)name&KZERO)==0 && (((ulong)name+1)&(BY2PG-1))==0)
			validaddr((ulong)name+1, 1, 0);
		name++;
	}
	return name;
}

char isfrog[]={
	/*NUL*/	1, 1, 1, 1, 1, 1, 1, 1,
	/*BKS*/	1, 1, 1, 1, 1, 1, 1, 1,
	/*DLE*/	1, 1, 1, 1, 1, 1, 1, 1,
	/*CAN*/	1, 1, 1, 1, 1, 1, 1, 1,
	[' ']	1,
	['/']	1,
	[0x7f]	1,
};

/*
 * name[0] should not be a slash.
 * Advance name to next element in path, copying current element into elem.
 * Return pointer to next element, skipping slashes.
 * &name[0] is known to be a valid address.
 */
char*
nextelem(char *name, char *elem)
{
	int i, user, c;
	char *end, *e;

	if(*name == '/')
		error(0, Efilename);
	end = vmemchr(name, 0, NAMELEN);
	if(end == 0){
		end = vmemchr(name, '/', NAMELEN);
		if(end == 0)
			error(0, Efilename);
	}else{
		e = memchr(name, '/', end-name);
		if(e)
			end = e;
	}
	while(name < end){
		c = *name++;
		if((c&0x80) || isfrog[c])
			error(0, Ebadchar);
		*elem++ = c;
	}
	*elem = 0;
	return skipslash(name);
}

void
isdir(Chan *c)
{
	if(c->qid & CHDIR)
		return;
	error(0, Enotdir);
}
.
## diffname gnot/chan.c 1990/0321
## diff -e /n/bootesdump/1990/03091/sys/src/9/68020/chan.c /n/bootesdump/1990/0321/sys/src/9/68020/chan.c
394c
		pprint("createdir: changed underfoot?\n");
.
355a
		nc->mountid = mnt->mountid;
.
336c
			pprint("walk: changed underfoot?\n");
.
289c
		pprint("domount: changed underfoot?\n");
.
248a
	}
	if(islast)
		pg->nmtab++;
.
247c
	if(mz){
		mz->c = old;
.
185d
182c
		islast++;
.
177d
175a
		}
.
174c
		else if(eqchan(mt->c, old, CHDIR|QPATH)){
			mz = 0;
.
163a
		if(mnt){
			mnt->c = 0;	/* caller will close new */
			closemount(mnt);
		}
.
160c
	islast = 0;
	mnt = 0;
.
152c
	int islast;
.
## diffname gnot/chan.c 1990/0603
## diff -e /n/bootesdump/1990/0321/sys/src/9/68020/chan.c /n/bootesdump/1990/0603/sys/src/9/68020/chan.c
217a
		else
			mnt->term = 1;
.
215d
210,213c
		mnt->next = mt->mnt;
.
137c
		incref(nmnt->next);
.
## diffname gnot/chan.c 1990/0604
## diff -e /n/bootesdump/1990/0603/sys/src/9/68020/chan.c /n/bootesdump/1990/0604/sys/src/9/68020/chan.c
130d
## diffname gnot/chan.c 1990/0707
## diff -e /n/bootesdump/1990/0604/sys/src/9/68020/chan.c /n/bootesdump/1990/0707/sys/src/9/68020/chan.c
74a
		c->type = 0;	/* if closed before changed, this calls rooterror, a nop */
.
## diffname gnot/chan.c 1990/08101
## diff -e /n/bootesdump/1990/0707/sys/src/9/68020/chan.c /n/bootesdump/1990/08101/sys/src/9/68020/chan.c
450c
		if(name[1]=='|' || name[1]=='M')
.
## diffname gnot/chan.c 1990/08141
## diff -e /n/bootesdump/1990/08101/sys/src/9/68020/chan.c /n/bootesdump/1990/08141/sys/src/9/68020/chan.c
535a
		if(omode & OCEXEC)
			c->flag |= CCEXEC;
.
513a
		if(omode & OCEXEC)
			c->flag |= CCEXEC;
.
## diffname gnot/chan.c 1990/0820
## diff -e /n/bootesdump/1990/08141/sys/src/9/68020/chan.c /n/bootesdump/1990/0820/sys/src/9/68020/chan.c
558a
	}
	if(*name == '.'){
		if(((ulong)name&KZERO)==0 && (((ulong)name+1)&(BY2PG-1))==0)
			validaddr((ulong)name+1, 1, 0);
		if(name[1]==0 || name[1]=='/'){
			name++;
			goto Again;
		}
.
554a
    Again:
.
529a
		if(isdot)
			error(0, Eisdir);
.
509,511c
		if(isdot)
			c = domount(c);
		else{
			if((nc=walk(c, elem, mntok)) == 0)
				error(0, Enonexist);
			c = nc;
		}
.
491,493c
		if(isdot)
			c = domount(c);
		else{
			if((nc=walk(c, elem, mntok)) == 0)
				error(0, Enonexist);
			c = nc;
		}
.
484a

.
471,472c

	if(mntok && !isdot)
	if(!(amode==Amount && elem[0]==0))	/* don't domount on slash */
.
463a
		name = skipslash(name);	/* eat leading ./ */
		if(*name == 0)
			isdot = 1;
	}
.
462c
	}else{
.
439a
	isdot = 0;
.
434c
	int mntok, isdot;
.
343c
			pprint("walk: changed underfoot? '%s'\n", name);
.
## diffname gnot/chan.c 1990/0821
## diff -e /n/bootesdump/1990/0820/sys/src/9/68020/chan.c /n/bootesdump/1990/0821/sys/src/9/68020/chan.c
615c
		end = memchr(name, '/', NAMELEN);
.
613c
	end = memchr(name, 0, NAMELEN);
.
603d
578,585d
576a
	if(*name=='.' && (name[1]==0 || name[1]=='/')){
		name++;
		goto Again;
.
573,575c
	while(*name == '/')
.
449,450d
438a

	/*
	 * Make sure all of name is o.k.  first byte is validated
	 * externally so if it's a kernel address we know it's o.k.
	 */
	if(!((ulong)name & KZERO)){
		p = name;
		t = BY2PG-((ulong)p&(BY2PG-1));
		while(vmemchr(p, 0, t) == 0){
			p += t;
			t = BY2PG;
		}
	}

	elem = u->elem;
.
435c
	char *p;
	char *elem;
.
## diffname gnot/chan.c 1990/0914
## diff -e /n/bootesdump/1990/0821/sys/src/9/68020/chan.c /n/bootesdump/1990/0914/sys/src/9/68020/chan.c
541a
		saveregisters();	/* else error() in open has wrong value of c saved */
.
424a
void
saveregisters(void)
{
}

.
375,376c

.
371a
	if(domnt){
		if(waserror()){
			print("domount error\n");
			if(!first)
				close(c);
			return 0;
		}
		c = domount(c);
		poperror();
	}

.
103a
		c->flag = CFREE;
.
98a
	if(c->flag & CFREE)
		panic("close");
.
## diffname gnot/chan.c 1990/09141
## diff -e /n/bootesdump/1990/0914/sys/src/9/68020/chan.c /n/bootesdump/1990/09141/sys/src/9/68020/chan.c
377d
## diffname gnot/chan.c 1990/1006
## diff -e /n/bootesdump/1990/09141/sys/src/9/68020/chan.c /n/bootesdump/1990/1006/sys/src/9/68020/chan.c
482c
		if(name[1]=='M')
.
## diffname gnot/chan.c 1990/1104
## diff -e /n/bootesdump/1990/1006/sys/src/9/68020/chan.c /n/bootesdump/1990/1104/sys/src/9/68020/chan.c
29c
	return x;
.
27c
	x = --r->ref;
.
25a
	int x;

.
20c
	return x;
.
18c
	x = ++r->ref;
.
16a
	int x;

.
## diffname gnot/chan.c 1990/11062
## diff -e /n/bootesdump/1990/1104/sys/src/9/68020/chan.c /n/bootesdump/1990/11062/sys/src/9/68020/chan.c
82a
		c->dev = 0;
.
## diffname gnot/chan.c 1990/11211
## diff -e /n/bootesdump/1990/11062/sys/src/9/68020/chan.c /n/bootesdump/1990/11211/sys/src/9/68020/chan.c
667c
	error(Enotdir);
.
665c
	if(c->qid.path & CHDIR)
.
655c
			error(Ebadchar);
.
646c
			error(Efilename);
.
641c
		error(Efilename);
.
583c
			error(Eisdir);
.
577c
			error(Enonexist);
.
560c
				error(Enonexist);
.
551,552c
		if(!(c->qid.path & CHDIR))
			error(Enotdir);
.
549c
			error(Enonexist);
.
538c
				error(Enonexist);
.
523c
			error(Enonexist);
.
491c
			error(Ebadsharp);
.
488c
			error(Enonexist);
.
461c
		error(Enonexist);
.
424c
			error(Enocreate);
.
420c
		error(Enocreate);
.
231c
			error(Enotunion);
.
214c
			error(Enotunion);
.
194c
			error(Enomount);
.
162,165c
	if(CHDIR & (old->qid.path^new->qid.path))
		error(Emount);
	if((old->qid.path&CHDIR)==0 && (flag&MORDER)!=MREPL)
		error(Emount);
.
123a
	if(!pathonly && a->qid.vers!=b->qid.vers)
		return 0;
.
122c
	return a.path==b.path && a.vers==b.vers;
}

int
eqchan(Chan *a, Chan *b, int pathonly)
{
	if(a->qid.path != b->qid.path)
.
120c
eqqid(Qid a, Qid b)
.
88c
		c->mqid = (Qid){0, 0};
.
## diffname gnot/chan.c 1990/1122
## diff -e /n/bootesdump/1990/11211/sys/src/9/68020/chan.c /n/bootesdump/1990/1122/sys/src/9/68020/chan.c
311c
	if(!eqchan(mt->c, c, 1)){	/* table changed underfoot */
.
299c
		if(mt->c && eqchan(mt->c, c, 1))
.
195c
		else if(eqchan(mt->c, old, 1)){
.
## diffname gnot/chan.c 1990/1210 # deleted
## diff -e /n/bootesdump/1990/1122/sys/src/9/68020/chan.c /n/bootesdump/1990/1210/sys/src/9/68020/chan.c
1,676d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].