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 if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
67 buf = emalloc9p(n+1); /* +1 for NUL in swrite */
68 memmove(buf, s->rbuf, n);
71 if(convM2Su(buf, n, &f, s->dotu) != n){
76 if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */
77 r = emalloc9p(sizeof *r);
88 fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
96 memset(&r->ofcall, 0, sizeof r->ofcall);
97 r->type = r->ifcall.type;
101 fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
103 fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
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;
127 r->newfid->qid = r->newfid->file->dir.qid;
133 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
138 if(r->fid == r->newfid && r->ifcall.nwname > 1){
139 respond(r, "lib9p: unused documented feature not implemented");
143 if(r->fid != r->newfid){
144 r->newfid->qid = r->fid->qid;
145 if(clone && (e = clone(r->fid, r->newfid, arg))){
152 for(i=0; i<r->ifcall.nwname; i++){
153 if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
155 r->ofcall.wqid[i] = r->newfid->qid;
166 sversion(Srv *srv, Req *r)
170 if(strncmp(r->ifcall.version, "9P", 2) != 0){
171 r->ofcall.version = "unknown";
176 if(strncmp(r->ifcall.version, "9P2000.u", 8) == 0){
177 r->ofcall.version = "9P2000.u";
180 r->ofcall.version = "9P2000";
183 r->ofcall.msize = r->ifcall.msize;
187 rversion(Req *r, char *error)
189 assert(error == nil);
190 changemsize(r->srv, r->ofcall.msize);
194 sauth(Srv *srv, Req *r)
198 if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
205 snprint(e, sizeof e, "%s: authentication not required", argv0);
210 rauth(Req *r, char *error)
213 closefid(removefid(r->srv->fpool, r->afid->fid));
217 sattach(Srv *srv, Req *r)
219 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
224 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
225 respond(r, Eunknownfid);
228 r->fid->uid = estrdup9p(r->ifcall.uname);
230 r->fid->file = srv->tree->root;
231 /* BUG? incref(r->fid->file) ??? */
232 r->ofcall.qid = r->fid->file->dir.qid;
233 r->fid->qid = r->ofcall.qid;
242 rattach(Req *r, char *error)
245 closefid(removefid(r->srv->fpool, r->fid->fid));
249 sflush(Srv *srv, Req *r)
251 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
252 if(r->oldreq == nil || r->oldreq == r)
260 rflush(Req *r, char *error)
264 assert(error == nil);
268 if(or->responded == 0){
269 or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
270 or->flush[or->nflush++] = r;
272 return -1; /* delay response until or is responded */
282 oldwalk1(Fid *fid, char *name, void *arg)
289 e = srv->walk1(fid, name, &qid);
297 oldclone(Fid *fid, Fid *newfid, void *arg)
302 if(srv->clone == nil)
304 return srv->clone(fid, newfid);
308 swalk(Srv *srv, Req *r)
310 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
311 respond(r, Eunknownfid);
314 if(r->fid->omode != -1){
315 respond(r, "cannot clone open fid");
318 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
319 respond(r, Ewalknodir);
322 if(r->ifcall.fid != r->ifcall.newfid){
323 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
327 r->newfid->uid = estrdup9p(r->fid->uid);
329 incref(&r->fid->ref);
335 walkandclone(r, oldwalk1, oldclone, srv);
339 sysfatal("no walk function, no file trees");
342 rwalk(Req *r, char *error)
344 if(error || r->ofcall.nwqid < r->ifcall.nwname){
345 if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
346 closefid(removefid(r->srv->fpool, r->newfid->fid));
347 if (r->ofcall.nwqid==0){
348 if(error==nil && r->ifcall.nwname!=0)
349 r->error = Enotfound;
351 r->error = nil; /* No error on partial walks */
353 if(r->ofcall.nwqid == 0){
355 r->newfid->qid = r->fid->qid;
357 /* if file trees are in use, filewalk took care of the rest */
358 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
364 sopen(Srv *srv, Req *r)
368 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
369 respond(r, Eunknownfid);
372 if(r->fid->omode != -1){
376 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
380 r->ofcall.qid = r->fid->qid;
381 switch(r->ifcall.mode&3){
397 if(r->ifcall.mode&OTRUNC)
399 if((r->fid->qid.type&QTDIR) && p!=AREAD){
404 if(!hasperm(r->fid->file, r->fid->uid, p)){
409 if((r->ifcall.mode&ORCLOSE)
410 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
414 r->ofcall.qid = r->fid->file->dir.qid;
415 if((r->ofcall.qid.type&QTDIR)
416 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
417 respond(r, "opendirfile failed");
427 ropen(Req *r, char *error)
433 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
434 write(2, errbuf, strlen(errbuf));
436 r->fid->omode = r->ifcall.mode;
437 r->fid->qid = r->ofcall.qid;
438 if(r->ofcall.qid.type&QTDIR)
439 r->fid->diroffset = 0;
443 screate(Srv *srv, Req *r)
445 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
446 respond(r, Eunknownfid);
447 else if(r->fid->omode != -1)
449 else if(!(r->fid->qid.type&QTDIR))
450 respond(r, Ecreatenondir);
451 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
456 respond(r, Enocreate);
459 rcreate(Req *r, char *error)
463 r->fid->omode = r->ifcall.mode;
464 r->fid->qid = r->ofcall.qid;
468 sread(Srv *srv, Req *r)
472 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
473 respond(r, Eunknownfid);
476 if(r->ifcall.count < 0){
480 if(r->ifcall.offset < 0
481 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
482 respond(r, Ebadoffset);
486 if(r->ifcall.count > srv->msize - IOHDRSZ)
487 r->ifcall.count = srv->msize - IOHDRSZ;
488 r->rbuf = emalloc9p(r->ifcall.count);
489 r->ofcall.data = r->rbuf;
490 o = r->fid->omode & 3;
491 if(o != OREAD && o != ORDWR && o != OEXEC){
495 if((r->fid->qid.type&QTDIR) && r->fid->file){
496 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
503 respond(r, "no srv->read");
506 rread(Req *r, char *error)
508 if(error==nil && (r->fid->qid.type&QTDIR))
509 r->fid->diroffset += r->ofcall.count;
513 swrite(Srv *srv, Req *r)
518 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
519 respond(r, Eunknownfid);
522 if(r->ifcall.count < 0){
526 if(r->ifcall.offset < 0){
530 if(r->ifcall.count > srv->msize - IOHDRSZ)
531 r->ifcall.count = srv->msize - IOHDRSZ;
532 o = r->fid->omode & 3;
533 if(o != OWRITE && o != ORDWR){
534 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
539 r->ifcall.data[r->ifcall.count] = 0; /* enough room - see getreq */
542 respond(r, "no srv->write");
545 rwrite(Req *r, char *error)
550 r->fid->file->dir.qid.vers++;
554 sclunk(Srv *srv, Req *r)
556 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
557 respond(r, Eunknownfid);
562 rclunk(Req *r, char *msg)
569 sremove(Srv *srv, Req *r)
571 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
572 respond(r, Eunknownfid);
576 if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
583 respond(r, r->fid->file ? nil : Enoremove);
586 rremove(Req *r, char *error, char *errbuf)
591 if(removefile(r->fid->file) < 0){
592 snprint(errbuf, ERRMAX, "remove %s: %r",
593 r->fid->file->dir.name);
601 sstat(Srv *srv, Req *r)
603 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
604 respond(r, Eunknownfid);
608 r->d = r->fid->file->dir;
610 r->d.name = estrdup9p(r->d.name);
612 r->d.uid = estrdup9p(r->d.uid);
614 r->d.gid = estrdup9p(r->d.gid);
616 r->d.muid = estrdup9p(r->d.muid);
620 else if(r->fid->file)
626 rstat(Req *r, char *error)
634 if(convD2Mu(&r->d, tmp, BIT16SZ, r->srv->dotu) != BIT16SZ){
635 r->error = "convD2Mu(_,_,BIT16SZ,_) did not return BIT16SZ";
638 n = GBIT16(tmp)+BIT16SZ;
639 statbuf = emalloc9p(n);
641 r->error = "out of memory";
644 r->ofcall.nstat = convD2Mu(&r->d, statbuf, n, r->srv->dotu);
645 r->ofcall.stat = statbuf; /* freed in closereq */
646 if(r->ofcall.nstat <= BIT16SZ){
647 r->error = "convD2Mu fails";
654 swstat(Srv *srv, Req *r)
656 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
657 respond(r, Eunknownfid);
660 if(srv->wstat == nil){
661 respond(r, Enowstat);
664 if(convM2Du(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat, srv->dotu) != r->ifcall.nstat){
668 if((ushort)~r->d.type){
669 respond(r, "wstat -- attempt to change type");
673 respond(r, "wstat -- attempt to change dev");
676 if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
677 respond(r, "wstat -- attempt to change qid");
680 if(r->d.muid && r->d.muid[0]){
681 respond(r, "wstat -- attempt to change muid");
684 if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
685 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);
799 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
802 n = convS2Mu(&r->ofcall, srv->wbuf, srv->msize, srv->dotu);
804 fprint(2, "n = %d %F\n", n, &r->ofcall);
809 * There is a race here - we must remove the entry before
810 * the write, so that if the client is very fast and reuses the
811 * tag, the read loop won't think it is still in use.
813 * By removing the entry before the write, we open up a
814 * race with incoming Tflush messages. Specifically, an
815 * incoming Tflush might not see r even though it has not
816 * yet been responded to. It would then send an Rflush
817 * immediately, potentially before we do the write. This can't
818 * happen because we already old srv->wlock, so nothing
819 * is going out on the wire before this write.
821 if(r->pool) /* not a fake */
822 closereq(removereq(r->pool, r->ifcall.tag));
827 if(r->ref.ref == 1+r->nflush)
830 * There are no references other than in our r->flush array,
831 * so no one else should be accessing r concurrently.
832 * Close the fid now, before responding to the message.
834 * If the client is behaving (there are no outstanding T-messages
835 * that reference r->fid) and the message is a Tclunk or Tremove,
836 * then this closefid will call destroyfid.
838 * This means destroyfid can't piddle around
839 * indefinitely (we're holding srv->wlock!), but it provides
840 * for tighter semantics as to when destroyfid is called.
842 * LANL has observed cases where waiting until after the write
843 * can delay a closefid on a Twrite for many 9P transactions,
844 * so that a handful of transactions can happen including a Tclunk
845 * and a Topen, and the original fid will still not be destroyed.
851 m = write(srv->outfd, srv->wbuf, n);
853 sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
854 qunlock(&srv->wlock);
857 qlock(&r->lk); /* no one will add flushes now */
859 for(i=0; i<r->nflush; i++){
860 r->flush[i]->oldreq = nil; /* so it doesn't try to lock us! */
861 respond(r->flush[i], nil);
875 postfd(char *name, int pfd)
880 snprint(buf, sizeof buf, "/srv/%s", name);
882 fprint(2, "postfd %s\n", buf);
883 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
886 fprint(2, "create fails: %r\n");
889 if(fprint(fd, "%d", pfd) < 0){
891 fprint(2, "write fails: %r\n");
896 fprint(2, "postfd successful\n");