Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "stdinc.h"
2
 
3
#include "9.h"
4
 
5
enum {
6
	OMODE		= 0x7,		/* Topen/Tcreate mode */
7
};
8
 
9
enum {
10
	PermX		= 1,
11
	PermW		= 2,
12
	PermR		= 4,
13
};
14
 
15
static char EPermission[] = "permission denied";
16
 
17
static int
18
permFile(File* file, Fid* fid, int perm)
19
{
20
	char *u;
21
	DirEntry de;
22
 
23
	if(!fileGetDir(file, &de))
24
		return -1;
25
 
26
	/*
27
	 * User none only gets other permissions.
28
	 */
29
	if(strcmp(fid->uname, unamenone) != 0){
30
		/*
31
		 * There is only one uid<->uname mapping
32
		 * and it's already cached in the Fid, but
33
		 * it might have changed during the lifetime
34
		 * if this Fid.
35
		 */
36
		if((u = unameByUid(de.uid)) != nil){
37
			if(strcmp(fid->uname, u) == 0 && ((perm<<6) & de.mode)){
38
				vtMemFree(u);
39
				deCleanup(&de);
40
				return 1;
41
			}
42
			vtMemFree(u);
43
		}
44
		if(groupMember(de.gid, fid->uname) && ((perm<<3) & de.mode)){
45
			deCleanup(&de);
46
			return 1;
47
		}
48
	}
49
	if(perm & de.mode){
50
		if(perm == PermX && (de.mode & ModeDir)){
51
			deCleanup(&de);
52
			return 1;
53
		}
54
		if(!groupMember(uidnoworld, fid->uname)){
55
			deCleanup(&de);
56
			return 1;
57
		}
58
	}
59
	if(fsysNoPermCheck(fid->fsys) || (fid->con->flags&ConNoPermCheck)){
60
		deCleanup(&de);
61
		return 1;
62
	}
63
	vtSetError(EPermission);
64
 
65
	deCleanup(&de);
66
	return 0;
67
}
68
 
69
static int
70
permFid(Fid* fid, int p)
71
{
72
	return permFile(fid->file, fid, p);
73
}
74
 
75
static int
76
permParent(Fid* fid, int p)
77
{
78
	int r;
79
	File *parent;
80
 
81
	parent = fileGetParent(fid->file);
82
	r = permFile(parent, fid, p);
83
	fileDecRef(parent);
84
 
85
	return r;
86
}
87
 
88
int
89
validFileName(char* name)
90
{
91
	char *p;
92
 
93
	if(name == nil || name[0] == '\0'){
94
		vtSetError("no file name");
95
		return 0;
96
	}
97
	if(name[0] == '.'){
98
		if(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')){
99
			vtSetError(". and .. illegal as file name");
100
			return 0;
101
		}
102
	}
103
 
104
	for(p = name; *p != '\0'; p++){
105
		if((*p & 0xFF) < 040){
106
			vtSetError("bad character in file name");
107
			return 0;
108
		}
109
	}
110
 
111
	return 1;
112
}
113
 
114
static int
115
rTwstat(Msg* m)
116
{
117
	Dir dir;
118
	Fid *fid;
119
	ulong mode, oldmode;
120
	DirEntry de;
121
	char *gid, *strs, *uid;
122
	int gl, op, retval, tsync, wstatallow;
123
 
124
	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
125
		return 0;
126
 
127
	gid = uid = nil;
128
	retval = 0;
129
 
130
	if(strcmp(fid->uname, unamenone) == 0 || (fid->qid.type & QTAUTH)){
131
		vtSetError(EPermission);
132
		goto error0;
133
	}
134
	if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
135
		vtSetError("read-only filesystem");
136
		goto error0;
137
	}
138
 
139
	if(!fileGetDir(fid->file, &de))
140
		goto error0;
141
 
142
	strs = vtMemAlloc(m->t.nstat);
143
	if(convM2D(m->t.stat, m->t.nstat, &dir, strs) == 0){
144
		vtSetError("wstat -- protocol botch");
145
		goto error;
146
	}
147
 
148
	/*
149
	 * Run through each of the (sub-)fields in the provided Dir
150
	 * checking for validity and whether it's a default:
151
	 * .type, .dev and .atime are completely ignored and not checked;
152
	 * .qid.path, .qid.vers and .muid are checked for validity but
153
	 * any attempt to change them is an error.
154
	 * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
155
	 * possibly be changed.
156
	 *
157
	 * 'Op' flags there are changed fields, i.e. it's not a no-op.
158
	 * 'Tsync' flags all fields are defaulted.
159
	 */
