request.c (13772B)
1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail> 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 "ixp_local.h" 10 11 static void handlereq(Ixp9Req *r); 12 13 /** 14 * Variable: ixp_printfcall 15 * 16 * When set to a non-null value, ixp_printfcall is called once for 17 * every incoming and outgoing Fcall. It is intended to simplify the 18 * writing of debugging code for clients, but may be used for any 19 * arbitrary purpose. 20 * 21 * See also: 22 * F<ixp_respond>, F<ixp_serve9conn> 23 */ 24 void (*ixp_printfcall)(IxpFcall*); 25 26 static int 27 min(int a, int b) { 28 if(a < b) 29 return a; 30 return b; 31 } 32 33 static char 34 Eduptag[] = "tag in use", 35 Edupfid[] = "fid in use", 36 Enofunc[] = "function not implemented", 37 Eopen[] = "fid is already open", 38 Enofile[] = "file does not exist", 39 Enoread[] = "file not open for reading", 40 Enofid[] = "fid does not exist", 41 Enotag[] = "tag does not exist", 42 Enotdir[] = "not a directory", 43 Eintr[] = "interrupted", 44 Eisdir[] = "cannot perform operation on a directory"; 45 46 enum { 47 TAG_BUCKETS = 61, 48 FID_BUCKETS = 61, 49 }; 50 51 struct Ixp9Conn { 52 Map tagmap; 53 Map fidmap; 54 MapEnt* taghash[TAG_BUCKETS]; 55 MapEnt* fidhash[FID_BUCKETS]; 56 Ixp9Srv* srv; 57 IxpConn* conn; 58 IxpMutex rlock; 59 IxpMutex wlock; 60 IxpMsg rmsg; 61 IxpMsg wmsg; 62 int ref; 63 }; 64 65 static void 66 decref_p9conn(Ixp9Conn *p9conn) { 67 thread->lock(&p9conn->wlock); 68 if(--p9conn->ref > 0) { 69 thread->unlock(&p9conn->wlock); 70 return; 71 } 72 thread->unlock(&p9conn->wlock); 73 74 assert(p9conn->conn == nil); 75 76 thread->mdestroy(&p9conn->rlock); 77 thread->mdestroy(&p9conn->wlock); 78 79 ixp_mapfree(&p9conn->tagmap, nil); 80 ixp_mapfree(&p9conn->fidmap, nil); 81 82 free(p9conn->rmsg.data); 83 free(p9conn->wmsg.data); 84 free(p9conn); 85 } 86 87 static void* 88 createfid(Map *map, int fid, Ixp9Conn *p9conn) { 89 IxpFid *f; 90 91 f = emallocz(sizeof *f); 92 p9conn->ref++; 93 f->conn = p9conn; 94 f->fid = fid; 95 f->omode = -1; 96 f->map = map; 97 if(ixp_mapinsert(map, fid, f, false)) 98 return f; 99 free(f); 100 return nil; 101 } 102 103 static int 104 destroyfid(Ixp9Conn *p9conn, ulong fid) { 105 IxpFid *f; 106 107 f = ixp_maprm(&p9conn->fidmap, fid); 108 if(f == nil) 109 return 0; 110 111 if(p9conn->srv->freefid) 112 p9conn->srv->freefid(f); 113 114 decref_p9conn(p9conn); 115 free(f); 116 return 1; 117 } 118 119 static void 120 handlefcall(IxpConn *c) { 121 IxpFcall fcall = {0}; 122 Ixp9Conn *p9conn; 123 Ixp9Req *req; 124 125 p9conn = c->aux; 126 127 thread->lock(&p9conn->rlock); 128 if(ixp_recvmsg(c->fd, &p9conn->rmsg) == 0) 129 goto Fail; 130 if(ixp_msg2fcall(&p9conn->rmsg, &fcall) == 0) 131 goto Fail; 132 thread->unlock(&p9conn->rlock); 133 134 req = emallocz(sizeof *req); 135 p9conn->ref++; 136 req->conn = p9conn; 137 req->srv = p9conn->srv; 138 req->ifcall = fcall; 139 p9conn->conn = c; 140 141 if(!ixp_mapinsert(&p9conn->tagmap, fcall.hdr.tag, req, false)) { 142 ixp_respond(req, Eduptag); 143 return; 144 } 145 146 handlereq(req); 147 return; 148 149 Fail: 150 thread->unlock(&p9conn->rlock); 151 ixp_hangup(c); 152 return; 153 } 154 155 static void 156 handlereq(Ixp9Req *r) { 157 Ixp9Conn *p9conn; 158 Ixp9Srv *srv; 159 160 p9conn = r->conn; 161 srv = p9conn->srv; 162 163 if(ixp_printfcall) 164 ixp_printfcall(&r->ifcall); 165 166 switch(r->ifcall.hdr.type) { 167 default: 168 ixp_respond(r, Enofunc); 169 break; 170 case TVersion: 171 if(!strcmp(r->ifcall.version.version, "9P")) 172 r->ofcall.version.version = "9P"; 173 else if(!strcmp(r->ifcall.version.version, "9P2000")) 174 r->ofcall.version.version = "9P2000"; 175 else 176 r->ofcall.version.version = "unknown"; 177 r->ofcall.version.msize = r->ifcall.version.msize; 178 ixp_respond(r, nil); 179 break; 180 case TAttach: 181 if(!(r->fid = createfid(&p9conn->fidmap, r->ifcall.hdr.fid, p9conn))) { 182 ixp_respond(r, Edupfid); 183 return; 184 } 185 /* attach is a required function */ 186 srv->attach(r); 187 break; 188 case TClunk: 189 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 190 ixp_respond(r, Enofid); 191 return; 192 } 193 if(!srv->clunk) { 194 ixp_respond(r, nil); 195 return; 196 } 197 srv->clunk(r); 198 break; 199 case TFlush: 200 if(!(r->oldreq = ixp_mapget(&p9conn->tagmap, r->ifcall.tflush.oldtag))) { 201 ixp_respond(r, Enotag); 202 return; 203 } 204 if(!srv->flush) { 205 ixp_respond(r, Enofunc); 206 return; 207 } 208 srv->flush(r); 209 break; 210 case TCreate: 211 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 212 ixp_respond(r, Enofid); 213 return; 214 } 215 if(r->fid->omode != -1) { 216 ixp_respond(r, Eopen); 217 return; 218 } 219 if(!(r->fid->qid.type&QTDIR)) { 220 ixp_respond(r, Enotdir); 221 return; 222 } 223 if(!p9conn->srv->create) { 224 ixp_respond(r, Enofunc); 225 return; 226 } 227 p9conn->srv->create(r); 228 break; 229 case TOpen: 230 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 231 ixp_respond(r, Enofid); 232 return; 233 } 234 if((r->fid->qid.type&QTDIR) && (r->ifcall.topen.mode|P9_ORCLOSE) != (P9_OREAD|P9_ORCLOSE)) { 235 ixp_respond(r, Eisdir); 236 return; 237 } 238 r->ofcall.ropen.qid = r->fid->qid; 239 if(!p9conn->srv->open) { 240 ixp_respond(r, Enofunc); 241 return; 242 } 243 p9conn->srv->open(r); 244 break; 245 case TRead: 246 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 247 ixp_respond(r, Enofid); 248 return; 249 } 250 if(r->fid->omode == -1 || r->fid->omode == P9_OWRITE) { 251 ixp_respond(r, Enoread); 252 return; 253 } 254 if(!p9conn->srv->read) { 255 ixp_respond(r, Enofunc); 256 return; 257 } 258 p9conn->srv->read(r); 259 break; 260 case TRemove: 261 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 262 ixp_respond(r, Enofid); 263 return; 264 } 265 if(!p9conn->srv->remove) { 266 ixp_respond(r, Enofunc); 267 return; 268 } 269 p9conn->srv->remove(r); 270 break; 271 case TStat: 272 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 273 ixp_respond(r, Enofid); 274 return; 275 } 276 if(!p9conn->srv->stat) { 277 ixp_respond(r, Enofunc); 278 return; 279 } 280 p9conn->srv->stat(r); 281 break; 282 case TWalk: 283 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 284 ixp_respond(r, Enofid); 285 return; 286 } 287 if(r->fid->omode != -1) { 288 ixp_respond(r, "cannot walk from an open fid"); 289 return; 290 } 291 if(r->ifcall.twalk.nwname && !(r->fid->qid.type&QTDIR)) { 292 ixp_respond(r, Enotdir); 293 return; 294 } 295 if((r->ifcall.hdr.fid != r->ifcall.twalk.newfid)) { 296 if(!(r->newfid = createfid(&p9conn->fidmap, r->ifcall.twalk.newfid, p9conn))) { 297 ixp_respond(r, Edupfid); 298 return; 299 } 300 }else 301 r->newfid = r->fid; 302 if(!p9conn->srv->walk) { 303 ixp_respond(r, Enofunc); 304 return; 305 } 306 p9conn->srv->walk(r); 307 break; 308 case TWrite: 309 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 310 ixp_respond(r, Enofid); 311 return; 312 } 313 if((r->fid->omode&3) != P9_OWRITE && (r->fid->omode&3) != P9_ORDWR) { 314 ixp_respond(r, "write on fid not opened for writing"); 315 return; 316 } 317 if(!p9conn->srv->write) { 318 ixp_respond(r, Enofunc); 319 return; 320 } 321 p9conn->srv->write(r); 322 break; 323 case TWStat: 324 if(!(r->fid = ixp_mapget(&p9conn->fidmap, r->ifcall.hdr.fid))) { 325 ixp_respond(r, Enofid); 326 return; 327 } 328 if(~r->ifcall.twstat.stat.type) { 329 ixp_respond(r, "wstat of type"); 330 return; 331 } 332 if(~r->ifcall.twstat.stat.dev) { 333 ixp_respond(r, "wstat of dev"); 334 return; 335 } 336 if(~r->ifcall.twstat.stat.qid.type || (ulong)~r->ifcall.twstat.stat.qid.version || ~r->ifcall.twstat.stat.qid.path) { 337 ixp_respond(r, "wstat of qid"); 338 return; 339 } 340 if(r->ifcall.twstat.stat.muid && r->ifcall.twstat.stat.muid[0]) { 341 ixp_respond(r, "wstat of muid"); 342 return; 343 } 344 if(~r->ifcall.twstat.stat.mode && ((r->ifcall.twstat.stat.mode&DMDIR)>>24) != r->fid->qid.type&QTDIR) { 345 ixp_respond(r, "wstat on DMDIR bit"); 346 return; 347 } 348 if(!p9conn->srv->wstat) { 349 ixp_respond(r, Enofunc); 350 return; 351 } 352 p9conn->srv->wstat(r); 353 break; 354 /* Still to be implemented: auth */ 355 } 356 } 357 358 /** 359 * Function: ixp_respond 360 * 361 * Sends a response to the given request. The response is 362 * constructed from the P<ofcall> member of the P<req> parameter, or 363 * from the P<error> parameter if it is non-null. In the latter 364 * case, the response is of type RError, while in any other case it 365 * is of the same type as P<req>->P<ofcall>, which must match the 366 * request type in P<req>->P<ifcall>. 367 * 368 * See also: 369 * T<Ixp9Req>, V<ixp_printfcall> 370 */ 371 void 372 ixp_respond(Ixp9Req *req, const char *error) { 373 Ixp9Conn *p9conn; 374 int msize; 375 376 p9conn = req->conn; 377 378 switch(req->ifcall.hdr.type) { 379 default: 380 if(!error) 381 assert(!"Respond called on unsupported fcall type"); 382 break; 383 case TVersion: 384 assert(error == nil); 385 free(req->ifcall.version.version); 386 387 thread->lock(&p9conn->rlock); 388 thread->lock(&p9conn->wlock); 389 msize = min(req->ofcall.version.msize, IXP_MAX_MSG); 390 p9conn->rmsg.data = erealloc(p9conn->rmsg.data, msize); 391 p9conn->wmsg.data = erealloc(p9conn->wmsg.data, msize); 392 p9conn->rmsg.size = msize; 393 p9conn->wmsg.size = msize; 394 thread->unlock(&p9conn->wlock); 395 thread->unlock(&p9conn->rlock); 396 req->ofcall.version.msize = msize; 397 break; 398 case TAttach: 399 if(error) 400 destroyfid(p9conn, req->fid->fid); 401 free(req->ifcall.tattach.uname); 402 free(req->ifcall.tattach.aname); 403 break; 404 case TOpen: 405 case TCreate: 406 if(!error) { 407 req->ofcall.ropen.iounit = p9conn->rmsg.size - 24; 408 req->fid->iounit = req->ofcall.ropen.iounit; 409 req->fid->omode = req->ifcall.topen.mode; 410 req->fid->qid = req->ofcall.ropen.qid; 411 } 412 free(req->ifcall.tcreate.name); 413 break; 414 case TWalk: 415 if(error || req->ofcall.rwalk.nwqid < req->ifcall.twalk.nwname) { 416 if(req->ifcall.hdr.fid != req->ifcall.twalk.newfid && req->newfid) 417 destroyfid(p9conn, req->newfid->fid); 418 if(!error && req->ofcall.rwalk.nwqid == 0) 419 error = Enofile; 420 }else{ 421 if(req->ofcall.rwalk.nwqid == 0) 422 req->newfid->qid = req->fid->qid; 423 else 424 req->newfid->qid = req->ofcall.rwalk.wqid[req->ofcall.rwalk.nwqid-1]; 425 } 426 free(*req->ifcall.twalk.wname); 427 break; 428 case TWrite: 429 free(req->ifcall.twrite.data); 430 break; 431 case TRemove: 432 if(req->fid) 433 destroyfid(p9conn, req->fid->fid); 434 break; 435 case TClunk: 436 if(req->fid) 437 destroyfid(p9conn, req->fid->fid); 438 break; 439 case TFlush: 440 if((req->oldreq = ixp_mapget(&p9conn->tagmap, req->ifcall.tflush.oldtag))) 441 ixp_respond(req->oldreq, Eintr); 442 break; 443 case TWStat: 444 ixp_freestat(&req->ifcall.twstat.stat); 445 break; 446 case TRead: 447 case TStat: 448 break; 449 /* Still to be implemented: auth */ 450 } 451 452 req->ofcall.hdr.tag = req->ifcall.hdr.tag; 453 454 if(error == nil) 455 req->ofcall.hdr.type = req->ifcall.hdr.type + 1; 456 else { 457 req->ofcall.hdr.type = RError; 458 req->ofcall.error.ename = (char*)error; 459 } 460 461 if(ixp_printfcall) 462 ixp_printfcall(&req->ofcall); 463 464 ixp_maprm(&p9conn->tagmap, req->ifcall.hdr.tag);; 465 466 if(p9conn->conn) { 467 thread->lock(&p9conn->wlock); 468 msize = ixp_fcall2msg(&p9conn->wmsg, &req->ofcall); 469 if(ixp_sendmsg(p9conn->conn->fd, &p9conn->wmsg) != msize) 470 ixp_hangup(p9conn->conn); 471 thread->unlock(&p9conn->wlock); 472 } 473 474 switch(req->ofcall.hdr.type) { 475 case RStat: 476 free(req->ofcall.rstat.stat); 477 break; 478 case RRead: 479 free(req->ofcall.rread.data); 480 break; 481 } 482 free(req); 483 decref_p9conn(p9conn); 484 } 485 486 /* Flush a pending request */ 487 static void 488 voidrequest(void *context, void *arg) { 489 Ixp9Req *orig_req, *flush_req; 490 Ixp9Conn *conn; 491 492 orig_req = arg; 493 conn = orig_req->conn; 494 conn->ref++; 495 496 flush_req = emallocz(sizeof *orig_req); 497 flush_req->ifcall.hdr.type = TFlush; 498 flush_req->ifcall.hdr.tag = IXP_NOTAG; 499 flush_req->ifcall.tflush.oldtag = orig_req->ifcall.hdr.tag; 500 flush_req->conn = conn; 501 502 flush_req->aux = *(void**)context; 503 *(void**)context = flush_req; 504 } 505 506 /* Clunk an open IxpFid */ 507 static void 508 voidfid(void *context, void *arg) { 509 Ixp9Conn *p9conn; 510 Ixp9Req *clunk_req; 511 IxpFid *fid; 512 513 fid = arg; 514 p9conn = fid->conn; 515 p9conn->ref++; 516 517 clunk_req = emallocz(sizeof *clunk_req); 518 clunk_req->ifcall.hdr.type = TClunk; 519 clunk_req->ifcall.hdr.tag = IXP_NOTAG; 520 clunk_req->ifcall.hdr.fid = fid->fid; 521 clunk_req->fid = fid; 522 clunk_req->conn = p9conn; 523 524 clunk_req->aux = *(void**)context; 525 *(void**)context = clunk_req; 526 } 527 528 static void 529 cleanupconn(IxpConn *c) { 530 Ixp9Conn *p9conn; 531 Ixp9Req *req, *r; 532 533 p9conn = c->aux; 534 p9conn->conn = nil; 535 req = nil; 536 if(p9conn->ref > 1) { 537 ixp_mapexec(&p9conn->fidmap, voidfid, &req); 538 ixp_mapexec(&p9conn->tagmap, voidrequest, &req); 539 } 540 while((r = req)) { 541 req = r->aux; 542 r->aux = nil; 543 handlereq(r); 544 } 545 decref_p9conn(p9conn); 546 } 547 548 /* Handle incoming 9P connections */ 549 /** 550 * Type: Ixp9Srv 551 * Type: Ixp9Req 552 * Function: ixp_serve9conn 553 * 554 * The ixp_serve9conn handles incoming 9P connections. It is 555 * ordinarily passed as the P<read> member to F<ixp_listen> with an 556 * Ixp9Srv structure passed as the P<aux> member. The handlers 557 * defined in the Ixp9Srv structure are called whenever a matching 558 * Fcall type is received. The handlers are expected to call 559 * F<ixp_respond> at some point, whether before they return or at 560 * some undefined point in the future. Whenever a client 561 * disconnects, libixp generates whatever flush and clunk events are 562 * required to leave the connection in a clean state and waits for 563 * all responses before freeing the connections associated data 564 * structures. 565 * 566 * Whenever a file is closed and an T<IxpFid> is about to be freed, 567 * the P<freefid> member is called to perform any necessary cleanup 568 * and to free any associated resources. 569 * 570 * See also: 571 * F<ixp_listen>, F<ixp_respond>, F<ixp_printfcall>, 572 * F<IxpFcall>, F<IxpFid> 573 */ 574 void 575 ixp_serve9conn(IxpConn *c) { 576 Ixp9Conn *p9conn; 577 int fd; 578 579 fd = accept(c->fd, nil, nil); 580 if(fd < 0) 581 return; 582 583 p9conn = emallocz(sizeof *p9conn); 584 p9conn->ref++; 585 p9conn->srv = c->aux; 586 p9conn->rmsg.size = 1024; 587 p9conn->wmsg.size = 1024; 588 p9conn->rmsg.data = emalloc(p9conn->rmsg.size); 589 p9conn->wmsg.data = emalloc(p9conn->wmsg.size); 590 591 ixp_mapinit(&p9conn->tagmap, p9conn->taghash, nelem(p9conn->taghash)); 592 ixp_mapinit(&p9conn->fidmap, p9conn->fidhash, nelem(p9conn->fidhash)); 593 thread->initmutex(&p9conn->rlock); 594 thread->initmutex(&p9conn->wlock); 595 596 ixp_listen(c->srv, fd, p9conn, handlefcall, cleanupconn); 597 }