1 #include "all.h"
2
3 #define MSIZE (MAXDAT+128)
4
5 static void
seterror(Fcall * ou,int err)6 seterror(Fcall *ou, int err)
7 {
8
9 if(0 <= err && err < MAXERR)
10 ou->ename = errstring[err];
11 else
12 ou->ename = "unknown error";
13 }
14
15 static int
fsversion(Chan * chan,Fcall * f,Fcall * r)16 fsversion(Chan* chan, Fcall* f, Fcall* r)
17 {
18 if(f->msize < MSIZE)
19 r->msize = f->msize;
20 else
21 r->msize = MSIZE;
22 /*
23 * Should check the '.' stuff here.
24 * What happens if Tversion has already been seen?
25 */
26 if(strcmp(f->version, VERSION9P) == 0){
27 r->version = VERSION9P;
28 chan->msize = r->msize;
29 }else
30 r->version = "unknown";
31
32 fileinit(chan);
33 return 0;
34 }
35
36 char *keyspec = "proto=p9any role=server";
37
38 static int
fsauth(Chan * chan,Fcall * f,Fcall * r)39 fsauth(Chan *chan, Fcall *f, Fcall *r)
40 {
41 int err, fd;
42 char *aname;
43 File *file;
44 int afd;
45 AuthRpc *rpc;
46
47 err = 0;
48 if(chan == cons.srvchan)
49 return Eauthmsg;
50 file = filep(chan, f->afid, 1);
51 if(file == nil)
52 return Efidinuse;
53
54 /* forget any previous authentication */
55 file->cuid = 0;
56
57 if(access("/mnt/factotum", 0) < 0)
58 if((fd = open("/srv/factotum", ORDWR)) >= 0)
59 mount(fd, -1, "/mnt", MBEFORE, "");
60
61 afd = open("/mnt/factotum/rpc", ORDWR);
62 if(afd < 0){
63 err = Esystem;
64 goto out;
65 }
66 rpc = auth_allocrpc(afd);
67 if(rpc == nil){
68 close(afd);
69 err = Esystem;
70 goto out;
71 }
72 file->rpc = rpc;
73 if(auth_rpc(rpc, "start", keyspec, strlen(keyspec)) != ARok){
74 err = Esystem;
75 goto out;
76 }
77
78 aname = f->aname;
79 if(!aname[0])
80 aname = "main";
81 file->fs = fsstr(aname);
82 if(file->fs == nil){
83 err = Ebadspc;
84 goto out;
85 }
86 file->uid = strtouid(f->uname);
87 if(file->uid < 0){
88 err = Ebadu;
89 goto out;
90 }
91 file->qid.path = 0;
92 file->qid.vers = 0;
93 file->qid.type = QTAUTH;
94 r->qid = file->qid;
95
96 out:
97 if(file != nil){
98 qunlock(file);
99 if(err != 0)
100 freefp(file);
101 }
102 return err;
103 }
104
105 int
authread(File * file,uchar * data,int count)106 authread(File *file, uchar *data, int count)
107 {
108 AuthInfo *ai;
109 AuthRpc *rpc;
110 int rv;
111
112 rpc = file->rpc;
113 if(rpc == nil)
114 return -1;
115
116 rv = auth_rpc(rpc, "read", nil, 0);
117 switch(rv){
118 case ARdone:
119 ai = auth_getinfo(rpc);
120 if(ai == nil)
121 return -1;
122 if(chat)
123 print("authread identifies user as %s\n", ai->cuid);
124 file->cuid = strtouid(ai->cuid);
125 auth_freeAI(ai);
126 if(file->cuid == 0)
127 return -1;
128 if(chat)
129 print("%s is a known user\n", ai->cuid);
130 return 0;
131 case ARok:
132 if(count < rpc->narg)
133 return -1;
134 memmove(data, rpc->arg, rpc->narg);
135 return rpc->narg;
136 case ARphase:
137 return -1;
138 default:
139 return -1;
140 }
141 }
142
143 int
authwrite(File * file,uchar * data,int count)144 authwrite(File *file, uchar *data, int count)
145 {
146 int ret;
147
148 ret = auth_rpc(file->rpc, "write", data, count);
149 if(ret != ARok)
150 return -1;
151 return count;
152 }
153
154 void
mkqid9p1(Qid9p1 * qid9p1,Qid * qid)155 mkqid9p1(Qid9p1* qid9p1, Qid* qid)
156 {
157 if(qid->path & 0xFFFFFFFF00000000LL)
158 panic("mkqid9p1: path %lluX\n", qid->path);
159 qid9p1->path = qid->path & 0xFFFFFFFF;
160 if(qid->type & QTDIR)
161 qid9p1->path |= QPDIR;
162 qid9p1->version = qid->vers;
163 }
164
165 void
authfree(File * fp)166 authfree(File *fp)
167 {
168 if(fp->rpc != nil){
169 close(fp->rpc->afd);
170 free(fp->rpc);
171 fp->rpc = nil;
172 }
173 }
174
175 void
mkqid9p2(Qid * qid,Qid9p1 * qid9p1,int mode)176 mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode)
177 {
178 qid->path = (ulong)(qid9p1->path & ~QPDIR);
179 qid->vers = qid9p1->version;
180 qid->type = 0;
181 if(mode & DDIR)
182 qid->type |= QTDIR;
183 if(mode & DAPND)
184 qid->type |= QTAPPEND;
185 if(mode & DLOCK)
186 qid->type |= QTEXCL;
187 }
188
189 static int
checkattach(Chan * chan,File * afile,File * file,Filsys * fs)190 checkattach(Chan *chan, File *afile, File *file, Filsys *fs)
191 {
192 uchar buf[1];
193
194 if(chan == cons.srvchan || chan == cons.chan)
195 return 0;
196
197 /* if no afile, this had better be none */
198 if(afile == nil){
199 if(file->uid == 0){
200 if(!allownone && !chan->authed)
201 return Eauth;
202 return 0;
203 }
204 return Eauth;
205 }
206
207 /* otherwise, we'ld better have a usable cuid */
208 if(!(afile->qid.type&QTAUTH))
209 return Eauth;
210 if(afile->uid != file->uid || afile->fs != fs)
211 return Eauth;
212 if(afile->cuid <= 0){
213 if(authread(afile, buf, 0) != 0)
214 return Eauth;
215 if(afile->cuid <= 0)
216 return Eauth;
217 }
218 file->uid = afile->cuid;
219
220 /* once someone has authenticated on the channel, others can become none */
221 chan->authed = 1;
222
223 return 0;
224 }
225
226 static int
fsattach(Chan * chan,Fcall * f,Fcall * r)227 fsattach(Chan* chan, Fcall* f, Fcall* r)
228 {
229 char *aname;
230 Iobuf *p;
231 Dentry *d;
232 File *file;
233 File *afile;
234 Filsys *fs;
235 long raddr;
236 int error, u;
237
238 aname = f->aname;
239 if(!aname[0]) /* default */
240 aname = "main";
241 p = nil;
242 afile = filep(chan, f->afid, 0);
243 file = filep(chan, f->fid, 1);
244 if(file == nil){
245 error = Efidinuse;
246 goto out;
247 }
248
249 u = -1;
250 if(chan != cons.chan){
251 if(strcmp(f->uname, "adm") == 0){
252 error = Eauth;
253 goto out;
254 }
255 u = strtouid(f->uname);
256 if(u < 0){
257 error = Ebadu;
258 goto out;
259 }
260 }
261 file->uid = u;
262
263 fs = fsstr(aname);
264 if(fs == nil){
265 error = Ebadspc;
266 goto out;
267 }
268
269 if(error = checkattach(chan, afile, file, fs))
270 goto out;
271
272 raddr = getraddr(fs->dev);
273 p = getbuf(fs->dev, raddr, Bread);
274 d = getdir(p, 0);
275 if(d == nil || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)){
276 error = Ealloc;
277 goto out;
278 }
279 if(iaccess(file, d, DEXEC)){
280 error = Eaccess;
281 goto out;
282 }
283 if(file->uid == 0 && isro(fs->dev)) {
284 /*
285 * 'none' not allowed on dump
286 */
287 error = Eaccess;
288 goto out;
289 }
290 accessdir(p, d, FREAD);
291 mkqid(&file->qid, d, 1);
292 file->fs = fs;
293 file->addr = raddr;
294 file->slot = 0;
295 file->open = 0;
296 freewp(file->wpath);
297 file->wpath = 0;
298
299 r->qid = file->qid;
300
301 // if(cons.flags & attachflag)
302 // print("9p2: attach %s %T to \"%s\" C%d\n",
303 // chan->whoname, chan->whotime, fs->name, chan->chan);
304
305 out:
306 // if((cons.flags & attachflag) && error)
307 // print("9p2: attach %s %T SUCK EGGS --- %s\n",
308 // f->uname, time(), errstr[error]);
309 if(p != nil)
310 putbuf(p);
311 if(afile != nil)
312 qunlock(afile);
313 if(file != nil){
314 qunlock(file);
315 if(error)
316 freefp(file);
317 }
318
319 return error;
320 }
321
322 static int
fsflush(Chan * chan,Fcall *,Fcall *)323 fsflush(Chan* chan, Fcall*, Fcall*)
324 {
325 runlock(&chan->reflock);
326 wlock(&chan->reflock);
327 wunlock(&chan->reflock);
328 rlock(&chan->reflock);
329
330 return 0;
331 }
332
333 static void
clone(File * nfile,File * file)334 clone(File* nfile, File* file)
335 {
336 Wpath *wpath;
337
338 nfile->qid = file->qid;
339
340 lock(&wpathlock);
341 nfile->wpath = file->wpath;
342 for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up)
343 wpath->refs++;
344 unlock(&wpathlock);
345
346 nfile->fs = file->fs;
347 nfile->addr = file->addr;
348 nfile->slot = file->slot;
349 nfile->uid = file->uid;
350 nfile->cuid = 0;
351 nfile->open = file->open & ~FREMOV;
352 }
353
354 static int
walkname(File * file,char * wname,Qid * wqid)355 walkname(File* file, char* wname, Qid* wqid)
356 {
357 Wpath *w;
358 Iobuf *p, *p1;
359 Dentry *d, *d1;
360 int error, slot;
361 long addr, qpath;
362
363 p = p1 = nil;
364
365 /*
366 * File must not have been opened for I/O by an open
367 * or create message and must represent a directory.
368 */
369 if(file->open != 0){
370 error = Emode;
371 goto out;
372 }
373
374 p = getbuf(file->fs->dev, file->addr, Bread);
375 if(p == nil || checktag(p, Tdir, QPNONE)){
376 error = Edir1;
377 goto out;
378 }
379 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
380 error = Ealloc;
381 goto out;
382 }
383 if(!(d->mode & DDIR)){
384 error = Edir1;
385 goto out;
386 }
387 if(error = mkqidcmp(&file->qid, d))
388 goto out;
389
390 /*
391 * For walked elements the implied user must
392 * have permission to search the directory.
393 */
394 if(file->cp != cons.chan && iaccess(file, d, DEXEC)){
395 error = Eaccess;
396 goto out;
397 }
398 accessdir(p, d, FREAD);
399
400 if(strcmp(wname, ".") == 0){
401 setdot:
402 if(wqid != nil)
403 *wqid = file->qid;
404 goto out;
405 }
406 if(strcmp(wname, "..") == 0){
407 if(file->wpath == 0)
408 goto setdot;
409 putbuf(p);
410 p = nil;
411 addr = file->wpath->addr;
412 slot = file->wpath->slot;
413 p1 = getbuf(file->fs->dev, addr, Bread);
414 if(p1 == nil || checktag(p1, Tdir, QPNONE)){
415 error = Edir1;
416 goto out;
417 }
418 if((d1 = getdir(p1, slot)) == nil || !(d1->mode & DALLOC)){
419 error = Ephase;
420 goto out;
421 }
422 lock(&wpathlock);
423 file->wpath->refs--;
424 file->wpath = file->wpath->up;
425 unlock(&wpathlock);
426 goto found;
427 }
428
429 for(addr = 0; ; addr++){
430 if(p == nil){
431 p = getbuf(file->fs->dev, file->addr, Bread);
432 if(p == nil || checktag(p, Tdir, QPNONE)){
433 error = Ealloc;
434 goto out;
435 }
436 d = getdir(p, file->slot);
437 if(d == nil || !(d->mode & DALLOC)){
438 error = Ealloc;
439 goto out;
440 }
441 }
442 qpath = d->qid.path;
443 p1 = dnodebuf1(p, d, addr, 0);
444 p = nil;
445 if(p1 == nil || checktag(p1, Tdir, qpath)){
446 error = Eentry;
447 goto out;
448 }
449 for(slot = 0; slot < DIRPERBUF; slot++){
450 d1 = getdir(p1, slot);
451 if(!(d1->mode & DALLOC))
452 continue;
453 if(strncmp(wname, d1->name, NAMELEN) != 0)
454 continue;
455 /*
456 * update walk path
457 */
458 if((w = newwp()) == nil){
459 error = Ewalk;
460 goto out;
461 }
462 w->addr = file->addr;
463 w->slot = file->slot;
464 w->up = file->wpath;
465 file->wpath = w;
466 slot += DIRPERBUF*addr;
467 goto found;
468 }
469 putbuf(p1);
470 p1 = nil;
471 }
472
473 found:
474 file->addr = p1->addr;
475 mkqid(&file->qid, d1, 1);
476 putbuf(p1);
477 p1 = nil;
478 file->slot = slot;
479 if(wqid != nil)
480 *wqid = file->qid;
481
482 out:
483 if(p1 != nil)
484 putbuf(p1);
485 if(p != nil)
486 putbuf(p);
487
488 return error;
489 }
490
491 static int
fswalk(Chan * chan,Fcall * f,Fcall * r)492 fswalk(Chan* chan, Fcall* f, Fcall* r)
493 {
494 int error, nwname;
495 File *file, *nfile, tfile;
496
497 /*
498 * The file identified by f->fid must be valid in the
499 * current session and must not have been opened for I/O
500 * by an open or create message.
501 */
502 if((file = filep(chan, f->fid, 0)) == nil)
503 return Efid;
504 if(file->open != 0){
505 qunlock(file);
506 return Emode;
507 }
508
509 /*
510 * If newfid is not the same as fid, allocate a new file;
511 * a side effect is checking newfid is not already in use (error);
512 * if there are no names to walk this will be equivalent to a
513 * simple 'clone' operation.
514 * Otherwise, fid and newfid are the same and if there are names
515 * to walk make a copy of 'file' to be used during the walk as
516 * 'file' must only be updated on success.
517 * Finally, it's a no-op if newfid is the same as fid and f->nwname
518 * is 0.
519 */
520 r->nwqid = 0;
521 if(f->newfid != f->fid){
522 if((nfile = filep(chan, f->newfid, 1)) == nil){
523 qunlock(file);
524 return Efidinuse;
525 }
526 }
527 else if(f->nwname != 0){
528 nfile = &tfile;
529 memset(nfile, 0, sizeof(File));
530 nfile->cp = chan;
531 nfile->fid = ~0;
532 }
533 else{
534 qunlock(file);
535 return 0;
536 }
537 clone(nfile, file);
538
539 /*
540 * Should check name is not too long.
541 */
542 error = 0;
543 for(nwname = 0; nwname < f->nwname; nwname++){
544 error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]);
545 if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid))
546 break;
547 }
548
549 if(f->nwname == 0){
550 /*
551 * Newfid must be different to fid (see above)
552 * so this is a simple 'clone' operation - there's
553 * nothing to do except unlock unless there's
554 * an error.
555 */
556 if(error){
557 freewp(nfile->wpath);
558 qunlock(nfile);
559 freefp(nfile);
560 }
561 else
562 qunlock(nfile);
563 }
564 else if(r->nwqid < f->nwname){
565 /*
566 * Didn't walk all elements, 'clunk' nfile
567 * and leave 'file' alone.
568 * Clear error if some of the elements were
569 * walked OK.
570 */
571 freewp(nfile->wpath);
572 if(nfile != &tfile){
573 qunlock(nfile);
574 freefp(nfile);
575 }
576 if(r->nwqid != 0)
577 error = 0;
578 }
579 else{
580 /*
581 * Walked all elements. If newfid is the same
582 * as fid must update 'file' from the temporary
583 * copy used during the walk.
584 * Otherwise just unlock (when using tfile there's
585 * no need to unlock as it's a local).
586 */
587 if(nfile == &tfile){
588 file->qid = nfile->qid;
589 freewp(file->wpath);
590 file->wpath = nfile->wpath;
591 file->addr = nfile->addr;
592 file->slot = nfile->slot;
593 }
594 else
595 qunlock(nfile);
596 }
597 qunlock(file);
598
599 return error;
600 }
601
602 static int
fsopen(Chan * chan,Fcall * f,Fcall * r)603 fsopen(Chan* chan, Fcall* f, Fcall* r)
604 {
605 Iobuf *p;
606 Dentry *d;
607 File *file;
608 Tlock *t;
609 Qid qid;
610 int error, ro, fmod, wok;
611
612 wok = 0;
613 p = nil;
614
615 if(chan == cons.chan || writeallow)
616 wok = 1;
617
618 if((file = filep(chan, f->fid, 0)) == nil){
619 error = Efid;
620 goto out;
621 }
622
623 /*
624 * if remove on close, check access here
625 */
626 ro = isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup));
627 if(f->mode & ORCLOSE){
628 if(ro){
629 error = Eronly;
630 goto out;
631 }
632 /*
633 * check on parent directory of file to be deleted
634 */
635 if(file->wpath == 0 || file->wpath->addr == file->addr){
636 error = Ephase;
637 goto out;
638 }
639 p = getbuf(file->fs->dev, file->wpath->addr, Bread);
640 if(p == nil || checktag(p, Tdir, QPNONE)){
641 error = Ephase;
642 goto out;
643 }
644 if((d = getdir(p, file->wpath->slot)) == nil || !(d->mode & DALLOC)){
645 error = Ephase;
646 goto out;
647 }
648 if(iaccess(file, d, DWRITE)){
649 error = Eaccess;
650 goto out;
651 }
652 putbuf(p);
653 }
654 p = getbuf(file->fs->dev, file->addr, Bread);
655 if(p == nil || checktag(p, Tdir, QPNONE)){
656 error = Ealloc;
657 goto out;
658 }
659 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
660 error = Ealloc;
661 goto out;
662 }
663 if(error = mkqidcmp(&file->qid, d))
664 goto out;
665 mkqid(&qid, d, 1);
666 switch(f->mode & 7){
667
668 case OREAD:
669 if(iaccess(file, d, DREAD) && !wok)
670 goto badaccess;
671 fmod = FREAD;
672 break;
673
674 case OWRITE:
675 if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
676 goto badaccess;
677 if(ro){
678 error = Eronly;
679 goto out;
680 }
681 fmod = FWRITE;
682 break;
683
684 case ORDWR:
685 if((d->mode & DDIR)
686 || (iaccess(file, d, DREAD) && !wok)
687 || (iaccess(file, d, DWRITE) && !wok))
688 goto badaccess;
689 if(ro){
690 error = Eronly;
691 goto out;
692 }
693 fmod = FREAD+FWRITE;
694 break;
695
696 case OEXEC:
697 if((d->mode & DDIR) || (iaccess(file, d, DEXEC) && !wok))
698 goto badaccess;
699 fmod = FREAD;
700 break;
701
702 default:
703 error = Emode;
704 goto out;
705 }
706 if(f->mode & OTRUNC){
707 if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
708 goto badaccess;
709 if(ro){
710 error = Eronly;
711 goto out;
712 }
713 }
714 t = 0;
715 if(d->mode & DLOCK){
716 if((t = tlocked(p, d)) == nil){
717 error = Elocked;
718 goto out;
719 }
720 }
721 if(f->mode & ORCLOSE)
722 fmod |= FREMOV;
723 file->open = fmod;
724 if((f->mode & OTRUNC) && !(d->mode & DAPND)){
725 dtrunc(p, d);
726 qid.vers = d->qid.version;
727 }
728 r->qid = qid;
729 file->tlock = t;
730 if(t != nil)
731 t->file = file;
732 file->lastra = 1;
733 goto out;
734
735 badaccess:
736 error = Eaccess;
737 file->open = 0;
738
739 out:
740 if(p != nil)
741 putbuf(p);
742 if(file != nil)
743 qunlock(file);
744
745 r->iounit = chan->msize-IOHDRSZ;
746
747 return error;
748 }
749
750 static int
dir9p2(Dir * dir,Dentry * dentry,void * strs)751 dir9p2(Dir* dir, Dentry* dentry, void* strs)
752 {
753 char *op, *p;
754
755 memset(dir, 0, sizeof(Dir));
756 mkqid(&dir->qid, dentry, 1);
757 dir->mode = (dir->qid.type<<24)|(dentry->mode & 0777);
758 dir->atime = dentry->atime;
759 dir->mtime = dentry->mtime;
760 dir->length = dentry->size;
761
762 op = p = strs;
763 dir->name = p;
764 p += sprint(p, "%s", dentry->name)+1;
765
766 dir->uid = p;
767 uidtostr(p, dentry->uid);
768 p += strlen(p)+1;
769
770 dir->gid = p;
771 uidtostr(p, dentry->gid);
772 p += strlen(p)+1;
773
774 dir->muid = p;
775 strcpy(p, "");
776 p += strlen(p)+1;
777
778 return p-op;
779 }
780
781 static int
checkname9p2(char * name)782 checkname9p2(char* name)
783 {
784 char *p;
785
786 /*
787 * Return length of string if valid, 0 if not.
788 */
789 if(name == nil)
790 return 0;
791
792 for(p = name; *p != 0; p++){
793 if((*p & 0xFF) <= 040)
794 return 0;
795 }
796
797 return p-name;
798 }
799
800 static int
fscreate(Chan * chan,Fcall * f,Fcall * r)801 fscreate(Chan* chan, Fcall* f, Fcall* r)
802 {
803 Iobuf *p, *p1;
804 Dentry *d, *d1;
805 File *file;
806 int error, slot, slot1, fmod, wok, l;
807 long addr, addr1, path;
808 Tlock *t;
809 Wpath *w;
810
811 wok = 0;
812 p = nil;
813
814 if(chan == cons.chan || writeallow)
815 wok = 1;
816
817 if((file = filep(chan, f->fid, 0)) == nil){
818 error = Efid;
819 goto out;
820 }
821 if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){
822 error = Eronly;
823 goto out;
824 }
825
826 p = getbuf(file->fs->dev, file->addr, Bread);
827 if(p == nil || checktag(p, Tdir, QPNONE)){
828 error = Ealloc;
829 goto out;
830 }
831 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
832 error = Ealloc;
833 goto out;
834 }
835 if(error = mkqidcmp(&file->qid, d))
836 goto out;
837 if(!(d->mode & DDIR)){
838 error = Edir2;
839 goto out;
840 }
841 if(iaccess(file, d, DWRITE) && !wok) {
842 error = Eaccess;
843 goto out;
844 }
845 accessdir(p, d, FREAD);
846
847 /*
848 * Check the name is valid and will fit in an old
849 * directory entry.
850 */
851 if((l = checkname9p2(f->name)) == 0){
852 error = Ename;
853 goto out;
854 }
855 if(l+1 > NAMELEN){
856 error = Etoolong;
857 goto out;
858 }
859 if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){
860 error = Edot;
861 goto out;
862 }
863
864 addr1 = 0;
865 slot1 = 0; /* set */
866 for(addr = 0; ; addr++){
867 if((p1 = dnodebuf(p, d, addr, 0)) == nil){
868 if(addr1 != 0)
869 break;
870 p1 = dnodebuf(p, d, addr, Tdir);
871 }
872 if(p1 == nil){
873 error = Efull;
874 goto out;
875 }
876 if(checktag(p1, Tdir, d->qid.path)){
877 putbuf(p1);
878 goto phase;
879 }
880 for(slot = 0; slot < DIRPERBUF; slot++){
881 d1 = getdir(p1, slot);
882 if(!(d1->mode & DALLOC)){
883 if(addr1 == 0){
884 addr1 = p1->addr;
885 slot1 = slot + addr*DIRPERBUF;
886 }
887 continue;
888 }
889 if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){
890 putbuf(p1);
891 error = Eexist;
892 goto out;
893 }
894 }
895 putbuf(p1);
896 }
897
898 switch(f->mode & 7){
899 case OEXEC:
900 case OREAD: /* seems only useful to make directories */
901 fmod = FREAD;
902 break;
903
904 case OWRITE:
905 fmod = FWRITE;
906 break;
907
908 case ORDWR:
909 fmod = FREAD+FWRITE;
910 break;
911
912 default:
913 error = Emode;
914 goto out;
915 }
916 if(f->perm & PDIR)
917 if((f->mode & OTRUNC) || (f->perm & PAPND) || (fmod & FWRITE))
918 goto badaccess;
919 /*
920 * do it
921 */
922 path = qidpathgen(&file->fs->dev);
923 if((p1 = getbuf(file->fs->dev, addr1, Bread|Bimm|Bmod)) == nil)
924 goto phase;
925 d1 = getdir(p1, slot1);
926 if(d1 == nil || checktag(p1, Tdir, d->qid.path)) {
927 putbuf(p1);
928 goto phase;
929 }
930 if(d1->mode & DALLOC){
931 putbuf(p1);
932 goto phase;
933 }
934
935 strncpy(d1->name, f->name, sizeof(d1->name));
936 if(chan == cons.chan){
937 d1->uid = cons.uid;
938 d1->gid = cons.gid;
939 }
940 else{
941 d1->uid = file->uid;
942 d1->gid = d->gid;
943 f->perm &= d->mode | ~0666;
944 if(f->perm & PDIR)
945 f->perm &= d->mode | ~0777;
946 }
947 d1->qid.path = path;
948 d1->qid.version = 0;
949 d1->mode = DALLOC | (f->perm & 0777);
950 if(f->perm & PDIR) {
951 d1->mode |= DDIR;
952 d1->qid.path |= QPDIR;
953 }
954 if(f->perm & PAPND)
955 d1->mode |= DAPND;
956 t = nil;
957 if(f->perm & PLOCK){
958 d1->mode |= DLOCK;
959 t = tlocked(p1, d1);
960 /* if nil, out of tlock structures */
961 }
962 accessdir(p1, d1, FWRITE);
963 mkqid(&r->qid, d1, 0);
964 putbuf(p1);
965 accessdir(p, d, FWRITE);
966
967 /*
968 * do a walk to new directory entry
969 */
970 if((w = newwp()) == nil){
971 error = Ewalk;
972 goto out;
973 }
974 w->addr = file->addr;
975 w->slot = file->slot;
976 w->up = file->wpath;
977 file->wpath = w;
978 file->qid = r->qid;
979 file->tlock = t;
980 if(t != nil)
981 t->file = file;
982 file->lastra = 1;
983 if(f->mode & ORCLOSE)
984 fmod |= FREMOV;
985 file->open = fmod;
986 file->addr = addr1;
987 file->slot = slot1;
988 goto out;
989
990 badaccess:
991 error = Eaccess;
992 goto out;
993
994 phase:
995 error = Ephase;
996
997 out:
998 if(p != nil)
999 putbuf(p);
1000 if(file != nil)
1001 qunlock(file);
1002
1003 r->iounit = chan->msize-IOHDRSZ;
1004
1005 return error;
1006 }
1007
1008 static int
fsread(Chan * chan,Fcall * f,Fcall * r)1009 fsread(Chan* chan, Fcall* f, Fcall* r)
1010 {
1011 uchar *data;
1012 Iobuf *p, *p1;
1013 File *file;
1014 Dentry *d, *d1;
1015 Tlock *t;
1016 long addr, offset, start, tim;
1017 int error, iounit, nread, count, n, o, slot;
1018 Dir dir;
1019 char strdata[28*10];
1020
1021 p = nil;
1022 data = (uchar*)r->data;
1023 count = f->count;
1024 offset = f->offset;
1025 nread = 0;
1026 if((file = filep(chan, f->fid, 0)) == nil){
1027 error = Efid;
1028 goto out;
1029 }
1030 if(file->qid.type & QTAUTH){
1031 nread = authread(file, data, count);
1032 if(nread < 0)
1033 error = Esystem;
1034 else
1035 error = 0;
1036 goto out;
1037 }
1038 if(!(file->open & FREAD)){
1039 error = Eopen;
1040 goto out;
1041 }
1042 iounit = chan->msize-IOHDRSZ;
1043 if(count < 0 || count > iounit){
1044 error = Ecount;
1045 goto out;
1046 }
1047 if(offset < 0){
1048 error = Eoffset;
1049 goto out;
1050 }
1051 p = getbuf(file->fs->dev, file->addr, Bread);
1052 if(p == nil || checktag(p, Tdir, QPNONE)){
1053 error = Ealloc;
1054 goto out;
1055 }
1056 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1057 error = Ealloc;
1058 goto out;
1059 }
1060 if(error = mkqidcmp(&file->qid, d))
1061 goto out;
1062 if(t = file->tlock){
1063 tim = time(0);
1064 if(t->time < tim || t->file != file){
1065 error = Ebroken;
1066 goto out;
1067 }
1068 /* renew the lock */
1069 t->time = tim + TLOCK;
1070 }
1071 accessdir(p, d, FREAD);
1072 if(d->mode & DDIR)
1073 goto dread;
1074 if(offset+count > d->size)
1075 count = d->size - offset;
1076 while(count > 0){
1077 if(p == nil){
1078 p = getbuf(file->fs->dev, file->addr, Bread);
1079 if(p == nil || checktag(p, Tdir, QPNONE)){
1080 error = Ealloc;
1081 goto out;
1082 }
1083 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1084 error = Ealloc;
1085 goto out;
1086 }
1087 }
1088 addr = offset / BUFSIZE;
1089 o = offset % BUFSIZE;
1090 n = BUFSIZE - o;
1091 if(n > count)
1092 n = count;
1093 p1 = dnodebuf1(p, d, addr, 0);
1094 p = nil;
1095 if(p1 != nil){
1096 if(checktag(p1, Tfile, QPNONE)){
1097 error = Ephase;
1098 putbuf(p1);
1099 goto out;
1100 }
1101 memmove(data+nread, p1->iobuf+o, n);
1102 putbuf(p1);
1103 }
1104 else
1105 memset(data+nread, 0, n);
1106 count -= n;
1107 nread += n;
1108 offset += n;
1109 }
1110 goto out;
1111
1112 dread:
1113 /*
1114 * Pick up where we left off last time if nothing has changed,
1115 * otherwise must scan from the beginning.
1116 */
1117 if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){
1118 addr = file->dslot/DIRPERBUF;
1119 slot = file->dslot%DIRPERBUF;
1120 start = offset;
1121 }
1122 else{
1123 addr = 0;
1124 slot = 0;
1125 start = 0;
1126 }
1127
1128 dread1:
1129 if(p == nil){
1130 /*
1131 * This is just a check to ensure the entry hasn't
1132 * gone away during the read of each directory block.
1133 */
1134 p = getbuf(file->fs->dev, file->addr, Bread);
1135 if(p == nil || checktag(p, Tdir, QPNONE)){
1136 error = Ealloc;
1137 goto out1;
1138 }
1139 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1140 error = Ealloc;
1141 goto out1;
1142 }
1143 }
1144 p1 = dnodebuf1(p, d, addr, 0);
1145 p = nil;
1146 if(p1 == nil)
1147 goto out1;
1148 if(checktag(p1, Tdir, QPNONE)){
1149 error = Ephase;
1150 putbuf(p1);
1151 goto out1;
1152 }
1153
1154 for(; slot < DIRPERBUF; slot++){
1155 d1 = getdir(p1, slot);
1156 if(!(d1->mode & DALLOC))
1157 continue;
1158 dir9p2(&dir, d1, strdata);
1159 if((n = convD2M(&dir, data+nread, iounit - nread)) <= BIT16SZ){
1160 putbuf(p1);
1161 goto out1;
1162 }
1163 start += n;
1164 if(start < offset)
1165 continue;
1166 if(count < n){
1167 putbuf(p1);
1168 goto out1;
1169 }
1170 count -= n;
1171 nread += n;
1172 offset += n;
1173 }
1174 putbuf(p1);
1175 slot = 0;
1176 addr++;
1177 goto dread1;
1178
1179 out1:
1180 if(error == 0){
1181 file->doffset = offset;
1182 file->dvers = file->qid.vers;
1183 file->dslot = slot+DIRPERBUF*addr;
1184 }
1185
1186 out:
1187 /*
1188 * Do we need this any more?
1189 count = f->count - nread;
1190 if(count > 0)
1191 memset(data+nread, 0, count);
1192 */
1193 if(p != nil)
1194 putbuf(p);
1195 if(file != nil)
1196 qunlock(file);
1197 r->count = nread;
1198 r->data = (char*)data;
1199
1200 return error;
1201 }
1202
1203 static int
fswrite(Chan * chan,Fcall * f,Fcall * r)1204 fswrite(Chan* chan, Fcall* f, Fcall* r)
1205 {
1206 Iobuf *p, *p1;
1207 Dentry *d;
1208 File *file;
1209 Tlock *t;
1210 long offset, addr, tim, qpath;
1211 int count, error, nwrite, o, n;
1212
1213 offset = f->offset;
1214 count = f->count;
1215
1216 nwrite = 0;
1217 p = nil;
1218
1219 if((file = filep(chan, f->fid, 0)) == nil){
1220 error = Efid;
1221 goto out;
1222 }
1223 if(file->qid.type & QTAUTH){
1224 nwrite = authwrite(file, (uchar*)f->data, count);
1225 if(nwrite < 0)
1226 error = Esystem;
1227 else
1228 error = 0;
1229 goto out;
1230 }
1231 if(!(file->open & FWRITE)){
1232 error = Eopen;
1233 goto out;
1234 }
1235 if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){
1236 error = Eronly;
1237 goto out;
1238 }
1239 if(count < 0 || count > chan->msize-IOHDRSZ){
1240 error = Ecount;
1241 goto out;
1242 }
1243 if(offset < 0) {
1244 error = Eoffset;
1245 goto out;
1246 }
1247 if((p = getbuf(file->fs->dev, file->addr, Bread|Bmod)) == nil){
1248 error = Ealloc;
1249 goto out;
1250 }
1251 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1252 error = Ealloc;
1253 goto out;
1254 }
1255 if(error = mkqidcmp(&file->qid, d))
1256 goto out;
1257 if(t = file->tlock) {
1258 tim = time(0);
1259 if(t->time < tim || t->file != file){
1260 error = Ebroken;
1261 goto out;
1262 }
1263 /* renew the lock */
1264 t->time = tim + TLOCK;
1265 }
1266 accessdir(p, d, FWRITE);
1267 if(d->mode & DAPND)
1268 offset = d->size;
1269 if(offset+count > d->size)
1270 d->size = offset+count;
1271 while(count > 0){
1272 if(p == nil){
1273 p = getbuf(file->fs->dev, file->addr, Bread|Bmod);
1274 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1275 error = Ealloc;
1276 goto out;
1277 }
1278 }
1279 addr = offset / BUFSIZE;
1280 o = offset % BUFSIZE;
1281 n = BUFSIZE - o;
1282 if(n > count)
1283 n = count;
1284 qpath = d->qid.path;
1285 p1 = dnodebuf1(p, d, addr, Tfile);
1286 p = nil;
1287 if(p1 == nil) {
1288 error = Efull;
1289 goto out;
1290 }
1291 if(checktag(p1, Tfile, qpath)){
1292 putbuf(p1);
1293 error = Ephase;
1294 goto out;
1295 }
1296 memmove(p1->iobuf+o, f->data+nwrite, n);
1297 p1->flags |= Bmod;
1298 putbuf(p1);
1299 count -= n;
1300 nwrite += n;
1301 offset += n;
1302 }
1303
1304 out:
1305 if(p != nil)
1306 putbuf(p);
1307 if(file != nil)
1308 qunlock(file);
1309 r->count = nwrite;
1310
1311 return error;
1312 }
1313
1314 static int
_clunk(File * file,int remove,int wok)1315 _clunk(File* file, int remove, int wok)
1316 {
1317 Tlock *t;
1318 int error;
1319
1320 error = 0;
1321 if(t = file->tlock){
1322 if(t->file == file)
1323 t->time = 0; /* free the lock */
1324 file->tlock = 0;
1325 }
1326 if(remove)
1327 error = doremove(file, wok);
1328 file->open = 0;
1329 freewp(file->wpath);
1330 freefp(file);
1331 qunlock(file);
1332
1333 return error;
1334 }
1335
1336 static int
fsclunk(Chan * chan,Fcall * f,Fcall *)1337 fsclunk(Chan* chan, Fcall* f, Fcall*)
1338 {
1339 File *file;
1340
1341 if((file = filep(chan, f->fid, 0)) == nil)
1342 return Efid;
1343
1344 _clunk(file, file->open & FREMOV, 0);
1345 return 0;
1346 }
1347
1348 static int
fsremove(Chan * chan,Fcall * f,Fcall *)1349 fsremove(Chan* chan, Fcall* f, Fcall*)
1350 {
1351 File *file;
1352
1353 if((file = filep(chan, f->fid, 0)) == nil)
1354 return Efid;
1355
1356 return _clunk(file, 1, chan == cons.chan);
1357 }
1358
1359 static int
fsstat(Chan * chan,Fcall * f,Fcall * r,uchar * data)1360 fsstat(Chan* chan, Fcall* f, Fcall* r, uchar* data)
1361 {
1362 Dir dir;
1363 Iobuf *p;
1364 Dentry *d;
1365 File *file;
1366 int error, len;
1367
1368 if((file = filep(chan, f->fid, 0)) == nil)
1369 return Efid;
1370
1371 p = getbuf(file->fs->dev, file->addr, Bread);
1372 if(p == nil || checktag(p, Tdir, QPNONE)){
1373 error = Edir1;
1374 goto out;
1375 }
1376 if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){
1377 error = Ealloc;
1378 goto out;
1379 }
1380 if(error = mkqidcmp(&file->qid, d))
1381 goto out;
1382
1383 if(d->qid.path == QPROOT) /* stat of root gives time */
1384 d->atime = time(0);
1385
1386 len = dir9p2(&dir, d, data);
1387 data += len;
1388 if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0)
1389 error = Ersc;
1390 else
1391 r->stat = data;
1392
1393 out:
1394 if(p != nil)
1395 putbuf(p);
1396 if(file != nil)
1397 qunlock(file);
1398
1399 return error;
1400 }
1401
1402 static int
fswstat(Chan * chan,Fcall * f,Fcall *,char * strs)1403 fswstat(Chan* chan, Fcall* f, Fcall*, char *strs)
1404 {
1405 Iobuf *p, *p1;
1406 Dentry *d, *d1, xd;
1407 File *file;
1408 int error, slot, uid, gid, l;
1409 long addr;
1410 Dir dir;
1411 ulong mode;
1412
1413 p = p1 = nil;
1414 d1 = nil;
1415
1416 if((file = filep(chan, f->fid, 0)) == nil){
1417 error = Efid;
1418 goto out;
1419 }
1420
1421 /*
1422 * if user none,
1423 * can't do anything
1424 * unless allow.
1425 */
1426 if(file->uid == 0 && !wstatallow){
1427 error = Eaccess;
1428 goto out;
1429 }
1430
1431 if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){
1432 error = Eronly;
1433 goto out;
1434 }
1435
1436 /*
1437 * first get parent
1438 */
1439 if(file->wpath){
1440 p1 = getbuf(file->fs->dev, file->wpath->addr, Bread);
1441 if(p1 == nil){
1442 error = Ephase;
1443 goto out;
1444 }
1445 d1 = getdir(p1, file->wpath->slot);
1446 if(d1 == nil || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)){
1447 error = Ephase;
1448 goto out;
1449 }
1450 }
1451
1452 if((p = getbuf(file->fs->dev, file->addr, Bread)) == nil){
1453 error = Ealloc;
1454 goto out;
1455 }
1456 d = getdir(p, file->slot);
1457 if(d == nil || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)){
1458 error = Ealloc;
1459 goto out;
1460 }
1461 if(error = mkqidcmp(&file->qid, d))
1462 goto out;
1463
1464 /*
1465 * Convert the message and fix up
1466 * fields not to be changed.
1467 */
1468 if(convM2D(f->stat, f->nstat, &dir, strs) == 0){
1469 print("9p2: convM2D returns 0\n");
1470 error = Econvert;
1471 goto out;
1472 }
1473 if(dir.uid == nil || strlen(dir.uid) == 0)
1474 uid = d->uid;
1475 else
1476 uid = strtouid(dir.uid);
1477 if(dir.gid == nil || strlen(dir.gid) == 0)
1478 gid = d->gid;
1479 else
1480 gid = strtouid(dir.gid);
1481 if(dir.name == nil || strlen(dir.name) == 0)
1482 dir.name = d->name;
1483 else{
1484 if((l = checkname9p2(dir.name)) == 0){
1485 error = Ename;
1486 goto out;
1487 }
1488 if(l > NAMELEN){
1489 error = Etoolong;
1490 goto out;
1491 }
1492 }
1493
1494 /*
1495 * Before doing sanity checks, find out what the
1496 * new 'mode' should be:
1497 * if 'type' and 'mode' are both defaults, take the
1498 * new mode from the old directory entry;
1499 * else if 'type' is the default, use the new mode entry;
1500 * else if 'mode' is the default, create the new mode from
1501 * 'type' or'ed with the old directory mode;
1502 * else neither are defaults, use the new mode but check
1503 * it agrees with 'type'.
1504 */
1505 if(dir.qid.type == 0xFF && dir.mode == ~0){
1506 dir.mode = d->mode & 0777;
1507 if(d->mode & DLOCK)
1508 dir.mode |= DMEXCL;
1509 if(d->mode & DAPND)
1510 dir.mode |= DMAPPEND;
1511 if(d->mode & DDIR)
1512 dir.mode |= DMDIR;
1513 }
1514 else if(dir.qid.type == 0xFF){
1515 /* nothing to do */
1516 }
1517 else if(dir.mode == ~0)
1518 dir.mode = (dir.qid.type<<24)|(d->mode & 0777);
1519 else if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
1520 error = Eqidmode;
1521 goto out;
1522 }
1523
1524 /*
1525 * Check for unknown type/mode bits
1526 * and an attempt to change the directory bit.
1527 */
1528 if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){
1529 error = Enotm;
1530 goto out;
1531 }
1532 if(d->mode & DDIR)
1533 mode = DMDIR;
1534 else
1535 mode = 0;
1536 if((dir.mode^mode) & DMDIR){
1537 error = Enotd;
1538 goto out;
1539 }
1540
1541 if(dir.mtime == ~0)
1542 dir.mtime = d->mtime;
1543 if(dir.length == ~0)
1544 dir.length = d->size;
1545
1546 /*
1547 * Currently, can't change length.
1548 */
1549 if(dir.length != d->size){
1550 error = Enotl;
1551 goto out;
1552 }
1553
1554 /*
1555 * if chown,
1556 * must be god
1557 * wstatallow set to allow chown during boot
1558 */
1559 if(uid != d->uid && !wstatallow) {
1560 error = Enotu;
1561 goto out;
1562 }
1563
1564 /*
1565 * if chgroup,
1566 * must be either
1567 * a) owner and in new group
1568 * b) leader of both groups
1569 * wstatallow and writeallow are set to allow chgrp during boot
1570 */
1571 while(gid != d->gid) {
1572 if(wstatallow || writeallow)
1573 break;
1574 if(d->uid == file->uid && ingroup(file->uid, gid))
1575 break;
1576 if(leadgroup(file->uid, gid))
1577 if(leadgroup(file->uid, d->gid))
1578 break;
1579 error = Enotg;
1580 goto out;
1581 }
1582
1583 /*
1584 * if rename,
1585 * must have write permission in parent
1586 */
1587 while(strncmp(d->name, dir.name, sizeof(d->name)) != 0) {
1588 if(checkname(dir.name) || d1 == nil) {
1589 error = Ename;
1590 goto out;
1591 }
1592 if(strcmp(dir.name, ".") == 0 || strcmp(xd.name, "..") == 0) {
1593 error = Ename;
1594 goto out;
1595 }
1596
1597 /*
1598 * drop entry to prevent lock, then
1599 * check that destination name is unique,
1600 */
1601 putbuf(p);
1602 for(addr = 0; ; addr++) {
1603 if((p = dnodebuf(p1, d1, addr, 0)) == nil)
1604 break;
1605 if(checktag(p, Tdir, d1->qid.path)) {
1606 putbuf(p);
1607 continue;
1608 }
1609 for(slot = 0; slot < DIRPERBUF; slot++) {
1610 d = getdir(p, slot);
1611 if(!(d->mode & DALLOC))
1612 continue;
1613 if(strncmp(dir.name, d->name, sizeof(d->name)) == 0) {
1614 error = Eexist;
1615 goto out;
1616 }
1617 }
1618 putbuf(p);
1619 }
1620
1621 /*
1622 * reacquire entry
1623 */
1624 if((p = getbuf(file->fs->dev, file->addr, Bread)) == nil){
1625 error = Ephase;
1626 goto out;
1627 }
1628 d = getdir(p, file->slot);
1629 if(d == nil || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1630 error = Ephase;
1631 goto out;
1632 }
1633
1634 if(wstatallow || writeallow) /* set to allow rename during boot */
1635 break;
1636 if(d1 == nil || iaccess(file, d1, DWRITE)) {
1637 error = Eaccess;
1638 goto out;
1639 }
1640 break;
1641 }
1642
1643 /*
1644 * if mode/time, either
1645 * a) owner
1646 * b) leader of either group
1647 */
1648 mode = dir.mode & 0777;
1649 if(dir.mode & DMAPPEND)
1650 mode |= DAPND;
1651 if(dir.mode & DMEXCL)
1652 mode |= DLOCK;
1653 while(d->mtime != dir.mtime || ((d->mode^mode) & (DAPND|DLOCK|0777))) {
1654 if(wstatallow) /* set to allow chmod during boot */
1655 break;
1656 if(d->uid == file->uid)
1657 break;
1658 if(leadgroup(file->uid, gid))
1659 break;
1660 if(leadgroup(file->uid, d->gid))
1661 break;
1662 error = Enotu;
1663 goto out;
1664 }
1665 d->mtime = dir.mtime;
1666 d->uid = uid;
1667 d->gid = gid;
1668 d->mode = (mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR));
1669
1670 strncpy(d->name, dir.name, sizeof(d->name));
1671 accessdir(p, d, FWSTAT);
1672
1673 out:
1674 if(p != nil)
1675 putbuf(p);
1676 if(p1 != nil)
1677 putbuf(p1);
1678 if(file != nil)
1679 qunlock(file);
1680
1681 return error;
1682 }
1683
1684 static int
recv(Chan * c,uchar * buf,int n)1685 recv(Chan *c, uchar *buf, int n)
1686 {
1687 int fd, m, len;
1688
1689 fd = c->chan;
1690 /* read count */
1691 qlock(&c->rlock);
1692 m = readn(fd, buf, BIT32SZ);
1693 if(m != BIT32SZ){
1694 qunlock(&c->rlock);
1695 if(m < 0){
1696 print("readn(BIT32SZ) fails: %r\n");
1697 return -1;
1698 }
1699 print("readn(BIT32SZ) returns %d: %r\n", m);
1700 return 0;
1701 }
1702
1703 len = GBIT32(buf);
1704 if(len <= BIT32SZ || len > n){
1705 print("recv bad length %d\n", len);
1706 werrstr("bad length in 9P2000 message header");
1707 qunlock(&c->rlock);
1708 return -1;
1709 }
1710 len -= BIT32SZ;
1711 m = readn(fd, buf+BIT32SZ, len);
1712 qunlock(&c->rlock);
1713 if(m < len){
1714 print("recv wanted %d got %d\n", len, m);
1715 return 0;
1716 }
1717 return BIT32SZ+m;
1718 }
1719
1720 static void
send(Chan * c,uchar * buf,int n)1721 send(Chan *c, uchar *buf, int n)
1722 {
1723 int fd, m;
1724
1725 fd = c->chan;
1726 qlock(&c->wlock);
1727 m = write(fd, buf, n);
1728 qunlock(&c->wlock);
1729 if(m == n)
1730 return;
1731 panic("write failed");
1732 }
1733
1734 void
serve9p2(Chan * chan,uchar * ib,int nib)1735 serve9p2(Chan *chan, uchar *ib, int nib)
1736 {
1737 uchar inbuf[MSIZE+IOHDRSZ], outbuf[MSIZE+IOHDRSZ];
1738 Fcall f, r;
1739 char ename[64];
1740 int error, n, type;
1741
1742 chan->msize = MSIZE;
1743 fmtinstall('F', fcallfmt);
1744
1745 for(;;){
1746 if(nib){
1747 memmove(inbuf, ib, nib);
1748 n = nib;
1749 nib = 0;
1750 }else
1751 n = recv(chan, inbuf, sizeof inbuf);
1752 if(chat){
1753 print("read msg %d (fd %d)\n", n, chan->chan);
1754 if(n <= 0)
1755 print("\terr: %r\n");
1756 }
1757 if(n == 0 && (chan == cons.srvchan || chan == cons.chan))
1758 continue;
1759 if(n <= 0)
1760 break;
1761 if(convM2S(inbuf, n, &f) != n){
1762 print("9p2: cannot decode\n");
1763 continue;
1764 }
1765
1766 type = f.type;
1767 if(type < Tversion || type >= Tmax || (type&1) || type == Terror){
1768 print("9p2: bad message type %d\n", type);
1769 continue;
1770 }
1771
1772 if(CHAT(chan))
1773 print("9p2: f %F\n", &f);
1774
1775 r.type = type+1;
1776 r.tag = f.tag;
1777 error = 0;
1778
1779 rlock(&mainlock);
1780 rlock(&chan->reflock);
1781 switch(type){
1782 default:
1783 r.type = Rerror;
1784 snprint(ename, sizeof ename, "unknown message: %F", &f);
1785 r.ename = ename;
1786 break;
1787 case Tversion:
1788 error = fsversion(chan, &f, &r);
1789 break;
1790 case Tauth:
1791 error = fsauth(chan, &f, &r);
1792 break;
1793 case Tattach:
1794 error = fsattach(chan, &f, &r);
1795 break;
1796 case Tflush:
1797 error = fsflush(chan, &f, &r);
1798 break;
1799 case Twalk:
1800 error = fswalk(chan, &f, &r);
1801 break;
1802 case Topen:
1803 error = fsopen(chan, &f, &r);
1804 break;
1805 case Tcreate:
1806 error = fscreate(chan, &f, &r);
1807 break;
1808 case Tread:
1809 r.data = (char*)inbuf;
1810 error = fsread(chan, &f, &r);
1811 break;
1812 case Twrite:
1813 error = fswrite(chan, &f, &r);
1814 break;
1815 case Tclunk:
1816 error = fsclunk(chan, &f, &r);
1817 break;
1818 case Tremove:
1819 error = fsremove(chan, &f, &r);
1820 break;
1821 case Tstat:
1822 error = fsstat(chan, &f, &r, inbuf);
1823 break;
1824 case Twstat:
1825 error = fswstat(chan, &f, &r, (char*)outbuf);
1826 break;
1827 }
1828 runlock(&chan->reflock);
1829 runlock(&mainlock);
1830
1831 if(error != 0){
1832 r.type = Rerror;
1833 if(error >= MAXERR){
1834 snprint(ename, sizeof(ename), "error %d", error);
1835 r.ename = ename;
1836 }
1837 else
1838 r.ename = errstring[error];
1839 }
1840 if(CHAT(chan))
1841 print("9p2: r %F\n", &r);
1842
1843 n = convS2M(&r, outbuf, sizeof outbuf);
1844 if(n == 0){
1845 type = r.type;
1846 r.type = Rerror;
1847 snprint(ename, sizeof(ename), "9p2: convS2M: type %d", type);
1848 r.ename = ename;
1849 print(ename);
1850 n = convS2M(&r, outbuf, sizeof outbuf);
1851 if(n == 0){
1852 /*
1853 * What to do here, the failure notification failed?
1854 */
1855 panic("can't write anything at all");
1856 }
1857 }
1858 send(chan, outbuf, n);
1859 }
1860 fileinit(chan);
1861 close(chan->chan);
1862 if(chan == cons.srvchan || chan == cons.chan)
1863 print("console chan read error");
1864 }
1865
1866