160
	tsync = 1;
161
	if(dir.qid.path != ~0){
162
		if(dir.qid.path != de.qid){
163
			vtSetError("wstat -- attempt to change qid.path");
164
			goto error;
165
		}
166
		tsync = 0;
167
	}
168
	if(dir.qid.vers != ~0){
169
		if(dir.qid.vers != de.mcount){
170
			vtSetError("wstat -- attempt to change qid.vers");
171
			goto error;
172
		}
173
		tsync = 0;
174
	}
175
	if(dir.muid != nil && *dir.muid != '\0'){
176
		if((uid = uidByUname(dir.muid)) == nil){
177
			vtSetError("wstat -- unknown muid");
178
			goto error;
179
		}
180
		if(strcmp(uid, de.mid) != 0){
181
			vtSetError("wstat -- attempt to change muid");
182
			goto error;
183
		}
184
		vtMemFree(uid);
185
		uid = nil;
186
		tsync = 0;
187
	}
188
 
189
	/*
190
	 * Check .qid.type and .mode agree if neither is defaulted.
191
	 */
192
	if(dir.qid.type != (uchar)~0 && dir.mode != ~0){
193
		if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
194
			vtSetError("wstat -- qid.type/mode mismatch");
195
			goto error;
196
		}
197
	}
198
 
199
	op = 0;
200
 
201
	oldmode = de.mode;
202
	if(dir.qid.type != (uchar)~0 || dir.mode != ~0){
203
		/*
204
		 * .qid.type or .mode isn't defaulted, check for unknown bits.
205
		 */
206
		if(dir.mode == ~0)
207
			dir.mode = (dir.qid.type<<24)|(de.mode & 0777);
208
		if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
209
			vtSetError("wstat -- unknown bits in qid.type/mode");
210
			goto error;
211
		}
212
 
213
		/*
214
		 * Synthesise a mode to check against the current settings.
215
		 */
216
		mode = dir.mode & 0777;
217
		if(dir.mode & DMEXCL)
218
			mode |= ModeExclusive;
219
		if(dir.mode & DMAPPEND)
220
			mode |= ModeAppend;
221
		if(dir.mode & DMDIR)
222
			mode |= ModeDir;
223
		if(dir.mode & DMTMP)
224
			mode |= ModeTemporary;
225
 
226
		if((de.mode^mode) & ModeDir){
227
			vtSetError("wstat -- attempt to change directory bit");
228
			goto error;
229
		}
230
 
231
		if((de.mode & (ModeAppend|ModeExclusive|ModeTemporary|0777)) != mode){
232
			de.mode &= ~(ModeAppend|ModeExclusive|ModeTemporary|0777);
233
			de.mode |= mode;
234
			op = 1;
235
		}
236
		tsync = 0;
237
	}
238
 
239
	if(dir.mtime != ~0){
240
		if(dir.mtime != de.mtime){
241
			de.mtime = dir.mtime;
242
			op = 1;
243
		}
244
		tsync = 0;
245
	}
246
 
247
	if(dir.length != ~0){
248
		if(dir.length != de.size){
249
			/*
250
			 * Cannot change length on append-only files.
251
			 * If we're changing the append bit, it's okay.
252
			 */
253
			if(de.mode & oldmode & ModeAppend){
254
				vtSetError("wstat -- attempt to change length of append-only file");
255
				goto error;
256
			}
257
			if(de.mode & ModeDir){
258
				vtSetError("wstat -- attempt to change length of directory");
259
				goto error;
260
			}
261
			de.size = dir.length;
262
			op = 1;
263
		}
264
		tsync = 0;
265
	}
266
 
267
	/*
268
	 * Check for permission to change .mode, .mtime or .length,
269
	 * must be owner or leader of either group, for which test gid
270
	 * is needed; permission checks on gid will be done later.
271
	 */
272
	if(dir.gid != nil && *dir.gid != '\0'){
273
		if((gid = uidByUname(dir.gid)) == nil){
274
			vtSetError("wstat -- unknown gid");
275
			goto error;
276
		}
277
		tsync = 0;
278
	}
279
	else
280
		gid = vtStrDup(de.gid);
281
 
282
	wstatallow = (fsysWstatAllow(fid->fsys) || (m->con->flags&ConWstatAllow));
283
 
284
	/*
285
	 * 'Gl' counts whether neither, one or both groups are led.
286
	 */
287
	gl = groupLeader(gid, fid->uname) != 0;
288
	gl += groupLeader(de.gid, fid->uname) != 0;
