PageRenderTime 133ms CodeModel.GetById 2ms app.highlight 119ms RepoModel.GetById 1ms app.codeStats 0ms

/sys/src/cmd/cwfs/9p2.c

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