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 n = read9pmsg(s->infd, s->rbuf, s->msize);
69 memmove(buf, s->rbuf, n);
72 if(convM2S(buf, n, &f) != n){
77 if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */
78 r = emalloc9p(sizeof *r);
89 fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
97 memset(&r->ofcall, 0, sizeof r->ofcall);
98 r->type = r->ifcall.type;
102 fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
104 fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
119 for(i=0; i<r->ifcall.nwname; i++)
120 if(f = walkfile(f, r->ifcall.wname[i]))
121 r->ofcall.wqid[i] = f->qid;
128 r->newfid->qid = r->newfid->file->qid;
134 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
139 if(r->fid == r->newfid && r->ifcall.nwname > 1){
140 respond(r, "lib9p: unused documented feature not implemented");
144 if(r->fid != r->newfid){
145 r->newfid->qid = r->fid->qid;
146 if(clone && (e = clone(r->fid, r->newfid, arg))){
153 for(i=0; i<r->ifcall.nwname; i++){
154 if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
156 r->ofcall.wqid[i] = r->newfid->qid;
167 sversion(Srv *srv, Req *r)
169 if(srv->rref.ref != 1){
173 if(strncmp(r->ifcall.version, "9P", 2) != 0){
174 r->ofcall.version = "unknown";
175 r->ofcall.msize = 256;
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)
220 closefid(removefid(r->srv->fpool, r->afid->fid));
223 if(r->afid->omode == -1)
224 r->afid->omode = ORDWR;
228 sattach(Srv *srv, Req *r)
230 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
235 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
236 respond(r, Eunknownfid);
239 r->fid->uid = estrdup9p(r->ifcall.uname);
241 r->fid->file = srv->tree->root;
242 incref(r->fid->file);
243 r->ofcall.qid = r->fid->file->qid;
244 r->fid->qid = r->ofcall.qid;
253 rattach(Req *r, char *error)
256 closefid(removefid(r->srv->fpool, r->fid->fid));
260 sflush(Srv *srv, Req *r)
262 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
263 if(r->oldreq == nil || r->oldreq == r)
271 rflush(Req *r, char *error)
275 assert(error == nil);
279 if(or->responded == 0){
280 or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
281 or->flush[or->nflush++] = r;
283 return -1; /* delay response until or is responded */
293 oldwalk1(Fid *fid, char *name, void *arg)
300 e = srv->walk1(fid, name, &qid);
308 oldclone(Fid *fid, Fid *newfid, void *arg)
313 if(srv->clone == nil)
315 return srv->clone(fid, newfid);
319 swalk(Srv *srv, Req *r)
321 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
322 respond(r, Eunknownfid);
325 if(r->fid->omode != -1){
326 respond(r, "cannot clone open fid");
329 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
330 respond(r, Ewalknodir);
333 if(r->ifcall.fid != r->ifcall.newfid){
334 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
338 r->newfid->uid = estrdup9p(r->fid->uid);
340 incref(&r->fid->ref);
346 walkandclone(r, oldwalk1, oldclone, srv);
350 sysfatal("no walk function, no file trees");
353 rwalk(Req *r, char *error)
355 if(error || r->ofcall.nwqid < r->ifcall.nwname){
356 if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
357 closefid(removefid(r->srv->fpool, r->newfid->fid));
358 if (r->ofcall.nwqid==0){
359 if(error==nil && r->ifcall.nwname!=0)
360 r->error = Enotfound;
362 r->error = nil; // No error on partial walks
364 if(r->ofcall.nwqid == 0){
366 r->newfid->qid = r->fid->qid;
368 /* if file trees are in use, filewalk took care of the rest */
369 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
375 dirwritable(Fid *fid)
382 if(f->parent && !hasperm(f->parent, fid->uid, AWRITE)){
392 sopen(Srv *srv, Req *r)
396 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
397 respond(r, Eunknownfid);
400 if(r->fid->omode != -1){
404 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
408 r->ofcall.qid = r->fid->qid;
409 switch(r->ifcall.mode&3){
426 if(r->ifcall.mode&OTRUNC)
428 if((r->fid->qid.type&QTDIR) && p!=AREAD){
433 if(!hasperm(r->fid->file, r->fid->uid, p)){
437 if((r->ifcall.mode&ORCLOSE) && !dirwritable(r->fid)){
441 r->ofcall.qid = r->fid->file->qid;
442 if((r->ofcall.qid.type&QTDIR)
443 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
444 respond(r, "opendirfile failed");
455 screate(Srv *srv, Req *r)
457 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
458 respond(r, Eunknownfid);
459 else if(r->fid->omode != -1)
461 else if(!(r->fid->qid.type&QTDIR))
462 respond(r, Ecreatenondir);
463 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
468 respond(r, Enocreate);
472 ropen(Req *r, char *error)
477 fprint(2, "fid mode is %x\n", (int)r->ifcall.mode);
478 if(r->ofcall.qid.type&QTDIR)
479 r->fid->diroffset = 0;
480 r->fid->qid = r->ofcall.qid;
481 r->fid->omode = r->ifcall.mode;
485 sread(Srv *srv, Req *r)
489 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
490 respond(r, Eunknownfid);
507 if((int)r->ifcall.count < 0){
511 if(r->ifcall.offset < 0
512 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
513 respond(r, Ebadoffset);
516 if(r->ifcall.count > srv->msize - IOHDRSZ)
517 r->ifcall.count = srv->msize - IOHDRSZ;
518 r->rbuf = emalloc9p(r->ifcall.count);
519 r->ofcall.data = r->rbuf;
520 if((r->fid->qid.type&QTDIR) && r->fid->file){
521 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count, r->ifcall.offset);
528 respond(r, "no srv->read");
531 rread(Req *r, char *error)
533 if(error==nil && (r->fid->qid.type&QTDIR))
534 r->fid->diroffset = r->ifcall.offset + r->ofcall.count;
538 swrite(Srv *srv, Req *r)
542 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
543 respond(r, Eunknownfid);
559 if(r->fid->qid.type&QTDIR){
563 if((int)r->ifcall.count < 0){
567 if(r->ifcall.offset < 0){
571 if(r->ifcall.count > srv->msize - IOHDRSZ)
572 r->ifcall.count = srv->msize - IOHDRSZ;
576 respond(r, Enowrite);
579 rwrite(Req *r, char *error)
584 r->fid->file->qid.vers++;
588 sclunk(Srv *srv, Req *r)
590 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
591 respond(r, Eunknownfid);
601 sremove(Srv *srv, Req *r)
603 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
604 respond(r, Eunknownfid);
607 if(!dirwritable(r->fid)){
614 respond(r, r->fid->file ? nil : Enoremove);
617 rremove(Req *r, char *error, char *errbuf)
622 if(removefile(r->fid->file) < 0){
623 snprint(errbuf, ERRMAX, "remove %s: %r",
632 sstat(Srv *srv, Req *r)
634 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
635 respond(r, Eunknownfid);
639 /* should we rlock the file? */
640 r->d = r->fid->file->Dir;
642 r->d.name = estrdup9p(r->d.name);
644 r->d.uid = estrdup9p(r->d.uid);
646 r->d.gid = estrdup9p(r->d.gid);
648 r->d.muid = estrdup9p(r->d.muid);
652 else if(r->fid->file)
658 rstat(Req *r, char *error)
666 if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
667 r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
670 n = GBIT16(tmp)+BIT16SZ;
671 statbuf = emalloc9p(n);
673 r->error = "out of memory";
676 r->ofcall.nstat = convD2M(&r->d, statbuf, n);
677 r->ofcall.stat = statbuf; /* freed in closereq */
678 if(r->ofcall.nstat <= BIT16SZ){
679 r->error = "convD2M fails";
686 swstat(Srv *srv, Req *r)
688 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
689 respond(r, Eunknownfid);
692 if(srv->wstat == nil){
693 respond(r, Enowstat);
696 if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
700 if(r->d.qid.path != ~0 && r->d.qid.path != r->fid->qid.path){
701 respond(r, "wstat -- attempt to change qid.path");
704 if(r->d.qid.vers != ~0 && r->d.qid.vers != r->fid->qid.vers){
705 respond(r, "wstat -- attempt to change qid.vers");
709 if(r->d.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
710 respond(r, "wstat -- unknown bits in mode");
713 if(r->d.qid.type != (uchar)~0 && r->d.qid.type != ((r->d.mode>>24)&0xFF)){
714 respond(r, "wstat -- qid.type/mode mismatch");
717 if(((r->d.mode>>24) ^ r->fid->qid.type) & ~(QTAPPEND|QTEXCL|QTTMP)){
718 respond(r, "wstat -- attempt to change qid.type");
722 if(r->d.qid.type != (uchar)~0 && r->d.qid.type != r->fid->qid.type){
723 respond(r, "wstat -- attempt to change qid.type");
734 static void srvclose(Srv *);
742 while(r = getreq(srv)){
745 respond(r, r->error);
749 switch(r->ifcall.type){
751 respond(r, "unknown message");
753 case Tversion: sversion(srv, r); break;
754 case Tauth: sauth(srv, r); break;
755 case Tattach: sattach(srv, r); break;
756 case Tflush: sflush(srv, r); break;
757 case Twalk: swalk(srv, r); break;
758 case Topen: sopen(srv, r); break;
759 case Tcreate: screate(srv, r); break;
760 case Tread: sread(srv, r); break;
761 case Twrite: swrite(srv, r); break;
762 case Tclunk: sclunk(srv, r); break;
763 case Tremove: sremove(srv, r); break;
764 case Tstat: sstat(srv, r); break;
765 case Twstat: swstat(srv, r); break;
767 if(srv->sref.ref > 8 && srv->spid != getpid()){
769 qunlock(&srv->slock);
772 qunlock(&srv->slock);
775 if(srv->end && srv->sref.ref == 1)
777 if(decref(&srv->sref) == 0)
784 if(srv->rref.ref || srv->sref.ref)
788 fprint(2, "srvclose\n");
795 freefidpool(srv->fpool);
797 freereqpool(srv->rpool);
814 if(decref(&srv->sref) == 0){
816 _forker(srvwork, srv, 0);
818 qunlock(&srv->slock);
824 fmtinstall('D', dirfmt);
825 fmtinstall('F', fcallfmt);
827 srv->spid = getpid();
828 memset(&srv->sref, 0, sizeof(srv->sref));
829 memset(&srv->rref, 0, sizeof(srv->rref));
831 if(srv->fpool == nil)
832 srv->fpool = allocfidpool(srv->destroyfid);
833 if(srv->rpool == nil)
834 srv->rpool = allocreqpool(srv->destroyreq);
836 srv->msize = 8192+IOHDRSZ;
838 changemsize(srv, srv->msize);
840 srv->fpool->srv = srv;
841 srv->rpool->srv = srv;
851 respond(Req *r, char *error)
860 assert(r->responded == 0);
863 switch(r->ifcall.type){
865 * Flush is special. If the handler says so, we return
866 * without further processing. Respond will be called
867 * again once it is safe.
870 if(rflush(r, error)<0)
873 case Tversion: rversion(r, error); break;
874 case Tauth: rauth(r, error); break;
875 case Tattach: rattach(r, error); break;
876 case Twalk: rwalk(r, error); break;
877 case Topen: ropen(r, error); break;
878 case Tcreate: ropen(r, error); break;
879 case Tread: rread(r, error); break;
880 case Twrite: rwrite(r, error); break;
881 case Tclunk: rclunk(r, error); break;
882 case Tremove: rremove(r, error, errbuf); break;
883 case Tstat: rstat(r, error); break;
884 case Twstat: rwstat(r, error); break;
887 r->ofcall.tag = r->ifcall.tag;
888 r->ofcall.type = r->ifcall.type+1;
890 setfcallerror(&r->ofcall, r->error);
893 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
896 n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
898 fprint(2, "msize = %d n = %d %F\n", srv->msize, n, &r->ofcall);
902 if(r->pool) /* not a fake */
903 closereq(removereq(r->pool, r->ifcall.tag));
904 m = write(srv->outfd, srv->wbuf, n);
906 fprint(2, "lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
907 qunlock(&srv->wlock);
909 qlock(&r->lk); /* no one will add flushes now */
913 for(i=0; i<r->nflush; i++)
914 respond(r->flush[i], nil);
924 if(decref(&srv->rref) == 0)
933 rerrstr(errbuf, sizeof errbuf);