289
 
290
	if(op && !wstatallow){
291
		if(strcmp(fid->uid, de.uid) != 0 && !gl){
292
			vtSetError("wstat -- not owner or group leader");
293
			goto error;
294
		}
295
	}
296
 
297
	/*
298
	 * Check for permission to change group, must be
299
	 * either owner and in new group or leader of both groups.
300
	 * If gid is nil here then
301
	 */
302
	if(strcmp(gid, de.gid) != 0){
303
		if(!wstatallow
304
		&& !(strcmp(fid->uid, de.uid) == 0 && groupMember(gid, fid->uname))
305
		&& !(gl == 2)){
306
			vtSetError("wstat -- not owner and not group leaders");
307
			goto error;
308
		}
309
		vtMemFree(de.gid);
310
		de.gid = gid;
311
		gid = nil;
312
		op = 1;
313
		tsync = 0;
314
	}
315
 
316
	/*
317
	 * Rename.
318
	 * Check .name is valid and different to the current.
319
	 * If so, check write permission in parent.
320
	 */
321
	if(dir.name != nil && *dir.name != '\0'){
322
		if(!validFileName(dir.name))
323
			goto error;
324
		if(strcmp(dir.name, de.elem) != 0){
325
			if(permParent(fid, PermW) <= 0)
326
				goto error;
327
			vtMemFree(de.elem);
328
			de.elem = vtStrDup(dir.name);
329
			op = 1;
330
		}
331
		tsync = 0;
332
	}
333
 
334
	/*
335
	 * Check for permission to change owner - must be god.
336
	 */
337
	if(dir.uid != nil && *dir.uid != '\0'){
338
		if((uid = uidByUname(dir.uid)) == nil){
339
			vtSetError("wstat -- unknown uid");
340
			goto error;
341
		}
342
		if(strcmp(uid, de.uid) != 0){
343
			if(!wstatallow){
344
				vtSetError("wstat -- not owner");
345
				goto error;
346
			}
347
			if(strcmp(uid, uidnoworld) == 0){
348
				vtSetError(EPermission);
349
				goto error;
350
			}
351
			vtMemFree(de.uid);
352
			de.uid = uid;
353
			uid = nil;
354
			op = 1;
355
		}
356
		tsync = 0;
357
	}
358
 
359
	if(op)
360
		retval = fileSetDir(fid->file, &de, fid->uid);
361
	else
362
		retval = 1;
363
 
364
	if(tsync){
365
		/*
366
		 * All values were defaulted,
367
		 * make the state of the file exactly what it
368
		 * claims to be before returning...
369
		 */
370
		USED(tsync);
371
	}
372
 
373
error:
374
	deCleanup(&de);
375
	vtMemFree(strs);
376
	if(gid != nil)
377
		vtMemFree(gid);
378
	if(uid != nil)
379
		vtMemFree(uid);
380
error0:
381
	fidPut(fid);
382
	return retval;
383
};
384
 
385
static int
386
rTstat(Msg* m)
387
{
388
	Dir dir;
389
	Fid *fid;
390
	DirEntry de;
391
 
392
	if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
393
		return 0;
394
	if(fid->qid.type & QTAUTH){
395
		memset(&dir, 0, sizeof(Dir));
396
		dir.qid = fid->qid;
397
		dir.mode = DMAUTH;
398
		dir.atime = time(0L);
399
		dir.mtime = dir.atime;
400
		dir.length = 0;
401
		dir.name = "#¿";
402
		dir.uid = fid->uname;
403
		dir.gid = fid->uname;
404
		dir.muid = fid->uname;
405
 
406
		if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){
407
			vtSetError("stat QTAUTH botch");
408
			fidPut(fid);
409
			return 0;
410
		}
411
		m->r.stat = m->data;
412
 
413
		fidPut(fid);
414
		return 1;
415
	}
416
	if(!fileGetDir(fid->file, &de)){
417
		fidPut(fid);
418
		return 0;
419
	}
420
	fidPut(fid);
421
 
422
	/*
423
	 * TODO: optimise this copy (in convS2M) away somehow.
424
	 * This pettifoggery with m->data will do for the moment.
425
	 */
426
	m->r.nstat = dirDe2M(&de, m->data, m->con->msize);
427
	m->r.stat = m->data;
428
	deCleanup(&de);
429
 
430
	return 1;
431
}
432
 
