Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include	"all.h"
2
 
3
#define MSIZE	(MAXDAT+128)
4
 
5
static void
6
seterror(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
 
15
static int
16
fsversion(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
 
36
char *keyspec = "proto=p9any role=server";
37
 
38
static int
39
fsauth(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
 
96
out:
97
	if(file != nil){
98
		qunlock(file);
99
		if(err != 0)
100
			freefp(file);
101
	}
102
	return err;
103
}
104
 
105
int
106
authread(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
 
143
int
144
authwrite(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
 
154
void
155
mkqid9p1(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
 
165
void
166
authfree(File *fp)
167
{
168
	if(fp->rpc != nil){
169
		close(fp->rpc->afd);
170
		free(fp->rpc);
171
		fp->rpc = nil;
172
	}
173
}
174
 
175
void
176
mkqid9p2(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
 
189
static int
190
checkattach(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
 
226
static int
227
fsattach(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
 
305
out:
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
 
322
static int
323
fsflush(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
 
333
static void
334
clone(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
 
354
static int
355
walkname(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){
401
setdot:
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
 
473
found:
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
 
482
out:
483
	if(p1 != nil)
484
		putbuf(p1);
485
	if(p != nil)
486
		putbuf(p);
487
 
488
	return error;
489
}
490
 
491
static int
492
fswalk(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
 
602
static int
603
fsopen(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
 
735
badaccess:
736
	error = Eaccess;
737
	file->open = 0;
738
 
739
out:
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
 
750
static int
751
dir9p2(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
 
781
static int
782
checkname9p2(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
 
800
static int
801
fscreate(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
 
990
badaccess:
991
	error = Eaccess;
992
	goto out;
993
 
994
phase:
995
	error = Ephase;
996
 
997
out:
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
 
1008
static int
1009
fsread(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
 
1112
dread:
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
 
1128
dread1:
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
 
1179
out1:
1180
	if(error == 0){
1181
		file->doffset = offset;
1182
		file->dvers = file->qid.vers;
1183
		file->dslot = slot+DIRPERBUF*addr;
1184
	}
1185
 
1186
out:
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
 
1203
static int
1204
fswrite(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
 
1304
out:
1305
	if(p != nil)
1306
		putbuf(p);
1307
	if(file != nil)
1308
		qunlock(file);
1309
	r->count = nwrite;
1310
 
1311
	return error;
1312
}
1313
 
1314
static 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
 
1336
static int
1337
fsclunk(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
 
1348
static int
1349
fsremove(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
 
1359
static int
1360
fsstat(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
 
1393
out:
1394
	if(p != nil)
1395
		putbuf(p);
1396
	if(file != nil)
1397
		qunlock(file);
1398
 
1399
	return error;
1400
}
1401
 
1402
static int
1403
fswstat(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
 
1673
out:
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
 
1684
static int
1685
recv(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
 
1720
static void
1721
send(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
 
1734
void
1735
serve9p2(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