PageRenderTime 11ms CodeModel.GetById 3ms app.highlight 96ms RepoModel.GetById 1ms app.codeStats 0ms

/sys/src/cmd/disk/kfs/9p2.c

https://bitbucket.org/rminnich/sysfromiso
C | 1865 lines | 1531 code | 169 blank | 165 comment | 569 complexity | 59a30f288d69447311c36b87248a9649 MD5 | raw file
   1#include	"all.h"
   2
   3#define MSIZE	(MAXDAT+128)
   4
   5static void
   6seterror(Fcall *ou, int err)
   7{
   8
   9	if(0 <= err && err < MAXERR)
  10		ou->ename = errstring[err];
  11	else
  12		ou->ename = "unknown error";
  13}
  14
  15static int
  16fsversion(Chan* chan, Fcall* f, Fcall* r)
  17{
  18	if(f->msize < MSIZE)
  19		r->msize = f->msize;
  20	else
  21		r->msize = MSIZE;
  22	/*
  23	 * Should check the '.' stuff here.
  24	 * What happens if Tversion has already been seen?
  25	 */
  26	if(strcmp(f->version, VERSION9P) == 0){
  27		r->version = VERSION9P;
  28		chan->msize = r->msize;
  29	}else
  30		r->version = "unknown";
  31
  32	fileinit(chan);
  33	return 0;
  34}
  35
  36char *keyspec = "proto=p9any role=server";
  37
  38static int
  39fsauth(Chan *chan, Fcall *f, Fcall *r)
  40{
  41	int err, fd;
  42	char *aname;
  43	File *file;
  44	int afd;
  45	AuthRpc *rpc;
  46
  47	err = 0;
  48	if(chan == cons.srvchan)
  49		return Eauthmsg;
  50	file = filep(chan, f->afid, 1);
  51	if(file == nil)
  52		return Efidinuse;
  53
  54	/* forget any previous authentication */
  55	file->cuid = 0;
  56
  57	if(access("/mnt/factotum", 0) < 0)
  58		if((fd = open("/srv/factotum", ORDWR)) >= 0)
  59			mount(fd, -1, "/mnt", MBEFORE, "");
  60
  61	afd = open("/mnt/factotum/rpc", ORDWR);
  62	if(afd < 0){
  63		err = Esystem;
  64		goto out;
  65	}
  66	rpc = auth_allocrpc(afd);
  67	if(rpc == nil){
  68		close(afd);
  69		err = Esystem;
  70		goto out;
  71	}
  72	file->rpc = rpc;
  73	if(auth_rpc(rpc, "start", keyspec, strlen(keyspec)) != ARok){
  74		err = Esystem;
  75		goto out;
  76	}
  77
  78	aname = f->aname;
  79	if(!aname[0])
  80		aname = "main";
  81	file->fs = fsstr(aname);
  82	if(file->fs == nil){
  83		err = Ebadspc;
  84		goto out;
  85	}
  86	file->uid = strtouid(f->uname);
  87	if(file->uid < 0){
  88		err = Ebadu;
  89		goto out;
  90	}
  91	file->qid.path = 0;
  92	file->qid.vers = 0;
  93	file->qid.type = QTAUTH;
  94	r->qid = file->qid;
  95
  96out:
  97	if(file != nil){
  98		qunlock(file);
  99		if(err != 0)
 100			freefp(file);
 101	}
 102	return err;
 103}
 104
 105int
 106authread(File *file, uchar *data, int count)
 107{
 108	AuthInfo *ai;
 109	AuthRpc *rpc;
 110	int rv;
 111
 112	rpc = file->rpc;
 113	if(rpc == nil)
 114		return -1;
 115
 116	rv = auth_rpc(rpc, "read", nil, 0);
 117	switch(rv){
 118	case ARdone:
 119		ai = auth_getinfo(rpc);
 120		if(ai == nil)
 121			return -1;
 122		if(chat)
 123			print("authread identifies user as %s\n", ai->cuid);
 124		file->cuid = strtouid(ai->cuid);
 125		auth_freeAI(ai);
 126		if(file->cuid == 0)
 127			return -1;
 128		if(chat)
 129			print("%s is a known user\n", ai->cuid);
 130		return 0;
 131	case ARok:
 132		if(count < rpc->narg)
 133			return -1;
 134		memmove(data, rpc->arg, rpc->narg);
 135		return rpc->narg;
 136	case ARphase:
 137		return -1;
 138	default:
 139		return -1;
 140	}
 141}
 142
 143int
 144authwrite(File *file, uchar *data, int count)
 145{
 146	int ret;
 147
 148	ret = auth_rpc(file->rpc, "write", data, count);
 149	if(ret != ARok)
 150		return -1;
 151	return count;
 152}
 153
 154void
 155mkqid9p1(Qid9p1* qid9p1, Qid* qid)
 156{
 157	if(qid->path & 0xFFFFFFFF00000000LL)
 158		panic("mkqid9p1: path %lluX\n", qid->path);
 159	qid9p1->path = qid->path & 0xFFFFFFFF;
 160	if(qid->type & QTDIR)
 161		qid9p1->path |= QPDIR;
 162	qid9p1->version = qid->vers;
 163}
 164
 165void
 166authfree(File *fp)
 167{
 168	if(fp->rpc != nil){
 169		close(fp->rpc->afd);
 170		free(fp->rpc);
 171		fp->rpc = nil;
 172	}
 173}
 174
 175void
 176mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode)
 177{
 178	qid->path = (ulong)(qid9p1->path & ~QPDIR);
 179	qid->vers = qid9p1->version;
 180	qid->type = 0;
 181	if(mode & DDIR)
 182		qid->type |= QTDIR;
 183	if(mode & DAPND)
 184		qid->type |= QTAPPEND;
 185	if(mode & DLOCK)
 186		qid->type |= QTEXCL;
 187}
 188
 189static int
 190checkattach(Chan *chan, File *afile, File *file, Filsys *fs)
 191{
 192	uchar buf[1];
 193
 194	if(chan == cons.srvchan || chan == cons.chan)
 195		return 0;
 196
 197	/* if no afile, this had better be none */
 198	if(afile == nil){
 199		if(file->uid == 0){
 200			if(!allownone && !chan->authed)
 201				return Eauth;
 202			return 0;
 203		}
 204		return Eauth;
 205	}
 206
 207	/* otherwise, we'ld better have a usable cuid */
 208	if(!(afile->qid.type&QTAUTH))
 209		return Eauth;
 210	if(afile->uid != file->uid || afile->fs != fs)
 211		return Eauth;
 212	if(afile->cuid <= 0){
 213		if(authread(afile, buf, 0) != 0)
 214			return Eauth;
 215		if(afile->cuid <= 0)
 216			return Eauth;
 217	}
 218	file->uid = afile->cuid;
 219
 220	/* once someone has authenticated on the channel, others can become none */
 221	chan->authed = 1;
 222
 223	return 0;
 224}		
 225
 226static int
 227fsattach(Chan* chan, Fcall* f, Fcall* r)
 228{
 229	char *aname;
 230	Iobuf *p;
 231	Dentry *d;
 232	File *file;
 233	File *afile;
 234	Filsys *fs;
 235	long raddr;
 236	int error, u;
 237
 238	aname = f->aname;
 239	if(!aname[0])	/* default */
 240		aname = "main";
 241	p = nil;
 242	afile = filep(chan, f->afid, 0);
 243	file = filep(chan, f->fid, 1);
 244	if(file == nil){
 245		error = Efidinuse;
 246		goto out;
 247	}
 248
 249	u = -1;
 250	if(chan != cons.chan){
 251		if(strcmp(f->uname, "adm") == 0){
 252			error = Eauth;
 253			goto out;
 254		}
 255		u = strtouid(f->uname);
 256		if(u < 0){
 257			error = Ebadu;
 258			goto out;
 259		}
 260	}
 261	file->uid = u;
 262
 263	fs = fsstr(aname);
 264	if(fs == nil){
 265		error = Ebadspc;
 266		goto out;
 267	}
 268
 269	if(error = checkattach(chan, afile, file, fs))
 270		goto out;
 271
 272	raddr = getraddr(fs->dev);
 273	p = getbuf(fs->dev, raddr, Bread);
 274	d = getdir(p, 0);
 275	if(d == nil || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)){
 276		error = Ealloc;
 277		goto out;
 278	}
 279	if(iaccess(file, d, DEXEC)){
 280		error = Eaccess;
 281		goto out;
 282	}
 283	if(file->uid == 0 && isro(fs->dev)) {
 284		/*
 285		 * 'none' not allowed on dump
 286		 */
 287		error = Eaccess;
 288		goto out;
 289	}
 290	accessdir(p, d, FREAD);
 291	mkqid(&file->qid, d, 1);
 292	file->fs = fs;
 293	file->addr = raddr;
 294	file->slot = 0;
 295	file->open = 0;
 296	freewp(file->wpath);
 297	file->wpath = 0;
 298
 299	r->qid = file->qid;
 300
 301//	if(cons.flags & attachflag)
 302//		print("9p2: attach %s %T to \"%s\" C%d\n",
 303//			chan->whoname, chan->whotime, fs->name, chan->chan);
 304
 305out:
 306//	if((cons.flags & attachflag) && error)
 307//		print("9p2: attach %s %T SUCK EGGS --- %s\n",
 308//			f->uname, time(), errstr[error]);
 309	if(p != nil)
 310		putbuf(p);
 311	if(afile != nil)
 312		qunlock(afile);
 313	if(file != nil){
 314		qunlock(file);
 315		if(error)
 316			freefp(file);
 317	}
 318
 319	return error;
 320}
 321
 322static int
 323fsflush(Chan* chan, Fcall*, Fcall*)
 324{
 325	runlock(&chan->reflock);
 326	wlock(&chan->reflock);
 327	wunlock(&chan->reflock);
 328	rlock(&chan->reflock);
 329
 330	return 0;
 331}
 332
 333static void
 334clone(File* nfile, File* file)
 335{
 336	Wpath *wpath;
 337
 338	nfile->qid = file->qid;
 339
 340	lock(&wpathlock);
 341	nfile->wpath = file->wpath;
 342	for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up)
 343		wpath->refs++;
 344	unlock(&wpathlock);
 345
 346	nfile->fs = file->fs;
 347	nfile->addr = file->addr;
 348	nfile->slot = file->slot;
 349	nfile->uid = file->uid;
 350	nfile->cuid = 0;
 351	nfile->open = file->open & ~FREMOV;
 352}
 353
 354static int
 355walkname(File* file, char* wname, Qid* wqid)
 356{
 357	Wpath *w;
 358	Iobuf *p, *p1;
 359	Dentry *d, *d1;
 360	int error, slot;
 361	long addr, qpath;
 362
 363	p = p1 = nil;
 364
 365	/*
 366	 * File must not have been opened for I/O by an open
 367	 * or create message and must represent a directory.
 368	 */
 369	if(file->open != 0){
 370		error = Emode;
 371		goto out;
 372	}
 373
 374	p = getbuf(file->fs->dev, file->addr, Bread);
 375	if(p == nil || checktag(p, Tdir, QPNONE)){
 376		error = Edir1;
 377		goto out;
 378	}
 379	if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
 380		error = Ealloc;
 381		goto out;
 382	}
 383	if(!(d->mode & DDIR)){
 384		error = Edir1;
 385		goto out;
 386	}
 387	if(error = mkqidcmp(&file->qid, d))
 388		goto out;
 389
 390	/*
 391	 * For walked elements the implied user must
 392	 * have permission to search the directory.
 393	 */
 394	if(file->cp != cons.chan && iaccess(file, d, DEXEC)){
 395		error = Eaccess;
 396		goto out;
 397	}
 398	accessdir(p, d, FREAD);
 399
 400	if(strcmp(wname, ".") == 0){
 401setdot:
 402		if(wqid != nil)
 403			*wqid = file->qid;
 404		goto out;
 405	}
 406	if(strcmp(wname, "..") == 0){
 407		if(file->wpath == 0)
 408			goto setdot;
 409		putbuf(p);
 410		p = nil;
 411		addr = file->wpath->addr;
 412		slot = file->wpath->slot;
 413		p1 = getbuf(file->fs->dev, addr, Bread);
 414		if(p1 == nil || checktag(p1, Tdir, QPNONE)){
 415			error = Edir1;
 416			goto out;
 417		}
 418		if((d1 = getdir(p1, slot)) == nil || !(d1->mode & DALLOC)){
 419			error = Ephase;
 420			goto out;
 421		}
 422		lock(&wpathlock);
 423		file->wpath->refs--;
 424		file->wpath = file->wpath->up;
 425		unlock(&wpathlock);
 426		goto found;
 427	}
 428
 429	for(addr = 0; ; addr++){
 430		if(p == nil){
 431			p = getbuf(file->fs->dev, file->addr, Bread);
 432			if(p == nil || checktag(p, Tdir, QPNONE)){
 433				error = Ealloc;
 434				goto out;
 435			}
 436			d = getdir(p, file->slot);
 437			if(d == nil ||  !(d->mode & DALLOC)){
 438				error = Ealloc;
 439				goto out;
 440			}
 441		}
 442		qpath = d->qid.path;
 443		p1 = dnodebuf1(p, d, addr, 0);
 444		p = nil;
 445		if(p1 == nil || checktag(p1, Tdir, qpath)){
 446			error = Eentry;
 447			goto out;
 448		}
 449		for(slot = 0; slot < DIRPERBUF; slot++){
 450			d1 = getdir(p1, slot);
 451			if(!(d1->mode & DALLOC))
 452				continue;
 453			if(strncmp(wname, d1->name, NAMELEN) != 0)
 454				continue;
 455			/*
 456			 * update walk path
 457			 */
 458			if((w = newwp()) == nil){
 459				error = Ewalk;
 460				goto out;
 461			}
 462			w->addr = file->addr;
 463			w->slot = file->slot;
 464			w->up = file->wpath;
 465			file->wpath = w;
 466			slot += DIRPERBUF*addr;
 467			goto found;
 468		}
 469		putbuf(p1);
 470		p1 = nil;
 471	}
 472
 473found:
 474	file->addr = p1->addr;
 475	mkqid(&file->qid, d1, 1);
 476	putbuf(p1);
 477	p1 = nil;
 478	file->slot = slot;
 479	if(wqid != nil)
 480		*wqid = file->qid;
 481
 482out:
 483	if(p1 != nil)
 484		putbuf(p1);
 485	if(p != nil)
 486		putbuf(p);
 487
 488	return error;
 489}
 490
 491static int
 492fswalk(Chan* chan, Fcall* f, Fcall* r)
 493{
 494	int error, nwname;
 495	File *file, *nfile, tfile;
 496
 497	/*
 498	 * The file identified by f->fid must be valid in the
 499	 * current session and must not have been opened for I/O
 500	 * by an open or create message.
 501	 */
 502	if((file = filep(chan, f->fid, 0)) == nil)
 503		return Efid;
 504	if(file->open != 0){
 505		qunlock(file);
 506		return Emode;
 507	}
 508
 509	/*
 510	 * If newfid is not the same as fid, allocate a new file;
 511	 * a side effect is checking newfid is not already in use (error);
 512	 * if there are no names to walk this will be equivalent to a
 513	 * simple 'clone' operation.
 514	 * Otherwise, fid and newfid are the same and if there are names
 515	 * to walk make a copy of 'file' to be used during the walk as
 516	 * 'file' must only be updated on success.
 517	 * Finally, it's a no-op if newfid is the same as fid and f->nwname
 518	 * is 0.
 519	 */
 520	r->nwqid = 0;
 521	if(f->newfid != f->fid){
 522		if((nfile = filep(chan, f->newfid, 1)) == nil){
 523			qunlock(file);
 524			return Efidinuse;
 525		}
 526	}
 527	else if(f->nwname != 0){
 528		nfile = &tfile;
 529		memset(nfile, 0, sizeof(File));
 530		nfile->cp = chan;
 531		nfile->fid = ~0;
 532	}
 533	else{
 534		qunlock(file);
 535		return 0;
 536	}
 537	clone(nfile, file);
 538
 539	/*
 540	 * Should check name is not too long.
 541	 */
 542	error = 0;
 543	for(nwname = 0; nwname < f->nwname; nwname++){
 544		error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]);
 545		if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid))
 546			break;
 547	}
 548
 549	if(f->nwname == 0){
 550		/*
 551		 * Newfid must be different to fid (see above)
 552		 * so this is a simple 'clone' operation - there's
 553		 * nothing to do except unlock unless there's
 554		 * an error.
 555		 */
 556		if(error){
 557			freewp(nfile->wpath);
 558			qunlock(nfile);
 559			freefp(nfile);
 560		}
 561		else
 562			qunlock(nfile);
 563	}
 564	else if(r->nwqid < f->nwname){
 565		/*
 566		 * Didn't walk all elements, 'clunk' nfile
 567		 * and leave 'file' alone.
 568		 * Clear error if some of the elements were
 569		 * walked OK.
 570		 */
 571		freewp(nfile->wpath);
 572		if(nfile != &tfile){
 573			qunlock(nfile);
 574			freefp(nfile);
 575		}
 576		if(r->nwqid != 0)
 577			error = 0;
 578	}
 579	else{
 580		/*
 581		 * Walked all elements. If newfid is the same
 582		 * as fid must update 'file' from the temporary
 583		 * copy used during the walk.
 584		 * Otherwise just unlock (when using tfile there's
 585		 * no need to unlock as it's a local).
 586		 */
 587		if(nfile == &tfile){
 588			file->qid = nfile->qid;
 589			freewp(file->wpath);
 590			file->wpath = nfile->wpath;
 591			file->addr = nfile->addr;
 592			file->slot = nfile->slot;
 593		}
 594		else
 595			qunlock(nfile);
 596	}
 597	qunlock(file);
 598
 599	return error;
 600}
 601
 602static int
 603fsopen(Chan* chan, Fcall* f, Fcall* r)
 604{
 605	Iobuf *p;
 606	Dentry *d;
 607	File *file;
 608	Tlock *t;
 609	Qid qid;
 610	int error, ro, fmod, wok;
 611
 612	wok = 0;
 613	p = nil;
 614
 615	if(chan == cons.chan || writeallow)
 616		wok = 1;
 617
 618	if((file = filep(chan, f->fid, 0)) == nil){
 619		error = Efid;
 620		goto out;
 621	}
 622
 623	/*
 624	 * if remove on close, check access here
 625	 */
 626	ro = isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup));
 627	if(f->mode & ORCLOSE){
 628		if(ro){
 629			error = Eronly;
 630			goto out;
 631		}
 632		/*
 633		 * check on parent directory of file to be deleted
 634		 */
 635		if(file->wpath == 0 || file->wpath->addr == file->addr){
 636			error = Ephase;
 637			goto out;
 638		}
 639		p = getbuf(file->fs->dev, file->wpath->addr, Bread);
 640		if(p == nil || checktag(p, Tdir, QPNONE)){
 641			error = Ephase;
 642			goto out;
 643		}
 644		if((d = getdir(p, file->wpath->slot)) == nil || !(d->mode & DALLOC)){
 645			error = Ephase;
 646			goto out;
 647		}
 648		if(iaccess(file, d, DWRITE)){
 649			error = Eaccess;
 650			goto out;
 651		}
 652		putbuf(p);
 653	}
 654	p = getbuf(file->fs->dev, file->addr, Bread);
 655	if(p == nil || checktag(p, Tdir, QPNONE)){
 656		error = Ealloc;
 657		goto out;
 658	}
 659	if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
 660		error = Ealloc;
 661		goto out;
 662	}
 663	if(error = mkqidcmp(&file->qid, d))
 664		goto out;
 665	mkqid(&qid, d, 1);
 666	switch(f->mode & 7){
 667
 668	case OREAD:
 669		if(iaccess(file, d, DREAD) && !wok)
 670			goto badaccess;
 671		fmod = FREAD;
 672		break;
 673
 674	case OWRITE:
 675		if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
 676			goto badaccess;
 677		if(ro){
 678			error = Eronly;
 679			goto out;
 680		}
 681		fmod = FWRITE;
 682		break;
 683
 684	case ORDWR:
 685		if((d->mode & DDIR)
 686		|| (iaccess(file, d, DREAD) && !wok)
 687		|| (iaccess(file, d, DWRITE) && !wok))
 688			goto badaccess;
 689		if(ro){
 690			error = Eronly;
 691			goto out;
 692		}
 693		fmod = FREAD+FWRITE;
 694		break;
 695
 696	case OEXEC:
 697		if((d->mode & DDIR) || (iaccess(file, d, DEXEC) && !wok))
 698			goto badaccess;
 699		fmod = FREAD;
 700		break;
 701
 702	default:
 703		error = Emode;
 704		goto out;
 705	}
 706	if(f->mode & OTRUNC){
 707		if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
 708			goto badaccess;
 709		if(ro){
 710			error = Eronly;
 711			goto out;
 712		}
 713	}
 714	t = 0;
 715	if(d->mode & DLOCK){
 716		if((t = tlocked(p, d)) == nil){
 717			error = Elocked;
 718			goto out;
 719		}
 720	}
 721	if(f->mode & ORCLOSE)
 722		fmod |= FREMOV;
 723	file->open = fmod;
 724	if((f->mode & OTRUNC) && !(d->mode & DAPND)){
 725		dtrunc(p, d);
 726		qid.vers = d->qid.version;
 727	}
 728	r->qid = qid;
 729	file->tlock = t;
 730	if(t != nil)
 731		t->file = file;
 732	file->lastra = 1;
 733	goto out;
 734
 735badaccess:
 736	error = Eaccess;
 737	file->open = 0;
 738
 739out:
 740	if(p != nil)
 741		putbuf(p);
 742	if(file != nil)
 743		qunlock(file);
 744
 745	r->iounit = chan->msize-IOHDRSZ;
 746
 747	return error;
 748}
 749
 750static int
 751dir9p2(Dir* dir, Dentry* dentry, void* strs)
 752{
 753	char *op, *p;
 754
 755	memset(dir, 0, sizeof(Dir));
 756	mkqid(&dir->qid, dentry, 1);
 757	dir->mode = (dir->qid.type<<24)|(dentry->mode & 0777);
 758	dir->atime = dentry->atime;
 759	dir->mtime = dentry->mtime;
 760	dir->length = dentry->size;
 761
 762	op = p = strs;
 763	dir->name = p;
 764	p += sprint(p, "%s", dentry->name)+1;
 765
 766	dir->uid = p;
 767	uidtostr(p, dentry->uid);
 768	p += strlen(p)+1;
 769
 770	dir->gid = p;
 771	uidtostr(p, dentry->gid);
 772	p += strlen(p)+1;
 773
 774	dir->muid = p;
 775	strcpy(p, "");
 776	p += strlen(p)+1;
 777
 778	return p-op;
 779}
 780
 781static int
 782checkname9p2(char* name)
 783{
 784	char *p;
 785
 786	/*
 787	 * Return length of string if valid, 0 if not.
 788	 */
 789	if(name == nil)
 790		return 0;
 791
 792	for(p = name; *p != 0; p++){
 793		if((*p & 0xFF) <= 040)
 794			return 0;
 795	}
 796
 797	return p-name;
 798}
 799
 800static int
 801fscreate(Chan* chan, Fcall* f, Fcall* r)
 802{
 803	Iobuf *p, *p1;
 804	Dentry *d, *d1;
 805	File *file;
 806	int error, slot, slot1, fmod, wok, l;
 807	long addr, addr1, path;
 808	Tlock *t;
 809	Wpath *w;
 810
 811	wok = 0;
 812	p = nil;
 813
 814	if(chan == cons.chan || writeallow)
 815		wok = 1;
 816
 817	if((file = filep(chan, f->fid, 0)) == nil){
 818		error = Efid;
 819		goto out;
 820	}
 821	if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){
 822		error = Eronly;
 823		goto out;
 824	}
 825
 826	p = getbuf(file->fs->dev, file->addr, Bread);
 827	if(p == nil || checktag(p, Tdir, QPNONE)){
 828		error = Ealloc;
 829		goto out;
 830	}
 831	if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
 832		error = Ealloc;
 833		goto out;
 834	}
 835	if(error = mkqidcmp(&file->qid, d))
 836		goto out;
 837	if(!(d->mode & DDIR)){
 838		error = Edir2;
 839		goto out;
 840	}
 841	if(iaccess(file, d, DWRITE) && !wok) {
 842		error = Eaccess;
 843		goto out;
 844	}
 845	accessdir(p, d, FREAD);
 846
 847	/*
 848	 * Check the name is valid and will fit in an old
 849	 * directory entry.
 850	 */
 851	if((l = checkname9p2(f->name)) == 0){
 852		error = Ename;
 853		goto out;
 854	}
 855	if(l+1 > NAMELEN){
 856		error = Etoolong;
 857		goto out;
 858	}
 859	if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){
 860		error = Edot;
 861		goto out;
 862	}
 863
 864	addr1 = 0;
 865	slot1 = 0;	/* set */
 866	for(addr = 0; ; addr++){
 867		if((p1 = dnodebuf(p, d, addr, 0)) == nil){
 868			if(addr1 != 0)
 869				break;
 870			p1 = dnodebuf(p, d, addr, Tdir);
 871		}
 872		if(p1 == nil){
 873			error = Efull;
 874			goto out;
 875		}
 876		if(checktag(p1, Tdir, d->qid.path)){
 877			putbuf(p1);
 878			goto phase;
 879		}
 880		for(slot = 0; slot < DIRPERBUF; slot++){
 881			d1 = getdir(p1, slot);
 882			if(!(d1->mode & DALLOC)){
 883				if(addr1 == 0){
 884					addr1 = p1->addr;
 885					slot1 = slot + addr*DIRPERBUF;
 886				}
 887				continue;
 888			}
 889			if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){
 890				putbuf(p1);
 891				error = Eexist;
 892				goto out;
 893			}
 894		}
 895		putbuf(p1);
 896	}
 897
 898	switch(f->mode & 7){
 899	case OEXEC:
 900	case OREAD:		/* seems only useful to make directories */
 901		fmod = FREAD;
 902		break;
 903
 904	case OWRITE:
 905		fmod = FWRITE;
 906		break;
 907
 908	case ORDWR:
 909		fmod = FREAD+FWRITE;
 910		break;
 911
 912	default:
 913		error = Emode;
 914		goto out;
 915	}
 916	if(f->perm & PDIR)
 917		if((f->mode & OTRUNC) || (f->perm & PAPND) || (fmod & FWRITE))
 918			goto badaccess;
 919	/*
 920	 * do it
 921	 */
 922	path = qidpathgen(&file->fs->dev);
 923	if((p1 = getbuf(file->fs->dev, addr1, Bread|Bimm|Bmod)) == nil)
 924		goto phase;
 925	d1 = getdir(p1, slot1);
 926	if(d1 == nil || checktag(p1, Tdir, d->qid.path)) {
 927		putbuf(p1);
 928		goto phase;
 929	}
 930	if(d1->mode & DALLOC){
 931		putbuf(p1);
 932		goto phase;
 933	}
 934
 935	strncpy(d1->name, f->name, sizeof(d1->name));
 936	if(chan == cons.chan){
 937		d1->uid = cons.uid;
 938		d1->gid = cons.gid;
 939	}
 940	else{
 941		d1->uid = file->uid;
 942		d1->gid = d->gid;
 943		f->perm &= d->mode | ~0666;
 944		if(f->perm & PDIR)
 945			f->perm &= d->mode | ~0777;
 946	}
 947	d1->qid.path = path;
 948	d1->qid.version = 0;
 949	d1->mode = DALLOC | (f->perm & 0777);
 950	if(f->perm & PDIR) {
 951		d1->mode |= DDIR;
 952		d1->qid.path |= QPDIR;
 953	}
 954	if(f->perm & PAPND)
 955		d1->mode |= DAPND;
 956	t = nil;
 957	if(f->perm & PLOCK){
 958		d1->mode |= DLOCK;
 959		t = tlocked(p1, d1);
 960		/* if nil, out of tlock structures */
 961	}
 962	accessdir(p1, d1, FWRITE);
 963	mkqid(&r->qid, d1, 0);
 964	putbuf(p1);
 965	accessdir(p, d, FWRITE);
 966
 967	/*
 968	 * do a walk to new directory entry
 969	 */
 970	if((w = newwp()) == nil){
 971		error = Ewalk;
 972		goto out;
 973	}
 974	w->addr = file->addr;
 975	w->slot = file->slot;
 976	w->up = file->wpath;
 977	file->wpath = w;
 978	file->qid = r->qid;
 979	file->tlock = t;
 980	if(t != nil)
 981		t->file = file;
 982	file->lastra = 1;
 983	if(f->mode & ORCLOSE)
 984		fmod |= FREMOV;
 985	file->open = fmod;
 986	file->addr = addr1;
 987	file->slot = slot1;
 988	goto out;
 989
 990badaccess:
 991	error = Eaccess;
 992	goto out;
 993
 994phase:
 995	error = Ephase;
 996
 997out:
 998	if(p != nil)
 999		putbuf(p);