433
static int
434
_rTclunk(Fid* fid, int remove)
435
{
436
	int rok;
437
 
438
	if(fid->excl)
439
		exclFree(fid);
440
 
441
	rok = 1;
442
	if(remove && !(fid->qid.type & QTAUTH)){
443
		if((rok = permParent(fid, PermW)) > 0)
444
			rok = fileRemove(fid->file, fid->uid);
445
	}
446
	fidClunk(fid);
447
 
448
	return rok;
449
}
450
 
451
static int
452
rTremove(Msg* m)
453
{
454
	Fid *fid;
455
 
456
	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
457
		return 0;
458
	return _rTclunk(fid, 1);
459
}
460
 
461
static int
462
rTclunk(Msg* m)
463
{
464
	Fid *fid;
465
 
466
	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
467
		return 0;
468
	_rTclunk(fid, (fid->open & FidORclose));
469
 
470
	return 1;
471
}
472
 
473
static int
474
rTwrite(Msg* m)
475
{
476
	Fid *fid;
477
	int count, n;
478
 
479
	if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
480
		return 0;
481
	if(!(fid->open & FidOWrite)){
482
		vtSetError("fid not open for write");
483
		goto error;
484
	}
485
 
486
	count = m->t.count;
487
	if(count < 0 || count > m->con->msize-IOHDRSZ){
488
		vtSetError("write count too big");
489
		goto error;
490
	}
491
	if(m->t.offset < 0){
492
		vtSetError("write offset negative");
493
		goto error;
494
	}
495
	if(fid->excl != nil && !exclUpdate(fid))
496
		goto error;
497
 
498
	if(fid->qid.type & QTDIR){
499
		vtSetError("is a directory");
500
		goto error;
501
	}
502
	else if(fid->qid.type & QTAUTH)
503
		n = authWrite(fid, m->t.data, count);
504
	else
505
		n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid);
506
	if(n < 0)
507
		goto error;
508
 
509
 
510
	m->r.count = n;
511
 
512
	fidPut(fid);
513
	return 1;
514
 
515
error:
516
	fidPut(fid);
517
	return 0;
518
}
519
 
520
static int
521
rTread(Msg* m)
522
{
523
	Fid *fid;
524
	uchar *data;
525
	int count, n;
526
 
527
	if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
528
		return 0;
529
	if(!(fid->open & FidORead)){
530
		vtSetError("fid not open for read");
531
		goto error;
532
	}
533
 
534
	count = m->t.count;
535
	if(count < 0 || count > m->con->msize-IOHDRSZ){
536
		vtSetError("read count too big");
537
		goto error;
538
	}
539
	if(m->t.offset < 0){
540
		vtSetError("read offset negative");
541
		goto error;
542
	}
543
	if(fid->excl != nil && !exclUpdate(fid))
544
		goto error;
545
 
546
	/*
547
	 * TODO: optimise this copy (in convS2M) away somehow.
548
	 * This pettifoggery with m->data will do for the moment.
549
	 */
550
	data = m->data+IOHDRSZ;
551
	if(fid->qid.type & QTDIR)
552
		n = dirRead(fid, data, count, m->t.offset);
553
	else if(fid->qid.type & QTAUTH)
554
		n = authRead(fid, data, count);
555
	else
556
		n = fileRead(fid->file, data, count, m->t.offset);
557
	if(n < 0)
558
		goto error;
559
 
560
	m->r.count = n;
561
	m->r.data = (char*)data;
562
 
563
	fidPut(fid);
564
	return 1;
565
 
566
error:
567
	fidPut(fid);
568
	return 0;
569
}
570
 
571
static int
572
rTcreate(Msg* m)
573
{
574
	Fid *fid;
575
	File *file;
576
	ulong mode;
577
	int omode, open, perm;
578
 
579
	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
580
		return 0;
581
	if(fid->open){
582
		vtSetError("fid open for I/O");
583
		goto error;
584
	}
585
	if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
586
		vtSetError("read-only filesystem");
587
		goto error;
588
	}
589
	if(!fileIsDir(fid->file)){
590
		vtSetError("not a directory");
591
		goto error;
592
	}
593
	if(permFid(fid, PermW) <= 0)
594
		goto error;
595
	if(!validFileName(m->t.name))
596
		goto error;
597
	if(strcmp(fid->uid, uidnoworld) == 0){
598
		vtSetError(EPermission);
599
		goto error;
600
	}
601
 
602
	omode = m->t.mode & OMODE;
603
	open = 0;
604
 
605
	if(omode == OREAD || omode == ORDWR || omode == OEXEC)
606
		open |= FidORead;
607
	if(omode == OWRITE || omode == ORDWR)
