8 void (*_forker)(void(*)(void*), void*, int);
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";
31 setfcallerror(Fcall *f, char *err)
38 changemsize(Srv *srv, int msize)
40 if(srv->rbuf && srv->wbuf && srv->msize == msize)
47 srv->rbuf = emalloc9p(msize);
48 srv->wbuf = emalloc9p(msize);
62 while((n = read9pmsg(s->infd, s->rbuf, s->msize)) == 0)
70 memmove(buf, s->rbuf, n);
73 if(convM2S(buf, n, &f) != n){
78 if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */
79 r = emalloc9p(sizeof *r);
90 fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
98 memset(&r->ofcall, 0, sizeof r->ofcall);
99 r->type = r->ifcall.type;
103 fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
105 fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
120 for(i=0; i<r->ifcall.nwname; i++)
121 if(f = walkfile(f, r->ifcall.wname[i]))
122 r->ofcall.wqid[i] = f->qid;
129 r->newfid->qid = r->newfid->file->qid;
135 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
140 if(r->fid == r->newfid && r->ifcall.nwname > 1){
141 respond(r, "lib9p: unused documented feature not implemented");
145 if(r->fid != r->newfid){
146 r->newfid->qid = r->fid->qid;
147 if(clone && (e = clone(r->fid, r->newfid, arg))){
154 for(i=0; i<r->ifcall.nwname; i++){
155 if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
157 r->ofcall.wqid[i] = r->newfid->qid;
168 sversion(Srv *srv, Req *r)
170 if(srv->rref.ref != 2){
174 if(strncmp(r->ifcall.version, "9P", 2) != 0){
175 r->ofcall.version = "unknown";
179 r->ofcall.version = "9P2000";
180 if(r->ifcall.msize < 256){
181 respond(r, "version: message size too small");
184 if(r->ifcall.msize < 1024*1024)
185 r->ofcall.msize = r->ifcall.msize;
187 r->ofcall.msize = 1024*1024;
192 rversion(Req *r, char *error)
195 changemsize(r->srv, r->ofcall.msize);
199 sauth(Srv *srv, Req *r)
203 if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
210 snprint(e, sizeof e, "%s: authentication not required", argv0);
215 rauth(Req *r, char *error)
218 closefid(removefid(r->srv->fpool, r->afid->fid));
222 sattach(Srv *srv, Req *r)
224 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
229 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
230 respond(r, Eunknownfid);
233 r->fid->uid = estrdup9p(r->ifcall.uname);
235 r->fid->file = srv->tree->root;
236 incref(r->fid->file);
237 r->ofcall.qid = r->fid->file->qid;
238 r->fid->qid = r->ofcall.qid;
247 rattach(Req *r, char *error)
250 closefid(removefid(r->srv->fpool, r->fid->fid));
254 sflush(Srv *srv, Req *r)
256 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
257 if(r->oldreq == nil || r->oldreq == r)
265 rflush(Req *r, char *error)
269 assert(error == nil);
273 if(or->responded == 0){
274 or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
275 or->flush[or->nflush++] = r;
277 return -1; /* delay response until or is responded */
287 oldwalk1(Fid *fid, char *name, void *arg)
294 e = srv->walk1(fid, name, &qid);
302 oldclone(Fid *fid, Fid *newfid, void *arg)
307 if(srv->clone == nil)
309 return srv->clone(fid, newfid);
313 swalk(Srv *srv, Req *r)
315 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
316 respond(r, Eunknownfid);
319 if(r->fid->omode != -1){
320 respond(r, "cannot clone open fid");
323 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
324 respond(r, Ewalknodir);
327 if(r->ifcall.fid != r->ifcall.newfid){
328 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
332 r->newfid->uid = estrdup9p(r->fid->uid);
334 incref(&r->fid->ref);
340 walkandclone(r, oldwalk1, oldclone, srv);
344 sysfatal("no walk function, no file trees");
347 rwalk(Req *r, char *error)
349 if(error || r->ofcall.nwqid < r->ifcall.nwname){
350 if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
351 closefid(removefid(r->srv->fpool, r->newfid->fid));
352 if (r->ofcall.nwqid==0){
353 if(error==nil && r->ifcall.nwname!=0)
354 r->error = Enotfound;
356 r->error = nil; // No error on partial walks
358 if(r->ofcall.nwqid == 0){
360 r->newfid->qid = r->fid->qid;
362 /* if file trees are in use, filewalk took care of the rest */
363 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
369 sopen(Srv *srv, Req *r)
373 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
374 respond(r, Eunknownfid);
377 if(r->fid->omode != -1){
381 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
385 r->ofcall.qid = r->fid->qid;
386 switch(r->ifcall.mode&3){
402 if(r->ifcall.mode&OTRUNC)
404 if((r->fid->qid.type&QTDIR) && p!=AREAD){
409 if(!hasperm(r->fid->file, r->fid->uid, p)){
414 if((r->ifcall.mode&ORCLOSE)
415 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
419 r->ofcall.qid = r->fid->file->qid;
420 if((r->ofcall.qid.type&QTDIR)
421 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
422 respond(r, "opendirfile failed");
432 ropen(Req *r, char *error)
438 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
439 write(2, errbuf, strlen(errbuf));
441 r->fid->omode = r->ifcall.mode;
442 r->fid->qid = r->ofcall.qid;
443 if(r->ofcall.qid.type&QTDIR)
444 r->fid->diroffset = 0;
448 screate(Srv *srv, Req *r)
450 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
451 respond(r, Eunknownfid);
452 else if(r->fid->omode != -1)
454 else if(!(r->fid->qid.type&QTDIR))
455 respond(r, Ecreatenondir);
456 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
461 respond(r, Enocreate);
464 rcreate(Req *r, char *error)
468 r->fid->omode = r->ifcall.mode;
469 r->fid->qid = r->ofcall.qid;
473 sread(Srv *srv, Req *r)
477 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
478 respond(r, Eunknownfid);
481 if((int)r->ifcall.count < 0){
485 if(r->ifcall.offset < 0
486 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
487 respond(r, Ebadoffset);
491 if(r->ifcall.count > srv->msize - IOHDRSZ)
492 r->ifcall.count = srv->msize - IOHDRSZ;
493 r->rbuf = emalloc9p(r->ifcall.count);
494 r->ofcall.data = r->rbuf;
495 o = r->fid->omode & 3;
496 if(o != OREAD && o != ORDWR && o != OEXEC){
500 if((r->fid->qid.type&QTDIR) && r->fid->file){
501 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
508 respond(r, "no srv->read");
511 rread(Req *r, char *error)
513 if(error==nil && (r->fid->qid.type&QTDIR))
514 r->fid->diroffset += r->ofcall.count;
518 swrite(Srv *srv, Req *r)
523 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
524 respond(r, Eunknownfid);
527 if((int)r->ifcall.count < 0){
531 if(r->ifcall.offset < 0){
535 if(r->ifcall.count > srv->msize - IOHDRSZ)
536 r->ifcall.count = srv->msize - IOHDRSZ;
537 o = r->fid->omode & 3;
538 if(o != OWRITE && o != ORDWR){
539 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
546 respond(r, "no srv->write");
549 rwrite(Req *r, char *error)
554 r->fid->file->qid.vers++;
558 sclunk(Srv *srv, Req *r)
560 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
561 respond(r, Eunknownfid);
571 sremove(Srv *srv, Req *r)
573 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
574 respond(r, Eunknownfid);
578 if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
585 respond(r, r->fid->file ? nil : Enoremove);
588 rremove(Req *r, char *error, char *errbuf)
593 if(removefile(r->fid->file) < 0){
594 snprint(errbuf, ERRMAX, "remove %s: %r",
603 sstat(Srv *srv, Req *r)
605 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
606 respond(r, Eunknownfid);
610 /* should we rlock the file? */
611 r->d = r->fid->file->Dir;
613 r->d.name = estrdup9p(r->d.name);
615 r->d.uid = estrdup9p(r->d.uid);
617 r->d.gid = estrdup9p(r->d.gid);
619 r->d.muid = estrdup9p(r->d.muid);
623 else if(r->fid->file)
629 rstat(Req *r, char *error)
637 if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
638 r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
641 n = GBIT16(tmp)+BIT16SZ;
642 statbuf = emalloc9p(n);
644 r->error = "out of memory";
647 r->ofcall.nstat = convD2M(&r->d, statbuf, n);
648 r->ofcall.stat = statbuf; /* freed in closereq */
649 if(r->ofcall.nstat <= BIT16SZ){
650 r->error = "convD2M fails";
657 swstat(Srv *srv, Req *r)
659 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
660 respond(r, Eunknownfid);
663 if(srv->wstat == nil){
664 respond(r, Enowstat);
667 if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
671 if(r->d.type != (ushort)~0){
672 respond(r, "wstat -- attempt to change type");
676 respond(r, "wstat -- attempt to change dev");
679 if(r->d.qid.type != (uchar)~0 || r->d.qid.vers != ~0 || r->d.qid.path != ~0){
680 respond(r, "wstat -- attempt to change qid");
683 if(r->d.muid && r->d.muid[0]){
684 respond(r, "wstat -- attempt to change muid");
687 if(r->d.mode != ~0 && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
688 respond(r, "wstat -- attempt to change DMDIR bit");
698 static void srvclose(Srv *);
708 while(r = getreq(srv)){
711 respond(r, r->error);
715 switch(r->ifcall.type){
717 respond(r, "unknown message");
719 case Tversion: sversion(srv, r); break;
720 case Tauth: sauth(srv, r); break;
721 case Tattach: sattach(srv, r); break;
722 case Tflush: sflush(srv, r); break;
723 case Twalk: swalk(srv, r); break;
724 case Topen: sopen(srv, r); break;
725 case Tcreate: screate(srv, r); break;
726 case Tread: sread(srv, r); break;
727 case Twrite: swrite(srv, r); break;
728 case Tclunk: sclunk(srv, r); break;
729 case Tremove: sremove(srv, r); break;
730 case Tstat: sstat(srv, r); break;
731 case Twstat: swstat(srv, r); break;
733 qunlock(&srv->slock);
742 if(decref(&srv->rref))
746 fprint(2, "srvclose\n");
753 freefidpool(srv->fpool);
755 freereqpool(srv->rpool);
772 if(decref(&srv->sref) == 0)
773 _forker(srvwork, srv, 0);
774 qunlock(&srv->slock);
780 fmtinstall('D', dirfmt);
781 fmtinstall('F', fcallfmt);
786 if(srv->fpool == nil)
787 srv->fpool = allocfidpool(srv->destroyfid);
788 if(srv->rpool == nil)
789 srv->rpool = allocreqpool(srv->destroyreq);
791 srv->msize = 8192+IOHDRSZ;
793 changemsize(srv, srv->msize);
795 srv->fpool->srv = srv;
796 srv->rpool->srv = srv;
805 respond(Req *r, char *error)
814 assert(r->responded == 0);
817 switch(r->ifcall.type){
819 * Flush is special. If the handler says so, we return
820 * without further processing. Respond will be called
821 * again once it is safe.
824 if(rflush(r, error)<0)
827 case Tversion: rversion(r, error); break;
828 case Tauth: rauth(r, error); break;
829 case Tattach: rattach(r, error); break;
830 case Twalk: rwalk(r, error); break;
831 case Topen: ropen(r, error); break;
832 case Tcreate: rcreate(r, error); break;
833 case Tread: rread(r, error); break;
834 case Twrite: rwrite(r, error); break;
835 case Tclunk: rclunk(r, error); break;
836 case Tremove: rremove(r, error, errbuf); break;
837 case Tstat: rstat(r, error); break;
838 case Twstat: rwstat(r, error); break;
841 r->ofcall.tag = r->ifcall.tag;
842 r->ofcall.type = r->ifcall.type+1;
844 setfcallerror(&r->ofcall, r->error);
847 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
850 n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
852 fprint(2, "msize = %d n = %d %F\n", srv->msize, n, &r->ofcall);
856 if(r->pool) /* not a fake */
857 closereq(removereq(r->pool, r->ifcall.tag));
858 m = write(srv->outfd, srv->wbuf, n);
860 fprint(2, "lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
861 qunlock(&srv->wlock);
863 qlock(&r->lk); /* no one will add flushes now */
867 for(i=0; i<r->nflush; i++)
868 respond(r->flush[i], nil);
886 rerrstr(errbuf, sizeof errbuf);
891 postfd(char *name, int pfd)
896 snprint(buf, sizeof buf, "/srv/%s", name);
898 fprint(2, "postfd %s\n", buf);
899 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
902 fprint(2, "create fails: %r\n");
905 if(fprint(fd, "%d", pfd) < 0){
907 fprint(2, "write fails: %r\n");
912 fprint(2, "postfd successful\n");
917 sharefd(char *name, char *desc, int pfd)
922 snprint(buf, sizeof buf, "#σc/%s", name);
923 if((fd = create(buf, OREAD, 0700|DMDIR)) >= 0)
925 snprint(buf, sizeof buf, "#σc/%s/%s", name, desc);
927 fprint(2, "sharefd %s\n", buf);
928 fd = create(buf, OWRITE, 0600);
931 fprint(2, "create fails: %r\n");
934 if(fprint(fd, "%d\n", pfd) < 0){
936 fprint(2, "write fails: %r\n");
942 fprint(2, "sharefd successful\n");