1 /* Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com>
2 * See LICENSE file for license details.
8 #include <sys/socket.h>
9 #include "mixp_local.h"
11 #include <9p-mixp/transport.h>
12 #include <9p-mixp/intmap.h>
16 static void handlereq(Ixp9Req
*r
);
26 Eduptag
[] = "tag in use",
27 Edupfid
[] = "fid in use",
28 Enofunc
[] = "function not implemented",
29 Ebotch
[] = "9P protocol botch",
30 Enofile
[] = "file does not exist",
31 Enofid
[] = "fid does not exist",
32 Enotag
[] = "tag does not exist",
33 Enotdir
[] = "not a directory",
34 Eintr
[] = "interrupted",
35 Eisdir
[] = "cannot perform operation on a directory";
46 MIXP_CONNECTION
*conn
;
47 MIXP_MUTEX rlock
, wlock
;
51 void *taghash
[TAG_BUCKETS
];
52 void *fidhash
[FID_BUCKETS
];
56 decref_p9conn(Ixp9Conn
*pc
) {
57 mixp_thread
->lock(&pc
->wlock
);
59 mixp_thread
->unlock(&pc
->wlock
);
62 mixp_thread
->unlock(&pc
->wlock
);
64 mixp_thread
->mdestroy(&pc
->rlock
);
65 mixp_thread
->mdestroy(&pc
->wlock
);
67 mixp_intmap_free(&pc
->tagmap
, NULL
);
68 mixp_intmap_free(&pc
->fidmap
, NULL
);
70 MIXP_FREE(pc
->rmsg
.data
)
71 MIXP_FREE(pc
->wmsg
.data
)
76 createfid(MIXP_INTMAP
*map
, int fid
, Ixp9Conn
*pc
) {
79 f
= calloc(1,sizeof(IxpFid
));
85 if(mixp_intmap_caninsertkey(map
, fid
, f
))
87 fprintf(stderr
,"couldnt insert key (already exists): %d\n",fid
);
93 destroyfid(Ixp9Conn
*pc
, unsigned long fid
) {
96 f
= mixp_intmap_deletekey(&pc
->fidmap
, fid
);
108 Ixp9Req
* mixp_9req_alloc(Ixp9Conn
* conn
)
110 Ixp9Req
* req
= (Ixp9Req
*) calloc(1,sizeof(Ixp9Req
));
111 req
->ifcall
= (IxpFcall
*)calloc(1,sizeof(IxpFcall
));
112 req
->ofcall
= (IxpFcall
*)calloc(1,sizeof(IxpFcall
));
117 req
->srv
= conn
->srv
;
124 void mixp_9req_free(Ixp9Req
* req
)
128 mixp_fcall_free(req
->ifcall
);
129 mixp_fcall_free(req
->ofcall
);
133 decref_p9conn(req
->conn
);
140 handlefcall(MIXP_CONNECTION
*c
)
142 Ixp9Conn
* pc
= c
->aux
;
143 Ixp9Req
* req
= mixp_9req_alloc(pc
);
145 // lock the connection
146 mixp_thread
->lock(&pc
->rlock
);
148 // try to receive an packet - jump to Fail: if failed
149 if(mixp_recvmsg(c
->fd
, &pc
->rmsg
) == 0)
152 // decode incoming packet to to req->ifcall (IxpFcall)
153 if(ixp_msg2fcall(&pc
->rmsg
, req
->ifcall
) == 0)
156 // unlock the connection
157 mixp_thread
->unlock(&pc
->rlock
);
161 if(!mixp_intmap_caninsertkey(&pc
->tagmap
, req
->ifcall
->tag
, req
)) {
162 fprintf(stderr
,"duplicate tag: %d\n", req
->ifcall
->tag
);
163 ixp_respond(req
, Eduptag
);
173 mixp_thread
->unlock(&pc
->rlock
);
180 handlereq(Ixp9Req
*r
)
188 if (r
->ifcall
== NULL
)
190 fprintf(stderr
,"EMERG: handlereq() r->ifcall corrupt\n");
194 switch(r
->ifcall
->type
)
197 ixp_respond(r
, Enofunc
);
200 if(!strcmp(r
->ifcall
->Tversion
.version
, "9P"))
201 r
->ofcall
->Rversion
.version
= "9P";
202 else if(!strcmp(r
->ifcall
->Tversion
.version
, "9P2000"))
203 r
->ofcall
->Rversion
.version
= "9P2000";
206 fprintf(stderr
,"handlereq() TVersion: unknown version requested \"%s\"\n", r
->ifcall
->Tversion
.version
);
207 r
->ofcall
->Rversion
.version
= "unknown";
209 r
->ofcall
->Rversion
.msize
= r
->ifcall
->Tversion
.msize
;
210 if (r
->ofcall
->Rversion
.msize
== 0)
212 fprintf(stderr
,"handlereq() TVersion: got zero msize. setting to default: %d\n", IXP_MAX_MSG
);
213 r
->ofcall
->Rversion
.msize
= IXP_MAX_MSG
;
215 ixp_respond(r
, NULL
);
218 if(!(r
->fid
= createfid(&pc
->fidmap
, r
->ifcall
->fid
, pc
))) {
219 fprintf(stderr
,"TAttach: duplicate fid\n");
220 ixp_respond(r
, Edupfid
);
223 /* attach is a required function */
227 if(!(r
->fid
= mixp_intmap_lookupkey(&pc
->fidmap
, r
->ifcall
->fid
))) {
228 ixp_respond(r
, Enofid
);
232 ixp_respond(r
, NULL
);
238 if(!(r
->oldreq
= mixp_intmap_lookupkey(&pc
->tagmap
, r
->ifcall
->Tflush
.oldtag
))) {
239 ixp_respond(r
, Enotag
);
243 ixp_respond(r
, Enofunc
);
249 if(!(r
->fid
= mixp_intmap_lookupkey(&pc
->fidmap
, r
->ifcall
->fid
))) {
250 ixp_respond(r
, Enofid
);
253 if(r
->fid
->omode
!= -1) {
254 ixp_respond(r
, Ebotch
);
257 if(!(r
->fid
->qid
.type
& P9_QTDIR
)) {
258 ixp_respond(r
, Enotdir
);
261 if(!pc
->srv
->create
) {
262 ixp_respond(r
, Enofunc
);
268 if(!(r
->fid
= mixp_intmap_lookupkey(&pc
->fidmap
, r
->ifcall
->fid
))) {
269 ixp_respond(r
, Enofid
);
272 if((r
->fid
->qid
.type
& P9_QTDIR
) && (r
->ifcall
->Topen
.mode
|P9_ORCLOSE
) != (P9_OREAD
|P9_ORCLOSE
)) {
273 ixp_respond(r
, Eisdir
);
276 r
->ofcall
->Ropen
.qid
= r
->fid
->qid
;
278 ixp_respond(r
, Enofunc
);
284 if(!(r
->fid
= mixp_intmap_lookupkey(&pc
->fidmap
, r
->ifcall
->fid
))) {
285 ixp_respond(r
, Enofid
);
288 if(r
->fid
->omode
== -1 || r
->fid
->omode
== P9_OWRITE
) {
289 ixp_respond(r
, Ebotch
);
293 ixp_respond(r
, Enofunc
);
299 if(!(r
->fid
= mixp_intmap_lookupkey(&pc
->fidmap
, r
->ifcall
->fid
))) {
300 ixp_respond(r
, Enofid
);
303 if(!pc
->srv
->remove
) {
304 ixp_respond(r
, Enofunc
);
310 if(!(r
->fid
= mixp_intmap_lookupkey(&pc
->fidmap
, r
->ifcall
->fid
))) {
311 ixp_respond(r
, Enofid
);
315 ixp_respond(r
, Enofunc
);
321 if(!(r
->fid
= mixp_intmap_lookupkey(&pc
->fidmap
, r
->ifcall
->fid
))) {
322 ixp_respond(r
, Enofid
);
325 if(r
->fid
->omode
!= -1) {
326 ixp_respond(r
, "cannot walk from an open fid");
329 if(r
->ifcall
->Twalk
.nwname
&& !(r
->fid
->qid
.type
& P9_QTDIR
)) {
330 ixp_respond(r
, Enotdir
);
333 if((r
->ifcall
->fid
!= r
->ifcall
->Twalk
.newfid
)) {
334 if(!(r
->newfid
= createfid(&pc
->fidmap
, r
->ifcall
->Twalk
.newfid
, pc
))) {
335 ixp_respond(r
, Edupfid
);
341 ixp_respond(r
, Enofunc
);
347 if(!(r
->fid
= mixp_intmap_lookupkey(&pc
->fidmap
, r
->ifcall
->fid
))) {
348 ixp_respond(r
, Enofid
);
351 if((r
->fid
->omode
&3) != P9_OWRITE
&& (r
->fid
->omode
&3) != P9_ORDWR
) {
352 ixp_respond(r
, "write on fid not opened for writing");
355 if(!pc
->srv
->write
) {
356 ixp_respond(r
, Enofunc
);
361 /* Still to be implemented: wstat, auth */
366 ixp_respond(Ixp9Req
*r
, const char *error
) {
372 switch(r
->ifcall
->type
) {
375 assert(!"ixp_respond called on unsupported fcall type");
378 assert(error
== NULL
);
379 MIXP_FREE(r
->ifcall
->Tversion
.version
); // move this to mixp_fcall_free()
380 mixp_thread
->lock(&pc
->rlock
);
381 mixp_thread
->lock(&pc
->wlock
);
382 msize
= min(r
->ofcall
->Rversion
.msize
, IXP_MAX_MSG
);
385 fprintf(stderr
,"ixp_respond() P9_TVersion: msize<1 ! tweaking to IXP_MAX_MSG\n");
388 pc
->rmsg
.data
= ixp_erealloc(pc
->rmsg
.data
, msize
);
389 pc
->wmsg
.data
= ixp_erealloc(pc
->wmsg
.data
, msize
);
390 pc
->rmsg
.size
= msize
;
391 pc
->wmsg
.size
= msize
;
392 mixp_thread
->unlock(&pc
->wlock
);
393 mixp_thread
->unlock(&pc
->rlock
);
394 r
->ofcall
->Rversion
.msize
= msize
;
398 destroyfid(pc
, r
->fid
->fid
);
399 MIXP_FREE(r
->ifcall
->Tattach
.uname
)
400 MIXP_FREE(r
->ifcall
->Tattach
.aname
)
404 r
->fid
->omode
= r
->ifcall
->Tcreate
.mode
;
405 r
->fid
->qid
= r
->ofcall
->Rcreate
.qid
;
407 MIXP_FREE(r
->ifcall
->Tcreate
.name
)
408 printf("P9_TOpen: rmsg.size=%d\n", pc
->rmsg
.size
);
409 r
->ofcall
->Rcreate
.iounit
= pc
->rmsg
.size
- 16;
410 printf("P9_TOpen: sending iounit: %d\n", r
->ofcall
->Rcreate
.iounit
);
414 r
->fid
->omode
= r
->ifcall
->Tcreate
.mode
;
415 r
->fid
->qid
= r
->ofcall
->Rcreate
.qid
;
417 MIXP_FREE(r
->ifcall
->Tcreate
.name
)
418 r
->ofcall
->Rcreate
.iounit
= pc
->rmsg
.size
- 16;
421 if(error
|| r
->ofcall
->Rwalk
.nwqid
< r
->ifcall
->Twalk
.nwname
) {
422 if(r
->ifcall
->fid
!= r
->ifcall
->Twalk
.newfid
&& r
->newfid
)
423 destroyfid(pc
, r
->newfid
->fid
);
424 if(!error
&& r
->ofcall
->Rwalk
.nwqid
== 0)
427 if(r
->ofcall
->Rwalk
.nwqid
== 0)
428 r
->newfid
->qid
= r
->fid
->qid
;
430 r
->newfid
->qid
= r
->ofcall
->Rwalk
.wqid
[r
->ofcall
->Rwalk
.nwqid
-1];
432 MIXP_FREE(*r
->ifcall
->Twalk
.wname
)
435 MIXP_FREE(r
->ifcall
->Twrite
.data
)
439 destroyfid(pc
, r
->fid
->fid
);
443 destroyfid(pc
, r
->fid
->fid
);
444 r
->ofcall
->fid
= r
->ifcall
->fid
;
447 if((r
->oldreq
= mixp_intmap_lookupkey(&pc
->tagmap
, r
->ifcall
->Tflush
.oldtag
)))
448 ixp_respond(r
->oldreq
, Eintr
);
453 /* Still to be implemented: wstat, auth */
456 r
->ofcall
->tag
= r
->ifcall
->tag
;
459 r
->ofcall
->type
= r
->ifcall
->type
+ 1;
461 r
->ofcall
->type
= P9_RError
;
462 r
->ofcall
->Rerror
.ename
= strdup(error
);
465 mixp_intmap_deletekey(&pc
->tagmap
, r
->ifcall
->tag
);;
468 mixp_thread
->lock(&pc
->wlock
);
469 msize
= ixp_fcall2msg(&pc
->wmsg
, r
->ofcall
);
470 if(mixp_sendmsg(pc
->conn
->fd
, &pc
->wmsg
) != msize
)
471 ixp_hangup(pc
->conn
);
472 mixp_thread
->unlock(&pc
->wlock
);
475 switch(r
->ofcall
->type
) {
477 MIXP_FREE(r
->ofcall
->Rstat
.stat
)
480 // MIXP_FREE(r->ofcall->Rread.data)
485 /* Flush a pending request */
487 voidrequest(void *t
) {
491 // printf("voidrequest: t=%ld\n", t);
495 tr
= mixp_9req_alloc(pc
);
496 tr
->ifcall
->type
= P9_TFlush
;
497 tr
->ifcall
->tag
= IXP_NOTAG
;
498 tr
->ifcall
->Tflush
.oldtag
= r
->ifcall
->tag
;
502 /* Clunk an open Fid -- called by intmap (callback) */
506 Ixp9Conn
* pc
= f
->conn
;
507 Ixp9Req
* tr
= mixp_9req_alloc(pc
);
508 tr
->ifcall
->type
= P9_TClunk
;
509 tr
->ifcall
->tag
= IXP_NOTAG
;
510 tr
->ifcall
->fid
= f
->fid
;
517 cleanupconn(MIXP_CONNECTION
*c
) {
523 mixp_intmap_exec(&pc
->tagmap
, voidrequest
);
524 mixp_intmap_exec(&pc
->fidmap
, voidfid
);
529 /* Handle incoming 9P connections */
531 serve_9pcon(MIXP_CONNECTION
*c
) {
535 fd
= accept(c
->fd
, NULL
, NULL
);
539 pc
= calloc(1,sizeof(Ixp9Conn
));
542 pc
->rmsg
.size
= 1024;
543 pc
->wmsg
.size
= 1024;
544 pc
->rmsg
.data
= malloc(pc
->rmsg
.size
);
545 pc
->wmsg
.data
= malloc(pc
->wmsg
.size
);
547 mixp_intmap_init(&pc
->tagmap
, TAG_BUCKETS
, &pc
->taghash
, "tags");
548 mixp_intmap_init(&pc
->fidmap
, FID_BUCKETS
, &pc
->fidhash
, "fids");
549 mixp_thread
->initmutex(&pc
->rlock
);
550 mixp_thread
->initmutex(&pc
->wlock
);
552 ixp_listen(c
->srv
, fd
, pc
, handlefcall
, cleanupconn
);