608
		open |= FidOWrite;
609
	if((open & (FidOWrite|FidORead)) == 0){
610
		vtSetError("unknown mode");
611
		goto error;
612
	}
613
	if(m->t.perm & DMDIR){
614
		if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){
615
			vtSetError("illegal mode");
616
			goto error;
617
		}
618
		if(m->t.perm & DMAPPEND){
619
			vtSetError("illegal perm");
620
			goto error;
621
		}
622
	}
623
 
624
	mode = fileGetMode(fid->file);
625
	perm = m->t.perm;
626
	if(m->t.perm & DMDIR)
627
		perm &= ~0777|(mode & 0777);
628
	else
629
		perm &= ~0666|(mode & 0666);
630
	mode = perm & 0777;
631
	if(m->t.perm & DMDIR)
632
		mode |= ModeDir;
633
	if(m->t.perm & DMAPPEND)
634
		mode |= ModeAppend;
635
	if(m->t.perm & DMEXCL)
636
		mode |= ModeExclusive;
637
	if(m->t.perm & DMTMP)
638
		mode |= ModeTemporary;
639
 
640
	if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){
641
		fidPut(fid);
642
		return 0;
643
	}
644
	fileDecRef(fid->file);
645
 
646
	fid->qid.vers = fileGetMcount(file);
647
	fid->qid.path = fileGetId(file);
648
	fid->file = file;
649
	mode = fileGetMode(fid->file);
650
	if(mode & ModeDir)
651
		fid->qid.type = QTDIR;
652
	else
653
		fid->qid.type = QTFILE;
654
	if(mode & ModeAppend)
655
		fid->qid.type |= QTAPPEND;
656
	if(mode & ModeExclusive){
657
		fid->qid.type |= QTEXCL;
658
		assert(exclAlloc(fid) != 0);
659
	}
660
	if(m->t.mode & ORCLOSE)
661
		open |= FidORclose;
662
	fid->open = open;
663
 
664
	m->r.qid = fid->qid;
665
	m->r.iounit = m->con->msize-IOHDRSZ;
666
 
667
	fidPut(fid);
668
	return 1;
669
 
670
error:
671
	fidPut(fid);
672
	return 0;
673
}
674
 
675
static int
676
rTopen(Msg* m)
677
{
678
	Fid *fid;
679
	int isdir, mode, omode, open, rofs;
680
 
681
	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
682
		return 0;
683
	if(fid->open){
684
		vtSetError("fid open for I/O");
685
		goto error;
686
	}
687
 
688
	isdir = fileIsDir(fid->file);
689
	open = 0;
690
	rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname);
691
 
692
	if(m->t.mode & ORCLOSE){
693
		if(isdir){
694
			vtSetError("is a directory");
695
			goto error;
696
		}
697
		if(rofs){
698
			vtSetError("read-only filesystem");
699
			goto error;
700
		}
701
		if(permParent(fid, PermW) <= 0)
702
			goto error;
703
 
704
		open |= FidORclose;
705
	}
706
 
707
	omode = m->t.mode & OMODE;
708
	if(omode == OREAD || omode == ORDWR){
709
		if(permFid(fid, PermR) <= 0)
710
			goto error;
711
		open |= FidORead;
712
	}
713
	if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){
714
		if(isdir){
715
			vtSetError("is a directory");
716
			goto error;
717
		}
718
		if(rofs){
719
			vtSetError("read-only filesystem");
720
			goto error;
721
		}
722
		if(permFid(fid, PermW) <= 0)
723
			goto error;
724
		open |= FidOWrite;
725
	}
726
	if(omode == OEXEC){
727
		if(isdir){
728
			vtSetError("is a directory");
729
			goto error;
730
		}
731
		if(permFid(fid, PermX) <= 0)
732
			goto error;
733
		open |= FidORead;
734
	}
735
	if((open & (FidOWrite|FidORead)) == 0){
736
		vtSetError("unknown mode");
737
		goto error;
738
	}
739
 
740
	mode = fileGetMode(fid->file);
741
	if((mode & ModeExclusive) && exclAlloc(fid) == 0)
742
		goto error;
743
 
744
	/*
745
	 * Everything checks out, try to commit any changes.
746
	 */
747
	if((m->t.mode & OTRUNC) && !(mode & ModeAppend))
748
		if(!fileTruncate(fid->file, fid->uid))
749
			goto error;
750
 
751
	if(isdir && fid->db != nil){
752
		dirBufFree(fid->db);
753
		fid->db = nil;
754
	}
755
 