1000	if(file != nil)
1001		qunlock(file);
1002
1003	r->iounit = chan->msize-IOHDRSZ;
1004
1005	return error;
1006}
1007
1008static int
1009fsread(Chan* chan, Fcall* f, Fcall* r)
1010{
1011	uchar *data;
1012	Iobuf *p, *p1;
1013	File *file;
1014	Dentry *d, *d1;
1015	Tlock *t;
1016	long addr, offset, start, tim;
1017	int error, iounit, nread, count, n, o, slot;
1018	Dir dir;
1019	char strdata[28*10];
1020
1021	p = nil;
1022	data = (uchar*)r->data;
1023	count = f->count;
1024	offset = f->offset;
1025	nread = 0;
1026	if((file = filep(chan, f->fid, 0)) == nil){
1027		error = Efid;
1028		goto out;
1029	}
1030	if(file->qid.type & QTAUTH){
1031		nread = authread(file, data, count);
1032		if(nread < 0)
1033			error = Esystem;
1034		else
1035			error = 0;
1036		goto out;
1037	}
1038	if(!(file->open & FREAD)){
1039		error = Eopen;
1040		goto out;
1041	}
1042	iounit = chan->msize-IOHDRSZ;
1043	if(count < 0 || count > iounit){
1044		error = Ecount;
1045		goto out;
1046	}
1047	if(offset < 0){
1048		error = Eoffset;
1049		goto out;
1050	}
1051	p = getbuf(file->fs->dev, file->addr, Bread);
1052	if(p == nil || checktag(p, Tdir, QPNONE)){
1053		error = Ealloc;
1054		goto out;
1055	}
1056	if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1057		error = Ealloc;
1058		goto out;
1059	}
1060	if(error = mkqidcmp(&file->qid, d))
1061		goto out;
1062	if(t = file->tlock){
1063		tim = time(0);
1064		if(t->time < tim || t->file != file){
1065			error = Ebroken;
1066			goto out;
1067		}
1068		/* renew the lock */
1069		t->time = tim + TLOCK;
1070	}
1071	accessdir(p, d, FREAD);
1072	if(d->mode & DDIR)
1073		goto dread;
1074	if(offset+count > d->size)
1075		count = d->size - offset;
1076	while(count > 0){
1077		if(p == nil){
1078			p = getbuf(file->fs->dev, file->addr, Bread);
1079			if(p == nil || checktag(p, Tdir, QPNONE)){
1080				error = Ealloc;
1081				goto out;
1082			}
1083			if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1084				error = Ealloc;
1085				goto out;
1086			}
1087		}
1088		addr = offset / BUFSIZE;
1089		o = offset % BUFSIZE;
1090		n = BUFSIZE - o;
1091		if(n > count)
1092			n = count;
1093		p1 = dnodebuf1(p, d, addr, 0);
1094		p = nil;
1095		if(p1 != nil){
1096			if(checktag(p1, Tfile, QPNONE)){
1097				error = Ephase;
1098				putbuf(p1);
1099				goto out;
1100			}
1101			memmove(data+nread, p1->iobuf+o, n);
1102			putbuf(p1);
1103		}
1104		else
1105			memset(data+nread, 0, n);
1106		count -= n;
1107		nread += n;
1108		offset += n;
1109	}
1110	goto out;
1111
1112dread:
1113	/*
1114	 * Pick up where we left off last time if nothing has changed,
1115	 * otherwise must scan from the beginning.
1116	 */
1117	if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){
1118		addr = file->dslot/DIRPERBUF;
1119		slot = file->dslot%DIRPERBUF;
1120		start = offset;
1121	}
1122	else{
1123		addr = 0;
1124		slot = 0;
1125		start = 0;
1126	}
1127
1128dread1:
1129	if(p == nil){
1130		/*
1131		 * This is just a check to ensure the entry hasn't
1132		 * gone away during the read of each directory block.
1133		 */
1134		p = getbuf(file->fs->dev, file->addr, Bread);
1135		if(p == nil || checktag(p, Tdir, QPNONE)){
1136			error = Ealloc;
1137			goto out1;
1138		}
1139		if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1140			error = Ealloc;
1141			goto out1;
1142		}
1143	}
1144	p1 = dnodebuf1(p, d, addr, 0);
1145	p = nil;
1146	if(p1 == nil)
1147		goto out1;
1148	if(checktag(p1, Tdir, QPNONE)){
1149		error = Ephase;
1150		putbuf(p1);
1151		goto out1;
1152	}
1153
1154	for(; slot < DIRPERBUF; slot++){
1155		d1 = getdir(p1, slot);
1156		if(!(d1->mode & DALLOC))
1157			continue;
1158		dir9p2(&dir, d1, strdata);
1159		if((n = convD2M(&dir, data+nread, iounit - nread)) <= BIT16SZ){
1160			putbuf(p1);
1161			goto out1;
1162		}
1163		start += n;
1164		if(start < offset)
1165			continue;
1166		if(count < n){
1167			putbuf(p1);
1168			goto out1;
1169		}
1170		count -= n;
1171		nread += n;
1172		offset += n;
1173	}
1174	putbuf(p1);
1175	slot = 0;
1176	addr++;
1177	goto dread1;
1178
1179out1:
1180	if(error == 0){
1181		file->doffset = offset;
1182		file->dvers = file->qid.vers;
1183		file->dslot = slot+DIRPERBUF*addr;
1184	}
1185
1186out:
1187	/*
1188	 * Do we need this any more?
1189	count = f->count - nread;
1190	if(count > 0)
1191		memset(data+nread, 0, count);
1192	 */
1193	if(p != nil)
1194		putbuf(p);
1195	if(file != nil)
1196		qunlock(file);
1197	r->count = nread;
1198	r->data = (char*)data;
1199
1200	return error;
1201}
1202
1203static int
1204fswrite(Chan* chan, Fcall* f, Fcall* r)
1205{
1206	Iobuf *p, *p1;
1207	Dentry *d;
1208	File *file;
1209	Tlock *t;
1210	long offset, addr, tim, qpath;
1211	int count, error, nwrite, o, n;
1212
1213	offset = f->offset;
1214	count = f->count;
1215
1216	nwrite = 0;
1217	p = nil;
1218
1219	if((file = filep(chan, f->fid, 0)) == nil){
1220		error = Efid;
1221		goto out;
1222	}
1223	if(file->qid.type & QTAUTH){
1224		nwrite = authwrite(file, (uchar*)f->data, count);
1225		if(nwrite < 0)
1226			error = Esystem;
1227		else
1228			error = 0;
1229		goto out;
1230	}
1231	if(!(file->open & FWRITE)){
1232		error = Eopen;
1233		goto out;
1234	}
1235	if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){
1236		error = Eronly;
1237		goto out;
1238	}
1239	if(count < 0 || count > chan->msize-IOHDRSZ){
1240		error = Ecount;
1241		goto out;
1242	}
1243	if(offset < 0) {
1244		error = Eoffset;
1245		goto out;
1246	}
1247	if((p = getbuf(file->fs->dev, file->addr, Bread|Bmod)) == nil){
1248		error = Ealloc;
1249		goto out;
1250	}
1251	if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1252		error = Ealloc;
1253		goto out;
1254	}
1255	if(error = mkqidcmp(&file->qid, d))
1256		goto out;
1257	if(t = file->tlock) {
1258		tim = time(0);
1259		if(t->time < tim || t->file != file){
1260			error = Ebroken;
1261			goto out;
1262		}
1263		/* renew the lock */
1264		t->time = tim + TLOCK;
1265	}
1266	accessdir(p, d, FWRITE);
1267	if(d->mode & DAPND)
1268		offset = d->size;
1269	if(offset+count > d->size)
1270		d->size = offset+count;
1271	while(count > 0){
1272		if(p == nil){
1273			p = getbuf(file->fs->dev, file->addr, Bread|Bmod);
1274			if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1275				error = Ealloc;
1276				goto out;
1277			}
1278		}
1279		addr = offset / BUFSIZE;
1280		o = offset % BUFSIZE;
1281		n = BUFSIZE - o;
1282		if(n > count)
1283			n = count;
1284		qpath = d->qid.path;
1285		p1 = dnodebuf1(p, d, addr, Tfile);
1286		p = nil;
1287		if(p1 == nil) {
1288			error = Efull;
1289			goto out;
1290		}
1291		if(checktag(p1, Tfile, qpath)){
1292			putbuf(p1);
1293			error = Ephase;
1294			goto out;
1295		}
1296		memmove(p1->iobuf+o, f->data+nwrite, n);
1297		p1->flags |= Bmod;
1298		putbuf(p1);
1299		count -= n;
1300		nwrite += n;
1301		offset += n;
1302	}
1303
1304out:
1305	if(p != nil)
1306		putbuf(p);
1307	if(file != nil)
1308		qunlock(file);
1309	r->count = nwrite;
1310
1311	return error;
1312}
1313
1314static int
1315_clunk(File* file, int remove, int wok)
1316{
1317	Tlock *t;
1318	int error;
1319
1320	error = 0;
1321	if(t = file->tlock){
1322		if(t->file == file)
1323			t->time = 0;		/* free the lock */
1324		file->tlock = 0;
1325	}
1326	if(remove)
1327		error = doremove(file, wok);
1328	file->open = 0;
1329	freewp(file->wpath);
1330	freefp(file);
1331	qunlock(file);
1332
1333	return error;
1334}
1335
1336static int
1337fsclunk(Chan* chan, Fcall* f, Fcall*)
1338{
1339	File *file;
1340
1341	if((file = filep(chan, f->fid, 0)) == nil)
1342		return Efid;
1343
1344	_clunk(file, file->open & FREMOV, 0);
1345	return 0;
1346}
1347
1348static int
1349fsremove(Chan* chan, Fcall* f, Fcall*)
1350{
1351	File *file;
1352
1353	if((file = filep(chan, f->fid, 0)) == nil)
1354		return Efid;
1355
1356	return _clunk(file, 1, chan == cons.chan);
1357}
1358
1359static int
1360fsstat(Chan* chan, Fcall* f, Fcall* r, uchar* data)
1361{
1362	Dir dir;
1363	Iobuf *p;
1364	Dentry *d;
1365	File *file;
1366	int error, len;
1367
1368	if((file = filep(chan, f->fid, 0)) == nil)
1369		return Efid;
1370
1371	p = getbuf(file->fs->dev, file->addr, Bread);
1372	if(p == nil || checktag(p, Tdir, QPNONE)){
1373		error = Edir1;
1374		goto out;
1375	}
1376	if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1377		error = Ealloc;
1378		goto out;
1379	}
1380	if(error = mkqidcmp(&file->qid, d))
1381		goto out;
1382
1383	if(d->qid.path == QPROOT)	/* stat of root gives time */
1384		d->atime = time(0);
1385
1386	len = dir9p2(&dir, d, data);
1387	data += len;
1388	if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0)
1389		error = Ersc;
1390	else
1391		r->stat = data;
1392
1393out:
1394	if(p != nil)
1395		putbuf(p);
1396	if(file != nil)
1397		qunlock(file);
1398
1399	return error;
1400}
1401
1402static int
1403fswstat(Chan* chan, Fcall* f, Fcall*, char *strs)
1404{
1405	Iobuf *p, *p1;
1406	Dentry *d, *d1, xd;
1407	File *file;
1408	int error, slot, uid, gid, l;
1409	long addr;
1410	Dir dir;
1411	ulong mode;
1412
1413	p = p1 = nil;
1414	d1 = nil;
1415
1416	if((file = filep(chan, f->fid, 0)) == nil){
1417		error = Efid;
1418		goto out;
1419	}
1420
1421	/*
1422	 * if user none,
1423	 * can't do anything
1424	 * unless allow.
1425	 */
1426	if(file->uid == 0 && !wstatallow){
1427		error = Eaccess;
1428		goto out;
1429	}
1430
1431	if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){
1432		error = Eronly;
1433		goto out;
1434	}
1435
1436	/*
1437	 * first get parent
1438	 */
1439	if(file->wpath){
1440		p1 = getbuf(file->fs->dev, file->wpath->addr, Bread);
1441		if(p1 == nil){
1442			error = Ephase;
1443			goto out;
1444		}
1445		d1 = getdir(p1, file->wpath->slot);
1446		if(d1 == nil || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)){
1447			error = Ephase;
1448			goto out;
1449		}
1450	}
1451
1452	if((p = getbuf(file->fs->dev, file->addr, Bread)) == nil){
1453		error = Ealloc;
1454		goto out;
1455	}
1456	d = getdir(p, file->slot);
1457	if(d == nil || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)){
1458		error = Ealloc;
1459		goto out;
1460	}
1461	if(error = mkqidcmp(&file->qid, d))
1462		goto out;
1463
1464	/*
1465	 * Convert the message and fix up
1466	 * fields not to be changed.
1467	 */
1468	if(convM2D(f->stat, f->nstat, &dir, strs) == 0){
1469		print("9p2: convM2D returns 0\n");
1470		error = Econvert;
1471		goto out;
1472	}
1473	if(dir.uid == nil || strlen(dir.uid) == 0)
1474		uid = d->uid;
1475	else
1476		uid = strtouid(dir.uid);
1477	if(dir.gid == nil || strlen(dir.gid) == 0)
1478		gid = d->gid;
1479	else
1480		gid = strtouid(dir.gid);
1481	if(dir.name == nil || strlen(dir.name) == 0)
1482		dir.name = d->name;
1483	else{
1484		if((l = checkname9p2(dir.name)) == 0){
1485			error = Ename;
1486			goto out;
1487		}
1488		if(l > NAMELEN){
1489			error = Etoolong;
1490			goto out;
1491		}
1492	}
1493
1494	/*
1495	 * Before doing sanity checks, find out what the
1496	 * new 'mode' should be:
1497	 * if 'type' and 'mode' are both defaults, take the
1498	 * new mode from the old directory entry;
1499	 * else if 'type' is the default, use the new mode entry;
1500	 * else if 'mode' is the default, create the new mode from
1501	 * 'type' or'ed with the old directory mode;
1502	 * else neither are defaults, use the new mode but check
1503	 * it agrees with 'type'.
1504	 */
1505	if(dir.qid.type == 0xFF && dir.mode == ~0){
1506		dir.mode = d->mode & 0777;
1507		if(d->mode & DLOCK)
1508			dir.mode |= DMEXCL;
1509		if(d->mode & DAPND)
1510			dir.mode |= DMAPPEND;
1511		if(d->mode & DDIR)
1512			dir.mode |= DMDIR;
1513	}
1514	else if(dir.qid.type == 0xFF){
1515		/* nothing to do */
1516	}
1517	else if(dir.mode == ~0)
1518		dir.mode = (dir.qid.type<<24)|(d->mode & 0777);
1519	else if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
1520		error = Eqidmode;
1521		goto out;
1522	}
1523
1524	/*
1525	 * Check for unknown type/mode bits
1526	 * and an attempt to change the directory bit.
1527	 */
1528	if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){
1529		error = Enotm;
1530		goto out;
1531	}
1532	if(d->mode & DDIR)
1533		mode = DMDIR;
1534	else
1535		mode = 0;
1536	if((dir.mode^mode) & DMDIR){
1537		error = Enotd;
1538		goto out;
1539	}
1540
1541	if(dir.mtime == ~0)
1542		dir.mtime = d->mtime;
1543	if(dir.length == ~0)
1544		dir.length = d->size;
1545
1546	/*
1547	 * Currently, can't change length.
1548	 */
1549	if(dir.length != d->size){
1550		error = Enotl;
1551		goto out;
1552	}
1553
1554	/*
1555	 * if chown,
1556	 * must be god
1557	 * wstatallow set to allow chown during boot
1558	 */
1559	if(uid != d->uid && !wstatallow) {
1560		error = Enotu;
1561		goto out;
1562	}
1563
1564	/*
1565	 * if chgroup,
1566	 * must be either
1567	 *	a) owner and in new group
1568	 *	b) leader of both groups
1569	 * wstatallow and writeallow are set to allow chgrp during boot
1570	 */
1571	while(gid != d->gid) {
1572		if(wstatallow || writeallow)
1573			break;
1574		if(d->uid == file->uid && ingroup(file->uid, gid))
1575			break;
1576		if(leadgroup(file->uid, gid))
1577			if(leadgroup(file->uid, d->gid))
1578				break;
1579		error = Enotg;
1580		goto out;
1581	}
1582
1583	/*
1584	 * if rename,
1585	 * must have write permission in parent
1586	 */
1587	while(strncmp(d->name, dir.name, sizeof(d->name)) != 0) {
1588		if(checkname(dir.name) || d1 == nil) {
1589			error = Ename;
1590			goto out;
1591		}
1592		if(strcmp(dir.name, ".") == 0 || strcmp(xd.name, "..") == 0) {
1593			error = Ename;
1594			goto out;
1595		}
1596
1597		/*
1598		 * drop entry to prevent lock, then
1599		 * check that destination name is unique,
1600		 */
1601		putbuf(p);
1602		for(addr = 0; ; addr++) {
1603			if((p = dnodebuf(p1, d1, addr, 0)) == nil)
1604				break;
1605			if(checktag(p, Tdir, d1->qid.path)) {
1606				putbuf(p);
1607				continue;
1608			}
1609			for(slot = 0; slot < DIRPERBUF; slot++) {
1610				d = getdir(p, slot);
1611				if(!(d->mode & DALLOC))
1612					continue;
1613				if(strncmp(dir.name, d->name, sizeof(d->name)) == 0) {
1614					error = Eexist;
1615					goto out;
1616				}
1617			}
1618			putbuf(p);
1619		}
1620
1621		/*
1622		 * reacquire entry
1623		 */
1624		if((p = getbuf(file->fs->dev, file->addr, Bread)) == nil){
1625			error = Ephase;
1626			goto out;
1627		}
1628		d = getdir(p, file->slot);
1629		if(d == nil || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1630			error = Ephase;
1631			goto out;
1632		}
1633
1634		if(wstatallow || writeallow) /* set to allow rename during boot */
1635			break;
1636		if(d1 == nil || iaccess(file, d1, DWRITE)) {
1637			error = Eaccess;
1638			goto out;
1639		}
1640		break;
1641	}
1642
1643	/*
1644	 * if mode/time, either
1645	 *	a) owner
1646	 *	b) leader of either group
1647	 */
1648	mode = dir.mode & 0777;
1649	if(dir.mode & DMAPPEND)
1650		mode |= DAPND;
1651	if(dir.mode & DMEXCL)
1652		mode |= DLOCK;
1653	while(d->mtime != dir.mtime || ((d->mode^mode) & (DAPND|DLOCK|0777))) {
1654		if(wstatallow)			/* set to allow chmod during boot */
1655			break;
1656		if(d->uid == file->uid)
1657			break;
1658		if(leadgroup(file->uid, gid))
1659			break;
1660		if(leadgroup(file->uid, d->gid))
1661			break;
1662		error = Enotu;
1663		goto out;
1664	}
1665	d->mtime = dir.mtime;
1666	d->uid = uid;
1667	d->gid = gid;
1668	d->mode = (mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR));
1669
1670	strncpy(d->name, dir.name, sizeof(d->name));
1671	accessdir(p, d, FWSTAT);
1672
1673out:
1674	if(p != nil)
1675		putbuf(p);
1676	if(p1 != nil)
1677		putbuf(p1);
1678	if(file != nil)
1679		qunlock(file);
1680
1681	return error;
1682}
1683
1684static int
1685recv(Chan *c, uchar *buf, int n)
1686{
1687	int fd, m, len;
1688
1689	fd = c->chan;
1690	/* read count */
1691	qlock(&c->rlock);
1692	m = readn(fd, buf, BIT32SZ);
1693	if(m != BIT32SZ){
1694		qunlock(&c->rlock);
1695		if(m < 0){
1696			print("readn(BIT32SZ) fails: %r\n");
1697			return -1;
1698		}
1699		print("readn(BIT32SZ) returns %d: %r\n", m);
1700		return 0;
1701	}
1702
1703	len = GBIT32(buf);
1704	if(len <= BIT32SZ || len > n){
1705		print("recv bad length %d\n", len);
1706		werrstr("bad length in 9P2000 message header");
1707		qunlock(&c->rlock);
1708		return -1;
1709	}
1710	len -= BIT32SZ;
1711	m = readn(fd, buf+BIT32SZ, len);
1712	qunlock(&c->rlock);
1713	if(m < len){
1714		print("recv wanted %d got %d\n", len, m);
1715		return 0;
1716	}
1717	return BIT32SZ+m;
1718}
1719
1720static void
1721send(Chan *c, uchar *buf, int n)
1722{
1723	int fd, m;
1724
1725	fd = c->chan;
1726	qlock(&c->wlock);
1727	m = write(fd, buf, n);
1728	qunlock(&c->wlock);
1729	if(m == n)
1730		return;
1731	panic("write failed");
1732}
1733
1734void
1735serve9p2(Chan *chan, uchar *ib, int nib)
1736{
1737	uchar inbuf[MSIZE+IOHDRSZ], outbuf[MSIZE+IOHDRSZ];
1738	Fcall f, r;
1739	char ename[64];
1740	int error, n, type;
1741
1742	chan->msize = MSIZE;
1743	fmtinstall('F', fcallfmt);
1744
1745	for(;;){
1746		if(nib){
1747			memmove(inbuf, ib, nib);
1748			n = nib;
1749			nib = 0;
1750		}else
1751			n = recv(chan, inbuf, sizeof inbuf);
1752		if(chat){
1753			print("read msg %d (fd %d)\n", n, chan->chan);
1754			if(n <= 0)
1755				print("\terr: %r\n");
1756		}
1757		if(n == 0 && (chan == cons.srvchan || chan == cons.chan))
1758			continue;
1759		if(n <= 0)
1760			break;
1761		if(convM2S(inbuf, n, &f) != n){
1762			print("9p2: cannot decode\n");
1763			continue;
1764		}
1765
1766		type = f.type;
1767		if(type < Tversion || type >= Tmax || (type&1) || type == Terror){
1768			print("9p2: bad message type %d\n", type);
1769			continue;
1770		}
1771
1772		if(CHAT(chan))
1773			print("9p2: f %F\n", &f);
1774
1775		r.type = type+1;
1776		r.tag = f.tag;
1777		error = 0;
1778
1779		rlock(&mainlock);
1780		rlock(&chan->reflock);
1781		switch(type){
1782		default:
1783			r.type = Rerror;
1784			snprint(ename, sizeof ename, "unknown message: %F", &f);
1785			r.ename = ename;
1786			break;
1787		case Tversion:
1788			error = fsversion(chan, &f, &r);
1789			break;
1790		case Tauth:
1791			error = fsauth(chan, &f, &r);
1792			break;
1793		case Tattach:
1794			error = fsattach(chan, &f, &r);
1795			break;
1796		case Tflush:
1797			error = fsflush(chan, &f, &r);
1798			break;
1799		case Twalk:
1800			error = fswalk(chan, &f, &r);
1801			break;
1802		case Topen:
1803			error = fsopen(chan, &f, &r);
1804			break;
1805		case Tcreate:
1806			error = fscreate(chan, &f, &r);
1807			break;
1808		case Tread:
1809			r.data = (char*)inbuf;
1810			error = fsread(chan, &f, &r);
1811			break;
1812		case Twrite:
1813			error = fswrite(chan, &f, &r);
1814			break;
1815		case Tclunk:
1816			error = fsclunk(chan, &f, &r);
1817			break;
1818		case Tremove:
1819			error = fsremove(chan, &f, &r);
1820			break;
1821		case Tstat:
1822			error = fsstat(chan, &f, &r, inbuf);
1823			break;
1824		case Twstat:
1825			error = fswstat(chan, &f, &r, (char*)outbuf);
1826			break;
1827		}
1828		runlock(&chan->reflock);
1829		runlock(&mainlock);
1830
1831		if(error != 0){
1832			r.type = Rerror;
1833			if(error >= MAXERR){
1834				snprint(ename, sizeof(ename), "error %d", error);
1835				r.ename = ename;
1836			}
1837			else
1838				r.ename = errstring[error];
1839		}
1840		if(CHAT(chan))
1841			print("9p2: r %F\n", &r);
1842	
1843		n = convS2M(&r, outbuf, sizeof outbuf);
1844		if(n == 0){
1845			type = r.type;
1846			r.type = Rerror;
1847			snprint(ename, sizeof(ename), "9p2: convS2M: type %d", type);
1848			r.ename = ename;
1849			print(ename);
1850			n = convS2M(&r, outbuf, sizeof outbuf);
1851			if(n == 0){
1852				/*
1853				 * What to do here, the failure notification failed?
1854				 */
1855				panic("can't write anything at all");
1856			}
1857		}
1858		send(chan, outbuf, n);
1859	}
1860	fileinit(chan);
1861	close(chan->chan);
1862	if(chan == cons.srvchan || chan == cons.chan)
1863		print("console chan read error");
1864}
1865