9 /* static char Ebadattach[] = "unknown specifier in attach"; */
10 static char Ebadoffset[] = "bad offset";
11 /* static char Ebadcount[] = "bad count"; */
12 static char Ebotch[] = "9P protocol botch";
13 static char Ecreatenondir[] = "create in non-directory";
14 static char Edupfid[] = "duplicate fid";
15 static char Eduptag[] = "duplicate tag";
16 static char Eisdir[] = "is a directory";
17 static char Enocreate[] = "create prohibited";
18 /* static char Enomem[] = "out of memory"; */
19 static char Enoremove[] = "remove prohibited";
20 static char Enostat[] = "stat prohibited";
21 static char Enotfound[] = "file not found";
22 /* static char Enowrite[] = "write prohibited"; */
23 static char Enowstat[] = "wstat prohibited";
24 static char Eperm[] = "permission denied";
25 static char Eunknownfid[] = "unknown fid";
26 static char Ebaddir[] = "bad directory in wstat";
27 static char Ewalknodir[] = "walk in non-directory";
30 setfcallerror(Fcall *f, char *err)
37 changemsize(Srv *srv, int msize)
39 if(srv->rbuf && srv->wbuf && srv->msize == msize)
46 srv->rbuf = emalloc9p(msize);
47 srv->wbuf = emalloc9p(msize);
61 if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
66 buf = emalloc9p(n+1); /* +1 for NUL in swrite */
67 memmove(buf, s->rbuf, n);
70 if(convM2S(buf, n, &f) != n){
75 if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */
76 r = emalloc9p(sizeof *r);
87 fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
95 memset(&r->ofcall, 0, sizeof r->ofcall);
96 r->type = r->ifcall.type;
100 fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
102 fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
117 for(i=0; i<r->ifcall.nwname; i++)
118 if(f = walkfile(f, r->ifcall.wname[i]))
119 r->ofcall.wqid[i] = f->dir.qid;
126 r->newfid->qid = r->newfid->file->dir.qid;
132 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
137 if(r->fid == r->newfid && r->ifcall.nwname > 1){
138 respond(r, "lib9p: unused documented feature not implemented");
142 if(r->fid != r->newfid){
143 r->newfid->qid = r->fid->qid;
144 if(clone && (e = clone(r->fid, r->newfid, arg))){
151 for(i=0; i<r->ifcall.nwname; i++){
152 if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
154 r->ofcall.wqid[i] = r->newfid->qid;
165 sversion(Srv *srv, Req *r)
169 if(strncmp(r->ifcall.version, "9P2000", 6) != 0){
170 r->ofcall.version = "unknown";
174 r->ofcall.version = "9P2000";
175 r->ofcall.msize = r->ifcall.msize;
180 rversion(Req *r, char *error)
182 assert(error == nil);
183 changemsize(r->srv, r->ofcall.msize);
187 sauth(Srv *srv, Req *r)
191 if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
198 snprint(e, sizeof e, "%s: authentication not required", argv0);
204 rauth(Req *r, char *error)
207 closefid(removefid(r->srv->fpool, r->afid->fid));
211 sattach(Srv *srv, Req *r)
213 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
218 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
219 respond(r, Eunknownfid);
222 r->fid->uid = estrdup9p(r->ifcall.uname);
224 r->fid->file = srv->tree->root;
225 incref(&r->fid->file->ref);
226 r->ofcall.qid = r->fid->file->dir.qid;
227 r->fid->qid = r->ofcall.qid;
237 rattach(Req *r, char *error)
240 closefid(removefid(r->srv->fpool, r->fid->fid));
244 sflush(Srv *srv, Req *r)
246 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
247 if(r->oldreq == nil || r->oldreq == r)
256 rflush(Req *r, char *error)
260 assert(error == nil);
264 if(or->responded == 0){
265 or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
266 or->flush[or->nflush++] = r;
268 return -1; /* delay response until or is responded */
278 oldwalk1(Fid *fid, char *name, void *arg)
285 e = srv->walk1(fid, name, &qid);
293 oldclone(Fid *fid, Fid *newfid, void *arg)
298 if(srv->clone == nil)
300 return srv->clone(fid, newfid);
304 swalk(Srv *srv, Req *r)
306 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
307 respond(r, Eunknownfid);
310 if(r->fid->omode != -1){
311 respond(r, "cannot clone open fid");
314 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
315 respond(r, Ewalknodir);
318 if(r->ifcall.fid != r->ifcall.newfid){
319 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
323 r->newfid->uid = estrdup9p(r->fid->uid);
325 incref(&r->fid->ref);
331 walkandclone(r, oldwalk1, oldclone, srv);
335 sysfatal("no walk function, no file trees");
338 rwalk(Req *r, char *error)
340 if(error || r->ofcall.nwqid < r->ifcall.nwname){
341 if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
342 closefid(removefid(r->srv->fpool, r->newfid->fid));
343 if (r->ofcall.nwqid==0){
344 if(error==nil && r->ifcall.nwname!=0)
345 r->error = Enotfound;
347 r->error = nil; /* No error on partial walks */
349 if(r->ofcall.nwqid == 0){
351 r->newfid->qid = r->fid->qid;
353 /* if file trees are in use, filewalk took care of the rest */
354 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
360 sopen(Srv *srv, Req *r)
364 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
365 respond(r, Eunknownfid);
368 if(r->fid->omode != -1){
372 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
376 r->ofcall.qid = r->fid->qid;
377 switch(r->ifcall.mode&3){
393 if(r->ifcall.mode&OTRUNC)
395 if((r->fid->qid.type&QTDIR) && p!=AREAD){
400 if(!hasperm(r->fid->file, r->fid->uid, p)){
405 if((r->ifcall.mode&ORCLOSE)
406 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
410 r->ofcall.qid = r->fid->file->dir.qid;
411 if((r->ofcall.qid.type&QTDIR)
412 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
413 respond(r, "opendirfile failed");
424 ropen(Req *r, char *error)
430 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
431 write(2, errbuf, strlen(errbuf));
433 r->fid->omode = r->ifcall.mode;
434 r->fid->qid = r->ofcall.qid;
435 if(r->ofcall.qid.type&QTDIR)
436 r->fid->diroffset = 0;
440 screate(Srv *srv, Req *r)
442 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
443 respond(r, Eunknownfid);
444 else if(r->fid->omode != -1)
446 else if(!(r->fid->qid.type&QTDIR))
447 respond(r, Ecreatenondir);
448 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
453 respond(r, Enocreate);
457 rcreate(Req *r, char *error)
461 r->fid->omode = r->ifcall.mode;
462 r->fid->qid = r->ofcall.qid;
466 sread(Srv *srv, Req *r)
470 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
471 respond(r, Eunknownfid);
474 if((int32)r->ifcall.count < 0){
478 if(r->ifcall.offset < 0
479 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
480 respond(r, Ebadoffset);
484 if(r->ifcall.count > srv->msize - IOHDRSZ)
485 r->ifcall.count = srv->msize - IOHDRSZ;
486 r->rbuf = emalloc9p(r->ifcall.count);
487 r->ofcall.data = r->rbuf;
488 o = r->fid->omode & 3;
489 if(o != OREAD && o != ORDWR && o != OEXEC){
493 if((r->fid->qid.type&QTDIR) && r->fid->file){
494 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
501 respond(r, "no srv->read");
505 rread(Req *r, char *error)
507 if(error==nil && (r->fid->qid.type&QTDIR))
508 r->fid->diroffset = r->ifcall.offset + r->ofcall.count;
512 swrite(Srv *srv, Req *r)
517 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
518 respond(r, Eunknownfid);
521 if((int32)r->ifcall.count < 0){
525 if(r->ifcall.offset < 0){
529 if(r->ifcall.count > srv->msize - IOHDRSZ)
530 r->ifcall.count = srv->msize - IOHDRSZ;
531 o = r->fid->omode & 3;
532 if(o != OWRITE && o != ORDWR){
533 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
538 r->ifcall.data[r->ifcall.count] = 0; /* enough room - see getreq */
541 respond(r, "no srv->write");
544 rwrite(Req *r, char *error)
549 r->fid->file->dir.qid.vers++;
553 sclunk(Srv *srv, Req *r)
555 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
556 respond(r, Eunknownfid);
561 rclunk(Req *r, char *msg)
568 sremove(Srv *srv, Req *r)
570 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
571 respond(r, Eunknownfid);
575 if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
582 respond(r, r->fid->file ? nil : Enoremove);
585 rremove(Req *r, char *error, char *errbuf)
590 if(removefile(r->fid->file) < 0){
591 snprint(errbuf, ERRMAX, "remove %s: %r",
592 r->fid->file->dir.name);
600 sstat(Srv *srv, Req *r)
602 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
603 respond(r, Eunknownfid);
607 r->d = r->fid->file->dir;
609 r->d.name = estrdup9p(r->d.name);
611 r->d.uid = estrdup9p(r->d.uid);
613 r->d.gid = estrdup9p(r->d.gid);
615 r->d.muid = estrdup9p(r->d.muid);
619 else if(r->fid->file)
625 rstat(Req *r, char *error)
633 if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
634 r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
637 n = GBIT16(tmp)+BIT16SZ;
638 statbuf = emalloc9p(n);
640 r->error = "out of memory";
643 r->ofcall.nstat = convD2M(&r->d, statbuf, n);
644 r->ofcall.stat = statbuf; /* freed in closereq */
645 if(r->ofcall.nstat <= BIT16SZ){
646 r->error = "convD2M fails";
653 swstat(Srv *srv, Req *r)
655 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
656 respond(r, Eunknownfid);
659 if(srv->wstat == nil){
660 respond(r, Enowstat);
663 if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
667 if((ushort)~r->d.type){
668 respond(r, "wstat -- attempt to change type");
672 respond(r, "wstat -- attempt to change dev");
675 if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
676 respond(r, "wstat -- attempt to change qid");
679 if(r->d.muid && r->d.muid[0]){
680 respond(r, "wstat -- attempt to change muid");
683 if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
684 respond(r, "wstat -- attempt to change DMDIR bit");
691 rwstat(Req *r, char *msg)
702 fmtinstall('D', dirfmt);
703 fmtinstall('F', fcallfmt);
705 if(srv->fpool == nil)
706 srv->fpool = allocfidpool(srv->destroyfid);
707 if(srv->rpool == nil)
708 srv->rpool = allocreqpool(srv->destroyreq);
710 srv->msize = 8192+IOHDRSZ;
712 changemsize(srv, srv->msize);
714 srv->fpool->srv = srv;
715 srv->rpool->srv = srv;
720 while(r = getreq(srv)){
722 respond(r, r->error);
725 switch(r->ifcall.type){
727 respond(r, "unknown message");
729 case Tversion: sversion(srv, r); break;
730 case Tauth: sauth(srv, r); break;
731 case Tattach: sattach(srv, r); break;
732 case Tflush: sflush(srv, r); break;
733 case Twalk: swalk(srv, r); break;
734 case Topen: sopen(srv, r); break;
735 case Tcreate: screate(srv, r); break;
736 case Tread: sread(srv, r); break;
737 case Twrite: swrite(srv, r); break;
738 case Tclunk: sclunk(srv, r); break;
739 case Tremove: sremove(srv, r); break;
740 case Tstat: sstat(srv, r); break;
741 case Twstat: swstat(srv, r); break;
750 respond(Req *r, char *error)
764 assert(r->responded == 0);
767 switch(r->ifcall.type){
771 * Flush is special. If the handler says so, we return
772 * without further processing. Respond will be called
773 * again once it is safe.
776 if(rflush(r, error)<0)
779 case Tversion: rversion(r, error); break;
780 case Tauth: rauth(r, error); break;
781 case Tattach: rattach(r, error); break;
782 case Twalk: rwalk(r, error); break;
783 case Topen: ropen(r, error); break;
784 case Tcreate: rcreate(r, error); break;
785 case Tread: rread(r, error); break;
786 case Twrite: rwrite(r, error); break;
787 case Tclunk: rclunk(r, error); break;
788 case Tremove: rremove(r, error, errbuf); break;
789 case Tstat: rstat(r, error); break;
790 case Twstat: rwstat(r, error); break;
793 r->ofcall.tag = r->ifcall.tag;
794 r->ofcall.type = r->ifcall.type+1;
796 setfcallerror(&r->ofcall, r->error);
802 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
805 n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
807 fprint(2, "n = %d %F\n", n, &r->ofcall);
812 * There is a race here - we must remove the entry before
813 * the write, so that if the client is very fast and reuses the
814 * tag, the read loop won't think it is still in use.
816 * By removing the entry before the write, we open up a
817 * race with incoming Tflush messages. Specifically, an
818 * incoming Tflush might not see r even though it has not
819 * yet been responded to. It would then send an Rflush
820 * immediately, potentially before we do the write. This can't
821 * happen because we already old srv->wlock, so nothing
822 * is going out on the wire before this write.
824 if(r->pool) /* not a fake */
825 closereq(removereq(r->pool, r->ifcall.tag));
830 if(r->ref.ref == 1+r->nflush)
833 * There are no references other than in our r->flush array,
834 * so no one else should be accessing r concurrently.
835 * Close the fid now, before responding to the message.
837 * If the client is behaving (there are no outstanding T-messages
838 * that reference r->fid) and the message is a Tclunk or Tremove,
839 * then this closefid will call destroyfid.
841 * This means destroyfid can't piddle around
842 * indefinitely (we're holding srv->wlock!), but it provides
843 * for tighter semantics as to when destroyfid is called.
845 * LANL has observed cases where waiting until after the write
846 * can delay a closefid on a Twrite for many 9P transactions,
847 * so that a handful of transactions can happen including a Tclunk
848 * and a Topen, and the original fid will still not be destroyed.
854 m = write(srv->outfd, srv->wbuf, n);
856 sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
857 qunlock(&srv->wlock);
860 qlock(&r->lk); /* no one will add flushes now */
862 for(i=0; i<r->nflush; i++){
863 r->flush[i]->oldreq = nil; /* so it doesn't try to lock us! */
864 respond(r->flush[i], nil);
878 postfd(char *name, int pfd)
883 snprint(buf, sizeof buf, "/srv/%s", name);
885 fprint(2, "postfd %s\n", buf);
886 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
889 fprint(2, "create fails: %r\n");
892 if(fprint(fd, "%d", pfd) < 0){
894 fprint(2, "write fails: %r\n");
899 fprint(2, "postfd successful\n");