756
	fid->qid.vers = fileGetMcount(fid->file);
757
	m->r.qid = fid->qid;
758
	m->r.iounit = m->con->msize-IOHDRSZ;
759
 
760
	fid->open = open;
761
 
762
	fidPut(fid);
763
	return 1;
764
 
765
error:
766
	if(fid->excl != nil)
767
		exclFree(fid);
768
	fidPut(fid);
769
	return 0;
770
}
771
 
772
static int
773
rTwalk(Msg* m)
774
{
775
	Qid qid;
776
	Fcall *r, *t;
777
	int nwname, wlock;
778
	File *file, *nfile;
779
	Fid *fid, *ofid, *nfid;
780
 
781
	t = &m->t;
782
	if(t->fid == t->newfid)
783
		wlock = FidFWlock;
784
	else
785
		wlock = 0;
786
 
787
	/*
788
	 * The file identified by t->fid must be valid in the
789
	 * current session and must not have been opened for I/O
790
	 * by an open or create message.
791
	 */
792
	if((ofid = fidGet(m->con, t->fid, wlock)) == nil)
793
		return 0;
794
	if(ofid->open){
795
		vtSetError("file open for I/O");
796
		fidPut(ofid);
797
		return 0;
798
	}
799
 
800
	/*
801
	 * If newfid is not the same as fid, allocate a new file;
802
	 * a side effect is checking newfid is not already in use (error);
803
	 * if there are no names to walk this will be equivalent to a
804
	 * simple 'clone' operation.
805
	 * It's a no-op if newfid is the same as fid and t->nwname is 0.
806
	 */
807
	nfid = nil;
808
	if(t->fid != t->newfid){
809
		nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate);
810
		if(nfid == nil){
811
			vtSetError("%s: walk: newfid 0x%ud in use",
812
				argv0, t->newfid);
813
			fidPut(ofid);
814
			return 0;
815
		}
816
		nfid->open = ofid->open & ~FidORclose;
817
		nfid->file = fileIncRef(ofid->file);
818
		nfid->qid = ofid->qid;
819
		nfid->uid = vtStrDup(ofid->uid);
820
		nfid->uname = vtStrDup(ofid->uname);
821
		nfid->fsys = fsysIncRef(ofid->fsys);
822
		fid = nfid;
823
	}
824
	else
825
		fid = ofid;
826
 
827
	r = &m->r;
828
	r->nwqid = 0;
829
 
830
	if(t->nwname == 0){
831
		if(nfid != nil)
832
			fidPut(nfid);
833
		fidPut(ofid);
834
 
835
		return 1;
836
	}
837
 
838
	file = fid->file;
839
	fileIncRef(file);
840
	qid = fid->qid;
841
 
842
	for(nwname = 0; nwname < t->nwname; nwname++){
843
		/*
844
		 * Walked elements must represent a directory and
845
		 * the implied user must have permission to search
846
		 * the directory.  Walking .. is always allowed, so that
847
		 * you can't walk into a directory and then not be able
848
		 * to walk out of it.
849
		 */
850
		if(!(qid.type & QTDIR)){
851
			vtSetError("not a directory");
852
			break;
853
		}
854
		switch(permFile(file, fid, PermX)){
855
		case 1:
856
			break;
857
		case 0:
858
			if(strcmp(t->wname[nwname], "..") == 0)
859
				break;
860
		case -1:
861
			goto Out;
862
		}
863
		if((nfile = fileWalk(file, t->wname[nwname])) == nil)
864
			break;
865
		fileDecRef(file);
866
		file = nfile;
867
		qid.type = QTFILE;
868
		if(fileIsDir(file))
869
			qid.type = QTDIR;
870
		if(fileIsAppend(file))
871
			qid.type |= QTAPPEND;
872
		if(fileIsTemporary(file))
873
			qid.type |= QTTMP;
874
		if(fileIsExclusive(file))
875
			qid.type |= QTEXCL;
876
		qid.vers = fileGetMcount(file);
877
		qid.path = fileGetId(file);
878
		r->wqid[r->nwqid++] = qid;
879
	}
880
 
881
	if(nwname == t->nwname){
882
		/*
883
		 * Walked all elements. Update the target fid
884
		 * from the temporary qid used during the walk,
885
		 * and tidy up.
886
		 */
887
		fid->qid = r->wqid[r->nwqid-1];
888
		fileDecRef(fid->file);
889
		fid->file = file;
890
 
891
		if(nfid != nil)
892
			fidPut(nfid);
893
 
894
		fidPut(ofid);
895
		return 1;
896
	}
