added gentoo ebuilds
[libmixp.git] / libmixp / request.c
blobfa43a9b94fe9c5f4afbf96397bc3331f4f245868
1 /* Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com>
2 * See LICENSE file for license details.
3 */
4 #include <assert.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <sys/socket.h>
9 #include "mixp_local.h"
11 #include <9p-mixp/transport.h>
12 #include <9p-mixp/intmap.h>
14 int mixp_dump = 0;
16 static void handlereq(Ixp9Req *r);
18 static int
19 min(int a, int b) {
20 if(a < b)
21 return a;
22 return b;
25 static char
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";
37 enum {
38 TAG_BUCKETS = 61,
39 FID_BUCKETS = 61,
42 struct Ixp9Conn {
43 MIXP_INTMAP tagmap;
44 MIXP_INTMAP fidmap;
45 Ixp9Srv *srv;
46 MIXP_CONNECTION *conn;
47 MIXP_MUTEX rlock, wlock;
48 MIXP_MESSAGE rmsg;
49 MIXP_MESSAGE wmsg;
50 int ref;
51 void *taghash[TAG_BUCKETS];
52 void *fidhash[FID_BUCKETS];
55 static void
56 decref_p9conn(Ixp9Conn *pc) {
57 mixp_thread->lock(&pc->wlock);
58 if(--pc->ref > 0) {
59 mixp_thread->unlock(&pc->wlock);
60 return;
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)
72 MIXP_FREE(pc)
75 static void *
76 createfid(MIXP_INTMAP *map, int fid, Ixp9Conn *pc) {
77 IxpFid *f;
79 f = calloc(1,sizeof(IxpFid));
80 pc->ref++;
81 f->conn = pc;
82 f->fid = fid;
83 f->omode = -1;
84 f->map = map;
85 if(mixp_intmap_caninsertkey(map, fid, f))
86 return f;
87 fprintf(stderr,"couldnt insert key (already exists): %d\n",fid);
88 MIXP_FREE(f)
89 return NULL;
92 static int
93 destroyfid(Ixp9Conn *pc, unsigned long fid) {
94 IxpFid *f;
96 f = mixp_intmap_deletekey(&pc->fidmap, fid);
97 if(f == NULL)
98 return 0;
100 if(pc->srv->freefid)
101 pc->srv->freefid(f);
103 decref_p9conn(pc);
104 MIXP_FREE(f)
105 return 1;
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));
114 if (conn)
116 req->conn = conn;
117 req->srv = conn->srv;
118 conn->ref++;
121 return req;
124 void mixp_9req_free(Ixp9Req* req)
126 if (req==NULL);
127 return;
128 mixp_fcall_free(req->ifcall);
129 mixp_fcall_free(req->ofcall);
131 if (req->conn)
133 decref_p9conn(req->conn);
134 req->conn = NULL;
135 req->srv = NULL;
139 static void
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)
150 goto Fail;
152 // decode incoming packet to to req->ifcall (IxpFcall)
153 if(ixp_msg2fcall(&pc->rmsg, req->ifcall) == 0)
154 goto Fail;
156 // unlock the connection
157 mixp_thread->unlock(&pc->rlock);
159 pc->conn = c;
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);
164 decref_p9conn(pc);
165 return;
168 handlereq(req);
169 mixp_9req_free(req);
170 return;
172 Fail:
173 mixp_thread->unlock(&pc->rlock);
174 ixp_hangup(c);
175 mixp_9req_free(req);
176 return;
179 static void
180 handlereq(Ixp9Req *r)
182 Ixp9Conn *pc;
183 Ixp9Srv *srv;
185 pc = r->conn;
186 srv = pc->srv;
188 if (r->ifcall == NULL)
190 fprintf(stderr,"EMERG: handlereq() r->ifcall corrupt\n");
191 return;
194 switch(r->ifcall->type)
196 default:
197 ixp_respond(r, Enofunc);
198 break;
199 case P9_TVersion:
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";
204 else
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);
216 break;
217 case P9_TAttach:
218 if(!(r->fid = createfid(&pc->fidmap, r->ifcall->fid, pc))) {
219 fprintf(stderr,"TAttach: duplicate fid\n");
220 ixp_respond(r, Edupfid);
221 return;
223 /* attach is a required function */
224 srv->attach(r);
225 break;
226 case P9_TClunk:
227 if(!(r->fid = mixp_intmap_lookupkey(&pc->fidmap, r->ifcall->fid))) {
228 ixp_respond(r, Enofid);
229 return;
231 if(!srv->clunk) {
232 ixp_respond(r, NULL);
233 return;
235 srv->clunk(r);
236 break;
237 case P9_TFlush:
238 if(!(r->oldreq = mixp_intmap_lookupkey(&pc->tagmap, r->ifcall->Tflush.oldtag))) {
239 ixp_respond(r, Enotag);
240 return;
242 if(!srv->flush) {
243 ixp_respond(r, Enofunc);
244 return;
246 srv->flush(r);
247 break;
248 case P9_TCreate:
249 if(!(r->fid = mixp_intmap_lookupkey(&pc->fidmap, r->ifcall->fid))) {
250 ixp_respond(r, Enofid);
251 return;
253 if(r->fid->omode != -1) {
254 ixp_respond(r, Ebotch);
255 return;
257 if(!(r->fid->qid.type & P9_QTDIR)) {
258 ixp_respond(r, Enotdir);
259 return;
261 if(!pc->srv->create) {
262 ixp_respond(r, Enofunc);
263 return;
265 pc->srv->create(r);
266 break;
267 case P9_TOpen:
268 if(!(r->fid = mixp_intmap_lookupkey(&pc->fidmap, r->ifcall->fid))) {
269 ixp_respond(r, Enofid);
270 return;
272 if((r->fid->qid.type & P9_QTDIR) && (r->ifcall->Topen.mode|P9_ORCLOSE) != (P9_OREAD|P9_ORCLOSE)) {
273 ixp_respond(r, Eisdir);
274 return;
276 r->ofcall->Ropen.qid = r->fid->qid;
277 if(!pc->srv->open) {
278 ixp_respond(r, Enofunc);
279 return;
281 pc->srv->open(r);
282 break;
283 case P9_TRead:
284 if(!(r->fid = mixp_intmap_lookupkey(&pc->fidmap, r->ifcall->fid))) {
285 ixp_respond(r, Enofid);
286 return;
288 if(r->fid->omode == -1 || r->fid->omode == P9_OWRITE) {
289 ixp_respond(r, Ebotch);
290 return;
292 if(!pc->srv->read) {
293 ixp_respond(r, Enofunc);
294 return;
296 pc->srv->read(r);
297 break;
298 case P9_TRemove:
299 if(!(r->fid = mixp_intmap_lookupkey(&pc->fidmap, r->ifcall->fid))) {
300 ixp_respond(r, Enofid);
301 return;
303 if(!pc->srv->remove) {
304 ixp_respond(r, Enofunc);
305 return;
307 pc->srv->remove(r);
308 break;
309 case P9_TStat:
310 if(!(r->fid = mixp_intmap_lookupkey(&pc->fidmap, r->ifcall->fid))) {
311 ixp_respond(r, Enofid);
312 return;
314 if(!pc->srv->stat) {
315 ixp_respond(r, Enofunc);
316 return;
318 pc->srv->stat(r);
319 break;
320 case P9_TWalk:
321 if(!(r->fid = mixp_intmap_lookupkey(&pc->fidmap, r->ifcall->fid))) {
322 ixp_respond(r, Enofid);
323 return;
325 if(r->fid->omode != -1) {
326 ixp_respond(r, "cannot walk from an open fid");
327 return;
329 if(r->ifcall->Twalk.nwname && !(r->fid->qid.type & P9_QTDIR)) {
330 ixp_respond(r, Enotdir);
331 return;
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);
336 return;
338 }else
339 r->newfid = r->fid;
340 if(!pc->srv->walk) {
341 ixp_respond(r, Enofunc);
342 return;
344 pc->srv->walk(r);
345 break;
346 case P9_TWrite:
347 if(!(r->fid = mixp_intmap_lookupkey(&pc->fidmap, r->ifcall->fid))) {
348 ixp_respond(r, Enofid);
349 return;
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");
353 return;
355 if(!pc->srv->write) {
356 ixp_respond(r, Enofunc);
357 return;
359 pc->srv->write(r);
360 break;
361 /* Still to be implemented: wstat, auth */
365 void
366 ixp_respond(Ixp9Req *r, const char *error) {
367 Ixp9Conn *pc;
368 int msize;
370 pc = r->conn;
372 switch(r->ifcall->type) {
373 default:
374 if(!error)
375 assert(!"ixp_respond called on unsupported fcall type");
376 break;
377 case P9_TVersion:
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);
383 if (msize<1)
385 fprintf(stderr,"ixp_respond() P9_TVersion: msize<1 ! tweaking to IXP_MAX_MSG\n");
386 msize = IXP_MAX_MSG;
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;
395 break;
396 case P9_TAttach:
397 if(error)
398 destroyfid(pc, r->fid->fid);
399 MIXP_FREE(r->ifcall->Tattach.uname)
400 MIXP_FREE(r->ifcall->Tattach.aname)
401 break;
402 case P9_TOpen:
403 if(!error) {
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);
411 break;
412 case P9_TCreate:
413 if(!error) {
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;
419 break;
420 case P9_TWalk:
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)
425 error = Enofile;
426 }else{
427 if(r->ofcall->Rwalk.nwqid == 0)
428 r->newfid->qid = r->fid->qid;
429 else
430 r->newfid->qid = r->ofcall->Rwalk.wqid[r->ofcall->Rwalk.nwqid-1];
432 MIXP_FREE(*r->ifcall->Twalk.wname)
433 break;
434 case P9_TWrite:
435 MIXP_FREE(r->ifcall->Twrite.data)
436 break;
437 case P9_TRemove:
438 if(r->fid)
439 destroyfid(pc, r->fid->fid);
440 break;
441 case P9_TClunk:
442 if(r->fid)
443 destroyfid(pc, r->fid->fid);
444 r->ofcall->fid = r->ifcall->fid;
445 break;
446 case P9_TFlush:
447 if((r->oldreq = mixp_intmap_lookupkey(&pc->tagmap, r->ifcall->Tflush.oldtag)))
448 ixp_respond(r->oldreq, Eintr);
449 break;
450 case P9_TRead:
451 case P9_TStat:
452 break;
453 /* Still to be implemented: wstat, auth */
456 r->ofcall->tag = r->ifcall->tag;
458 if(error == NULL)
459 r->ofcall->type = r->ifcall->type + 1;
460 else {
461 r->ofcall->type = P9_RError;
462 r->ofcall->Rerror.ename = strdup(error);
465 mixp_intmap_deletekey(&pc->tagmap, r->ifcall->tag);;
467 if(pc->conn) {
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) {
476 case P9_RStat:
477 MIXP_FREE(r->ofcall->Rstat.stat)
478 break;
479 case P9_RRead:
480 // MIXP_FREE(r->ofcall->Rread.data)
481 break;
485 /* Flush a pending request */
486 static void
487 voidrequest(void *t) {
488 Ixp9Req *r, *tr;
489 Ixp9Conn *pc;
491 // printf("voidrequest: t=%ld\n", t);
493 r = t;
494 pc = r->conn;
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;
499 handlereq(tr);
502 /* Clunk an open Fid -- called by intmap (callback) */
503 static void
504 voidfid(void *t) {
505 IxpFid* f = t;
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;
511 tr->fid = f;
512 handlereq(tr);
513 mixp_9req_free(tr);
516 static void
517 cleanupconn(MIXP_CONNECTION *c) {
518 Ixp9Conn *pc;
520 pc = c->aux;
521 pc->conn = NULL;
522 if(pc->ref > 1) {
523 mixp_intmap_exec(&pc->tagmap, voidrequest);
524 mixp_intmap_exec(&pc->fidmap, voidfid);
526 decref_p9conn(pc);
529 /* Handle incoming 9P connections */
530 void
531 serve_9pcon(MIXP_CONNECTION *c) {
532 Ixp9Conn *pc;
533 int fd;
535 fd = accept(c->fd, NULL, NULL);
536 if(fd < 0)
537 return;
539 pc = calloc(1,sizeof(Ixp9Conn));
540 pc->ref++;
541 pc->srv = c->aux;
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);