~kris/9p

9hist

b0052da73b49af8280ef00ec91dc7402d2fd6c70 — David du Colombier 25 years ago 5fe022b
Plan 9 from Bell Labs 2001-04-05
10 files changed, 220 insertions(+), 131 deletions(-)

M bitsy/etherwavelan.c
M ip/devip.c
M ip/ip.h
M ip/iproute.c
M pc/etherwavelan.c
M pc/trap.c
M pc/vgas3.c
M port/devtls.c
M port/parse.c
M port/portdat.h
M bitsy/etherwavelan.c => bitsy/etherwavelan.c +1 -1
@@ 948,7 948,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
		ltv.len = 4;
		if (w_inltv(ctlr, &ltv))
			print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
		l += snprint(p+l, READSTR-l, "Base station: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
		l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
	}
	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));

M ip/devip.c => ip/devip.c +34 -8
@@ 66,6 66,8 @@ ip3gen(Chan *c, int i, Dir *dp)
	char *p;

	cv = ipfs[c->dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)];
	if(cv->owner[0] == 0)
		memmove(cv->owner, eve, NAMELEN);
	switch(i) {
	default:
		return -1;


@@ 282,6 284,24 @@ ipgetfs(int dev)
	return ipfs[dev];
}

IPaux*
newipaux(char *owner, char *tag)
{
	IPaux *a;
	int n;

	a = smalloc(sizeof(*a));
	memmove(a->owner, owner, NAMELEN);
	memset(a->tag, ' ', sizeof(a->tag));
	n = strlen(tag);
	if(n > sizeof(a->tag))
		n = sizeof(a->tag);
	memmove(a->tag, tag, n);
	return a;
}

#define ATTACHER(c) (((IPaux*)((c)->aux))->owner)

static Chan*
ipattach(char* spec)
{


@@ 297,13 317,19 @@ ipattach(char* spec)
	c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
	c->dev = dev;

	c->aux = newipaux(commonuser(), "none");

	return c;
}

static Chan*
ipclone(Chan* c, Chan* nc)
{
	return devclone(c, nc);
	IPaux *a = c->aux;

	nc = devclone(c, nc);
	nc->aux = newipaux(a->owner, a->tag);
	return nc;
}

static int


@@ 359,7 385,6 @@ ipopen(Chan* c, int omode)
		netlogopen(f);
		break;
	case Qiproute:
		memmove(c->tag, "none", sizeof(c->tag));
		break;
	case Qtopdir:
	case Qprotodir:


@@ 380,7 405,7 @@ ipopen(Chan* c, int omode)
			qunlock(p);
			nexterror();
		}
		cv = Fsprotoclone(p, commonuser());
		cv = Fsprotoclone(p, ATTACHER(c));
		qunlock(p);
		poperror();
		if(cv == nil) {


@@ 402,7 427,7 @@ ipopen(Chan* c, int omode)
			nexterror();
		}
		if((perm & (cv->perm>>6)) != perm) {
			if(strcmp(commonuser(), cv->owner) != 0)
			if(strcmp(ATTACHER(c), cv->owner) != 0)
				error(Eperm);
		 	if((perm & cv->perm) != perm)
				error(Eperm); 


@@ 410,7 435,7 @@ ipopen(Chan* c, int omode)
		}
		cv->inuse++;
		if(cv->inuse == 1){
			memmove(cv->owner, commonuser(), NAMELEN);
			memmove(cv->owner, ATTACHER(c), NAMELEN);
			cv->perm = 0660;
		}
		qunlock(cv);