897
 
898
Out:
899
	/*
900
	 * Didn't walk all elements, 'clunk' nfid if it exists
901
	 * and leave fid untouched.
902
	 * It's not an error if some of the elements were walked OK.
903
	 */
904
	fileDecRef(file);
905
	if(nfid != nil)
906
		fidClunk(nfid);
907
 
908
	fidPut(ofid);
909
	if(nwname == 0)
910
		return 0;
911
	return 1;
912
}
913
 
914
static int
915
rTflush(Msg* m)
916
{
917
	if(m->t.oldtag != NOTAG)
918
		msgFlush(m);
919
	return 1;
920
}
921
 
922
static void
923
parseAname(char *aname, char **fsname, char **path)
924
{
925
	char *s;
926
 
927
	if(aname && aname[0])
928
		s = vtStrDup(aname);
929
	else
930
		s = vtStrDup("main/active");
931
	*fsname = s;
932
	if((*path = strchr(s, '/')) != nil)
933
		*(*path)++ = '\0';
934
	else
935
		*path = "";
936
}
937
 
938
/*
939
 * Check remote IP address against /mnt/ipok.
940
 * Sources.cs.bell-labs.com uses this to disallow
941
 * network connections from Sudan, Libya, etc., 
942
 * following U.S. cryptography export regulations.
943
 */
944
static int
945
conIPCheck(Con* con)
946
{
947
	char ok[256], *p;
948
	int fd;
949
 
950
	if(con->flags&ConIPCheck){
951
		if(con->remote[0] == 0){
952
			vtSetError("cannot verify unknown remote address");
953
			return 0;
954
		}
955
		if(access("/mnt/ipok/ok", AEXIST) < 0){
956
			/* mount closes the fd on success */
957
			if((fd = open("/srv/ipok", ORDWR)) >= 0 
958
			&& mount(fd, -1, "/mnt/ipok", MREPL, "") < 0)
959
				close(fd);
960
			if(access("/mnt/ipok/ok", AEXIST) < 0){
961
				vtSetError("cannot verify remote address");
962
				return 0;
963
			}
964
		}
965
		snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote);
966
		if((p = strchr(ok, '!')) != nil)
967
			*p = 0;
968
		if(access(ok, AEXIST) < 0){
969
			vtSetError("restricted remote address");
970
			return 0;
971
		}
972
	}
973
	return 1;
974
}
975
 
976
static int
977
rTattach(Msg* m)
978
{
979
	Fid *fid;
980
	Fsys *fsys;
981
	char *fsname, *path;
982
 
983
	if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
984
		return 0;
985
 
986
	parseAname(m->t.aname, &fsname, &path);
987
	if((fsys = fsysGet(fsname)) == nil){
988
		fidClunk(fid);
989
		vtMemFree(fsname);
990
		return 0;
991
	}
992
	fid->fsys = fsys;
993
 
994
	if(m->t.uname[0] != '\0')
995
		fid->uname = vtStrDup(m->t.uname);
996
	else
997
		fid->uname = vtStrDup(unamenone);
998
 
999
	if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){
1000
		consPrint("reject %s from %s: %R\n", fid->uname, fid->con->remote);
1001
		fidClunk(fid);
1002
		vtMemFree(fsname);
1003
		return 0;
1004
	}
1005
	if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
1006
		if((fid->uid = uidByUname(fid->uname)) == nil)
1007
			fid->uid = vtStrDup(unamenone);
1008
	}
1009
	else if(!authCheck(&m->t, fid, fsys)){
1010
		fidClunk(fid);
1011
		vtMemFree(fsname);
1012
		return 0;
1013
	}
1014
 
1015
	fsysFsRlock(fsys);
1016
	if((fid->file = fsysGetRoot(fsys, path)) == nil){
1017
		fsysFsRUnlock(fsys);
1018
		fidClunk(fid);
1019
		vtMemFree(fsname);
1020
		return 0;
1021
	}
1022
	fsysFsRUnlock(fsys);
1023
	vtMemFree(fsname);
1024
 
1025
	fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
1026
	m->r.qid = fid->qid;
1027
 
1028
	fidPut(fid);
1029
	return 1;
1030
}
1031
 
1032
static int
1033
rTauth(Msg* m)
1034
{
1035
	int afd;
1036
	Con *con;
1037
	Fid *afid;
1038
	Fsys *fsys;
1039
	char *fsname, *path;
1040
 
1041
	parseAname(m->t.aname, &fsname, &path);
1042
	if((fsys = fsysGet(fsname)) == nil){
1043
		vtMemFree(fsname);
1044
		return 0;
1045
	}
1046
	vtMemFree(fsname);
1047
 
1048
	if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
1049
		m->con->aok = 1;
1050
		vtSetError("authentication disabled");
1051
		fsysPut(fsys);
1052
		return 0;
1053
	}
