plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

fsys.c (13315B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <draw.h>
      4 #include <thread.h>
      5 #include <cursor.h>
      6 #include <mouse.h>
      7 #include <keyboard.h>
      8 #include <frame.h>
      9 #include <fcall.h>
     10 #include <plumb.h>
     11 #include <libsec.h>
     12 #include "dat.h"
     13 #include "fns.h"
     14 
     15 static	int	sfd;
     16 
     17 enum
     18 {
     19 	Nhash	= 16,
     20 	DEBUG	= 0
     21 };
     22 
     23 static	Fid	*fids[Nhash];
     24 
     25 Fid	*newfid(int);
     26 
     27 static	Xfid*	fsysflush(Xfid*, Fid*);
     28 static	Xfid*	fsysauth(Xfid*, Fid*);
     29 static	Xfid*	fsysversion(Xfid*, Fid*);
     30 static	Xfid*	fsysattach(Xfid*, Fid*);
     31 static	Xfid*	fsyswalk(Xfid*, Fid*);
     32 static	Xfid*	fsysopen(Xfid*, Fid*);
     33 static	Xfid*	fsyscreate(Xfid*, Fid*);
     34 static	Xfid*	fsysread(Xfid*, Fid*);
     35 static	Xfid*	fsyswrite(Xfid*, Fid*);
     36 static	Xfid*	fsysclunk(Xfid*, Fid*);
     37 static	Xfid*	fsysremove(Xfid*, Fid*);
     38 static	Xfid*	fsysstat(Xfid*, Fid*);
     39 static	Xfid*	fsyswstat(Xfid*, Fid*);
     40 
     41 Xfid* 	(*fcall[Tmax])(Xfid*, Fid*);
     42 
     43 static void
     44 initfcall(void)
     45 {
     46 	fcall[Tflush]	= fsysflush;
     47 	fcall[Tversion]	= fsysversion;
     48 	fcall[Tauth]	= fsysauth;
     49 	fcall[Tattach]	= fsysattach;
     50 	fcall[Twalk]	= fsyswalk;
     51 	fcall[Topen]	= fsysopen;
     52 	fcall[Tcreate]	= fsyscreate;
     53 	fcall[Tread]	= fsysread;
     54 	fcall[Twrite]	= fsyswrite;
     55 	fcall[Tclunk]	= fsysclunk;
     56 	fcall[Tremove]= fsysremove;
     57 	fcall[Tstat]	= fsysstat;
     58 	fcall[Twstat]	= fsyswstat;
     59 }
     60 
     61 char Eperm[] = "permission denied";
     62 char Eexist[] = "file does not exist";
     63 char Enotdir[] = "not a directory";
     64 
     65 Dirtab dirtab[]=
     66 {
     67 	{ ".",			QTDIR,	Qdir,		0500|DMDIR },
     68 	{ "acme",		QTDIR,	Qacme,	0500|DMDIR },
     69 	{ "cons",		QTFILE,	Qcons,	0600 },
     70 	{ "consctl",	QTFILE,	Qconsctl,	0000 },
     71 	{ "draw",		QTDIR,	Qdraw,	0000|DMDIR },	/* to suppress graphics progs started in acme */
     72 	{ "editout",	QTFILE,	Qeditout,	0200 },
     73 	{ "index",		QTFILE,	Qindex,	0400 },
     74 	{ "label",		QTFILE,	Qlabel,	0600 },
     75 	{ "log",		QTFILE,	Qlog,	0400 },
     76 	{ "new",		QTDIR,	Qnew,	0500|DMDIR },
     77 	{ nil, }
     78 };
     79 
     80 Dirtab dirtabw[]=
     81 {
     82 	{ ".",			QTDIR,		Qdir,			0500|DMDIR },
     83 	{ "addr",		QTFILE,		QWaddr,		0600 },
     84 	{ "body",		QTAPPEND,	QWbody,		0600|DMAPPEND },
     85 	{ "ctl",		QTFILE,		QWctl,		0600 },
     86 	{ "data",		QTFILE,		QWdata,		0600 },
     87 	{ "editout",	QTFILE,		QWeditout,	0200 },
     88 	{ "errors",		QTFILE,		QWerrors,		0200 },
     89 	{ "event",		QTFILE,		QWevent,		0600 },
     90 	{ "rdsel",		QTFILE,		QWrdsel,		0400 },
     91 	{ "wrsel",		QTFILE,		QWwrsel,		0200 },
     92 	{ "tag",		QTAPPEND,	QWtag,		0600|DMAPPEND },
     93 	{ "xdata",		QTFILE,		QWxdata,		0600 },
     94 	{ nil, }
     95 };
     96 
     97 typedef struct Mnt Mnt;
     98 struct Mnt
     99 {
    100 	QLock	lk;
    101 	int		id;
    102 	Mntdir	*md;
    103 };
    104 
    105 Mnt	mnt;
    106 
    107 Xfid*	respond(Xfid*, Fcall*, char*);
    108 int		dostat(int, Dirtab*, uchar*, int, uint);
    109 uint	getclock(void);
    110 
    111 char	*user = "Wile E. Coyote";
    112 static int closing = 0;
    113 int	messagesize = Maxblock+IOHDRSZ;	/* good start */
    114 
    115 void	fsysproc(void *);
    116 
    117 void
    118 fsysinit(void)
    119 {
    120 	int p[2];
    121 	char *u;
    122 
    123 	initfcall();
    124 	if(pipe(p) < 0)
    125 		error("can't create pipe");
    126 	if(post9pservice(p[0], "acme", mtpt) < 0)
    127 		error("can't post service");
    128 	sfd = p[1];
    129 	fmtinstall('F', fcallfmt);
    130 	if((u = getuser()) != nil)
    131 		user = estrdup(u);
    132 	proccreate(fsysproc, nil, STACK);
    133 }
    134 
    135 void
    136 fsysproc(void *v)
    137 {
    138 	int n;
    139 	Xfid *x;
    140 	Fid *f;
    141 	Fcall t;
    142 	uchar *buf;
    143 
    144 	threadsetname("fsysproc");
    145 
    146 	USED(v);
    147 	x = nil;
    148 	for(;;){
    149 		buf = emalloc(messagesize+UTFmax);	/* overflow for appending partial rune in xfidwrite */
    150 		n = read9pmsg(sfd, buf, messagesize);
    151 		if(n <= 0){
    152 			if(closing)
    153 				break;
    154 			error("i/o error on server channel");
    155 		}
    156 		if(x == nil){
    157 			sendp(cxfidalloc, nil);
    158 			x = recvp(cxfidalloc);
    159 		}
    160 		x->buf = buf;
    161 		if(convM2S(buf, n, &x->fcall) != n)
    162 			error("convert error in convM2S");
    163 		if(DEBUG)
    164 			fprint(2, "%F\n", &x->fcall);
    165 		if(fcall[x->fcall.type] == nil)
    166 			x = respond(x, &t, "bad fcall type");
    167 		else{
    168 			switch(x->fcall.type){
    169 			case Tversion:
    170 			case Tauth:
    171 			case Tflush:
    172 				f = nil;
    173 				break;
    174 			case Tattach:
    175 				f = newfid(x->fcall.fid);
    176 				break;
    177 			default:
    178 				f = newfid(x->fcall.fid);
    179 				if(!f->busy){
    180 					x->f = f;
    181 					x = respond(x, &t, "fid not in use");
    182 					continue;
    183 				}
    184 				break;
    185 			}
    186 			x->f = f;
    187 			x  = (*fcall[x->fcall.type])(x, f);
    188 		}
    189 	}
    190 }
    191 
    192 Mntdir*
    193 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
    194 {
    195 	Mntdir *m;
    196 	int id;
    197 
    198 	qlock(&mnt.lk);
    199 	id = ++mnt.id;
    200 	m = emalloc(sizeof *m);
    201 	m->id = id;
    202 	m->dir =  dir;
    203 	m->ref = 1;	/* one for Command, one will be incremented in attach */
    204 	m->ndir = ndir;
    205 	m->next = mnt.md;
    206 	m->incl = incl;
    207 	m->nincl = nincl;
    208 	mnt.md = m;
    209 	qunlock(&mnt.lk);
    210 	return m;
    211 }
    212 
    213 void
    214 fsysincid(Mntdir *m)
    215 {
    216 	qlock(&mnt.lk);
    217 	m->ref++;
    218 	qunlock(&mnt.lk);
    219 }
    220 
    221 void
    222 fsysdelid(Mntdir *idm)
    223 {
    224 	Mntdir *m, *prev;
    225 	int i;
    226 	char buf[64];
    227 
    228 	if(idm == nil)
    229 		return;
    230 	qlock(&mnt.lk);
    231 	if(--idm->ref > 0){
    232 		qunlock(&mnt.lk);
    233 		return;
    234 	}
    235 	prev = nil;
    236 	for(m=mnt.md; m; m=m->next){
    237 		if(m == idm){
    238 			if(prev)
    239 				prev->next = m->next;
    240 			else
    241 				mnt.md = m->next;
    242 			for(i=0; i<m->nincl; i++)
    243 				free(m->incl[i]);
    244 			free(m->incl);
    245 			free(m->dir);
    246 			free(m);
    247 			qunlock(&mnt.lk);
    248 			return;
    249 		}
    250 		prev = m;
    251 	}
    252 	qunlock(&mnt.lk);
    253 	sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
    254 	sendp(cerr, estrdup(buf));
    255 }
    256 
    257 /*
    258  * Called only in exec.c:/^run(), from a different FD group
    259  */
    260 Mntdir*
    261 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
    262 {
    263 	return fsysaddid(dir, ndir, incl, nincl);
    264 }
    265 
    266 void
    267 fsysclose(void)
    268 {
    269 	closing = 1;
    270 	/*
    271 	 * apparently this is not kosher on openbsd.
    272 	 * perhaps because fsysproc is reading from sfd right now,
    273 	 * the close hangs indefinitely.
    274 	close(sfd);
    275 	 */
    276 }
    277 
    278 Xfid*
    279 respond(Xfid *x, Fcall *t, char *err)
    280 {
    281 	int n;
    282 
    283 	if(err){
    284 		t->type = Rerror;
    285 		t->ename = err;
    286 	}else
    287 		t->type = x->fcall.type+1;
    288 	t->fid = x->fcall.fid;
    289 	t->tag = x->fcall.tag;
    290 	if(x->buf == nil)
    291 		x->buf = emalloc(messagesize);
    292 	n = convS2M(t, x->buf, messagesize);
    293 	if(n <= 0)
    294 		error("convert error in convS2M");
    295 	if(write(sfd, x->buf, n) != n)
    296 		error("write error in respond");
    297 	free(x->buf);
    298 	x->buf = nil;
    299 	if(DEBUG)
    300 		fprint(2, "r: %F\n", t);
    301 	return x;
    302 }
    303 
    304 static
    305 Xfid*
    306 fsysversion(Xfid *x, Fid *f)
    307 {
    308 	Fcall t;
    309 
    310 	USED(f);
    311 	if(x->fcall.msize < 256)
    312 		return respond(x, &t, "version: message size too small");
    313 	messagesize = x->fcall.msize;
    314 	t.msize = messagesize;
    315 	if(strncmp(x->fcall.version, "9P2000", 6) != 0)
    316 		return respond(x, &t, "unrecognized 9P version");
    317 	t.version = "9P2000";
    318 	return respond(x, &t, nil);
    319 }
    320 
    321 static
    322 Xfid*
    323 fsysauth(Xfid *x, Fid *f)
    324 {
    325 	Fcall t;
    326 
    327 	USED(f);
    328 	return respond(x, &t, "acme: authentication not required");
    329 }
    330 
    331 static
    332 Xfid*
    333 fsysflush(Xfid *x, Fid *f)
    334 {
    335 	USED(f);
    336 	sendp(x->c, (void*)xfidflush);
    337 	return nil;
    338 }
    339 
    340 static
    341 Xfid*
    342 fsysattach(Xfid *x, Fid *f)
    343 {
    344 	Fcall t;
    345 	int id;
    346 	Mntdir *m;
    347 	char buf[128];
    348 
    349 	if(strcmp(x->fcall.uname, user) != 0)
    350 		return respond(x, &t, Eperm);
    351 	f->busy = TRUE;
    352 	f->open = FALSE;
    353 	f->qid.path = Qdir;
    354 	f->qid.type = QTDIR;
    355 	f->qid.vers = 0;
    356 	f->dir = dirtab;
    357 	f->nrpart = 0;
    358 	f->w = nil;
    359 	t.qid = f->qid;
    360 	f->mntdir = nil;
    361 	id = atoi(x->fcall.aname);
    362 	qlock(&mnt.lk);
    363 	for(m=mnt.md; m; m=m->next)
    364 		if(m->id == id){
    365 			f->mntdir = m;
    366 			m->ref++;
    367 			break;
    368 		}
    369 	if(m == nil && x->fcall.aname[0]){
    370 		snprint(buf, sizeof buf, "unknown id '%s' in attach", x->fcall.aname);
    371 		sendp(cerr, estrdup(buf));
    372 	}
    373 	qunlock(&mnt.lk);
    374 	return respond(x, &t, nil);
    375 }
    376 
    377 static
    378 Xfid*
    379 fsyswalk(Xfid *x, Fid *f)
    380 {
    381 	Fcall t;
    382 	int c, i, j, id;
    383 	Qid q;
    384 	uchar type;
    385 	ulong path;
    386 	Fid *nf;
    387 	Dirtab *d, *dir;
    388 	Window *w;
    389 	char *err;
    390 
    391 	nf = nil;
    392 	w = nil;
    393 	if(f->open)
    394 		return respond(x, &t, "walk of open file");
    395 	if(x->fcall.fid != x->fcall.newfid){
    396 		nf = newfid(x->fcall.newfid);
    397 		if(nf->busy)
    398 			return respond(x, &t, "newfid already in use");
    399 		nf->busy = TRUE;
    400 		nf->open = FALSE;
    401 		nf->mntdir = f->mntdir;
    402 		if(f->mntdir)
    403 			f->mntdir->ref++;
    404 		nf->dir = f->dir;
    405 		nf->qid = f->qid;
    406 		nf->w = f->w;
    407 		nf->nrpart = 0;	/* not open, so must be zero */
    408 		if(nf->w)
    409 			incref(&nf->w->ref);
    410 		f = nf;	/* walk f */
    411 	}
    412 
    413 	t.nwqid = 0;
    414 	err = nil;
    415 	dir = nil;
    416 	id = WIN(f->qid);
    417 	q = f->qid;
    418 
    419 	if(x->fcall.nwname > 0){
    420 		for(i=0; i<x->fcall.nwname; i++){
    421 			if((q.type & QTDIR) == 0){
    422 				err = Enotdir;
    423 				break;
    424 			}
    425 
    426 			if(strcmp(x->fcall.wname[i], "..") == 0){
    427 				type = QTDIR;
    428 				path = Qdir;
    429 				id = 0;
    430 				if(w){
    431 					winclose(w);
    432 					w = nil;
    433 				}
    434     Accept:
    435 				if(i == MAXWELEM){
    436 					err = "name too long";
    437 					break;
    438 				}
    439 				q.type = type;
    440 				q.vers = 0;
    441 				q.path = QID(id, path);
    442 				t.wqid[t.nwqid++] = q;
    443 				continue;
    444 			}
    445 
    446 			/* is it a numeric name? */
    447 			for(j=0; (c=x->fcall.wname[i][j]); j++)
    448 				if(c<'0' || '9'<c)
    449 					goto Regular;
    450 			/* yes: it's a directory */
    451 			if(w)	/* name has form 27/23; get out before losing w */
    452 				break;
    453 			id = atoi(x->fcall.wname[i]);
    454 			qlock(&row.lk);
    455 			w = lookid(id, FALSE);
    456 			if(w == nil){
    457 				qunlock(&row.lk);
    458 				break;
    459 			}
    460 			incref(&w->ref);	/* we'll drop reference at end if there's an error */
    461 			path = Qdir;
    462 			type = QTDIR;
    463 			qunlock(&row.lk);
    464 			dir = dirtabw;
    465 			goto Accept;
    466 
    467     Regular:
    468 			if(strcmp(x->fcall.wname[i], "new") == 0){
    469 				if(w)
    470 					error("w set in walk to new");
    471 				sendp(cnewwindow, nil);	/* signal newwindowthread */
    472 				w = recvp(cnewwindow);	/* receive new window */
    473 				incref(&w->ref);
    474 				type = QTDIR;
    475 				path = QID(w->id, Qdir);
    476 				id = w->id;
    477 				dir = dirtabw;
    478 				goto Accept;
    479 			}
    480 
    481 			if(id == 0)
    482 				d = dirtab;
    483 			else
    484 				d = dirtabw;
    485 			d++;	/* skip '.' */
    486 			for(; d->name; d++)
    487 				if(strcmp(x->fcall.wname[i], d->name) == 0){
    488 					path = d->qid;
    489 					type = d->type;
    490 					dir = d;
    491 					goto Accept;
    492 				}
    493 
    494 			break;	/* file not found */
    495 		}
    496 
    497 		if(i==0 && err == nil)
    498 			err = Eexist;
    499 	}
    500 
    501 	if(err!=nil || t.nwqid<x->fcall.nwname){
    502 		if(nf){
    503 			nf->busy = FALSE;
    504 			fsysdelid(nf->mntdir);
    505 		}
    506 	}else if(t.nwqid  == x->fcall.nwname){
    507 		if(w){
    508 			f->w = w;
    509 			w = nil;	/* don't drop the reference */
    510 		}
    511 		if(dir)
    512 			f->dir = dir;
    513 		f->qid = q;
    514 	}
    515 
    516 	if(w != nil)
    517 		winclose(w);
    518 
    519 	return respond(x, &t, err);
    520 }
    521 
    522 static
    523 Xfid*
    524 fsysopen(Xfid *x, Fid *f)
    525 {
    526 	Fcall t;
    527 	int m;
    528 
    529 	/* can't truncate anything, so just disregard */
    530 	x->fcall.mode &= ~(OTRUNC|OCEXEC);
    531 	/* can't execute or remove anything */
    532 	if(x->fcall.mode==OEXEC || (x->fcall.mode&ORCLOSE))
    533 		goto Deny;
    534 	switch(x->fcall.mode){
    535 	default:
    536 		goto Deny;
    537 	case OREAD:
    538 		m = 0400;
    539 		break;
    540 	case OWRITE:
    541 		m = 0200;
    542 		break;
    543 	case ORDWR:
    544 		m = 0600;
    545 		break;
    546 	}
    547 	if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
    548 		goto Deny;
    549 
    550 	sendp(x->c, (void*)xfidopen);
    551 	return nil;
    552 
    553     Deny:
    554 	return respond(x, &t, Eperm);
    555 }
    556 
    557 static
    558 Xfid*
    559 fsyscreate(Xfid *x, Fid *f)
    560 {
    561 	Fcall t;
    562 
    563 	USED(f);
    564 	return respond(x, &t, Eperm);
    565 }
    566 
    567 static
    568 int
    569 idcmp(const void *a, const void *b)
    570 {
    571 	return *(int*)a - *(int*)b;
    572 }
    573 
    574 static
    575 Xfid*
    576 fsysread(Xfid *x, Fid *f)
    577 {
    578 	Fcall t;
    579 	uchar *b;
    580 	int i, id, n, o, e, j, k, *ids, nids;
    581 	Dirtab *d, dt;
    582 	Column *c;
    583 	uint clock, len;
    584 	char buf[16];
    585 
    586 	if(f->qid.type & QTDIR){
    587 		if(FILE(f->qid) == Qacme){	/* empty dir */
    588 			t.data = nil;
    589 			t.count = 0;
    590 			respond(x, &t, nil);
    591 			return x;
    592 		}
    593 		o = x->fcall.offset;
    594 		e = x->fcall.offset+x->fcall.count;
    595 		clock = getclock();
    596 		b = emalloc(messagesize);
    597 		id = WIN(f->qid);
    598 		n = 0;
    599 		if(id > 0)
    600 			d = dirtabw;
    601 		else
    602 			d = dirtab;
    603 		d++;	/* first entry is '.' */
    604 		for(i=0; d->name!=nil && i<e; i+=len){
    605 			len = dostat(WIN(x->f->qid), d, b+n, x->fcall.count-n, clock);
    606 			if(len <= BIT16SZ)
    607 				break;
    608 			if(i >= o)
    609 				n += len;
    610 			d++;
    611 		}
    612 		if(id == 0){
    613 			qlock(&row.lk);
    614 			nids = 0;
    615 			ids = nil;
    616 			for(j=0; j<row.ncol; j++){
    617 				c = row.col[j];
    618 				for(k=0; k<c->nw; k++){
    619 					ids = realloc(ids, (nids+1)*sizeof(int));
    620 					ids[nids++] = c->w[k]->id;
    621 				}
    622 			}
    623 			qunlock(&row.lk);
    624 			qsort(ids, nids, sizeof ids[0], idcmp);
    625 			j = 0;
    626 			dt.name = buf;
    627 			for(; j<nids && i<e; i+=len){
    628 				k = ids[j];
    629 				sprint(dt.name, "%d", k);
    630 				dt.qid = QID(k, Qdir);
    631 				dt.type = QTDIR;
    632 				dt.perm = DMDIR|0700;
    633 				len = dostat(k, &dt, b+n, x->fcall.count-n, clock);
    634 				if(len == 0)
    635 					break;
    636 				if(i >= o)
    637 					n += len;
    638 				j++;
    639 			}
    640 			free(ids);
    641 		}
    642 		t.data = (char*)b;
    643 		t.count = n;
    644 		respond(x, &t, nil);
    645 		free(b);
    646 		return x;
    647 	}
    648 	sendp(x->c, (void*)xfidread);
    649 	return nil;
    650 }
    651 
    652 static
    653 Xfid*
    654 fsyswrite(Xfid *x, Fid *f)
    655 {
    656 	USED(f);
    657 	sendp(x->c, (void*)xfidwrite);
    658 	return nil;
    659 }
    660 
    661 static
    662 Xfid*
    663 fsysclunk(Xfid *x, Fid *f)
    664 {
    665 	fsysdelid(f->mntdir);
    666 	sendp(x->c, (void*)xfidclose);
    667 	return nil;
    668 }
    669 
    670 static
    671 Xfid*
    672 fsysremove(Xfid *x, Fid *f)
    673 {
    674 	Fcall t;
    675 
    676 	USED(f);
    677 	return respond(x, &t, Eperm);
    678 }
    679 
    680 static
    681 Xfid*
    682 fsysstat(Xfid *x, Fid *f)
    683 {
    684 	Fcall t;
    685 
    686 	t.stat = emalloc(messagesize-IOHDRSZ);
    687 	t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
    688 	x = respond(x, &t, nil);
    689 	free(t.stat);
    690 	return x;
    691 }
    692 
    693 static
    694 Xfid*
    695 fsyswstat(Xfid *x, Fid *f)
    696 {
    697 	Fcall t;
    698 
    699 	USED(f);
    700 	return respond(x, &t, Eperm);
    701 }
    702 
    703 Fid*
    704 newfid(int fid)
    705 {
    706 	Fid *f, *ff, **fh;
    707 
    708 	ff = nil;
    709 	fh = &fids[fid&(Nhash-1)];
    710 	for(f=*fh; f; f=f->next)
    711 		if(f->fid == fid)
    712 			return f;
    713 		else if(ff==nil && f->busy==FALSE)
    714 			ff = f;
    715 	if(ff){
    716 		ff->fid = fid;
    717 		return ff;
    718 	}
    719 	f = emalloc(sizeof *f);
    720 	f->fid = fid;
    721 	f->next = *fh;
    722 	*fh = f;
    723 	return f;
    724 }
    725 
    726 uint
    727 getclock(void)
    728 {
    729 	return time(0);
    730 }
    731 
    732 int
    733 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
    734 {
    735 	Dir d;
    736 
    737 	d.qid.path = QID(id, dir->qid);
    738 	d.qid.vers = 0;
    739 	d.qid.type = dir->type;
    740 	d.mode = dir->perm;
    741 	d.length = 0;	/* would be nice to do better */
    742 	d.name = dir->name;
    743 	d.uid = user;
    744 	d.gid = user;
    745 	d.muid = user;
    746 	d.atime = clock;
    747 	d.mtime = clock;
    748 	return convD2M(&d, buf, nbuf);
    749 }