Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "all.h"
2
#include <fcall.h>
3
 
4
enum { MSIZE = MAXDAT+MAXMSG };
5
 
6
static int
7
mkmode9p1(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
 
25
void
26
mkqid9p1(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
 
36
static int
37
mktype9p2(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
 
52
static ulong
53
mkmode9p2(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
 
68
void
69
mkqid9p2(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
 
76
static int
77
mkdir9p2(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
 
107
static int
108
checkname9p2(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
 
130
static int
131
version(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
 
155
struct {
156
	Lock;
157
	ulong	hi;
158
} authpath;
159
 
160
static int
161
auth(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
 
205
out:
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
 
217
int
218
authorize(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);
265
out:
266
	qunlock(af);
267
	return uid;
268
}
269
 
270
static int
271
attach(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
 
347
out:
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
 
361
static int
362
flush(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
 
372
static void
373
clone(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
 
392
static int
393
walkname(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){
440
setdot:
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
 
512
found:
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
 
521
out:
522
	if(p1 != nil)
523
		putbuf(p1);
524
	if(p != nil)
525
		putbuf(p);
526
 
527
	return error;
528
}
529
 
530
static int
531
walk(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
 
635
static int
636
fs_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
 
774
badaccess:
775
	error = Eaccess;
776
	file->open = 0;
777
 
778
out:
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
 
789
static int
790
fs_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
 
973
badaccess:
974
	error = Eaccess;
975
	goto out;
976
 
977
phase:
978
	error = Ephase;
979
 
980
out:
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
 
991
static int
992
fs_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
 
1096
dread:
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
	}
1164
out1:
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
 
1172
out:
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
 
1189
static int
1190
fs_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
 
1293
out:
1294
	if(p != nil)
1295
		putbuf(p);
1296
	if(file != nil)
1297
		qunlock(file);
1298
	r->count = nwrite;
1299
 
1300
	return error;
1301
}
1302
 
1303
static 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
 
1326
static int
1327
clunk(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
 
1338
static int
1339
fs_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
 
1349
static int
1350
fs_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
 
1397
out:
1398
	if(p != nil)
1399
		putbuf(p);
1400
	if(file != nil)
1401
		qunlock(file);
1402
 
1403
	return error;
1404
}
1405
 
1406
static int
1407
fs_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
 
1713
out:
1714
	if(p != nil)
1715
		putbuf(p);
1716
	if(p1 != nil)
1717
		putbuf(p1);
1718
	qunlock(file);
1719
 
1720
	return error;
1721
}
1722
 
1723
int
1724
serve9p2(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
{
1745
print("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
}