1054
	if(strcmp(m->t.uname, unamenone) == 0){
1055
		vtSetError("user 'none' requires no authentication");
1056
		fsysPut(fsys);
1057
		return 0;
1058
	}
1059
 
1060
	con = m->con;
1061
	if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
1062
		fsysPut(fsys);
1063
		return 0;
1064
	}
1065
	afid->fsys = fsys;
1066
 
1067
	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
1068
		vtSetError("can't open \"/mnt/factotum/rpc\"");
1069
		fidClunk(afid);
1070
		return 0;
1071
	}
1072
	if((afid->rpc = auth_allocrpc(afd)) == nil){
1073
		close(afd);
1074
		vtSetError("can't auth_allocrpc");
1075
		fidClunk(afid);
1076
		return 0;
1077
	}
1078
	if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
1079
		vtSetError("can't auth_rpc");
1080
		fidClunk(afid);
1081
		return 0;
1082
	}
1083
 
1084
	afid->open = FidOWrite|FidORead;
1085
	afid->qid.type = QTAUTH;
1086
	afid->qid.path = m->t.afid;
1087
	afid->uname = vtStrDup(m->t.uname);
1088
 
1089
	m->r.qid = afid->qid;
1090
 
1091
	fidPut(afid);
1092
	return 1;
1093
}
1094
 
1095
static int
1096
rTversion(Msg* m)
1097
{
1098
	int v;
1099
	Con *con;
1100
	Fcall *r, *t;
1101
 
1102
	t = &m->t;
1103
	r = &m->r;
1104
	con = m->con;
1105
 
1106
	vtLock(con->lock);
1107
	if(con->state != ConInit){
1108
		vtUnlock(con->lock);
1109
		vtSetError("Tversion: down");
1110
		return 0;
1111
	}
1112
	con->state = ConNew;
1113
 
1114
	/*
1115
	 * Release the karma of past lives and suffering.
1116
	 * Should this be done before or after checking the
1117
	 * validity of the Tversion?
1118
	 */
1119
	fidClunkAll(con);
1120
 
1121
	if(t->tag != NOTAG){
1122
		vtUnlock(con->lock);
1123
		vtSetError("Tversion: invalid tag");
1124
		return 0;
1125
	}
1126
 
1127
	if(t->msize < 256){
1128
		vtUnlock(con->lock);
1129
		vtSetError("Tversion: message size too small");
1130
		return 0;
1131
	}
1132
	if(t->msize < con->msize)
1133
		r->msize = t->msize;
1134
	else
1135
		r->msize = con->msize;
1136
 
1137
	r->version = "unknown";
1138
	if(t->version[0] == '9' && t->version[1] == 'P'){
1139
		/*
1140
		 * Currently, the only defined version
1141
		 * is "9P2000"; ignore any later versions.
1142
		 */
1143
		v = strtol(&t->version[2], 0, 10);
1144
		if(v >= 2000){
1145
			r->version = VERSION9P;
1146
			con->msize = r->msize;
1147
			con->state = ConUp;
1148
		}
1149
		else if(strcmp(t->version, "9PEoF") == 0){
1150
			r->version = "9PEoF";
1151
			con->msize = r->msize;
1152
			con->state = ConMoribund;
1153
 
1154
			/*
1155
			 * Don't want to attempt to write this
1156
			 * message as the connection may be already
1157
			 * closed.
1158
			 */
1159
			m->state = MsgF;
1160
		}
1161
	}
1162
	vtUnlock(con->lock);
1163
 
1164
	return 1;
1165
}
1166
 
1167
int (*rFcall[Tmax])(Msg*) = {
1168
	[Tversion]	= rTversion,
1169
	[Tauth]		= rTauth,
1170
	[Tattach]	= rTattach,
1171
	[Tflush]	= rTflush,
1172
	[Twalk]		= rTwalk,
1173
	[Topen]		= rTopen,
1174
	[Tcreate]	= rTcreate,
1175
	[Tread]		= rTread,
1176
	[Twrite]	= rTwrite,
1177
	[Tclunk]	= rTclunk,
1178
	[Tremove]	= rTremove,
1179
	[Tstat]		= rTstat,
1180
	[Twstat]	= rTwstat,
1181
};