xref: /plan9/sys/src/cmd/disk/kfs/9p1.c (revision 229d2d344532463b8438213e6bf550fed5babd4a)
1 #include	"all.h"
2 #include	"9p1.h"
3 
4 /*
5  * buggery to give false qid for
6  * the top 2 levels of the dump fs
7  */
8 void
mkqid(Qid * qid,Dentry * d,int buggery)9 mkqid(Qid* qid, Dentry *d, int buggery)
10 {
11 	int c;
12 
13 	if(buggery && d->qid.path == QPROOT && (d->qid.path & QPDIR)){
14 		c = d->name[0];
15 		if(c >= '0' && c <= '9'){
16 			qid->path = 3;
17 			qid->vers = d->qid.version;
18 			qid->type = QTDIR;
19 
20 			c = (c-'0')*10 + (d->name[1]-'0');
21 			if(c >= 1 && c <= 12)
22 				qid->path = 4;
23 			return;
24 		}
25 	}
26 
27 	mkqid9p2(qid, &d->qid, d->mode);
28 }
29 
30 int
mkqidcmp(Qid * qid,Dentry * d)31 mkqidcmp(Qid* qid, Dentry *d)
32 {
33 	Qid tmp;
34 
35 	mkqid(&tmp, d, 1);
36 	if(qid->path==tmp.path && (qid->type&QTDIR)==(tmp.type&QTDIR))
37 		return 0;
38 	return Eqid;
39 }
40 
41 void
f_nop(Chan * cp,Oldfcall * in,Oldfcall * ou)42 f_nop(Chan *cp, Oldfcall *in, Oldfcall *ou)
43 {
44 
45 	USED(in);
46 	USED(ou);
47 	if(CHAT(cp))
48 		print("c_nop %d\n", cp->chan);
49 }
50 
51 void
f_flush(Chan * cp,Oldfcall * in,Oldfcall * ou)52 f_flush(Chan *cp, Oldfcall *in, Oldfcall *ou)
53 {
54 
55 	USED(in);
56 	USED(ou);
57 	if(CHAT(cp))
58 		print("c_flush %d\n", cp->chan);
59 	runlock(&cp->reflock);
60 	wlock(&cp->reflock);
61 	wunlock(&cp->reflock);
62 	rlock(&cp->reflock);
63 }
64 
65 void
f_session(Chan * cp,Oldfcall * in,Oldfcall * ou)66 f_session(Chan *cp, Oldfcall *in, Oldfcall *ou)
67 {
68 	if(CHAT(cp))
69 		print("c_session %d\n", cp->chan);
70 
71 	memmove(cp->rchal, in->chal, sizeof(cp->rchal));
72 	if(wstatallow || cp == cons.srvchan){
73 		memset(ou->chal, 0, sizeof(ou->chal));
74 		memset(ou->authid, 0, sizeof(ou->authid));
75 	}else{
76 		mkchallenge(cp);
77 		memmove(ou->chal, cp->chal, sizeof(ou->chal));
78 		memmove(ou->authid, nvr.authid, sizeof(ou->authid));
79 	}
80 	sprint(ou->authdom, "%s.%s", service, nvr.authdom);
81 	fileinit(cp);
82 }
83 
84 void
f_attach(Chan * cp,Oldfcall * in,Oldfcall * ou)85 f_attach(Chan *cp, Oldfcall *in, Oldfcall *ou)
86 {
87 	Iobuf *p;
88 	Dentry *d;
89 	File *f;
90 	int u;
91 	Filsys *fs;
92 	long raddr;
93 
94 	if(CHAT(cp)) {
95 		print("c_attach %d\n", cp->chan);
96 		print("	fid = %d\n", in->fid);
97 		print("	uid = %s\n", in->uname);
98 		print("	arg = %s\n", in->aname);
99 	}
100 
101 	ou->qid = QID9P1(0,0);
102 	ou->fid = in->fid;
103 	if(!in->aname[0])	/* default */
104 		strncpy(in->aname, filesys[0].name, sizeof(in->aname));
105 	p = 0;
106 	f = filep(cp, in->fid, 1);
107 	if(!f) {
108 		ou->err = Efid;
109 		goto out;
110 	}
111 	u = -1;
112 	if(cp != cons.chan){
113 		if(authorize(cp, in, ou) == 0 || strcmp(in->uname, "adm") == 0){
114 			ou->err = Eauth;
115 			goto out;
116 		}
117 		u = strtouid(in->uname);
118 		if(u < 0){
119 			ou->err = Ebadu;
120 			goto out;
121 		}
122 	}
123 
124 	fs = fsstr(in->aname);
125 	if(fs == 0) {
126 		ou->err = Ebadspc;
127 		goto out;
128 	}
129 	raddr = getraddr(fs->dev);
130 	p = getbuf(fs->dev, raddr, Bread);
131 	d = getdir(p, 0);
132 	if(!d || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)) {
133 		ou->err = Ealloc;
134 		goto out;
135 	}
136 	f->uid = u;
137 	if(iaccess(f, d, DREAD)) {
138 		ou->err = Eaccess;
139 		goto out;
140 	}
141 	accessdir(p, d, FREAD);
142 	mkqid(&f->qid, d, 1);
143 	f->fs = fs;
144 	f->addr = raddr;
145 	f->slot = 0;
146 	f->open = 0;
147 	freewp(f->wpath);
148 	f->wpath = 0;
149 
150 	mkqid9p1(&ou->qid, &f->qid);
151 
152 out:
153 	if(p)
154 		putbuf(p);
155 	if(f) {
156 		qunlock(f);
157 		if(ou->err)
158 			freefp(f);
159 	}
160 }
161 
162 void
f_clone(Chan * cp,Oldfcall * in,Oldfcall * ou)163 f_clone(Chan *cp, Oldfcall *in, Oldfcall *ou)
164 {
165 	File *f1, *f2;
166 	int fid, fid1;
167 
168 	if(CHAT(cp)) {
169 		print("c_clone %d\n", cp->chan);
170 		print("	old fid = %d\n", in->fid);
171 		print("	new fid = %d\n", in->newfid);
172 	}
173 
174 	fid = in->fid;
175 	fid1 = in->newfid;
176 
177 	f1 = 0;
178 	f2 = 0;
179 	if(fid < fid1) {
180 		f1 = filep(cp, fid, 0);
181 		f2 = filep(cp, fid1, 1);
182 	} else
183 	if(fid1 < fid) {
184 		f2 = filep(cp, fid1, 1);
185 		f1 = filep(cp, fid, 0);
186 	}
187 	if(!f1 || !f2) {
188 		ou->err = Efid;
189 		goto out;
190 	}
191 
192 
193 	f2->fs = f1->fs;
194 	f2->addr = f1->addr;
195 	f2->open = f1->open & ~FREMOV;
196 	f2->uid = f1->uid;
197 	f2->slot = f1->slot;
198 	f2->qid = f1->qid;
199 
200 	freewp(f2->wpath);
201 	f2->wpath = getwp(f1->wpath);
202 
203 out:
204 	ou->fid = fid;
205 	if(f1)
206 		qunlock(f1);
207 	if(f2)
208 		qunlock(f2);
209 }
210 
211 void
f_walk(Chan * cp,Oldfcall * in,Oldfcall * ou)212 f_walk(Chan *cp, Oldfcall *in, Oldfcall *ou)
213 {
214 	Iobuf *p, *p1;
215 	Dentry *d, *d1;
216 	File *f;
217 	Wpath *w, *ow;
218 	int slot;
219 	long addr;
220 
221 	if(CHAT(cp)) {
222 		print("c_walk %d\n", cp->chan);
223 		print("	fid = %d\n", in->fid);
224 		print("	name = %s\n", in->name);
225 	}
226 
227 	ou->fid = in->fid;
228 	ou->qid = QID9P1(0,0);
229 	p = 0;
230 	f = filep(cp, in->fid, 0);
231 	if(!f) {
232 		ou->err = Efid;
233 		goto out;
234 	}
235 	p = getbuf(f->fs->dev, f->addr, Bread);
236 	d = getdir(p, f->slot);
237 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
238 		ou->err = Ealloc;
239 		goto out;
240 	}
241 	if(!(d->mode & DDIR)) {
242 		ou->err = Edir1;
243 		goto out;
244 	}
245 	if(ou->err = mkqidcmp(&f->qid, d))
246 		goto out;
247 	if(cp != cons.chan && iaccess(f, d, DEXEC)) {
248 		ou->err = Eaccess;
249 		goto out;
250 	}
251 	accessdir(p, d, FREAD);
252 	if(strcmp(in->name, ".") == 0)
253 		goto setdot;
254 	if(strcmp(in->name, "..") == 0) {
255 		if(f->wpath == 0)
256 			goto setdot;
257 		putbuf(p);
258 		p = 0;
259 		addr = f->wpath->addr;
260 		slot = f->wpath->slot;
261 		p1 = getbuf(f->fs->dev, addr, Bread);
262 		d1 = getdir(p1, slot);
263 		if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
264 			if(p1)
265 				putbuf(p1);
266 			ou->err = Ephase;
267 			goto out;
268 		}
269 		ow = f->wpath;
270 		f->wpath = ow->up;
271 		putwp(ow);
272 		goto found;
273 	}
274 	for(addr=0;; addr++) {
275 		p1 = dnodebuf(p, d, addr, 0);
276 		if(!p1 || checktag(p1, Tdir, d->qid.path) ) {
277 			if(p1)
278 				putbuf(p1);
279 			ou->err = Eentry;
280 			goto out;
281 		}
282 		for(slot=0; slot<DIRPERBUF; slot++) {
283 			d1 = getdir(p1, slot);
284 			if(!(d1->mode & DALLOC))
285 				continue;
286 			if(strncmp(in->name, d1->name, sizeof(in->name)))
287 				continue;
288 			/*
289 			 * update walk path
290 			 */
291 			w = newwp();
292 			if(!w) {
293 				ou->err = Ewalk;
294 				putbuf(p1);
295 				goto out;
296 			}
297 			w->addr = f->addr;
298 			w->slot = f->slot;
299 			w->up = f->wpath;
300 			f->wpath = w;
301 			slot += DIRPERBUF*addr;
302 			goto found;
303 		}
304 		putbuf(p1);
305 	}
306 
307 found:
308 	f->addr = p1->addr;
309 	mkqid(&f->qid, d1, 1);
310 	putbuf(p1);
311 	f->slot = slot;
312 
313 setdot:
314 	mkqid9p1(&ou->qid, &f->qid);
315 	f->open = 0;
316 
317 out:
318 	if(p)
319 		putbuf(p);
320 	if(f)
321 		qunlock(f);
322 }
323 
324 void
f_clunk(Chan * cp,Oldfcall * in,Oldfcall * ou)325 f_clunk(Chan *cp, Oldfcall *in, Oldfcall *ou)
326 {
327 	File *f;
328 	Tlock *t;
329 	long tim;
330 
331 	if(CHAT(cp)) {
332 		print("c_clunk %d\n", cp->chan);
333 		print("	fid = %d\n", in->fid);
334 	}
335 
336 	f = filep(cp, in->fid, 0);
337 	if(!f) {
338 		print("%p\n", f);
339 		ou->err = Efid;
340 		goto out;
341 	}
342 	if(t = f->tlock) {
343 		tim = time(0);
344 		if(t->time < tim || t->file != f)
345 			ou->err = Ebroken;
346 		t->time = 0;	/* free the lock */
347 		f->tlock = 0;
348 	}
349 	if(f->open & FREMOV)
350 		ou->err = doremove(f, 0);
351 	f->open = 0;
352 	freewp(f->wpath);
353 	freefp(f);
354 
355 out:
356 	if(f)
357 		qunlock(f);
358 	ou->fid = in->fid;
359 }
360 
361 void
f_clwalk(Chan * cp,Oldfcall * in,Oldfcall * ou)362 f_clwalk(Chan *cp, Oldfcall *in, Oldfcall *ou)
363 {
364 	int er, fid;
365 
366 	if(CHAT(cp))
367 		print("c_clwalk macro\n");
368 
369 	f_clone(cp, in, ou);		/* sets tag, fid */
370 	if(ou->err)
371 		return;
372 	fid = in->fid;
373 	in->fid = in->newfid;
374 	f_walk(cp, in, ou);		/* sets tag, fid, qid */
375 	er = ou->err;
376 	if(er == Eentry) {
377 		/*
378 		 * if error is "no entry"
379 		 * return non error and fid
380 		 */
381 		ou->err = 0;
382 		f_clunk(cp, in, ou);	/* sets tag, fid */
383 		ou->err = 0;
384 		ou->fid = fid;
385 		if(CHAT(cp))
386 			print("	error: %s\n", errstring[er]);
387 		return;
388 	}
389 	if(er) {
390 		/*
391 		 * if any other error
392 		 * return an error
393 		 */
394 		ou->err = 0;
395 		f_clunk(cp, in, ou);	/* sets tag, fid */
396 		ou->err = er;
397 		return;
398 	}
399 	/*
400 	 * non error
401 	 * return newfid
402 	 */
403 }
404 
405 void
f_open(Chan * cp,Oldfcall * in,Oldfcall * ou)406 f_open(Chan *cp, Oldfcall *in, Oldfcall *ou)
407 {
408 	Iobuf *p;
409 	Dentry *d;
410 	File *f;
411 	Tlock *t;
412 	Qid qid;
413 	int ro, fmod;
414 
415 	if(CHAT(cp)) {
416 		print("c_open %d\n", cp->chan);
417 		print("	fid = %d\n", in->fid);
418 		print("	mode = %o\n", in->mode);
419 	}
420 
421 	p = 0;
422 	f = filep(cp, in->fid, 0);
423 	if(!f) {
424 		ou->err = Efid;
425 		goto out;
426 	}
427 
428 	/*
429 	 * if remove on close, check access here
430 	 */
431 	ro = isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup));
432 	if(in->mode & MRCLOSE) {
433 		if(ro) {
434 			ou->err = Eronly;
435 			goto out;
436 		}
437 		/*
438 		 * check on parent directory of file to be deleted
439 		 */
440 		if(f->wpath == 0 || f->wpath->addr == f->addr) {
441 			ou->err = Ephase;
442 			goto out;
443 		}
444 		p = getbuf(f->fs->dev, f->wpath->addr, Bread);
445 		d = getdir(p, f->wpath->slot);
446 		if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
447 			ou->err = Ephase;
448 			goto out;
449 		}
450 		if(iaccess(f, d, DWRITE)) {
451 			ou->err = Eaccess;
452 			goto out;
453 		}
454 		putbuf(p);
455 	}
456 	p = getbuf(f->fs->dev, f->addr, Bread);
457 	d = getdir(p, f->slot);
458 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
459 		ou->err = Ealloc;
460 		goto out;
461 	}
462 	if(ou->err = mkqidcmp(&f->qid, d))
463 		goto out;
464 	mkqid(&qid, d, 1);
465 	switch(in->mode & 7) {
466 
467 	case MREAD:
468 		if(iaccess(f, d, DREAD) && !writeallow)
469 			goto badaccess;
470 		fmod = FREAD;
471 		break;
472 
473 	case MWRITE:
474 		if((d->mode & DDIR) ||
475 		   (iaccess(f, d, DWRITE) && !writeallow))
476 			goto badaccess;
477 		if(ro) {
478 			ou->err = Eronly;
479 			goto out;
480 		}
481 		fmod = FWRITE;
482 		break;
483 
484 	case MBOTH:
485 		if((d->mode & DDIR) ||
486 		   (iaccess(f, d, DREAD) && !writeallow) ||
487 		   (iaccess(f, d, DWRITE) && !writeallow))
488 			goto badaccess;
489 		if(ro) {
490 			ou->err = Eronly;
491 			goto out;
492 		}
493 		fmod = FREAD+FWRITE;
494 		break;
495 
496 	case MEXEC:
497 		if((d->mode & DDIR) ||
498 		   iaccess(f, d, DEXEC))
499 			goto badaccess;
500 		fmod = FREAD;
501 		break;
502 
503 	default:
504 		ou->err = Emode;
505 		goto out;
506 	}
507 	if(in->mode & MTRUNC) {
508 		if((d->mode & DDIR) ||
509 		   (iaccess(f, d, DWRITE) && !writeallow))
510 			goto badaccess;
511 		if(ro) {
512 			ou->err = Eronly;
513 			goto out;
514 		}
515 	}
516 	t = 0;
517 	if(d->mode & DLOCK) {
518 		t = tlocked(p, d);
519 		if(t == 0) {
520 			ou->err = Elocked;
521 			goto out;
522 		}
523 		t->file = f;
524 	}
525 	if(in->mode & MRCLOSE)
526 		fmod |= FREMOV;
527 	f->open = fmod;
528 	if(in->mode & MTRUNC)
529 		if(!(d->mode & DAPND))
530 			dtrunc(p, d);
531 	f->tlock = t;
532 	f->lastra = 0;
533 	mkqid9p1(&ou->qid, &qid);
534 	goto out;
535 
536 badaccess:
537 	ou->err = Eaccess;
538 	f->open = 0;
539 
540 out:
541 	if(p)
542 		putbuf(p);
543 	if(f)
544 		qunlock(f);
545 	ou->fid = in->fid;
546 }
547 
548 void
f_create(Chan * cp,Oldfcall * in,Oldfcall * ou)549 f_create(Chan *cp, Oldfcall *in, Oldfcall *ou)
550 {
551 	Iobuf *p, *p1;
552 	Dentry *d, *d1;
553 	File *f;
554 	int slot, slot1, fmod;
555 	long addr, addr1, path;
556 	Qid qid;
557 	Tlock *t;
558 	Wpath *w;
559 
560 	if(CHAT(cp)) {
561 		print("c_create %d\n", cp->chan);
562 		print("	fid = %d\n", in->fid);
563 		print("	name = %s\n", in->name);
564 		print("	perm = %lx+%lo\n", (in->perm>>28)&0xf,
565 				in->perm&0777);
566 		print("	mode = %d\n", in->mode);
567 	}
568 
569 	p = 0;
570 	f = filep(cp, in->fid, 0);
571 	if(!f) {
572 		ou->err = Efid;
573 		goto out;
574 	}
575 	if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) {
576 		ou->err = Eronly;
577 		goto out;
578 	}
579 
580 	p = getbuf(f->fs->dev, f->addr, Bread);
581 	d = getdir(p, f->slot);
582 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
583 		ou->err = Ealloc;
584 		goto out;
585 	}
586 	if(ou->err = mkqidcmp(&f->qid, d))
587 		goto out;
588 	if(!(d->mode & DDIR)) {
589 		ou->err = Edir2;
590 		goto out;
591 	}
592 	if(cp != cons.chan && iaccess(f, d, DWRITE) && !writeallow) {
593 		ou->err = Eaccess;
594 		goto out;
595 	}
596 	accessdir(p, d, FREAD);
597 	if(!strncmp(in->name, ".", sizeof(in->name)) ||
598 	   !strncmp(in->name, "..", sizeof(in->name))) {
599 		ou->err = Edot;
600 		goto out;
601 	}
602 	if(checkname(in->name)) {
603 		ou->err = Ename;
604 		goto out;
605 	}
606 	addr1 = 0;
607 	slot1 = 0;	/* set */
608 	for(addr=0;; addr++) {
609 		p1 = dnodebuf(p, d, addr, 0);
610 		if(!p1) {
611 			if(addr1)
612 				break;
613 			p1 = dnodebuf(p, d, addr, Tdir);
614 		}
615 		if(p1 == 0) {
616 			ou->err = Efull;
617 			goto out;
618 		}
619 		if(checktag(p1, Tdir, d->qid.path)) {
620 			putbuf(p1);
621 			goto phase;
622 		}
623 		for(slot=0; slot<DIRPERBUF; slot++) {
624 			d1 = getdir(p1, slot);
625 			if(!(d1->mode & DALLOC)) {
626 				if(!addr1) {
627 					addr1 = p1->addr;
628 					slot1 = slot + addr*DIRPERBUF;
629 				}
630 				continue;
631 			}
632 			if(!strncmp(in->name, d1->name, sizeof(in->name))) {
633 				putbuf(p1);
634 				ou->err = Eexist;
635 				goto out;
636 			}
637 		}
638 		putbuf(p1);
639 	}
640 	switch(in->mode & 7) {
641 	case MEXEC:
642 	case MREAD:		/* seems only useful to make directories */
643 		fmod = FREAD;
644 		break;
645 
646 	case MWRITE:
647 		fmod = FWRITE;
648 		break;
649 
650 	case MBOTH:
651 		fmod = FREAD+FWRITE;
652 		break;
653 
654 	default:
655 		ou->err = Emode;
656 		goto out;
657 	}
658 	if(in->perm & PDIR)
659 		if((in->mode & MTRUNC) || (in->perm & PAPND) || (fmod & FWRITE))
660 			goto badaccess;
661 	/*
662 	 * do it
663 	 */
664 	path = qidpathgen(&f->fs->dev);
665 	p1 = getbuf(f->fs->dev, addr1, Bread|Bimm|Bmod);
666 	d1 = getdir(p1, slot1);
667 	if(!d1 || checktag(p1, Tdir, d->qid.path)) {
668 		if(p1)
669 			putbuf(p1);
670 		goto phase;
671 	}
672 	if(d1->mode & DALLOC) {
673 		putbuf(p1);
674 		goto phase;
675 	}
676 
677 	strncpy(d1->name, in->name, sizeof(in->name));
678 	/*
679 	 * bogus argument passing -- see console.c
680 	 */
681 	if(cp == cons.chan) {
682 		d1->uid = cons.uid;
683 		d1->gid = cons.gid;
684 	} else {
685 		d1->uid = f->uid;
686 		d1->gid = d->gid;
687 		in->perm &= d->mode | ~0666;
688 		if(in->perm & PDIR)
689 			in->perm &= d->mode | ~0777;
690 	}
691 	d1->qid.path = path;
692 	d1->qid.version = 0;
693 	d1->mode = DALLOC | (in->perm & 0777);
694 	if(in->perm & PDIR) {
695 		d1->mode |= DDIR;
696 		d1->qid.path |= QPDIR;
697 	}
698 	if(in->perm & PAPND)
699 		d1->mode |= DAPND;
700 	t = 0;
701 	if(in->perm & PLOCK) {
702 		d1->mode |= DLOCK;
703 		t = tlocked(p1, d1);
704 	}
705 	accessdir(p1, d1, FWRITE);
706 	mkqid(&qid, d1, 0);
707 	putbuf(p1);
708 	accessdir(p, d, FWRITE);
709 
710 	/*
711 	 * do a walk to new directory entry
712 	 */
713 	w = newwp();
714 	if(!w) {
715 		ou->err = Ewalk;
716 		goto out;
717 	}
718 	w->addr = f->addr;
719 	w->slot = f->slot;
720 	w->up = f->wpath;
721 	f->wpath = w;
722 	f->qid = qid;
723 	f->tlock = t;
724 	f->lastra = 0;
725 	if(in->mode & MRCLOSE)
726 		fmod |= FREMOV;
727 	f->open = fmod;
728 	f->addr = addr1;
729 	f->slot = slot1;
730 	if(t)
731 		t->file = f;
732 	mkqid9p1(&ou->qid, &qid);
733 	goto out;
734 
735 badaccess:
736 	ou->err = Eaccess;
737 	goto out;
738 
739 phase:
740 	ou->err = Ephase;
741 
742 out:
743 	if(p)
744 		putbuf(p);
745 	if(f)
746 		qunlock(f);
747 	ou->fid = in->fid;
748 }
749 
750 void
f_read(Chan * cp,Oldfcall * in,Oldfcall * ou)751 f_read(Chan *cp, Oldfcall *in, Oldfcall *ou)
752 {
753 	Iobuf *p, *p1;
754 	File *f;
755 	Dentry *d, *d1;
756 	Tlock *t;
757 	long addr, offset, tim;
758 	int nread, count, n, o, slot;
759 
760 	if(CHAT(cp)) {
761 		print("c_read %d\n", cp->chan);
762 		print("	fid = %d\n", in->fid);
763 		print("	offset = %ld\n", in->offset);
764 		print("	count = %ld\n", in->count);
765 	}
766 
767 	p = 0;
768 	count = in->count;
769 	offset = in->offset;
770 	nread = 0;
771 	f = filep(cp, in->fid, 0);
772 	if(!f) {
773 		ou->err = Efid;
774 		goto out;
775 	}
776 	if(!(f->open & FREAD)) {
777 		ou->err = Eopen;
778 		goto out;
779 	}
780 	if(count < 0 || count > MAXDAT) {
781 		ou->err = Ecount;
782 		goto out;
783 	}
784 	if(offset < 0) {
785 		ou->err = Eoffset;
786 		goto out;
787 	}
788 	p = getbuf(f->fs->dev, f->addr, Bread);
789 	d = getdir(p, f->slot);
790 	if(!d || !(d->mode & DALLOC)) {
791 		ou->err = Ealloc;
792 		goto out;
793 	}
794 	if(ou->err = mkqidcmp(&f->qid, d))
795 		goto out;
796 	if(t = f->tlock) {
797 		tim = time(0);
798 		if(t->time < tim || t->file != f) {
799 			ou->err = Ebroken;
800 			goto out;
801 		}
802 		/* renew the lock */
803 		t->time = tim + TLOCK;
804 	}
805 	accessdir(p, d, FREAD);
806 	if(d->mode & DDIR) {
807 		addr = 0;
808 		goto dread;
809 	}
810 	if(offset+count > d->size)
811 		count = d->size - offset;
812 	while(count > 0) {
813 		addr = offset / BUFSIZE;
814 		if(addr == f->lastra+1)
815 			dbufread(p, d, addr+1);
816 		f->lastra = addr;
817 		o = offset % BUFSIZE;
818 		n = BUFSIZE - o;
819 		if(n > count)
820 			n = count;
821 		p1 = dnodebuf(p, d, addr, 0);
822 		if(p1) {
823 			if(checktag(p1, Tfile, QPNONE)) {
824 				ou->err = Ephase;
825 				putbuf(p1);
826 				goto out;
827 			}
828 			memmove(ou->data+nread, p1->iobuf+o, n);
829 			putbuf(p1);
830 		} else
831 			memset(ou->data+nread, 0, n);
832 		count -= n;
833 		nread += n;
834 		offset += n;
835 	}
836 	goto out;
837 
838 dread:
839 	p1 = dnodebuf(p, d, addr, 0);
840 	if(!p1)
841 		goto out;
842 	if(checktag(p1, Tdir, QPNONE)) {
843 		ou->err = Ephase;
844 		putbuf(p1);
845 		goto out;
846 	}
847 	n = DIRREC;
848 	for(slot=0; slot<DIRPERBUF; slot++) {
849 		d1 = getdir(p1, slot);
850 		if(!(d1->mode & DALLOC))
851 			continue;
852 		if(offset >= n) {
853 			offset -= n;
854 			continue;
855 		}
856 		if(count < n) {
857 			putbuf(p1);
858 			goto out;
859 		}
860 		if(convD2M9p1(d1, ou->data+nread) != n)
861 			print("dirread convD2M\n");
862 		nread += n;
863 		count -= n;
864 	}
865 	putbuf(p1);
866 	addr++;
867 	goto dread;
868 
869 out:
870 	count = in->count - nread;
871 	if(count > 0)
872 		memset(ou->data+nread, 0, count);
873 	if(p)
874 		putbuf(p);
875 	if(f)
876 		qunlock(f);
877 	ou->fid = in->fid;
878 	ou->count = nread;
879 	if(CHAT(cp))
880 		print("	nread = %d\n", nread);
881 }
882 
883 void
f_write(Chan * cp,Oldfcall * in,Oldfcall * ou)884 f_write(Chan *cp, Oldfcall *in, Oldfcall *ou)
885 {
886 	Iobuf *p, *p1;
887 	Dentry *d;
888 	File *f;
889 	Tlock *t;
890 	long offset, addr, tim;
891 	int count, nwrite, o, n;
892 
893 	if(CHAT(cp)) {
894 		print("c_write %d\n", cp->chan);
895 		print("	fid = %d\n", in->fid);
896 		print("	offset = %ld\n", in->offset);
897 		print("	count = %ld\n", in->count);
898 	}
899 
900 	offset = in->offset;
901 	count = in->count;
902 	nwrite = 0;
903 	p = 0;
904 	f = filep(cp, in->fid, 0);
905 	if(!f) {
906 		ou->err = Efid;
907 		goto out;
908 	}
909 	if(!(f->open & FWRITE)) {
910 		ou->err = Eopen;
911 		goto out;
912 	}
913 	if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) {
914 		ou->err = Eronly;
915 		goto out;
916 	}
917 	if(count < 0 || count > MAXDAT) {
918 		ou->err = Ecount;
919 		goto out;
920 	}
921 	if(offset < 0) {
922 		ou->err = Eoffset;
923 		goto out;
924 	}
925 	p = getbuf(f->fs->dev, f->addr, Bread|Bmod);
926 	d = getdir(p, f->slot);
927 	if(!d || !(d->mode & DALLOC)) {
928 		ou->err = Ealloc;
929 		goto out;
930 	}
931 	if(ou->err = mkqidcmp(&f->qid, d))
932 		goto out;
933 	if(t = f->tlock) {
934 		tim = time(0);
935 		if(t->time < tim || t->file != f) {
936 			ou->err = Ebroken;
937 			goto out;
938 		}
939 		/* renew the lock */
940 		t->time = tim + TLOCK;
941 	}
942 	accessdir(p, d, FWRITE);
943 	if(d->mode & DAPND)
944 		offset = d->size;
945 	if(offset+count > d->size)
946 		d->size = offset+count;
947 	while(count > 0) {
948 		addr = offset / BUFSIZE;
949 		o = offset % BUFSIZE;
950 		n = BUFSIZE - o;
951 		if(n > count)
952 			n = count;
953 		p1 = dnodebuf(p, d, addr, Tfile);
954 		if(p1 == 0) {
955 			ou->err = Efull;
956 			goto out;
957 		}
958 		if(checktag(p1, Tfile, d->qid.path)) {
959 			putbuf(p1);
960 			ou->err = Ephase;
961 			goto out;
962 		}
963 		memmove(p1->iobuf+o, in->data+nwrite, n);
964 		p1->flags |= Bmod;
965 		putbuf(p1);
966 		count -= n;
967 		nwrite += n;
968 		offset += n;
969 	}
970 	if(CHAT(cp))
971 		print("	nwrite = %d\n", nwrite);
972 
973 out:
974 	if(p)
975 		putbuf(p);
976 	if(f)
977 		qunlock(f);
978 	ou->fid = in->fid;
979 	ou->count = nwrite;
980 }
981 
982 int
doremove(File * f,int iscon)983 doremove(File *f, int iscon)
984 {
985 	Iobuf *p, *p1;
986 	Dentry *d, *d1;
987 	long addr;
988 	int slot, err;
989 
990 	p = 0;
991 	p1 = 0;
992 	if(isro(f->fs->dev) || (f->cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) {
993 		err = Eronly;
994 		goto out;
995 	}
996 	/*
997 	 * check on parent directory of file to be deleted
998 	 */
999 	if(f->wpath == 0 || f->wpath->addr == f->addr) {
1000 		err = Ephase;
1001 		goto out;
1002 	}
1003 	p1 = getbuf(f->fs->dev, f->wpath->addr, Bread);
1004 	d1 = getdir(p1, f->wpath->slot);
1005 	if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1006 		err = Ephase;
1007 		goto out;
1008 	}
1009 	if(!iscon && iaccess(f, d1, DWRITE)) {
1010 		err = Eaccess;
1011 		goto out;
1012 	}
1013 	accessdir(p1, d1, FWRITE);
1014 	putbuf(p1);
1015 	p1 = 0;
1016 
1017 	/*
1018 	 * check on file to be deleted
1019 	 */
1020 	p = getbuf(f->fs->dev, f->addr, Bread);
1021 	d = getdir(p, f->slot);
1022 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1023 		err = Ealloc;
1024 		goto out;
1025 	}
1026 	if(err = mkqidcmp(&f->qid, d))
1027 		goto out;
1028 
1029 	/*
1030 	 * if deleting a directory, make sure it is empty
1031 	 */
1032 	if((d->mode & DDIR))
1033 	for(addr=0;; addr++) {
1034 		p1 = dnodebuf(p, d, addr, 0);
1035 		if(!p1)
1036 			break;
1037 		if(checktag(p1, Tdir, d->qid.path)) {
1038 			err = Ephase;
1039 			goto out;
1040 		}
1041 		for(slot=0; slot<DIRPERBUF; slot++) {
1042 			d1 = getdir(p1, slot);
1043 			if(!(d1->mode & DALLOC))
1044 				continue;
1045 			err = Eempty;
1046 			goto out;
1047 		}
1048 		putbuf(p1);
1049 	}
1050 
1051 	/*
1052 	 * do it
1053 	 */
1054 	dtrunc(p, d);
1055 	memset(d, 0, sizeof(Dentry));
1056 	settag(p, Tdir, QPNONE);
1057 
1058 out:
1059 	if(p1)
1060 		putbuf(p1);
1061 	if(p)
1062 		putbuf(p);
1063 	return err;
1064 }
1065 
1066 void
f_remove(Chan * cp,Oldfcall * in,Oldfcall * ou)1067 f_remove(Chan *cp, Oldfcall *in, Oldfcall *ou)
1068 {
1069 	File *f;
1070 
1071 	if(CHAT(cp)) {
1072 		print("c_remove %d\n", cp->chan);
1073 		print("	fid = %d\n", in->fid);
1074 	}
1075 
1076 	f = filep(cp, in->fid, 0);
1077 	if(!f) {
1078 		ou->err = Efid;
1079 		goto out;
1080 	}
1081 	ou->err = doremove(f, cp==cons.chan);
1082 
1083 out:
1084 	ou->fid = in->fid;
1085 	if(f)
1086 		qunlock(f);
1087 }
1088 
1089 void
f_stat(Chan * cp,Oldfcall * in,Oldfcall * ou)1090 f_stat(Chan *cp, Oldfcall *in, Oldfcall *ou)
1091 {
1092 	Iobuf *p;
1093 	Dentry *d;
1094 	File *f;
1095 
1096 	if(CHAT(cp)) {
1097 		print("c_stat %d\n", cp->chan);
1098 		print("	fid = %d\n", in->fid);
1099 	}
1100 
1101 	p = 0;
1102 	memset(ou->stat, 0, sizeof(ou->stat));
1103 	f = filep(cp, in->fid, 0);
1104 	if(!f) {
1105 		ou->err = Efid;
1106 		goto out;
1107 	}
1108 	p = getbuf(f->fs->dev, f->addr, Bread);
1109 	d = getdir(p, f->slot);
1110 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1111 		ou->err = Ealloc;
1112 		goto out;
1113 	}
1114 	if(ou->err = mkqidcmp(&f->qid, d))
1115 		goto out;
1116 	if(d->qid.path == QPROOT)	/* stat of root gives time */
1117 		d->atime = time(0);
1118 	if(convD2M9p1(d, ou->stat) != DIRREC)
1119 		print("stat convD2M\n");
1120 
1121 out:
1122 	if(p)
1123 		putbuf(p);
1124 	if(f)
1125 		qunlock(f);
1126 	ou->fid = in->fid;
1127 }
1128 
1129 void
f_wstat(Chan * cp,Oldfcall * in,Oldfcall * ou)1130 f_wstat(Chan *cp, Oldfcall *in, Oldfcall *ou)
1131 {
1132 	Iobuf *p, *p1;
1133 	Dentry *d, *d1, xd;
1134 	File *f;
1135 	int slot;
1136 	long addr;
1137 
1138 	if(CHAT(cp)) {
1139 		print("c_wstat %d\n", cp->chan);
1140 		print("	fid = %d\n", in->fid);
1141 	}
1142 
1143 	p = 0;
1144 	p1 = 0;
1145 	d1 = 0;
1146 	f = filep(cp, in->fid, 0);
1147 	if(!f) {
1148 		ou->err = Efid;
1149 		goto out;
1150 	}
1151 	if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) {
1152 		ou->err = Eronly;
1153 		goto out;
1154 	}
1155 
1156 	/*
1157 	 * first get parent
1158 	 */
1159 	if(f->wpath) {
1160 		p1 = getbuf(f->fs->dev, f->wpath->addr, Bread);
1161 		d1 = getdir(p1, f->wpath->slot);
1162 		if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1163 			ou->err = Ephase;
1164 			goto out;
1165 		}
1166 	}
1167 
1168 	p = getbuf(f->fs->dev, f->addr, Bread);
1169 	d = getdir(p, f->slot);
1170 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1171 		ou->err = Ealloc;
1172 		goto out;
1173 	}
1174 	if(ou->err = mkqidcmp(&f->qid, d))
1175 		goto out;
1176 
1177 	convM2D9p1(in->stat, &xd);
1178 	if(CHAT(cp)) {
1179 		print("	d.name = %s\n", xd.name);
1180 		print("	d.uid  = %d\n", xd.uid);
1181 		print("	d.gid  = %d\n", xd.gid);
1182 		print("	d.mode = %.4x\n", xd.mode);
1183 	}
1184 
1185 	/*
1186 	 * if chown,
1187 	 * must be god
1188 	 */
1189 	while(xd.uid != d->uid) {
1190 		if(wstatallow)			/* set to allow chown during boot */
1191 			break;
1192 		ou->err = Enotu;
1193 		goto out;
1194 	}
1195 
1196 	/*
1197 	 * if chgroup,
1198 	 * must be either
1199 	 *	a) owner and in new group
1200 	 *	b) leader of both groups
1201 	 */
1202 	while(xd.gid != d->gid) {
1203 		if(wstatallow || writeallow)		/* set to allow chgrp during boot */
1204 			break;
1205 		if(d->uid == f->uid && ingroup(f->uid, xd.gid))
1206 			break;
1207 		if(leadgroup(f->uid, xd.gid))
1208 			if(leadgroup(f->uid, d->gid))
1209 				break;
1210 		ou->err = Enotg;
1211 		goto out;
1212 	}
1213 
1214 	/*
1215 	 * if rename,
1216 	 * must have write permission in parent
1217 	 */
1218 	if(xd.name[0] == 0)
1219 		strncpy(xd.name, d->name, sizeof(xd.name));
1220 	while(strncmp(d->name, xd.name, sizeof(d->name)) != 0) {
1221 		if(checkname(xd.name)) {
1222 			ou->err = Ename;
1223 			goto out;
1224 		}
1225 
1226 		if(strcmp(xd.name, ".") == 0 || strcmp(xd.name, "..") == 0) {
1227 			ou->err = Ename;
1228 			goto out;
1229 		}
1230 
1231 		/*
1232 		 * drop entry to prevent lock, then
1233 		 * check that destination name is unique,
1234 		 */
1235 		putbuf(p);
1236 		for(addr=0;; addr++) {
1237 			p = dnodebuf(p1, d1, addr, 0);
1238 			if(!p)
1239 				break;
1240 			if(checktag(p, Tdir, d1->qid.path)) {
1241 				putbuf(p);
1242 				continue;
1243 			}
1244 			for(slot=0; slot<DIRPERBUF; slot++) {
1245 				d = getdir(p, slot);
1246 				if(!(d->mode & DALLOC))
1247 					continue;
1248 				if(!strncmp(xd.name, d->name, sizeof(xd.name))) {
1249 					ou->err = Eexist;
1250 					goto out;
1251 				}
1252 			}
1253 			putbuf(p);
1254 		}
1255 
1256 		/*
1257 		 * reacquire entry
1258 		 */
1259 		p = getbuf(f->fs->dev, f->addr, Bread);
1260 		d = getdir(p, f->slot);
1261 		if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1262 			ou->err = Ephase;
1263 			goto out;
1264 		}
1265 
1266 		if(wstatallow || writeallow) /* set to allow rename during boot */
1267 			break;
1268 		if(!d1 || iaccess(f, d1, DWRITE)) {
1269 			ou->err = Eaccess;
1270 			goto out;
1271 		}
1272 		break;
1273 	}
1274 
1275 	/*
1276 	 * if mode/time, either
1277 	 *	a) owner
1278 	 *	b) leader of either group
1279 	 */
1280 	while(d->mtime != xd.mtime ||
1281 	     ((d->mode^xd.mode) & (DAPND|DLOCK|0777))) {
1282 		if(wstatallow)			/* set to allow chmod during boot */
1283 			break;
1284 		if(d->uid == f->uid)
1285 			break;
1286 		if(leadgroup(f->uid, xd.gid))
1287 			break;
1288 		if(leadgroup(f->uid, d->gid))
1289 			break;
1290 		ou->err = Enotu;
1291 		goto out;
1292 	}
1293 	d->mtime = xd.mtime;
1294 	d->uid = xd.uid;
1295 	d->gid = xd.gid;
1296 	d->mode = (xd.mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR));
1297 
1298 	strncpy(d->name, xd.name, sizeof(d->name));
1299 	if(wstatallow) {
1300 		p->flags |= Bmod;
1301 		if(xd.atime)
1302 			d->atime = xd.atime;
1303 		if(xd.mtime)
1304 			d->mtime = xd.mtime;
1305 	} else
1306 		accessdir(p, d, FWSTAT);
1307 
1308 out:
1309 	if(p)
1310 		putbuf(p);
1311 	if(p1)
1312 		putbuf(p1);
1313 	if(f)
1314 		qunlock(f);
1315 	ou->fid = in->fid;
1316 }
1317 
1318 void
1319 (*call9p1[MAXSYSCALL])(Chan*, Oldfcall*, Oldfcall*) =
1320 {
1321 	[Tnop9p1]		f_nop,
1322 	[Tosession9p1]	f_session,
1323 	[Tsession9p1]	f_session,
1324 	[Tflush9p1]	f_flush,
1325 	[Toattach9p1]	f_attach,
1326 	[Tattach9p1]	f_attach,
1327 	[Tclone9p1]	f_clone,
1328 	[Twalk9p1]		f_walk,
1329 	[Topen9p1]		f_open,
1330 	[Tcreate9p1]	f_create,
1331 	[Tread9p1]		f_read,
1332 	[Twrite9p1]	f_write,
1333 	[Tclunk9p1]	f_clunk,
1334 	[Tremove9p1]	f_remove,
1335 	[Tstat9p1]		f_stat,
1336 	[Twstat9p1]	f_wstat,
1337 	[Tclwalk9p1]	f_clwalk,
1338 };
1339 
1340 static void
send(Chan * c,uchar * buf,int n)1341 send(Chan *c, uchar *buf, int n)
1342 {
1343 	int fd, m;
1344 
1345 	fd = c->chan;
1346 	m = write(fd, buf, n);
1347 	if(m == n)
1348 		return;
1349 	panic("write failed");
1350 }
1351 
1352 void
error9p1(Chan * c,uchar * buf)1353 error9p1(Chan *c, uchar *buf)
1354 {
1355 	buf[0] = Rnop9p1;
1356 	buf[1] = ~0;
1357 	buf[2] = ~0;
1358 
1359 	send(c, buf, 3);
1360 }
1361 
1362 void
serve9p1(Chan * chan,uchar * ib,int nib)1363 serve9p1(Chan *chan, uchar *ib, int nib)
1364 {
1365 	int n, t;
1366 	uchar inbuf[MAXMSG+MAXDAT], outbuf[MAXMSG+MAXDAT];
1367 	Oldfcall fi, fo;
1368 
1369 	for(;;){
1370 		if(nib){
1371 			memmove(inbuf, ib, nib);
1372 			n = nib;
1373 			nib = 0;
1374 		}else
1375 			n = read(chan->chan, inbuf, sizeof inbuf);
1376 		if(chat)
1377 			print("read msg %d\n", n);
1378 		if(n == 0 && (chan == cons.srvchan || chan == cons.chan))
1379 			continue;
1380 		if(n <= 0)
1381 			return;
1382 		if(convM2S9p1(inbuf, &fi, n) != n){
1383 			error9p1(chan, outbuf);
1384 			continue;
1385 		}
1386 
1387 		t = fi.type;
1388 		if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) {
1389 			print("9p1: bad message type\n");
1390 			error9p1(chan, outbuf);
1391 			continue;
1392 		}
1393 
1394 		if(CHAT(chan))
1395 			print("9p1: fi %O\n", &fi);
1396 
1397 		/*
1398 		 * set up reply message
1399 		 */
1400 		fo.err = 0;
1401 		if(t == Tread9p1)
1402 			fo.data = (char*)outbuf + 8;
1403 
1404 		/*
1405 		 * call the file system
1406 		 */
1407 		cons.work.count++;
1408 		cons.rate.count += n;
1409 
1410 		/*
1411 		 * call the file system
1412 		 */
1413 		rlock(&mainlock);
1414 		rlock(&chan->reflock);
1415 
1416 		(*call9p1[t])(chan, &fi, &fo);
1417 
1418 		runlock(&chan->reflock);
1419 		runlock(&mainlock);
1420 
1421 		fo.type = t+1;
1422 		fo.tag = fi.tag;
1423 
1424 		if(chat)
1425 			print("9p1: fo %O\n", &fo);
1426 
1427 		if(fo.err) {
1428 			strcpy(fo.ename, errstring[fo.err]);
1429 			if(CHAT(cp))
1430 				print("	error: %s\n", fo.ename);
1431 			fo.type = Terror9p1+1;
1432 		}
1433 
1434 		n = convS2M9p1(&fo, outbuf);
1435 		if(n == 0) {
1436 			print("9p1: bad S2M conversion\n");
1437 			error9p1(chan, outbuf);
1438 			continue;
1439 		}
1440 
1441 		cons.rate.count += n;
1442 		send(chan, outbuf, n);
1443 	}
1444 }
1445