@@ 420,7 445,7 @@ ipopen(Chan* c, int omode)
	case Qlisten:
		cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)];
		if((perm & (cv->perm>>6)) != perm) {
			if(strcmp(commonuser(), cv->owner) != 0)
			if(strcmp(ATTACHER(c), cv->owner) != 0)
				error(Eperm);
		 	if((perm & cv->perm) != perm)
				error(Eperm); 


@@ 450,7 475,7 @@ ipopen(Chan* c, int omode)
			if(nc != nil){
				cv->incall = nc->next;
				c->qid = (Qid){QID(PROTO(c->qid), nc->x, Qctl), 0};
				memmove(cv->owner, commonuser(), NAMELEN);
				memmove(nc->owner, ATTACHER(c), NAMELEN);
			}
			qunlock(cv);



@@ 499,7 524,7 @@ ipwstat(Chan *c, char *dp)

	p = f->p[PROTO(c->qid)];
	cv = p->conv[CONV(c->qid)];
	if(!iseve() && strcmp(commonuser(), cv->owner) != 0)
	if(!iseve() && strcmp(ATTACHER(c), cv->owner) != 0)
		error(Eperm);
	if(d.uid[0]){
		strncpy(cv->owner, d.uid, sizeof(cv->owner));


@@ 558,6 583,7 @@ ipclose(Chan* c)
		if(c->flag & COPEN)
			closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]);
	}
	free(c->aux);
}

enum

M ip/ip.h => ip/ip.h +17 -0
@@ 3,6 3,7 @@ typedef struct	Fs	Fs;
typedef union	Hwaddr	Hwaddr;
typedef struct	Ifcconv	Ifcconv;
typedef struct	IP	IP;
typedef struct	IPaux	IPaux;
typedef struct	Ipself	Ipself;
typedef struct	Ipselftab	Ipselftab;
typedef struct	Iplink	Iplink;


@@ 418,6 419,22 @@ extern void	ipwalkroutes(Fs*, Routewalk*);
extern void	convroute(Route*, uchar*, uchar*, uchar*, char*, int*);

/*
 *  devip.c
 */

/*
 *  Hanging off every ip channel's ->aux is the following structure.
 *  It maintains the state used by devip and iproute.
 */
struct IPaux
{
	char	owner[NAMELEN];		/* the user that did the attach */
	char	tag[4];
};

extern IPaux*	newipaux(char*, char*);

/*
 *  arp.c
 */
struct Arpent

M ip/iproute.c => ip/iproute.c +9 -8
@@ 760,6 760,7 @@ routewrite(Fs *f, Chan *c, char *p, int n)
	uchar addr[IPaddrlen];
	uchar mask[IPaddrlen];
	uchar gate[IPaddrlen];
	IPaux *a, *na;

	cb = parsecmd(p, n);
	if(waserror()){


@@ 797,8 798,10 @@ routewrite(Fs *f, Chan *c, char *p, int n)
		parseipmask(mask, cb->f[2]);
		parseip(gate, cb->f[3]);
		tag = "none";
		if(c != nil)
			tag = c->tag;
		if(c != nil){
			a = c->aux;
			tag = a->tag;
		}
		if(memcmp(addr, v4prefix, IPv4off) == 0)
			v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0);
		else


@@ 807,12 810,10 @@ routewrite(Fs *f, Chan *c, char *p, int n)
		if(cb->nf < 2)
			error(Ebadarg);

		h = strlen(cb->f[1]);
		if(h > sizeof(c->tag))
			h = sizeof(c->tag);
		strncpy(c->tag, cb->f[1], h);
		if(h < 4)
			memset(c->tag+h, ' ', sizeof(c->tag)-h);
		a = c->aux;
		na = newipaux(a->owner, cb->f[1]);
		c->aux = na;
		free(a);
	}

	poperror();

M pc/etherwavelan.c => pc/etherwavelan.c +10 -3
@@ 114,6 114,7 @@ enum
	WTyp_Prom	= 0xfc85,
	WTyp_Keys	= 0xfcb0,
	WTyp_TxKey	= 0xfcb1,
	WTyp_StationID	= 0xfd20,
	WTyp_CurName	= 0xfd41,
	WTyp_BaseID	= 0xfd42,	// ID of the currently connected-to base station
	WTyp_CurTxRate	= 0xfd44,	// Current TX rate


