client.c (13483B)
1 /* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail> 2 * See LICENSE file for license details. 3 */ 4 #include <assert.h> 5 #include <stdarg.h> 6 #include <stdint.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/socket.h> 11 #include <sys/types.h> 12 #include <unistd.h> 13 #include "ixp_local.h" 14 15 #define nelem(ary) (sizeof(ary) / sizeof(*ary)) 16 17 enum { 18 RootFid = 1, 19 }; 20 21 static int 22 min(int a, int b) { 23 if(a < b) 24 return a; 25 return b; 26 } 27 28 static IxpCFid* 29 getfid(IxpClient *c) { 30 IxpCFid *f; 31 32 thread->lock(&c->lk); 33 f = c->freefid; 34 if(f != nil) 35 c->freefid = f->next; 36 else { 37 f = emallocz(sizeof *f); 38 f->client = c; 39 f->fid = ++c->lastfid; 40 thread->initmutex(&f->iolock); 41 } 42 f->next = nil; 43 f->open = 0; 44 thread->unlock(&c->lk); 45 return f; 46 } 47 48 static void 49 putfid(IxpCFid *f) { 50 IxpClient *c; 51 52 c = f->client; 53 thread->lock(&c->lk); 54 if(f->fid == c->lastfid) { 55 c->lastfid--; 56 thread->mdestroy(&f->iolock); 57 free(f); 58 }else { 59 f->next = c->freefid; 60 c->freefid = f; 61 } 62 thread->unlock(&c->lk); 63 } 64 65 static int 66 dofcall(IxpClient *c, IxpFcall *fcall) { 67 IxpFcall *ret; 68 69 ret = muxrpc(c, fcall); 70 if(ret == nil) 71 return 0; 72 if(ret->hdr.type == RError) { 73 werrstr("%s", ret->error.ename); 74 goto fail; 75 } 76 if(ret->hdr.type != (fcall->hdr.type^1)) { 77 werrstr("received mismatched fcall"); 78 goto fail; 79 } 80 memcpy(fcall, ret, sizeof *fcall); 81 free(ret); 82 return 1; 83 fail: 84 ixp_freefcall(fcall); 85 free(ret); 86 return 0; 87 } 88 89 /** 90 * Function: ixp_unmount 91 * 92 * Unmounts the client P<client> and frees its data structures. 93 * 94 * See also: 95 * F<ixp_mount> 96 */ 97 void 98 ixp_unmount(IxpClient *client) { 99 IxpCFid *f; 100 101 shutdown(client->fd, SHUT_RDWR); 102 close(client->fd); 103 104 muxfree(client); 105 106 while((f = client->freefid)) { 107 client->freefid = f->next; 108 thread->mdestroy(&f->iolock); 109 free(f); 110 } 111 free(client->rmsg.data); 112 free(client->wmsg.data); 113 free(client); 114 } 115 116 static void 117 allocmsg(IxpClient *c, int n) { 118 c->rmsg.size = n; 119 c->wmsg.size = n; 120 c->rmsg.data = erealloc(c->rmsg.data, n); 121 c->wmsg.data = erealloc(c->wmsg.data, n); 122 } 123 124 /** 125 * Function: ixp_mount 126 * Function: ixp_mountfd 127 * Function: ixp_nsmount 128 * Type: IxpClient 129 * 130 * Params: 131 * fd: A file descriptor which is already connected 132 * to a 9P server. 133 * address: An address (in Plan 9 resource fomat) at 134 * which to connect to a 9P server. 135 * name: The name of a socket in the process's canonical 136 * namespace directory. 137 * 138 * Initiate a 9P connection with the server at P<address>, 139 * connected to on P<fd>, or under the process's namespace 140 * directory as P<name>. 141 * 142 * Returns: 143 * A pointer to a new 9P client. 144 * See also: 145 * F<ixp_open>, F<ixp_create>, F<ixp_remove>, F<ixp_unmount> 146 */ 147 148 IxpClient* 149 ixp_mountfd(int fd) { 150 IxpClient *c; 151 IxpFcall fcall; 152 153 c = emallocz(sizeof *c); 154 c->fd = fd; 155 156 muxinit(c); 157 158 allocmsg(c, 256); 159 c->lastfid = RootFid; 160 /* Override tag matching on TVersion */ 161 c->mintag = IXP_NOTAG; 162 c->maxtag = IXP_NOTAG+1; 163 164 fcall.hdr.type = TVersion; 165 fcall.version.msize = IXP_MAX_MSG; 166 fcall.version.version = IXP_VERSION; 167 168 if(dofcall(c, &fcall) == 0) { 169 ixp_unmount(c); 170 return nil; 171 } 172 173 if(strcmp(fcall.version.version, IXP_VERSION) 174 || fcall.version.msize > IXP_MAX_MSG) { 175 werrstr("bad 9P version response"); 176 ixp_unmount(c); 177 return nil; 178 } 179 180 c->mintag = 0; 181 c->maxtag = 255; 182 c->msize = fcall.version.msize; 183 184 allocmsg(c, fcall.version.msize); 185 ixp_freefcall(&fcall); 186 187 fcall.hdr.type = TAttach; 188 fcall.hdr.fid = RootFid; 189 fcall.tattach.afid = IXP_NOFID; 190 fcall.tattach.uname = getenv("USER"); 191 fcall.tattach.aname = ""; 192 if(dofcall(c, &fcall) == 0) { 193 ixp_unmount(c); 194 return nil; 195 } 196 197 return c; 198 } 199 200 IxpClient* 201 ixp_mount(const char *address) { 202 int fd; 203 204 fd = ixp_dial(address); 205 if(fd < 0) 206 return nil; 207 return ixp_mountfd(fd); 208 } 209 210 IxpClient* 211 ixp_nsmount(const char *name) { 212 char *address; 213 IxpClient *c; 214 215 address = ixp_namespace(); 216 if(address) 217 address = ixp_smprint("unix!%s/%s", address, name); 218 if(address == nil) 219 return nil; 220 c = ixp_mount(address); 221 free(address); 222 return c; 223 } 224 225 static IxpCFid* 226 walk(IxpClient *c, const char *path) { 227 IxpCFid *f; 228 char *p; 229 IxpFcall fcall; 230 int n; 231 232 p = estrdup(path); 233 n = tokenize(fcall.twalk.wname, nelem(fcall.twalk.wname), p, '/'); 234 f = getfid(c); 235 236 fcall.hdr.type = TWalk; 237 fcall.hdr.fid = RootFid; 238 fcall.twalk.nwname = n; 239 fcall.twalk.newfid = f->fid; 240 if(dofcall(c, &fcall) == 0) 241 goto fail; 242 if(fcall.rwalk.nwqid < n) { 243 werrstr("File does not exist"); 244 if(fcall.rwalk.nwqid == 0) 245 werrstr("Protocol botch"); 246 goto fail; 247 } 248 249 f->qid = fcall.rwalk.wqid[n-1]; 250 251 ixp_freefcall(&fcall); 252 free(p); 253 return f; 254 fail: 255 putfid(f); 256 free(p); 257 return nil; 258 } 259 260 static IxpCFid* 261 walkdir(IxpClient *c, char *path, const char **rest) { 262 char *p; 263 264 p = path + strlen(path) - 1; 265 assert(p >= path); 266 while(*p == '/') 267 *p-- = '\0'; 268 269 while((p > path) && (*p != '/')) 270 p--; 271 if(*p != '/') { 272 werrstr("bad path"); 273 return nil; 274 } 275 276 *p++ = '\0'; 277 *rest = p; 278 return walk(c, path); 279 } 280 281 static int 282 clunk(IxpCFid *f) { 283 IxpClient *c; 284 IxpFcall fcall; 285 int ret; 286 287 c = f->client; 288 289 fcall.hdr.type = TClunk; 290 fcall.hdr.fid = f->fid; 291 ret = dofcall(c, &fcall); 292 if(ret) 293 putfid(f); 294 ixp_freefcall(&fcall); 295 return ret; 296 } 297 298 /** 299 * Function: ixp_remove 300 * 301 * Params: 302 * path: The path of the file to remove. 303 * 304 * Removes a file or directory from the remote server. 305 * 306 * Returns: 307 * ixp_remove returns 0 on failure, 1 on success. 308 * See also: 309 * F<ixp_mount> 310 */ 311 312 int 313 ixp_remove(IxpClient *c, const char *path) { 314 IxpFcall fcall; 315 IxpCFid *f; 316 int ret; 317 318 if((f = walk(c, path)) == nil) 319 return 0; 320 321 fcall.hdr.type = TRemove; 322 fcall.hdr.fid = f->fid;; 323 ret = dofcall(c, &fcall); 324 ixp_freefcall(&fcall); 325 putfid(f); 326 327 return ret; 328 } 329 330 static void 331 initfid(IxpCFid *f, IxpFcall *fcall) { 332 f->open = 1; 333 f->offset = 0; 334 f->iounit = fcall->ropen.iounit; 335 if(f->iounit == 0 || fcall->ropen.iounit > f->client->msize-24) 336 f->iounit = f->client->msize-24; 337 f->qid = fcall->ropen.qid; 338 } 339 340 /** 341 * Function: ixp_open 342 * Function: ixp_create 343 * Type: IxpCFid 344 * Type: IxpOMode 345 * 346 * Params: 347 * path: The path of the file to open or create. 348 * perm: The permissions with which to create the new 349 * file. These will be ANDed with those of the 350 * parent directory by the server. 351 * mode: The file's open mode. 352 * 353 * ixp_open and ixp_create each open a file at P<path>. 354 * P<mode> must include OREAD, OWRITE, or ORDWR, and may 355 * include any of the modes specified in T<IxpOMode>. 356 * ixp_create, additionally, creates a file at P<path> if it 357 * doesn't already exist. 358 * 359 * Returns: 360 * A pointer on which to operate on the newly 361 * opened file. 362 * 363 * See also: 364 * F<ixp_mount>, F<ixp_read>, F<ixp_write>, F<ixp_print>, 365 * F<ixp_fstat>, F<ixp_close> 366 */ 367 368 IxpCFid* 369 ixp_create(IxpClient *c, const char *path, uint perm, uint8_t mode) { 370 IxpFcall fcall; 371 IxpCFid *f; 372 char *tpath;; 373 374 tpath = estrdup(path); 375 376 f = walkdir(c, tpath, &path); 377 if(f == nil) 378 goto done; 379 380 fcall.hdr.type = TCreate; 381 fcall.hdr.fid = f->fid; 382 fcall.tcreate.name = (char*)(uintptr_t)path; 383 fcall.tcreate.perm = perm; 384 fcall.tcreate.mode = mode; 385 386 if(dofcall(c, &fcall) == 0) { 387 clunk(f); 388 f = nil; 389 goto done; 390 } 391 392 initfid(f, &fcall); 393 f->mode = mode; 394 395 ixp_freefcall(&fcall); 396 397 done: 398 free(tpath); 399 return f; 400 } 401 402 IxpCFid* 403 ixp_open(IxpClient *c, const char *path, uint8_t mode) { 404 IxpFcall fcall; 405 IxpCFid *f; 406 407 f = walk(c, path); 408 if(f == nil) 409 return nil; 410 411 fcall.hdr.type = TOpen; 412 fcall.hdr.fid = f->fid; 413 fcall.topen.mode = mode; 414 415 if(dofcall(c, &fcall) == 0) { 416 clunk(f); 417 return nil; 418 } 419 420 initfid(f, &fcall); 421 f->mode = mode; 422 423 ixp_freefcall(&fcall); 424 return f; 425 } 426 427 /** 428 * Function: ixp_close 429 * 430 * Closes the file pointed to by P<f> and frees its 431 * associated data structures; 432 * 433 * Returns: 434 * Returns 1 on success, and zero on failure. 435 * See also: 436 * F<ixp_mount>, F<ixp_open> 437 */ 438 439 int 440 ixp_close(IxpCFid *f) { 441 return clunk(f); 442 } 443 444 static IxpStat* 445 _stat(IxpClient *c, ulong fid) { 446 IxpMsg msg; 447 IxpFcall fcall; 448 IxpStat *stat; 449 450 fcall.hdr.type = TStat; 451 fcall.hdr.fid = fid; 452 if(dofcall(c, &fcall) == 0) 453 return nil; 454 455 msg = ixp_message((char*)fcall.rstat.stat, fcall.rstat.nstat, MsgUnpack); 456 457 stat = emalloc(sizeof *stat); 458 ixp_pstat(&msg, stat); 459 ixp_freefcall(&fcall); 460 if(msg.pos > msg.end) { 461 free(stat); 462 stat = nil; 463 } 464 return stat; 465 } 466 467 /** 468 * Function: ixp_stat 469 * Function: ixp_fstat 470 * Type: IxpStat 471 * Type: IxpQid 472 * Type: IxpQType 473 * Type: IxpDMode 474 * 475 * Params: 476 * path: The path of the file to stat. 477 * fid: An open file descriptor to stat. 478 * 479 * Stats the file at P<path> or pointed to by P<fid>. 480 * 481 * Returns: 482 * Returns an IxpStat structure, which must be freed by 483 * the caller with free(3). 484 * See also: 485 * F<ixp_mount>, F<ixp_open> 486 */ 487 488 IxpStat* 489 ixp_stat(IxpClient *c, const char *path) { 490 IxpStat *stat; 491 IxpCFid *f; 492 493 f = walk(c, path); 494 if(f == nil) 495 return nil; 496 497 stat = _stat(c, f->fid); 498 clunk(f); 499 return stat; 500 } 501 502 IxpStat* 503 ixp_fstat(IxpCFid *fid) { 504 return _stat(fid->client, fid->fid); 505 } 506 507 static long 508 _pread(IxpCFid *f, char *buf, long count, int64_t offset) { 509 IxpFcall fcall; 510 int n, len; 511 512 len = 0; 513 while(len < count) { 514 n = min(count-len, f->iounit); 515 516 fcall.hdr.type = TRead; 517 fcall.hdr.fid = f->fid; 518 fcall.tread.offset = offset; 519 fcall.tread.count = n; 520 if(dofcall(f->client, &fcall) == 0) 521 return -1; 522 if(fcall.rread.count > n) 523 return -1; 524 525 memcpy(buf+len, fcall.rread.data, fcall.rread.count); 526 offset += fcall.rread.count; 527 len += fcall.rread.count; 528 529 ixp_freefcall(&fcall); 530 if(fcall.rread.count < n) 531 break; 532 } 533 return len; 534 } 535 536 /** 537 * Function: ixp_read 538 * Function: ixp_pread 539 * 540 * Params: 541 * buf: A buffer in which to store the read data. 542 * count: The number of bytes to read. 543 * offset: The offset at which to begin reading. 544 * 545 * ixp_read and ixp_pread each read P<count> bytes of data 546 * from the file pointed to by P<fid>, into P<buf>. ixp_read 547 * begins reading at its stored offset, and increments it by 548 * the number of bytes read. ixp_pread reads beginning at 549 * P<offset> and does not alter P<fid>'s stored offset. 550 * 551 * Returns: 552 * These functions return the number of bytes read on 553 * success and -1 on failure. 554 * See also: 555 * F<ixp_mount>, F<ixp_open>, F<ixp_write> 556 */ 557 558 long 559 ixp_read(IxpCFid *fid, void *buf, long count) { 560 int n; 561 562 thread->lock(&fid->iolock); 563 n = _pread(fid, buf, count, fid->offset); 564 if(n > 0) 565 fid->offset += n; 566 thread->unlock(&fid->iolock); 567 return n; 568 } 569 570 long 571 ixp_pread(IxpCFid *fid, void *buf, long count, int64_t offset) { 572 int n; 573 574 thread->lock(&fid->iolock); 575 n = _pread(fid, buf, count, offset); 576 thread->unlock(&fid->iolock); 577 return n; 578 } 579 580 static long 581 _pwrite(IxpCFid *f, const void *buf, long count, int64_t offset) { 582 IxpFcall fcall; 583 int n, len; 584 585 len = 0; 586 do { 587 n = min(count-len, f->iounit); 588 fcall.hdr.type = TWrite; 589 fcall.hdr.fid = f->fid; 590 fcall.twrite.offset = offset; 591 fcall.twrite.data = (char*)buf + len; 592 fcall.twrite.count = n; 593 if(dofcall(f->client, &fcall) == 0) 594 return -1; 595 596 offset += fcall.rwrite.count; 597 len += fcall.rwrite.count; 598 599 ixp_freefcall(&fcall); 600 if(fcall.rwrite.count < n) 601 break; 602 } while(len < count); 603 return len; 604 } 605 606 /** 607 * Function: ixp_write 608 * Function: ixp_pwrite 609 * 610 * Params: 611 * buf: A buffer holding the contents to store. 612 * count: The number of bytes to store. 613 * offset: The offset at which to write the data. 614 * 615 * ixp_write and ixp_pwrite each write P<count> bytes of 616 * data stored in P<buf> to the file pointed to by C<fid>. 617 * ixp_write writes its data at its stored offset, and 618 * increments it by P<count>. ixp_pwrite writes its data a 619 * P<offset> and does not alter C<fid>'s stored offset. 620 * 621 * Returns: 622 * These functions return the number of bytes actually 623 * written. Any value less than P<count> must be considered 624 * a failure. 625 * See also: 626 * F<ixp_mount>, F<ixp_open>, F<ixp_read> 627 */ 628 629 long 630 ixp_write(IxpCFid *fid, const void *buf, long count) { 631 int n; 632 633 thread->lock(&fid->iolock); 634 n = _pwrite(fid, buf, count, fid->offset); 635 if(n > 0) 636 fid->offset += n; 637 thread->unlock(&fid->iolock); 638 return n; 639 } 640 641 long 642 ixp_pwrite(IxpCFid *fid, const void *buf, long count, int64_t offset) { 643 int n; 644 645 thread->lock(&fid->iolock); 646 n = _pwrite(fid, buf, count, offset); 647 thread->unlock(&fid->iolock); 648 return n; 649 } 650 651 /** 652 * Function: ixp_print 653 * Function: ixp_vprint 654 * Variable: ixp_vsmprint 655 * 656 * Params: 657 * fid: An open IxpCFid to which to write the result. 658 * fmt: The string with which to format the data. 659 * args: A va_list holding the arguments to the format 660 * string. 661 * ...: The arguments to the format string. 662 * 663 * These functions act like the standard formatted IO 664 * functions. They write the result of the formatting to the 665 * file pointed to by C<fid>. 666 * 667 * V<ixp_vsmprint> may be set to a function which will 668 * format its arguments and return a nul-terminated string 669 * allocated by malloc(3). The default formats its arguments as 670 * printf(3). 671 * 672 * Returns: 673 * These functions return the number of bytes written. 674 * There is currently no way to detect failure. 675 * See also: 676 * F<ixp_mount>, F<ixp_open>, printf(3) 677 */ 678 679 int 680 ixp_vprint(IxpCFid *fid, const char *fmt, va_list args) { 681 char *buf; 682 int n; 683 684 buf = ixp_vsmprint(fmt, args); 685 if(buf == nil) 686 return -1; 687 688 n = ixp_write(fid, buf, strlen(buf)); 689 free(buf); 690 return n; 691 } 692 693 int 694 ixp_print(IxpCFid *fid, const char *fmt, ...) { 695 va_list ap; 696 int n; 697 698 va_start(ap, fmt); 699 n = ixp_vprint(fid, fmt, ap); 700 va_end(ap); 701 702 return n; 703 } 704