1 #include "all.h"
2 #include "9p1.h"
3
4 extern Nvrsafe nvr;
5
6 typedef struct {
7 uchar chal[CHALLEN]; /* locally generated challenge */
8 uchar rchal[CHALLEN]; /* remotely generated challenge */
9 Lock idlock;
10 ulong idoffset; /* offset of id vector */
11 ulong idvec; /* vector of acceptable id's */
12 } Authinfo;
13
14 static void
f_nop(Chan * cp,Fcall *,Fcall *)15 f_nop(Chan *cp, Fcall*, Fcall*)
16 {
17 if(CHAT(cp))
18 print("c_nop %d\n", cp->chan);
19 }
20
21 static void
f_flush(Chan * cp,Fcall *,Fcall *)22 f_flush(Chan *cp, Fcall*, Fcall*)
23 {
24 if(CHAT(cp))
25 print("c_flush %d\n", cp->chan);
26 runlock(&cp->reflock);
27 wlock(&cp->reflock);
28 wunlock(&cp->reflock);
29 rlock(&cp->reflock);
30 }
31
32 /*
33 * create a challenge for a fid space
34 */
35 static void
mkchallenge(Authinfo * aip)36 mkchallenge(Authinfo *aip)
37 {
38 int i;
39
40 srand((uintptr)aip + time(nil));
41 for(i = 0; i < CHALLEN; i++)
42 aip->chal[i] = nrand(256);
43
44 aip->idoffset = 0;
45 aip->idvec = 0;
46 }
47
48 static void
f_session(Chan * cp,Fcall * in,Fcall * ou)49 f_session(Chan *cp, Fcall *in, Fcall *ou)
50 {
51 Authinfo *aip;
52
53 aip = (Authinfo*)cp->authinfo;
54
55 if(CHAT(cp))
56 print("c_session %d\n", cp->chan);
57 memmove(aip->rchal, in->chal, sizeof(aip->rchal));
58 mkchallenge(aip);
59 memmove(ou->chal, aip->chal, sizeof(ou->chal));
60 if(noauth || wstatallow)
61 memset(ou->authid, 0, sizeof(ou->authid));
62 else
63 memmove(ou->authid, nvr.authid, sizeof(ou->authid));
64
65 sprint(ou->authdom, "%s.%s", service, nvr.authdom);
66 fileinit(cp);
67 }
68
69 /*
70 * match a challenge from an attach
71 */
72 static int
authorize(Chan * cp,Fcall * in,Fcall * ou)73 authorize(Chan *cp, Fcall *in, Fcall *ou)
74 {
75 Ticket t;
76 Authenticator a;
77 int x;
78 ulong bit;
79 Authinfo *aip;
80
81 if(noauth || wstatallow) /* set to allow entry during boot */
82 return 1;
83
84 if(strcmp(in->uname, "none") == 0)
85 return 1;
86
87 if(in->type == Toattach)
88 return 0;
89
90 /* decrypt and unpack ticket */
91 convM2T9p1(in->ticket, &t, nvr.machkey);
92 if(t.num != AuthTs){
93 print("9p1: bad AuthTs num\n");
94 return 0;
95 }
96
97 /* decrypt and unpack authenticator */
98 convM2A9p1(in->auth, &a, t.key);
99 if(a.num != AuthAc){
100 print("9p1: bad AuthAc num\n");
101 return 0;
102 }
103
104 /* challenges must match */
105 aip = (Authinfo*)cp->authinfo;
106 if(memcmp(a.chal, aip->chal, sizeof(a.chal)) != 0){
107 print("9p1: bad challenge\n");
108 return 0;
109 }
110
111 /*
112 * the id must be in a valid range. the range is specified by a
113 * lower bound (idoffset) and a bit vector (idvec) where a
114 * bit set to 1 means unusable
115 */
116 lock(&aip->idlock);
117 x = a.id - aip->idoffset;
118 bit = 1<<x;
119 if(x < 0 || x > 31 || (bit&aip->idvec)){
120 unlock(&aip->idlock);
121 print("9p1: id out of range: idoff %ld idvec %lux id %ld\n",
122 aip->idoffset, aip->idvec, a.id);
123 return 0;
124 }
125 aip->idvec |= bit;
126
127 /* normalize the vector */
128 while(aip->idvec&0xffff0001){
129 aip->idvec >>= 1;
130 aip->idoffset++;
131 }
132 unlock(&aip->idlock);
133
134 /* ticket name and attach name must match */
135 if(memcmp(in->uname, t.cuid, sizeof(in->uname)) != 0){
136 print("9p1: names don't match\n");
137 return 0;
138 }
139
140 /* copy translated name into input record */
141 memmove(in->uname, t.suid, sizeof(in->uname));
142
143 /* craft a reply */
144 a.num = AuthAs;
145 memmove(a.chal, aip->rchal, CHALLEN);
146 convA2M9p1(&a, ou->rauth, t.key);
147
148 return 1;
149 }
150
151 /*
152 * buggery to give false qid for
153 * the top 2 levels of the dump fs
154 */
155 void
mkqid(Qid * qid,Dentry * d,int buggery)156 mkqid(Qid* qid, Dentry *d, int buggery)
157 {
158 int c;
159
160 if(buggery && d->qid.path == (QPDIR|QPROOT)){
161 c = d->name[0];
162 if(isascii(c) && isdigit(c)){
163 qid->path = 3;
164 qid->vers = d->qid.version;
165 qid->type = QTDIR;
166
167 c = (c-'0')*10 + (d->name[1]-'0');
168 if(c >= 1 && c <= 12)
169 qid->path = 4;
170 return;
171 }
172 }
173
174 mkqid9p2(qid, &d->qid, d->mode);
175 }
176
177 int
mkqidcmp(Qid * qid,Dentry * d)178 mkqidcmp(Qid* qid, Dentry *d)
179 {
180 Qid tmp;
181
182 mkqid(&tmp, d, 1);
183 if(qid->path == tmp.path && qid->type == tmp.type)
184 return 0;
185 return Eqid;
186 }
187
188 static void
f_attach(Chan * cp,Fcall * in,Fcall * ou)189 f_attach(Chan *cp, Fcall *in, Fcall *ou)
190 {
191 Iobuf *p;
192 Dentry *d;
193 File *f;
194 int u;
195 Filsys *fs;
196 Off raddr;
197
198 if(CHAT(cp)) {
199 print("c_attach %d\n", cp->chan);
200 print("\tfid = %d\n", in->fid);
201 print("\tuid = %s\n", in->uname);
202 print("\targ = %s\n", in->aname);
203 }
204
205 ou->qid = QID9P1(0,0);
206 ou->fid = in->fid;
207 if(!in->aname[0]) /* default */
208 strncpy(in->aname, "main", sizeof(in->aname));
209 p = 0;
210 f = filep(cp, in->fid, 1);
211 if(!f) {
212 ou->err = Efid;
213 goto out;
214 }
215
216 u = -1;
217 if(cp != cons.chan) {
218 if(noattach && strcmp(in->uname, "none")) {
219 ou->err = Enoattach;
220 goto out;
221 }
222 if(authorize(cp, in, ou) == 0 || strcmp(in->uname, "adm") == 0) {
223 ou->err = Eauth;
224 goto out;
225 }
226 u = strtouid(in->uname);
227 if(u < 0) {
228 ou->err = Ebadu;
229 goto out;
230 }
231 }
232 f->uid = u;
233
234 fs = fsstr(in->aname);
235 if(fs == 0) {
236 ou->err = Ebadspc;
237 goto out;
238 }
239 raddr = getraddr(fs->dev);
240 p = getbuf(fs->dev, raddr, Brd);
241 d = getdir(p, 0);
242 if(!d || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)) {
243 ou->err = Ealloc;
244 goto out;
245 }
246 if (iaccess(f, d, DEXEC) ||
247 f->uid == 0 && fs->dev->type == Devro) {
248 /*
249 * 'none' not allowed on dump
250 */
251 ou->err = Eaccess;
252 goto out;
253 }
254 accessdir(p, d, FREAD, f->uid);
255 mkqid(&f->qid, d, 1);
256 f->fs = fs;
257 f->addr = raddr;
258 f->slot = 0;
259 f->open = 0;
260 freewp(f->wpath);
261 f->wpath = 0;
262
263 mkqid9p1(&ou->qid, &f->qid);
264
265 strncpy(cp->whoname, in->uname, sizeof(cp->whoname));
266 cp->whotime = time(nil);
267 if(cons.flags & attachflag)
268 print("9p1: attach %s %T to \"%s\" C%d\n",
269 cp->whoname, cp->whotime, fs->name, cp->chan);
270
271 out:
272 if((cons.flags & attachflag) && ou->err)
273 print("9p1: attach %s %T SUCK EGGS --- %s\n",
274 in->uname, time(nil), errstr9p[ou->err]);
275 if(p)
276 putbuf(p);
277 if(f) {
278 qunlock(f);
279 if(ou->err)
280 freefp(f);
281 }
282 }
283
284 static void
f_clone(Chan * cp,Fcall * in,Fcall * ou)285 f_clone(Chan *cp, Fcall *in, Fcall *ou)
286 {
287 File *f1, *f2;
288 Wpath *p;
289 int fid, fid1;
290
291 if(CHAT(cp)) {
292 print("c_clone %d\n", cp->chan);
293 print("\told fid = %d\n", in->fid);
294 print("\tnew fid = %d\n", in->newfid);
295 }
296
297 fid = in->fid;
298 fid1 = in->newfid;
299
300 f1 = 0;
301 f2 = 0;
302 if(fid < fid1) {
303 f1 = filep(cp, fid, 0);
304 f2 = filep(cp, fid1, 1);
305 } else
306 if(fid1 < fid) {
307 f2 = filep(cp, fid1, 1);
308 f1 = filep(cp, fid, 0);
309 }
310 if(!f1 || !f2) {
311 ou->err = Efid;
312 goto out;
313 }
314
315
316 f2->fs = f1->fs;
317 f2->addr = f1->addr;
318 f2->open = f1->open & ~FREMOV;
319 f2->uid = f1->uid;
320 f2->slot = f1->slot;
321 f2->qid = f1->qid;
322
323 freewp(f2->wpath);
324 lock(&wpathlock);
325 f2->wpath = f1->wpath;
326 for(p = f2->wpath; p; p = p->up)
327 p->refs++;
328 unlock(&wpathlock);
329
330 out:
331 ou->fid = fid;
332 if(f1)
333 qunlock(f1);
334 if(f2) {
335 qunlock(f2);
336 if(ou->err)
337 freefp(f2);
338 }
339 }
340
341 static void
f_walk(Chan * cp,Fcall * in,Fcall * ou)342 f_walk(Chan *cp, Fcall *in, Fcall *ou)
343 {
344 Iobuf *p, *p1;
345 Dentry *d, *d1;
346 File *f;
347 Wpath *w;
348 int slot;
349 Off addr, qpath;
350
351 if(CHAT(cp)) {
352 print("c_walk %d\n", cp->chan);
353 print("\tfid = %d\n", in->fid);
354 print("\tname = %s\n", in->name);
355 }
356
357 ou->fid = in->fid;
358 ou->qid = QID9P1(0,0);
359 p = 0;
360 f = filep(cp, in->fid, 0);
361 if(!f) {
362 ou->err = Efid;
363 goto out;
364 }
365 p = getbuf(f->fs->dev, f->addr, Brd);
366 d = getdir(p, f->slot);
367 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
368 ou->err = Ealloc;
369 goto out;
370 }
371 if(!(d->mode & DDIR)) {
372 ou->err = Edir1;
373 goto out;
374 }
375 if(ou->err = mkqidcmp(&f->qid, d))
376 goto out;
377 if(cp != cons.chan && iaccess(f, d, DEXEC)) {
378 ou->err = Eaccess;
379 goto out;
380 }
381 accessdir(p, d, FREAD, f->uid);
382 if(strcmp(in->name, ".") == 0)
383 goto setdot;
384 if(strcmp(in->name, "..") == 0) {
385 if(f->wpath == 0)
386 goto setdot;
387 putbuf(p);
388 p = 0;
389 addr = f->wpath->addr;
390 slot = f->wpath->slot;
391 p1 = getbuf(f->fs->dev, addr, Brd);
392 d1 = getdir(p1, slot);
393 if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
394 if(p1)
395 putbuf(p1);
396 ou->err = Ephase;
397 goto out;
398 }
399 lock(&wpathlock);
400 f->wpath->refs--;
401 f->wpath = f->wpath->up;
402 unlock(&wpathlock);
403 goto found;
404 }
405 for(addr=0;; addr++) {
406 if(p == 0) {
407 p = getbuf(f->fs->dev, f->addr, Brd);
408 d = getdir(p, f->slot);
409 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
410 ou->err = Ealloc;
411 goto out;
412 }
413 }
414 qpath = d->qid.path;
415 p1 = dnodebuf1(p, d, addr, 0, f->uid);
416 p = 0;
417 if(!p1 || checktag(p1, Tdir, qpath) ) {
418 if(p1)
419 putbuf(p1);
420 ou->err = Eentry;
421 goto out;
422 }
423 for(slot=0; slot<DIRPERBUF; slot++) {
424 d1 = getdir(p1, slot);
425 if(!(d1->mode & DALLOC))
426 continue;
427 if(strncmp(in->name, d1->name, sizeof(in->name)) != 0)
428 continue;
429 /*
430 * update walk path
431 */
432 w = newwp();
433 if(!w) {
434 ou->err = Ewalk;
435 putbuf(p1);
436 goto out;
437 }
438 w->addr = f->addr;
439 w->slot = f->slot;
440 w->up = f->wpath;
441 f->wpath = w;
442 slot += DIRPERBUF*addr;
443 goto found;
444 }
445 putbuf(p1);
446 }
447
448 found:
449 f->addr = p1->addr;
450 mkqid(&f->qid, d1, 1);
451 putbuf(p1);
452 f->slot = slot;
453
454 setdot:
455 mkqid9p1(&ou->qid, &f->qid);
456 f->open = 0;
457
458 out:
459 if(p)
460 putbuf(p);
461 if(f)
462 qunlock(f);
463 }
464
465 static void
f_open(Chan * cp,Fcall * in,Fcall * ou)466 f_open(Chan *cp, Fcall *in, Fcall *ou)
467 {
468 Iobuf *p;
469 Dentry *d;
470 File *f;
471 Tlock *t;
472 Qid qid;
473 int ro, fmod, wok;
474
475 if(CHAT(cp)) {
476 print("c_open %d\n", cp->chan);
477 print("\tfid = %d\n", in->fid);
478 print("\tmode = %o\n", in->mode);
479 }
480
481 wok = 0;
482 if(cp == cons.chan || writeallow)
483 wok = 1;
484
485 p = 0;
486 f = filep(cp, in->fid, 0);
487 if(!f) {
488 ou->err = Efid;
489 goto out;
490 }
491
492 /*
493 * if remove on close, check access here
494 */
495 ro = f->fs->dev->type == Devro;
496 if(in->mode & ORCLOSE) {
497 if(ro) {
498 ou->err = Eronly;
499 goto out;
500 }
501 /*
502 * check on parent directory of file to be deleted
503 */
504 if(f->wpath == 0 || f->wpath->addr == f->addr) {
505 ou->err = Ephase;
506 goto out;
507 }
508 p = getbuf(f->fs->dev, f->wpath->addr, Brd);
509 d = getdir(p, f->wpath->slot);
510 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
511 ou->err = Ephase;
512 goto out;
513 }
514 if(iaccess(f, d, DWRITE)) {
515 ou->err = Eaccess;
516 goto out;
517 }
518 putbuf(p);
519 }
520 p = getbuf(f->fs->dev, f->addr, Brd);
521 d = getdir(p, f->slot);
522 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
523 ou->err = Ealloc;
524 goto out;
525 }
526 if(ou->err = mkqidcmp(&f->qid, d))
527 goto out;
528 mkqid(&qid, d, 1);
529 switch(in->mode & 7) {
530
531 case OREAD:
532 if(iaccess(f, d, DREAD) && !wok)
533 goto badaccess;
534 fmod = FREAD;
535 break;
536
537 case OWRITE:
538 if((d->mode & DDIR) ||
539 (iaccess(f, d, DWRITE) && !wok))
540 goto badaccess;
541 if(ro) {
542 ou->err = Eronly;
543 goto out;
544 }
545 fmod = FWRITE;
546 break;
547
548 case ORDWR:
549 if((d->mode & DDIR) ||
550 (iaccess(f, d, DREAD) && !wok) ||
551 (iaccess(f, d, DWRITE) && !wok))
552 goto badaccess;
553 if(ro) {
554 ou->err = Eronly;
555 goto out;
556 }
557 fmod = FREAD+FWRITE;
558 break;
559
560 case OEXEC:
561 if((d->mode & DDIR) ||
562 (iaccess(f, d, DEXEC) && !wok))
563 goto badaccess;
564 fmod = FREAD;
565 break;
566
567 default:
568 ou->err = Emode;
569 goto out;
570 }
571 if(in->mode & OTRUNC) {
572 if((d->mode & DDIR) ||
573 (iaccess(f, d, DWRITE) && !wok))
574 goto badaccess;
575 if(ro) {
576 ou->err = Eronly;
577 goto out;
578 }
579 }
580 t = 0;
581 if(d->mode & DLOCK) {
582 t = tlocked(p, d);
583 if(t == nil) {
584 ou->err = Elocked;
585 goto out;
586 }
587 }
588 if(in->mode & ORCLOSE)
589 fmod |= FREMOV;
590 f->open = fmod;
591 if(in->mode & OTRUNC)
592 if(!(d->mode & DAPND)) {
593 dtrunc(p, d, f->uid);
594 qid.vers = d->qid.version;
595 }
596 f->tlock = t;
597 if(t)
598 t->file = f;
599 f->lastra = 1;
600 mkqid9p1(&ou->qid, &qid);
601 goto out;
602
603 badaccess:
604 ou->err = Eaccess;
605 f->open = 0;
606
607 out:
608 if(p)
609 putbuf(p);
610 if(f)
611 qunlock(f);
612 ou->fid = in->fid;
613 }
614
615 static void
f_create(Chan * cp,Fcall * in,Fcall * ou)616 f_create(Chan *cp, Fcall *in, Fcall *ou)
617 {
618 Iobuf *p, *p1;
619 Dentry *d, *d1;
620 File *f;
621 int slot, slot1, fmod, wok;
622 Off addr, addr1, path;
623 Qid qid;
624 Tlock *t;
625 Wpath *w;
626
627 if(CHAT(cp)) {
628 print("c_create %d\n", cp->chan);
629 print("\tfid = %d\n", in->fid);
630 print("\tname = %s\n", in->name);
631 print("\tperm = %lx+%lo\n", (in->perm>>28)&0xf,
632 in->perm&0777);
633 print("\tmode = %o\n", in->mode);
634 }
635
636 wok = 0;
637 if(cp == cons.chan || writeallow)
638 wok = 1;
639
640 p = 0;
641 f = filep(cp, in->fid, 0);
642 if(!f) {
643 ou->err = Efid;
644 goto out;
645 }
646 if(f->fs->dev->type == Devro) {
647 ou->err = Eronly;
648 goto out;
649 }
650
651 p = getbuf(f->fs->dev, f->addr, Brd);
652 d = getdir(p, f->slot);
653 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
654 ou->err = Ealloc;
655 goto out;
656 }
657 if(ou->err = mkqidcmp(&f->qid, d))
658 goto out;
659 if(!(d->mode & DDIR)) {
660 ou->err = Edir2;
661 goto out;
662 }
663 if(iaccess(f, d, DWRITE) && !wok) {
664 ou->err = Eaccess;
665 goto out;
666 }
667 accessdir(p, d, FREAD, f->uid);
668 if(!strncmp(in->name, ".", sizeof(in->name)) ||
669 !strncmp(in->name, "..", sizeof(in->name))) {
670 ou->err = Edot;
671 goto out;
672 }
673 if(checkname(in->name)) {
674 ou->err = Ename;
675 goto out;
676 }
677 addr1 = 0;
678 slot1 = 0; /* set */
679 for(addr=0;; addr++) {
680 p1 = dnodebuf(p, d, addr, 0, f->uid);
681 if(!p1) {
682 if(addr1)
683 break;
684 p1 = dnodebuf(p, d, addr, Tdir, f->uid);
685 }
686 if(p1 == 0) {
687 ou->err = Efull;
688 goto out;
689 }
690 if(checktag(p1, Tdir, d->qid.path)) {
691 putbuf(p1);
692 goto phase;
693 }
694 for(slot=0; slot<DIRPERBUF; slot++) {
695 d1 = getdir(p1, slot);
696 if(!(d1->mode & DALLOC)) {
697 if(!addr1) {
698 addr1 = p1->addr;
699 slot1 = slot + addr*DIRPERBUF;
700 }
701 continue;
702 }
703 if(!strncmp(in->name, d1->name, sizeof(in->name))) {
704 putbuf(p1);
705 ou->err = Eexist;
706 goto out;
707 }
708 }
709 putbuf(p1);
710 }
711 switch(in->mode & 7) {
712 case OEXEC:
713 case OREAD: /* seems only useful to make directories */
714 fmod = FREAD;
715 break;
716
717 case OWRITE:
718 fmod = FWRITE;
719 break;
720
721 case ORDWR:
722 fmod = FREAD+FWRITE;
723 break;
724
725 default:
726 ou->err = Emode;
727 goto out;
728 }
729 if(in->perm & PDIR)
730 if((in->mode & OTRUNC) || (in->perm & PAPND) || (fmod & FWRITE))
731 goto badaccess;
732 /*
733 * do it
734 */
735 path = qidpathgen(f->fs->dev);
736 p1 = getbuf(f->fs->dev, addr1, Brd|Bimm|Bmod);
737 d1 = getdir(p1, slot1);
738 if(!d1 || checktag(p1, Tdir, d->qid.path)) {
739 if(p1)
740 putbuf(p1);
741 goto phase;
742 }
743 if(d1->mode & DALLOC) {
744 putbuf(p1);
745 goto phase;
746 }
747
748 strncpy(d1->name, in->name, sizeof(in->name));
749 if(cp == cons.chan) {
750 d1->uid = cons.uid;
751 d1->gid = cons.gid;
752 } else {
753 d1->uid = f->uid;
754 d1->gid = d->gid;
755 in->perm &= d->mode | ~0666;
756 if(in->perm & PDIR)
757 in->perm &= d->mode | ~0777;
758 }
759 d1->qid.path = path;
760 d1->qid.version = 0;
761 d1->mode = DALLOC | (in->perm & 0777);
762 if(in->perm & PDIR) {
763 d1->mode |= DDIR;
764 d1->qid.path |= QPDIR;
765 }
766 if(in->perm & PAPND)
767 d1->mode |= DAPND;
768 t = 0;
769 if(in->perm & PLOCK) {
770 d1->mode |= DLOCK;
771 t = tlocked(p1, d1);
772 /* if nil, out of tlock structures */
773 }
774 accessdir(p1, d1, FWRITE, f->uid);
775 mkqid(&qid, d1, 0);
776 putbuf(p1);
777 accessdir(p, d, FWRITE, f->uid);
778
779 /*
780 * do a walk to new directory entry
781 */
782 w = newwp();
783 if(!w) {
784 ou->err = Ewalk;
785 goto out;
786 }
787 w->addr = f->addr;
788 w->slot = f->slot;
789 w->up = f->wpath;
790 f->wpath = w;
791 f->qid = qid;
792 f->tlock = t;
793 if(t)
794 t->file = f;
795 f->lastra = 1;
796 if(in->mode & ORCLOSE)
797 fmod |= FREMOV;
798 f->open = fmod;
799 f->addr = addr1;
800 f->slot = slot1;
801 mkqid9p1(&ou->qid, &qid);
802 goto out;
803
804 badaccess:
805 ou->err = Eaccess;
806 goto out;
807
808 phase:
809 ou->err = Ephase;
810
811 out:
812 if(p)
813 putbuf(p);
814 if(f)
815 qunlock(f);
816 ou->fid = in->fid;
817 }
818
819 static void
f_read(Chan * cp,Fcall * in,Fcall * ou)820 f_read(Chan *cp, Fcall *in, Fcall *ou)
821 {
822 Iobuf *p, *p1;
823 File *f;
824 Dentry *d, *d1;
825 Tlock *t;
826 Off addr, offset;
827 Timet tim;
828 int nread, count, n, o, slot;
829
830 if(CHAT(cp)) {
831 print("c_read %d\n", cp->chan);
832 print("\tfid = %d\n", in->fid);
833 print("\toffset = %lld\n", (Wideoff)in->offset);
834 print("\tcount = %ld\n", in->count);
835 }
836
837 p = 0;
838 count = in->count;
839 offset = in->offset;
840 nread = 0;
841 f = filep(cp, in->fid, 0);
842 if(!f) {
843 ou->err = Efid;
844 goto out;
845 }
846 if(!(f->open & FREAD)) {
847 ou->err = Eopen;
848 goto out;
849 }
850 if(count < 0 || count > MAXDAT) {
851 ou->err = Ecount;
852 goto out;
853 }
854 if(offset < 0) {
855 ou->err = Eoffset;
856 goto out;
857 }
858 p = getbuf(f->fs->dev, f->addr, Brd);
859 d = getdir(p, f->slot);
860 if(!d || !(d->mode & DALLOC)) {
861 ou->err = Ealloc;
862 goto out;
863 }
864 if(ou->err = mkqidcmp(&f->qid, d))
865 goto out;
866 if(t = f->tlock) {
867 tim = toytime();
868 if(t->time < tim || t->file != f) {
869 ou->err = Ebroken;
870 goto out;
871 }
872 /* renew the lock */
873 t->time = tim + TLOCK;
874 }
875 accessdir(p, d, FREAD, f->uid);
876 if(d->mode & DDIR) {
877 addr = 0;
878 goto dread;
879 }
880
881 /* XXXX terrible hack to get at raw data XXXX */
882 if(rawreadok && strncmp(d->name, "--raw--", 7) == 0) {
883 Device *dev;
884 Devsize boff, bsize;
885
886 dev = p->dev;
887 putbuf(p);
888 p = 0;
889
890 boff = number(d->name + 7, 0, 10) * 100000;
891 if(boff < 0)
892 boff = 0;
893 if(boff > devsize(dev))
894 boff = devsize(dev);
895 bsize = devsize(dev) - boff;
896
897 if(offset+count >= 100000*RBUFSIZE)
898 count = 100000*RBUFSIZE - offset;
899
900 if((offset+count)/RBUFSIZE >= bsize)
901 /* will not overflow */
902 count = bsize*RBUFSIZE - offset;
903
904 while(count > 0) {
905 addr = offset / RBUFSIZE;
906 addr += boff;
907 o = offset % RBUFSIZE;
908 n = RBUFSIZE - o;
909 if(n > count)
910 n = count;
911
912 p1 = getbuf(dev, addr, Brd);
913 if(p1) {
914 memmove(ou->data+nread, p1->iobuf+o, n);
915 putbuf(p1);
916 } else
917 memset(ou->data+nread, 0, n);
918 count -= n;
919 nread += n;
920 offset += n;
921 }
922 goto out;
923 }
924
925 if(offset+count > d->size)
926 count = d->size - offset;
927 while(count > 0) {
928 if(p == 0) {
929 p = getbuf(f->fs->dev, f->addr, Brd);
930 d = getdir(p, f->slot);
931 if(!d || !(d->mode & DALLOC)) {
932 ou->err = Ealloc;
933 goto out;
934 }
935 }
936 addr = offset / BUFSIZE;
937 f->lastra = dbufread(p, d, addr, f->lastra, f->uid);
938 o = offset % BUFSIZE;
939 n = BUFSIZE - o;
940 if(n > count)
941 n = count;
942 p1 = dnodebuf1(p, d, addr, 0, f->uid);
943 p = 0;
944 if(p1) {
945 if(checktag(p1, Tfile, QPNONE)) {
946 ou->err = Ephase;
947 putbuf(p1);
948 goto out;
949 }
950 memmove(ou->data+nread, p1->iobuf+o, n);
951 putbuf(p1);
952 } else
953 memset(ou->data+nread, 0, n);
954 count -= n;
955 nread += n;
956 offset += n;
957 }
958 goto out;
959
960 dread:
961 for (;;) {
962 if(p == 0) {
963 p = getbuf(f->fs->dev, f->addr, Brd);
964 d = getdir(p, f->slot);
965 if(!d || !(d->mode & DALLOC)) {
966 ou->err = Ealloc;
967 goto out;
968 }
969 }
970 p1 = dnodebuf1(p, d, addr, 0, f->uid);
971 p = 0;
972 if(!p1)
973 goto out;
974 if(checktag(p1, Tdir, QPNONE)) {
975 ou->err = Ephase;
976 putbuf(p1);
977 goto out;
978 }
979 n = DIRREC;
980 for(slot=0; slot<DIRPERBUF; slot++) {
981 d1 = getdir(p1, slot);
982 if(!(d1->mode & DALLOC))
983 continue;
984 if(offset >= n) {
985 offset -= n;
986 continue;
987 }
988 if(count < n) {
989 putbuf(p1);
990 goto out;
991 }
992 if(convD2M9p1(d1, ou->data+nread) != n)
993 print("9p1: dirread convD2M1990\n");
994 nread += n;
995 count -= n;
996 }
997 putbuf(p1);
998 addr++;
999 }
1000 out:
1001 count = in->count - nread;
1002 if(count > 0)
1003 memset(ou->data+nread, 0, count);
1004 if(p)
1005 putbuf(p);
1006 if(f)
1007 qunlock(f);
1008 ou->fid = in->fid;
1009 ou->count = nread;
1010 if(CHAT(cp))
1011 print("\tnread = %d\n", nread);
1012 }
1013
1014 static void
f_write(Chan * cp,Fcall * in,Fcall * ou)1015 f_write(Chan *cp, Fcall *in, Fcall *ou)
1016 {
1017 Iobuf *p, *p1;
1018 Dentry *d;
1019 File *f;
1020 Tlock *t;
1021 Off offset, addr, qpath;
1022 Timet tim;
1023 int count, nwrite, o, n;
1024
1025 if(CHAT(cp)) {
1026 print("c_write %d\n", cp->chan);
1027 print("\tfid = %d\n", in->fid);
1028 print("\toffset = %lld\n", (Wideoff)in->offset);
1029 print("\tcount = %ld\n", in->count);
1030 }
1031
1032 offset = in->offset;
1033 count = in->count;
1034 nwrite = 0;
1035 p = 0;
1036 f = filep(cp, in->fid, 0);
1037 if(!f) {
1038 ou->err = Efid;
1039 goto out;
1040 }
1041 if(!(f->open & FWRITE)) {
1042 ou->err = Eopen;
1043 goto out;
1044 }
1045 if(f->fs->dev->type == Devro) {
1046 ou->err = Eronly;
1047 goto out;
1048 }
1049 if(count < 0 || count > MAXDAT) {
1050 ou->err = Ecount;
1051 goto out;
1052 }
1053 if(offset < 0) {
1054 ou->err = Eoffset;
1055 goto out;
1056 }
1057 p = getbuf(f->fs->dev, f->addr, Brd|Bmod);
1058 d = getdir(p, f->slot);
1059 if(!d || !(d->mode & DALLOC)) {
1060 ou->err = Ealloc;
1061 goto out;
1062 }
1063 if(ou->err = mkqidcmp(&f->qid, d))
1064 goto out;
1065 if(t = f->tlock) {
1066 tim = toytime();
1067 if(t->time < tim || t->file != f) {
1068 ou->err = Ebroken;
1069 goto out;
1070 }
1071 /* renew the lock */
1072 t->time = tim + TLOCK;
1073 }
1074 accessdir(p, d, FWRITE, f->uid);
1075 if(d->mode & DAPND)
1076 offset = d->size;
1077 if(offset+count > d->size)
1078 d->size = offset+count;
1079 while(count > 0) {
1080 if(p == 0) {
1081 p = getbuf(f->fs->dev, f->addr, Brd|Bmod);
1082 d = getdir(p, f->slot);
1083 if(!d || !(d->mode & DALLOC)) {
1084 ou->err = Ealloc;
1085 goto out;
1086 }
1087 }
1088 addr = offset / BUFSIZE;
1089 o = offset % BUFSIZE;
1090 n = BUFSIZE - o;
1091 if(n > count)
1092 n = count;
1093 qpath = d->qid.path;
1094 p1 = dnodebuf1(p, d, addr, Tfile, f->uid);
1095 p = 0;
1096 if(p1 == 0) {
1097 ou->err = Efull;
1098 goto out;
1099 }
1100 if(checktag(p1, Tfile, qpath)) {
1101 putbuf(p1);
1102 ou->err = Ephase;
1103 goto out;
1104 }
1105 memmove(p1->iobuf+o, in->data+nwrite, n);
1106 p1->flags |= Bmod;
1107 putbuf(p1);
1108 count -= n;
1109 nwrite += n;
1110 offset += n;
1111 }
1112 if(CHAT(cp))
1113 print("\tnwrite = %d\n", nwrite);
1114
1115 out:
1116 if(p)
1117 putbuf(p);
1118 if(f)
1119 qunlock(f);
1120 ou->fid = in->fid;
1121 ou->count = nwrite;
1122 }
1123
1124 int
doremove(File * f,int wok)1125 doremove(File *f, int wok)
1126 {
1127 Iobuf *p, *p1;
1128 Dentry *d, *d1;
1129 Off addr;
1130 int slot, err;
1131
1132 p = 0;
1133 p1 = 0;
1134 if(f->fs->dev->type == Devro) {
1135 err = Eronly;
1136 goto out;
1137 }
1138 /*
1139 * check on parent directory of file to be deleted
1140 */
1141 if(f->wpath == 0 || f->wpath->addr == f->addr) {
1142 err = Ephase;
1143 goto out;
1144 }
1145 p1 = getbuf(f->fs->dev, f->wpath->addr, Brd);
1146 d1 = getdir(p1, f->wpath->slot);
1147 if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1148 err = Ephase;
1149 goto out;
1150 }
1151 if(iaccess(f, d1, DWRITE) && !wok) {
1152 err = Eaccess;
1153 goto out;
1154 }
1155 accessdir(p1, d1, FWRITE, f->uid);
1156 putbuf(p1);
1157 p1 = 0;
1158
1159 /*
1160 * check on file to be deleted
1161 */
1162 p = getbuf(f->fs->dev, f->addr, Brd);
1163 d = getdir(p, f->slot);
1164 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1165 err = Ealloc;
1166 goto out;
1167 }
1168 if(err = mkqidcmp(&f->qid, d))
1169 goto out;
1170
1171 /*
1172 * if deleting a directory, make sure it is empty
1173 */
1174 if((d->mode & DDIR))
1175 for(addr=0;; addr++) {
1176 p1 = dnodebuf(p, d, addr, 0, f->uid);
1177 if(!p1)
1178 break;
1179 if(checktag(p1, Tdir, d->qid.path)) {
1180 err = Ephase;
1181 goto out;
1182 }
1183 for(slot=0; slot<DIRPERBUF; slot++) {
1184 d1 = getdir(p1, slot);
1185 if(!(d1->mode & DALLOC))
1186 continue;
1187 err = Eempty;
1188 goto out;
1189 }
1190 putbuf(p1);
1191 }
1192
1193 /*
1194 * do it
1195 */
1196 dtrunc(p, d, f->uid);
1197 memset(d, 0, sizeof(Dentry));
1198 settag(p, Tdir, QPNONE);
1199
1200 out:
1201 if(p1)
1202 putbuf(p1);
1203 if(p)
1204 putbuf(p);
1205 return err;
1206 }
1207
1208 static int
doclunk(File * f,int remove,int wok)1209 doclunk(File* f, int remove, int wok)
1210 {
1211 Tlock *t;
1212 int err;
1213
1214 err = 0;
1215 if(t = f->tlock) {
1216 if(t->file == f)
1217 t->time = 0; /* free the lock */
1218 f->tlock = 0;
1219 }
1220 if(remove)
1221 err = doremove(f, wok);
1222 f->open = 0;
1223 freewp(f->wpath);
1224 freefp(f);
1225
1226 return err;
1227 }
1228
1229 static void
f_clunk(Chan * cp,Fcall * in,Fcall * ou)1230 f_clunk(Chan *cp, Fcall *in, Fcall *ou)
1231 {
1232 File *f;
1233
1234 if(CHAT(cp)) {
1235 print("c_clunk %d\n", cp->chan);
1236 print("\tfid = %d\n", in->fid);
1237 }
1238
1239 f = filep(cp, in->fid, 0);
1240 if(!f)
1241 ou->err = Efid;
1242 else {
1243 doclunk(f, f->open & FREMOV, 0);
1244 qunlock(f);
1245 }
1246 ou->fid = in->fid;
1247 }
1248
1249 static void
f_remove(Chan * cp,Fcall * in,Fcall * ou)1250 f_remove(Chan *cp, Fcall *in, Fcall *ou)
1251 {
1252 File *f;
1253
1254 if(CHAT(cp)) {
1255 print("c_remove %d\n", cp->chan);
1256 print("\tfid = %d\n", in->fid);
1257 }
1258
1259 f = filep(cp, in->fid, 0);
1260 if(!f)
1261 ou->err = Efid;
1262 else {
1263 ou->err = doclunk(f, 1, cp==cons.chan);
1264 qunlock(f);
1265 }
1266 ou->fid = in->fid;
1267 }
1268
1269 static void
f_stat(Chan * cp,Fcall * in,Fcall * ou)1270 f_stat(Chan *cp, Fcall *in, Fcall *ou)
1271 {
1272 Iobuf *p;
1273 Dentry *d;
1274 File *f;
1275
1276 if(CHAT(cp)) {
1277 print("c_stat %d\n", cp->chan);
1278 print("\tfid = %d\n", in->fid);
1279 }
1280
1281 p = 0;
1282 memset(ou->stat, 0, sizeof(ou->stat));
1283 f = filep(cp, in->fid, 0);
1284 if(!f) {
1285 ou->err = Efid;
1286 goto out;
1287 }
1288 p = getbuf(f->fs->dev, f->addr, Brd);
1289 d = getdir(p, f->slot);
1290 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1291 ou->err = Ealloc;
1292 goto out;
1293 }
1294 if(ou->err = mkqidcmp(&f->qid, d))
1295 goto out;
1296 if(d->qid.path == QPROOT) /* stat of root gives time */
1297 d->atime = time(nil);
1298 if(convD2M9p1(d, ou->stat) != DIRREC)
1299 print("9p1: stat convD2M\n");
1300
1301 out:
1302 if(p)
1303 putbuf(p);
1304 if(f)
1305 qunlock(f);
1306 ou->fid = in->fid;
1307 }
1308
1309 static void
f_wstat(Chan * cp,Fcall * in,Fcall * ou)1310 f_wstat(Chan *cp, Fcall *in, Fcall *ou)
1311 {
1312 Iobuf *p, *p1;
1313 Dentry *d, *d1, xd;
1314 File *f;
1315 int slot;
1316 Off addr;
1317
1318 if(CHAT(cp)) {
1319 print("c_wstat %d\n", cp->chan);
1320 print("\tfid = %d\n", in->fid);
1321 }
1322
1323 p = 0;
1324 p1 = 0;
1325 d1 = 0;
1326 f = filep(cp, in->fid, 0);
1327 if(!f) {
1328 ou->err = Efid;
1329 goto out;
1330 }
1331 if(f->fs->dev->type == Devro) {
1332 ou->err = Eronly;
1333 goto out;
1334 }
1335
1336 /*
1337 * first get parent
1338 */
1339 if(f->wpath) {
1340 p1 = getbuf(f->fs->dev, f->wpath->addr, Brd);
1341 d1 = getdir(p1, f->wpath->slot);
1342 if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1343 ou->err = Ephase;
1344 goto out;
1345 }
1346 }
1347
1348 p = getbuf(f->fs->dev, f->addr, Brd);
1349 d = getdir(p, f->slot);
1350 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1351 ou->err = Ealloc;
1352 goto out;
1353 }
1354 if(ou->err = mkqidcmp(&f->qid, d))
1355 goto out;
1356
1357 convM2D9p1(in->stat, &xd);
1358 if(CHAT(cp)) {
1359 print("\td.name = %s\n", xd.name);
1360 print("\td.uid = %d\n", xd.uid);
1361 print("\td.gid = %d\n", xd.gid);
1362 print("\td.mode = %o\n", xd.mode);
1363 }
1364
1365 /*
1366 * if user none,
1367 * cant do anything
1368 */
1369 if(f->uid == 0) {
1370 ou->err = Eaccess;
1371 goto out;
1372 }
1373
1374 /*
1375 * if chown,
1376 * must be god
1377 */
1378 if(xd.uid != d->uid && !wstatallow) { /* set to allow chown during boot */
1379 ou->err = Ewstatu;
1380 goto out;
1381 }
1382
1383 /*
1384 * if chgroup,
1385 * must be either
1386 * a) owner and in new group
1387 * b) leader of both groups
1388 */
1389 if (xd.gid != d->gid &&
1390 (!wstatallow && !writeallow && /* set to allow chgrp during boot */
1391 (d->uid != f->uid || !ingroup(f->uid, xd.gid)) &&
1392 (!leadgroup(f->uid, xd.gid) || !leadgroup(f->uid, d->gid)))) {
1393 ou->err = Ewstatg;
1394 goto out;
1395 }
1396
1397 /*
1398 * if rename,
1399 * must have write permission in parent
1400 */
1401 if (strncmp(d->name, xd.name, sizeof(d->name)) != 0) {
1402 if (checkname(xd.name) || !d1 ||
1403 strcmp(xd.name, ".") == 0 || strcmp(xd.name, "..") == 0) {
1404 ou->err = Ename;
1405 goto out;
1406 }
1407
1408 /*
1409 * drop entry to prevent lock, then
1410 * check that destination name is unique,
1411 */
1412 putbuf(p);
1413 for(addr=0;; addr++) {
1414 p = dnodebuf(p1, d1, addr, 0, f->uid);
1415 if(!p)
1416 break;
1417 if(checktag(p, Tdir, d1->qid.path)) {
1418 putbuf(p);
1419 continue;
1420 }
1421 for(slot=0; slot<DIRPERBUF; slot++) {
1422 d = getdir(p, slot);
1423 if(!(d->mode & DALLOC))
1424 continue;
1425 if(!strncmp(xd.name, d->name, sizeof(xd.name))) {
1426 ou->err = Eexist;
1427 goto out;
1428 }
1429 }
1430 putbuf(p);
1431 }
1432
1433 /*
1434 * reacquire entry
1435 */
1436 p = getbuf(f->fs->dev, f->addr, Brd);
1437 d = getdir(p, f->slot);
1438 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1439 ou->err = Ephase;
1440 goto out;
1441 }
1442
1443 if (!wstatallow && !writeallow && /* set to allow rename during boot */
1444 (!d1 || iaccess(f, d1, DWRITE))) {
1445 ou->err = Eaccess;
1446 goto out;
1447 }
1448 }
1449
1450 /*
1451 * if mode/time, either
1452 * a) owner
1453 * b) leader of either group
1454 */
1455 if (d->mtime != xd.mtime ||
1456 ((d->mode^xd.mode) & (DAPND|DLOCK|0777)))
1457 if (!wstatallow && /* set to allow chmod during boot */
1458 d->uid != f->uid &&
1459 !leadgroup(f->uid, xd.gid) &&
1460 !leadgroup(f->uid, d->gid)) {
1461 ou->err = Ewstatu;
1462 goto out;
1463 }
1464 d->mtime = xd.mtime;
1465 d->uid = xd.uid;
1466 d->gid = xd.gid;
1467 d->mode = (xd.mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR));
1468
1469 strncpy(d->name, xd.name, sizeof(d->name));
1470 accessdir(p, d, FREAD, f->uid);
1471
1472 out:
1473 if(p)
1474 putbuf(p);
1475 if(p1)
1476 putbuf(p1);
1477 if(f)
1478 qunlock(f);
1479 ou->fid = in->fid;
1480 }
1481
1482 static void
f_clwalk(Chan * cp,Fcall * in,Fcall * ou)1483 f_clwalk(Chan *cp, Fcall *in, Fcall *ou)
1484 {
1485 int er, fid;
1486
1487 if(CHAT(cp))
1488 print("c_clwalk macro\n");
1489
1490 f_clone(cp, in, ou); /* sets tag, fid */
1491 if(ou->err)
1492 return;
1493 fid = in->fid;
1494 in->fid = in->newfid;
1495 f_walk(cp, in, ou); /* sets tag, fid, qid */
1496 er = ou->err;
1497 if(er == Eentry) {
1498 /*
1499 * if error is "no entry"
1500 * return non error and fid
1501 */
1502 ou->err = 0;
1503 f_clunk(cp, in, ou); /* sets tag, fid */
1504 ou->err = 0;
1505 ou->fid = fid;
1506 if(CHAT(cp))
1507 print("\terror: %s\n", errstr9p[er]);
1508 } else if(er) {
1509 /*
1510 * if any other error
1511 * return an error
1512 */
1513 ou->err = 0;
1514 f_clunk(cp, in, ou); /* sets tag, fid */
1515 ou->err = er;
1516 }
1517 /*
1518 * non error
1519 * return newfid
1520 */
1521 }
1522
1523 void (*call9p1[MAXSYSCALL])(Chan*, Fcall*, Fcall*) =
1524 {
1525 [Tnop] f_nop,
1526 [Tosession] f_session,
1527 [Tsession] f_session,
1528 [Tflush] f_flush,
1529 [Toattach] f_attach,
1530 [Tattach] f_attach,
1531 [Tclone] f_clone,
1532 [Twalk] f_walk,
1533 [Topen] f_open,
1534 [Tcreate] f_create,
1535 [Tread] f_read,
1536 [Twrite] f_write,
1537 [Tclunk] f_clunk,
1538 [Tremove] f_remove,
1539 [Tstat] f_stat,
1540 [Twstat] f_wstat,
1541 [Tclwalk] f_clwalk,
1542 };
1543
1544 int
error9p1(Chan * cp,Msgbuf * mb)1545 error9p1(Chan* cp, Msgbuf* mb)
1546 {
1547 Msgbuf *mb1;
1548
1549 print("type=%d count=%d\n", mb->data[0], mb->count);
1550 print(" %.2x %.2x %.2x %.2x\n",
1551 mb->data[1]&0xff, mb->data[2]&0xff,
1552 mb->data[3]&0xff, mb->data[4]&0xff);
1553 print(" %.2x %.2x %.2x %.2x\n",
1554 mb->data[5]&0xff, mb->data[6]&0xff,
1555 mb->data[7]&0xff, mb->data[8]&0xff);
1556 print(" %.2x %.2x %.2x %.2x\n",
1557 mb->data[9]&0xff, mb->data[10]&0xff,
1558 mb->data[11]&0xff, mb->data[12]&0xff);
1559
1560 mb1 = mballoc(3, cp, Mbreply4);
1561 mb1->data[0] = Rnop; /* your nop was ok */
1562 mb1->data[1] = ~0;
1563 mb1->data[2] = ~0;
1564 mb1->count = 3;
1565 mb1->param = mb->param;
1566 fs_send(cp->reply, mb1);
1567
1568 return 1;
1569 }
1570
1571 int
serve9p1(Msgbuf * mb)1572 serve9p1(Msgbuf* mb)
1573 {
1574 int t, n;
1575 Chan *cp;
1576 Msgbuf *mb1;
1577 Fcall fi, fo;
1578
1579 assert(mb != nil);
1580 cp = mb->chan;
1581 assert(mb->data != nil);
1582 if(convM2S9p1(mb->data, &fi, mb->count) == 0){
1583 assert(cp != nil);
1584 if(cp->protocol == nil)
1585 return 0;
1586 print("9p1: bad M2S conversion\n");
1587 return error9p1(cp, mb);
1588 }
1589
1590 t = fi.type;
1591 if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) {
1592 print("9p1: bad message type\n");
1593 return error9p1(cp, mb);
1594 }
1595
1596 /*
1597 * allocate reply message
1598 */
1599 if(t == Tread) {
1600 mb1 = mballoc(MAXMSG+MAXDAT, cp, Mbreply2);
1601 fo.data = (char*)(mb1->data + 8);
1602 } else
1603 mb1 = mballoc(MAXMSG, cp, Mbreply3);
1604
1605 /*
1606 * call the file system
1607 */
1608 assert(cp != nil);
1609 fo.err = 0;
1610
1611 (*call9p1[t])(cp, &fi, &fo);
1612
1613 fo.type = t+1;
1614 fo.tag = fi.tag;
1615
1616 if(fo.err) {
1617 if(cons.flags&errorflag)
1618 print("\ttype %d: error: %s\n", t, errstr9p[fo.err]);
1619 if(CHAT(cp))
1620 print("\terror: %s\n", errstr9p[fo.err]);
1621 fo.type = Rerror;
1622 strncpy(fo.ename, errstr9p[fo.err], sizeof(fo.ename));
1623 }
1624
1625 n = convS2M9p1(&fo, mb1->data);
1626 if(n == 0) {
1627 print("9p1: bad S2M conversion\n");
1628 mbfree(mb1);
1629 return error9p1(cp, mb);
1630 }
1631 mb1->count = n;
1632 mb1->param = mb->param;
1633 fs_send(cp->reply, mb1);
1634
1635 return 1;
1636 }
1637