@@ 942,8 943,14 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
	if(i == 3)
		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
	else {
		Wltv ltv;
		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
//		PRINTSTAT("Base station: %s\n", ltv_inname(ctlr, WTyp_BaseID));
		ltv.type = WTyp_BaseID;
		ltv.len = 4;
		if (w_inltv(ctlr, &ltv))
			print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
		l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
	}
	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));


@@ 1192,8 1199,8 @@ reset(Ether* ether)
		DEBUG("no wavelan found\n");
		goto abort;
	}
//	DEBUG("#l%d: port=0x%lx irq=%ld\n",
//			ether->ctlrno, ether->port, ether->irq);
	// DEBUG("#l%d: port=0x%lx irq=%ld\n",
	//		ether->ctlrno, ether->port, ether->irq);

	w_intdis(ctlr);
	if (w_cmd(ctlr,WCmdIni,0)){

M pc/trap.c => pc/trap.c +5 -8
@@ 390,15 390,12 @@ _dumpstack(Ureg *ureg)
		v = *(ulong*)l;
		if(KTZERO < v && v < (ulong)&etext){
			/*
			 * Pick off general CALL (0xE8) and CALL indirect
			 * through AX (0xFFD0).
			 * we could Pick off general CALL (((uchar*)v)[-5] == 0xE8)
			 * and CALL indirect through AX (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0),
			 * but this is too clever and misses faulting address.
			 */
			p = (uchar*)v;
			if(*(p-5) == 0xE8
			|| (*(p-2) == 0xFF && *(p-1) == 0xD0)){
				print("%.8lux=%.8lux ", l, v);
				i++;
			}
			print("%.8lux=%.8lux ", l, v);
			i++;
		}
		if(i == 4){
			i = 0;

M pc/vgas3.c => pc/vgas3.c +12 -1
@@ 100,7 100,18 @@ s3linear(VGAscr* scr, int* size, int* align)
	mmiosize = 0;
	mmiobase = 0;
	mmioname = nil;
	if(p = pcimatch(nil, PCIS3, 0)){

	/*
	 * S3 makes cards other than display controllers, so
	 * look for the first S3 display controller (device class 3)
	 * and not one of their sound cards.
	 */
	p = nil;
	while(p = pcimatch(p, PCIS3, 0)){
		if(p->ccrb == 0x03)
			break;
	}
	if(p != nil){
		for(i=0; i<nelem(p->mem); i++){
			if(p->mem[i].size >= *size
			&& ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0)

M port/devtls.c => port/devtls.c +122 -98
@@ 15,6 15,10 @@ typedef struct Secret Secret;

enum {
	MaxRecLen	= 1<<14,	/* max payload length of a record layer message */
	MaxCipherRecLen	= MaxRecLen + 2048,

//ZZZ
	Maxdstate	= 64,
	RecHdrLen	= 5,
	TLSVersion	= 0x0301,
	SSL3Version	= 0x0300,


@@ 102,17 106,20 @@ struct TlsRec
	void		(*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);

	/* input side -- protected by in.q */
	OneWay	in;
	Queue	*handq;				/* queue of handshake messages */
	Block	*processed;			/* next bunch of application data */
	Block	*unprocessed;			/* data read from c but not parsed into records */
	OneWay		in;
	Block		*processed;			/* next bunch of application data */
	Block		*unprocessed;			/* data read from c but not parsed into records */

	Lock		hqlock;
	int		hqref;
	Queue		*handq;				/* queue of handshake messages */

	/* output side */
	OneWay	out;
	OneWay		out;

	/* protections */
	char	user[NAMELEN];
	int	perm;
	char		user[NAMELEN];
	int		perm;
};

typedef struct TlsErrs	TlsErrs;


@@ 183,13 190,6 @@ static	TlsRec** dstate;
static	char	*encalgs;
static	char	*hashalgs;

enum
{
//ZZZ
	Maxdmsg=	1<<16,
	Maxdstate=	64
};

enum{
	Qtopdir		= 1,	/* top level directory */
	Qprotodir,


@@ 351,27 351,6 @@ tlsstat(Chan *c, char *db)
	devstat(c, db, 0, 0, tlsgen);
}

static void
closedown(Chan *c, TlsRec *tr)
{
	lock(&dslock);
	if(--tr->ref > 0) {
		unlock(&dslock);
		return;
	}
	dstate[CONV(c->qid)] = nil;
	unlock(&dslock);

	tlshangup(tr);
	if(tr->c)
		cclose(tr->c);
	free(tr->in.sec);
	free(tr->in.new);
	free(tr->out.sec);
	free(tr->out.new);
	free(tr);
}

static Chan*
tlsopen(Chan *c, int omode)
{


@@ 421,29 400,29 @@ tlsopen(Chan *c, int omode)
			dsnew(c, pp);
		else {
			if((perm & (tr->perm>>6)) != perm
			   && (strcmp(up->user, tr->user) != 0
			     || (perm & tr->perm) != perm))
			&& (strcmp(up->user, tr->user) != 0
			    || (perm & tr->perm) != perm))
				error(Eperm);

			if(t == Qhand){
				if(waserror()){
					unlock(&tr->hqlock);
					nexterror();
				}
				lock(&tr->hqlock);
				if(tr->handq != nil)
					error(Einuse);
//ZZZ what is the correct buffering here?
				tr->handq = qopen(2 * MaxRecLen, 0, nil, nil);
				if(tr->handq == nil)
					error("can't allocate handshake queue");
				tr->hqref = 1;
				unlock(&tr->hqlock);
				poperror();
			}
			tr->ref++;
		}
		unlock(&dslock);
		poperror();
		if(t == Qhand){
			if(waserror()){
				qunlock(&tr->in.io);
				closedown(c, tr);
			}
			qlock(&tr->in.io);
			if(tr->handq != nil)
				error(Einuse);
//ZZZ what is the correct buffering here?
			tr->handq = qopen(2 * MaxRecLen, 0, nil, nil);
			if(tr->handq == nil)
				error("can't allocate handshake queue");
			qunlock(&tr->in.io);
			poperror();
		}
		break;
	case Qencalgs:
	case Qhashalgs:


@@ 471,11 450,23 @@ tlswstat(Chan *c, char *dp)
	if(strcmp(tr->user, up->user) != 0)
		error(Eperm);

//ZZZ propagate chown to tr->datafd?
	memmove(tr->user, d.uid, NAMELEN);
	tr->perm = d.mode;
}

static void
dechandq(TlsRec *tr)
{
	lock(&tr->hqlock);
	if(--tr->hqref == 0 && tr->handq != nil){
		qfree(tr->handq);
		tr->handq = nil;
	}
	unlock(&tr->hqlock);
}

static void
tlsclose(Chan *c)
{
	TlsRec *tr;


@@ 493,15 484,25 @@ tlsclose(Chan *c)
		if(tr == nil)
			break;

		if(t == Qhand){
			qlock(&tr->in.io);
			if(tr->handq != nil){
				qfree(tr->handq);
				tr->handq = nil;
			}
			qunlock(&tr->in.io);
		if(t == Qhand)
			dechandq(tr);

		lock(&dslock);
		if(--tr->ref > 0) {
			unlock(&dslock);
			return;
		}
		closedown(c, tr);
		dstate[CONV(c->qid)] = nil;
		unlock(&dslock);

		tlshangup(tr);
		if(tr->c)
			cclose(tr->c);
		free(tr->in.sec);
		free(tr->in.new);
		free(tr->out.sec);
		free(tr->out.new);
		free(tr);
		break;
	}
}


@@ 524,7 525,7 @@ ensure(TlsRec *s, Block **l, int n)
	}

	while(sofar < n){
		bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0);
		bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
		if(bl == 0)
			error(Ehungup);
		*l = bl;


@@ 704,6 705,7 @@ tlsrecread(TlsRec *tr)
		(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
		if(memcmp(hmac, p+len, in->sec->maclen) != 0)
			rcvError(tr, EBadRecordMac, "record mac mismatch");
		b->wp -= in->sec->maclen;
	}
	qunlock(&in->seclock);
	poperror();


@@ 733,13 735,19 @@ tlsrecread(TlsRec *tr)
			rcvError(tr, EDecodeError, "invalid alert");
		if(p[0] == 1) {
			if(p[1] == ECloseNotify) {
				rcvError(tr, ECloseNotify, "remote close");
				tlsSetState(tr, SRemoteClosed);
//				handclose(tr, "remote close");
				error("remote close");
			}
			/*
			 * ignore EUserCancelled, it's meaningless
			 * need to handle ENoRenegotiation
			 * propate messages to handshaker
			 * EUserCancelled ENoRenegotiation
ZZZ better comment, better thoughts about this
			 */
//			if(p[1] == ENoRenegotiation)
//				handclose(tr, "no renegotiation");
//			if(p[1] == EUserCancelled)
//				handclose(tr, "user cancelled");
		} else {
			rcvAlert(tr, p[1]);
		}


@@ 752,9 760,13 @@ tlsrecread(TlsRec *tr)
		 * if there isn't any handshaker, ignore the request,
		 * but notify the other side we are doing so.
		 */
		lock(&tr->hqlock);
		if(tr->handq != nil){
			tr->hqref++;
			unlock(&tr->hqlock);
			qbwrite(tr->handq, b);
			b = nil;
			dechandq(tr);
		}else if(tr->verset && tr->version != SSL3Version)
			sendAlert(tr, ENoRenegotiation);
		break;


@@ 801,7 813,7 @@ tlsbread(Chan *c, long n, ulong offset)
	}
	qlock(&tr->in.io);
	if(TYPE(c->qid) == Qdata){
//ZZZ race setting state
//ZZZ race on state
		if(tr->state != SOpen)
			error(Ebadusefd);
		while(tr->processed == nil)


@@ 812,7 824,7 @@ tlsbread(Chan *c, long n, ulong offset)
		qunlock(&tr->in.io);
		poperror();
	}else{
//ZZZ race setting state
//ZZZ race on state
		while(tr->state == SHandshake && !qcanread(tr->handq))
			tlsrecread(tr);
		qunlock(&tr->in.io);


@@ 906,7 918,7 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)

	while(bb != nil){
//ZZZ race on state
		if(tr->state != SHandshake && tr->state != SOpen)
		if(tr->state != SHandshake && tr->state != SOpen && tr->state != SRemoteClosed)
			error(Ebadusefd);

		/*


@@ 949,7 961,7 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
			put64(seq, out->seq);
			out->seq++;
			(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, nb->wp);
			b->wp += maclen;
			nb->wp += maclen;
			n += maclen;

			/* update length */


@@ 958,6 970,14 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
			/* encrypt */
			rc4(&out->sec->rc4, p+RecHdrLen, n);
		}
		if(type == RChangeCipherSpec){
			if(out->new == nil)
				error("change cipher without a new cipher");
			free(out->sec);
			out->sec = out->new;
			out->new = nil;
			out->seq = 0;
		}
		qunlock(&out->seclock);
		poperror();



@@ 987,13 1007,10 @@ tlsbwrite(Chan *c, Block *b, ulong offset)
	default:
		return devbwrite(c, b, offset);
	case Qhand:
//ZZZ race setting state
//		if(tr->state != SHandshake && tr->state != SOpen)
//			error(Ebadusefd);
		tlsrecwrite(tr, RHandshake, b);
		break;
	case Qdata:
//ZZZ race setting state
//ZZZ race on state
		if(tr->state != SOpen)
			error(Ebadusefd);
		tlsrecwrite(tr, RApplication, b);


@@ 1172,7 1189,14 @@ tlswrite(Chan *c, void *a, long n, vlong off)
	}else if(strcmp(cb->f[0], "opened") == 0){
		if(cb->nf != 1)
			error("usage: opened");
		tlsSetState(tr, SOpen);
		lock(&tr->statelk);
		if(tr->state != SHandshake && tr->state != SOpen){
			unlock(&tr->statelk);
//ZZZ bad error message
			error("can't set open state");
		}
		tr->state = SOpen;
		unlock(&tr->statelk);
	}else if(strcmp(cb->f[0], "alert") == 0){
		if(cb->nf != 2)
			error("usage: alert n");


@@ 1195,24 1219,19 @@ tlswrite(Chan *c, void *a, long n, vlong off)
		if(tr->out.new == nil)
			error("can't change cipher spec without setting secret");

		toc = tr->out.new;
		tr->out.new = nil;

//ZZZ minor race; worth fixing?
		qunlock(&tr->in.seclock);
		qunlock(&tr->out.seclock);
		poperror();
		free(cb);
		poperror();
	

		/*
		 * the real work is done as the message is written
		 * so the stream is encrypted in sync.
		 */
		b = allocb(1);
		*b->wp++ = 1;
		tlsrecwrite(tr, RChangeCipherSpec, b);

		qlock(&tr->out.seclock);
		free(tr->out.sec);
		tr->out.sec = toc;
		qunlock(&tr->out.seclock);
		return n;
	}else if(strcmp(cb->f[0], "secret") == 0){
		if(cb->nf != 5)


@@ 1379,24 1398,26 @@ sendAlert(TlsRec *tr, int err)
		tlsSetState(tr, SError);
}

/*
 * got a fatal alert message
 */
static void
rcvAlert(TlsRec *tr, int err)
{
	char *s;
	int i, fatal;
	int i;

	s = "unknown error";
	fatal = 1;
	for(i=0; i < nelem(tlserrs); i++){
		if(tlserrs[i].err == err){
			s = tlserrs[i].msg;
			fatal = tlserrs[i].fatal;
			break;
		}
	}
//ZZZ need to kill session if fatal error
	if(fatal)
		tlsSetState(tr, SError);
//	handclose(tr, err);
	tlshangup(tr);
	tlsSetState(tr, SError);
	error(s);
}



@@ 1424,21 1445,24 @@ tlsSetState(TlsRec *tr, int newstate)

/* hand up a digest connection */
static void
tlshangup(TlsRec *s)
tlshangup(TlsRec *tr)
{
	Block *b;

	qlock(&s->in.io);
	for(b = s->processed; b; b = s->processed){
		s->processed = b->next;
	qlock(&tr->in.io);
	for(b = tr->processed; b; b = tr->processed){
		tr->processed = b->next;
		freeb(b);
	}
	if(s->unprocessed != nil){
		freeb(s->unprocessed);
		s->unprocessed = nil;
	if(tr->unprocessed != nil){
		freeb(tr->unprocessed);
		tr->unprocessed = nil;
	}
	s->state = SClosed;
	qunlock(&s->in.io);
	qunlock(&tr->in.io);

	tlsrecwrite(tr, RAlert, ECloseNotify);

	tlsSetState(tr, SClosed);
}

static TlsRec*

M port/parse.c => port/parse.c +10 -3
@@ 13,17 13,24 @@ parsecmd(char *p, int n)
{
	Cmdbuf *volatile cb;

	cb = smalloc(sizeof(*cb));
	if(up != nil)
		cb = smalloc(sizeof(*cb));
	else{
		cb = malloc(sizeof(*cb));
		if(cb == nil)
			return nil;
	}
	
	if(n > sizeof(cb->buf)-1)
		n = sizeof(cb->buf)-1;

	if(waserror()){
	if(up != nil && waserror()){
		free(cb);
		nexterror();
	}
	memmove(cb->buf, p, n);
	poperror();
	if(up != nil)
		poperror();

	if(n > 0 && cb->buf[n-1] == '\n')
		n--;

M port/portdat.h => port/portdat.h +0 -1
@@ 159,7 159,6 @@ struct Chan
		Qid	pgrpid;		/* for #p/notepg */
		Mnt*	mntptr;		/* for devmnt */
		ulong	mid;		/* for ns in devproc */
		char	tag[4];		/* for iproute */
	};
	Chan*	mchan;			/* channel to mounted server */
	Qid	mqid;			/* qid of root of mount point */