Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
8 int chatty9p;
10 // static char Ebadattach[] = "unknown specifier in attach";
11 static char Ebadoffset[] = "bad offset";
12 // static char Ebadcount[] = "bad count";
13 static char Ebotch[] = "9P protocol botch";
14 static char Ecreatenondir[] = "create in non-directory";
15 static char Edupfid[] = "duplicate fid";
16 static char Eduptag[] = "duplicate tag";
17 static char Eisdir[] = "is a directory";
18 static char Enocreate[] = "create prohibited";
19 // static char Enomem[] = "out of memory";
20 static char Enoremove[] = "remove prohibited";
21 static char Enostat[] = "stat prohibited";
22 static char Enotfound[] = "file not found";
23 // static char Enowrite[] = "write prohibited";
24 static char Enowstat[] = "wstat prohibited";
25 static char Eperm[] = "permission denied";
26 static char Eunknownfid[] = "unknown fid";
27 static char Ebaddir[] = "bad directory in wstat";
28 static char Ewalknodir[] = "walk in non-directory";
30 static void
31 setfcallerror(Fcall *f, char *err)
32 {
33 f->ename = err;
34 f->type = Rerror;
35 }
37 static void
38 changemsize(Srv *srv, int msize)
39 {
40 if(srv->rbuf && srv->wbuf && srv->msize == msize)
41 return;
42 qlock(&srv->rlock);
43 qlock(&srv->wlock);
44 srv->msize = msize;
45 free(srv->rbuf);
46 free(srv->wbuf);
47 srv->rbuf = emalloc9p(msize);
48 srv->wbuf = emalloc9p(msize);
49 qunlock(&srv->rlock);
50 qunlock(&srv->wlock);
51 }
53 static Req*
54 getreq(Srv *s)
55 {
56 long n;
57 uchar *buf;
58 Fcall f;
59 Req *r;
61 qlock(&s->rlock);
62 if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
63 qunlock(&s->rlock);
64 return nil;
65 }
67 buf = emalloc9p(n);
68 memmove(buf, s->rbuf, n);
69 qunlock(&s->rlock);
71 if(convM2S(buf, n, &f) != n){
72 free(buf);
73 return nil;
74 }
76 if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */
77 r = emalloc9p(sizeof *r);
78 incref(&r->ref);
79 r->tag = f.tag;
80 r->ifcall = f;
81 r->error = Eduptag;
82 r->buf = buf;
83 r->responded = 0;
84 r->type = 0;
85 r->srv = s;
86 r->pool = nil;
87 if(chatty9p)
88 fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
89 return r;
90 }
92 r->srv = s;
93 r->responded = 0;
94 r->buf = buf;
95 r->ifcall = f;
96 memset(&r->ofcall, 0, sizeof r->ofcall);
97 r->type = r->ifcall.type;
99 if(chatty9p)
100 if(r->error)
101 fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
102 else
103 fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
105 return r;
108 static void
109 filewalk(Req *r)
111 int i;
112 File *f;
114 f = r->fid->file;
115 assert(f != nil);
117 incref(&f->ref);
118 for(i=0; i<r->ifcall.nwname; i++)
119 if(f = walkfile(f, r->ifcall.wname[i]))
120 r->ofcall.wqid[i] = f->dir.qid;
121 else
122 break;
124 r->ofcall.nwqid = i;
125 if(f){
126 r->newfid->file = f;
127 r->newfid->qid = r->newfid->file->dir.qid;
129 respond(r, nil);
132 void
133 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
135 int i;
136 char *e;
138 if(r->fid == r->newfid && r->ifcall.nwname > 1){
139 respond(r, "lib9p: unused documented feature not implemented");
140 return;
143 if(r->fid != r->newfid){
144 r->newfid->qid = r->fid->qid;
145 if(clone && (e = clone(r->fid, r->newfid, arg))){
146 respond(r, e);
147 return;
151 e = nil;
152 for(i=0; i<r->ifcall.nwname; i++){
153 if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
154 break;
155 r->ofcall.wqid[i] = r->newfid->qid;
158 r->ofcall.nwqid = i;
159 if(e && i==0)
160 respond(r, e);
161 else
162 respond(r, nil);
165 static void
166 sversion(Srv *srv, Req *r)
168 USED(srv);
170 if(strncmp(r->ifcall.version, "9P", 2) != 0){
171 r->ofcall.version = "unknown";
172 respond(r, nil);
173 return;
176 r->ofcall.version = "9P2000";
177 r->ofcall.msize = r->ifcall.msize;
178 respond(r, nil);
180 static void
181 rversion(Req *r, char *error)
183 assert(error == nil);
184 changemsize(r->srv, r->ofcall.msize);
187 static void
188 sauth(Srv *srv, Req *r)
190 char e[ERRMAX];
192 if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
193 respond(r, Edupfid);
194 return;
196 if(srv->auth)
197 srv->auth(r);
198 else{
199 snprint(e, sizeof e, "%s: authentication not required", argv0);
200 respond(r, e);
203 static void
204 rauth(Req *r, char *error)
206 if(error && r->afid)
207 closefid(removefid(r->srv->fpool, r->afid->fid));
210 static void
211 sattach(Srv *srv, Req *r)
213 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
214 respond(r, Edupfid);
215 return;
217 r->afid = nil;
218 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
219 respond(r, Eunknownfid);
220 return;
222 r->fid->uid = estrdup9p(r->ifcall.uname);
223 if(srv->tree){
224 r->fid->file = srv->tree->root;
225 /* BUG? incref(r->fid->file) ??? */
226 r->ofcall.qid = r->fid->file->dir.qid;
227 r->fid->qid = r->ofcall.qid;
229 if(srv->attach)
230 srv->attach(r);
231 else
232 respond(r, nil);
233 return;
235 static void
236 rattach(Req *r, char *error)
238 if(error && r->fid)
239 closefid(removefid(r->srv->fpool, r->fid->fid));
242 static void
243 sflush(Srv *srv, Req *r)
245 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
246 if(r->oldreq == nil || r->oldreq == r)
247 respond(r, nil);
248 else if(srv->flush)
249 srv->flush(r);
250 else
251 respond(r, nil);
253 static int
254 rflush(Req *r, char *error)
256 Req *or;
258 assert(error == nil);
259 or = r->oldreq;
260 if(or){
261 qlock(&or->lk);
262 if(or->responded == 0){
263 or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
264 or->flush[or->nflush++] = r;
265 qunlock(&or->lk);
266 return -1; /* delay response until or is responded */
268 qunlock(&or->lk);
269 closereq(or);
271 r->oldreq = nil;
272 return 0;
275 static char*
276 oldwalk1(Fid *fid, char *name, void *arg)
278 char *e;
279 Qid qid;
280 Srv *srv;
282 srv = arg;
283 e = srv->walk1(fid, name, &qid);
284 if(e)
285 return e;
286 fid->qid = qid;
287 return nil;
290 static char*
291 oldclone(Fid *fid, Fid *newfid, void *arg)
293 Srv *srv;
295 srv = arg;
296 if(srv->clone == nil)
297 return nil;
298 return srv->clone(fid, newfid);
301 static void
302 swalk(Srv *srv, Req *r)
304 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
305 respond(r, Eunknownfid);
306 return;
308 if(r->fid->omode != -1){
309 respond(r, "cannot clone open fid");
310 return;
312 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
313 respond(r, Ewalknodir);
314 return;
316 if(r->ifcall.fid != r->ifcall.newfid){
317 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
318 respond(r, Edupfid);
319 return;
321 r->newfid->uid = estrdup9p(r->fid->uid);
322 }else{
323 incref(&r->fid->ref);
324 r->newfid = r->fid;
326 if(r->fid->file){
327 filewalk(r);
328 }else if(srv->walk1)
329 walkandclone(r, oldwalk1, oldclone, srv);
330 else if(srv->walk)
331 srv->walk(r);
332 else
333 sysfatal("no walk function, no file trees");
335 static void
336 rwalk(Req *r, char *error)
338 if(error || r->ofcall.nwqid < r->ifcall.nwname){
339 if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
340 closefid(removefid(r->srv->fpool, r->newfid->fid));
341 if (r->ofcall.nwqid==0){
342 if(error==nil && r->ifcall.nwname!=0)
343 r->error = Enotfound;
344 }else
345 r->error = nil; // No error on partial walks
346 }else{
347 if(r->ofcall.nwqid == 0){
348 /* Just a clone */
349 r->newfid->qid = r->fid->qid;
350 }else{
351 /* if file trees are in use, filewalk took care of the rest */
352 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
357 static void
358 sopen(Srv *srv, Req *r)
360 int p;
362 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
363 respond(r, Eunknownfid);
364 return;
366 if(r->fid->omode != -1){
367 respond(r, Ebotch);
368 return;
370 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
371 respond(r, Eisdir);
372 return;
374 r->ofcall.qid = r->fid->qid;
375 switch(r->ifcall.mode&3){
376 default:
377 assert(0);
378 case OREAD:
379 p = AREAD;
380 break;
381 case OWRITE:
382 p = AWRITE;
383 break;
384 case ORDWR:
385 p = AREAD|AWRITE;
386 break;
387 case OEXEC:
388 p = AEXEC;
389 break;
391 if(r->ifcall.mode&OTRUNC)
392 p |= AWRITE;
393 if((r->fid->qid.type&QTDIR) && p!=AREAD){
394 respond(r, Eperm);
395 return;
397 if(r->fid->file){
398 if(!hasperm(r->fid->file, r->fid->uid, p)){
399 respond(r, Eperm);
400 return;
402 /* BUG RACE */
403 if((r->ifcall.mode&ORCLOSE)
404 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
405 respond(r, Eperm);
406 return;
408 r->ofcall.qid = r->fid->file->dir.qid;
409 if((r->ofcall.qid.type&QTDIR)
410 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
411 respond(r, "opendirfile failed");
412 return;
415 if(srv->open)
416 srv->open(r);
417 else
418 respond(r, nil);
420 static void
421 ropen(Req *r, char *error)
423 char errbuf[ERRMAX];
424 if(error)
425 return;
426 if(chatty9p){
427 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
428 write(2, errbuf, strlen(errbuf));
430 r->fid->omode = r->ifcall.mode;
431 r->fid->qid = r->ofcall.qid;
432 if(r->ofcall.qid.type&QTDIR)
433 r->fid->diroffset = 0;
436 static void
437 screate(Srv *srv, Req *r)
439 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
440 respond(r, Eunknownfid);
441 else if(r->fid->omode != -1)
442 respond(r, Ebotch);
443 else if(!(r->fid->qid.type&QTDIR))
444 respond(r, Ecreatenondir);
445 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
446 respond(r, Eperm);
447 else if(srv->create)
448 srv->create(r);
449 else
450 respond(r, Enocreate);
452 static void
453 rcreate(Req *r, char *error)
455 if(error)
456 return;
457 r->fid->omode = r->ifcall.mode;
458 r->fid->qid = r->ofcall.qid;
461 static void
462 sread(Srv *srv, Req *r)
464 int o;
466 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
467 respond(r, Eunknownfid);
468 return;
470 if(r->ifcall.count < 0){
471 respond(r, Ebotch);
472 return;
474 if(r->ifcall.offset < 0
475 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
476 respond(r, Ebadoffset);
477 return;
480 if(r->ifcall.count > srv->msize - IOHDRSZ)
481 r->ifcall.count = srv->msize - IOHDRSZ;
482 r->rbuf = emalloc9p(r->ifcall.count);
483 r->ofcall.data = r->rbuf;
484 o = r->fid->omode & 3;
485 if(o != OREAD && o != ORDWR && o != OEXEC){
486 respond(r, Ebotch);
487 return;
489 if((r->fid->qid.type&QTDIR) && r->fid->file){
490 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
491 respond(r, nil);
492 return;
494 if(srv->read)
495 srv->read(r);
496 else
497 respond(r, "no srv->read");
499 static void
500 rread(Req *r, char *error)
502 if(error==nil && (r->fid->qid.type&QTDIR))
503 r->fid->diroffset += r->ofcall.count;
506 static void
507 swrite(Srv *srv, Req *r)
509 int o;
510 char e[ERRMAX];
512 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
513 respond(r, Eunknownfid);
514 return;
516 if(r->ifcall.count < 0){
517 respond(r, Ebotch);
518 return;
520 if(r->ifcall.offset < 0){
521 respond(r, Ebotch);
522 return;
524 if(r->ifcall.count > srv->msize - IOHDRSZ)
525 r->ifcall.count = srv->msize - IOHDRSZ;
526 o = r->fid->omode & 3;
527 if(o != OWRITE && o != ORDWR){
528 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
529 respond(r, e);
530 return;
532 if(srv->write)
533 srv->write(r);
534 else
535 respond(r, "no srv->write");
537 static void
538 rwrite(Req *r, char *error)
540 if(error)
541 return;
542 if(r->fid->file)
543 r->fid->file->dir.qid.vers++;
546 static void
547 sclunk(Srv *srv, Req *r)
549 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
550 respond(r, Eunknownfid);
551 else
552 respond(r, nil);
554 static void
555 rclunk(Req *r, char *msg)
557 USED(r);
558 USED(msg);
561 static void
562 sremove(Srv *srv, Req *r)
564 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
565 respond(r, Eunknownfid);
566 return;
568 /* BUG RACE */
569 if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
570 respond(r, Eperm);
571 return;
573 if(srv->remove)
574 srv->remove(r);
575 else
576 respond(r, r->fid->file ? nil : Enoremove);
578 static void
579 rremove(Req *r, char *error, char *errbuf)
581 if(error)
582 return;
583 if(r->fid->file){
584 if(removefile(r->fid->file) < 0){
585 snprint(errbuf, ERRMAX, "remove %s: %r",
586 r->fid->file->dir.name);
587 r->error = errbuf;
589 r->fid->file = nil;
593 static void
594 sstat(Srv *srv, Req *r)
596 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
597 respond(r, Eunknownfid);
598 return;
600 if(r->fid->file){
601 r->d = r->fid->file->dir;
602 if(r->d.name)
603 r->d.name = estrdup9p(r->d.name);
604 if(r->d.uid)
605 r->d.uid = estrdup9p(r->d.uid);
606 if(r->d.gid)
607 r->d.gid = estrdup9p(r->d.gid);
608 if(r->d.muid)
609 r->d.muid = estrdup9p(r->d.muid);
611 if(srv->stat)
612 srv->stat(r);
613 else if(r->fid->file)
614 respond(r, nil);
615 else
616 respond(r, Enostat);
618 static void
619 rstat(Req *r, char *error)
621 int n;
622 uchar *statbuf;
623 uchar tmp[BIT16SZ];
625 if(error)
626 return;
627 if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
628 r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
629 return;
631 n = GBIT16(tmp)+BIT16SZ;
632 statbuf = emalloc9p(n);
633 if(statbuf == nil){
634 r->error = "out of memory";
635 return;
637 r->ofcall.nstat = convD2M(&r->d, statbuf, n);
638 r->ofcall.stat = statbuf; /* freed in closereq */
639 if(r->ofcall.nstat <= BIT16SZ){
640 r->error = "convD2M fails";
641 free(statbuf);
642 return;
646 static void
647 swstat(Srv *srv, Req *r)
649 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
650 respond(r, Eunknownfid);
651 return;
653 if(srv->wstat == nil){
654 respond(r, Enowstat);
655 return;
657 if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
658 respond(r, Ebaddir);
659 return;
661 if((ushort)~r->d.type){
662 respond(r, "wstat -- attempt to change type");
663 return;
665 if((uint)~r->d.dev){
666 respond(r, "wstat -- attempt to change dev");
667 return;
669 if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
670 respond(r, "wstat -- attempt to change qid");
671 return;
673 if(r->d.muid && r->d.muid[0]){
674 respond(r, "wstat -- attempt to change muid");
675 return;
677 if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
678 respond(r, "wstat -- attempt to change DMDIR bit");
679 return;
681 srv->wstat(r);
683 static void
684 rwstat(Req *r, char *msg)
686 USED(r);
687 USED(msg);
690 void
691 srv(Srv *srv)
693 Req *r;
695 fmtinstall('D', dirfmt);
696 fmtinstall('F', fcallfmt);
698 if(srv->fpool == nil)
699 srv->fpool = allocfidpool(srv->destroyfid);
700 if(srv->rpool == nil)
701 srv->rpool = allocreqpool(srv->destroyreq);
702 if(srv->msize == 0)
703 srv->msize = 8192+IOHDRSZ;
705 changemsize(srv, srv->msize);
707 srv->fpool->srv = srv;
708 srv->rpool->srv = srv;
710 if(srv->start)
711 srv->start(srv);
713 while(r = getreq(srv)){
714 if(r->error){
715 respond(r, r->error);
716 continue;
718 switch(r->ifcall.type){
719 default:
720 respond(r, "unknown message");
721 break;
722 case Tversion: sversion(srv, r); break;
723 case Tauth: sauth(srv, r); break;
724 case Tattach: sattach(srv, r); break;
725 case Tflush: sflush(srv, r); break;
726 case Twalk: swalk(srv, r); break;
727 case Topen: sopen(srv, r); break;
728 case Tcreate: screate(srv, r); break;
729 case Tread: sread(srv, r); break;
730 case Twrite: swrite(srv, r); break;
731 case Tclunk: sclunk(srv, r); break;
732 case Tremove: sremove(srv, r); break;
733 case Tstat: sstat(srv, r); break;
734 case Twstat: swstat(srv, r); break;
738 if(srv->end)
739 srv->end(srv);
742 void
743 respond(Req *r, char *error)
745 int i, m, n;
746 char errbuf[ERRMAX];
747 Srv *srv;
749 srv = r->srv;
750 assert(srv != nil);
752 assert(r->responded == 0);
753 r->error = error;
755 switch(r->ifcall.type){
756 default:
757 assert(0);
758 /*
759 * Flush is special. If the handler says so, we return
760 * without further processing. Respond will be called
761 * again once it is safe.
762 */
763 case Tflush:
764 if(rflush(r, error)<0)
765 return;
766 break;
767 case Tversion: rversion(r, error); break;
768 case Tauth: rauth(r, error); break;
769 case Tattach: rattach(r, error); break;
770 case Twalk: rwalk(r, error); break;
771 case Topen: ropen(r, error); break;
772 case Tcreate: rcreate(r, error); break;
773 case Tread: rread(r, error); break;
774 case Twrite: rwrite(r, error); break;
775 case Tclunk: rclunk(r, error); break;
776 case Tremove: rremove(r, error, errbuf); break;
777 case Tstat: rstat(r, error); break;
778 case Twstat: rwstat(r, error); break;
781 r->ofcall.tag = r->ifcall.tag;
782 r->ofcall.type = r->ifcall.type+1;
783 if(r->error)
784 setfcallerror(&r->ofcall, r->error);
786 if(chatty9p)
787 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
789 qlock(&srv->wlock);
790 n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
791 if(n <= 0){
792 fprint(2, "n = %d %F\n", n, &r->ofcall);
793 abort();
795 assert(n > 2);
796 if(r->pool) /* not a fake */
797 closereq(removereq(r->pool, r->ifcall.tag));
798 m = write(srv->outfd, srv->wbuf, n);
799 if(m != n)
800 sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
801 qunlock(&srv->wlock);
803 qlock(&r->lk); /* no one will add flushes now */
804 r->responded = 1;
805 qunlock(&r->lk);
807 for(i=0; i<r->nflush; i++)
808 respond(r->flush[i], nil);
809 free(r->flush);
811 if(r->pool)
812 closereq(r);
813 else
814 free(r);
817 int
818 postfd(char *name, int pfd)
820 int fd;
821 char buf[80];
823 snprint(buf, sizeof buf, "/srv/%s", name);
824 if(chatty9p)
825 fprint(2, "postfd %s\n", buf);
826 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
827 if(fd < 0){
828 if(chatty9p)
829 fprint(2, "create fails: %r\n");
830 return -1;
832 if(fprint(fd, "%d", pfd) < 0){
833 if(chatty9p)
834 fprint(2, "write fails: %r\n");
835 close(fd);
836 return -1;
838 if(chatty9p)
839 fprint(2, "postfd successful\n");
840 return 0;