1 /* $OpenBSD: file.c,v 1.11 2021/06/10 07:51:43 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/uio.h> 22 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <imsg.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "tmux.h" 32 33 /* 34 * IPC file handling. Both client and server use the same data structures 35 * (client_file and client_files) to store list of active files. Most functions 36 * are for use either in client or server but not both. 37 */ 38 39 static int file_next_stream = 3; 40 41 RB_GENERATE(client_files, client_file, entry, file_cmp); 42 43 /* Get path for file, either as given or from working directory. */ 44 static char * 45 file_get_path(struct client *c, const char *file) 46 { 47 char *path; 48 49 if (*file == '/') 50 path = xstrdup(file); 51 else 52 xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file); 53 return (path); 54 } 55 56 /* Tree comparison function. */ 57 int 58 file_cmp(struct client_file *cf1, struct client_file *cf2) 59 { 60 if (cf1->stream < cf2->stream) 61 return (-1); 62 if (cf1->stream > cf2->stream) 63 return (1); 64 return (0); 65 } 66 67 /* 68 * Create a file object in the client process - the peer is the server to send 69 * messages to. Check callback is fired when the file is finished with so the 70 * process can decide if it needs to exit (if it is waiting for files to 71 * flush). 72 */ 73 struct client_file * 74 file_create_with_peer(struct tmuxpeer *peer, struct client_files *files, 75 int stream, client_file_cb cb, void *cbdata) 76 { 77 struct client_file *cf; 78 79 cf = xcalloc(1, sizeof *cf); 80 cf->c = NULL; 81 cf->references = 1; 82 cf->stream = stream; 83 84 cf->buffer = evbuffer_new(); 85 if (cf->buffer == NULL) 86 fatalx("out of memory"); 87 88 cf->cb = cb; 89 cf->data = cbdata; 90 91 cf->peer = peer; 92 cf->tree = files; 93 RB_INSERT(client_files, files, cf); 94 95 return (cf); 96 } 97 98 /* Create a file object in the server, communicating with the given client. */ 99 struct client_file * 100 file_create_with_client(struct client *c, int stream, client_file_cb cb, 101 void *cbdata) 102 { 103 struct client_file *cf; 104 105 if (c != NULL && (c->flags & CLIENT_ATTACHED)) 106 c = NULL; 107 108 cf = xcalloc(1, sizeof *cf); 109 cf->c = c; 110 cf->references = 1; 111 cf->stream = stream; 112 113 cf->buffer = evbuffer_new(); 114 if (cf->buffer == NULL) 115 fatalx("out of memory"); 116 117 cf->cb = cb; 118 cf->data = cbdata; 119 120 if (cf->c != NULL) { 121 cf->peer = cf->c->peer; 122 cf->tree = &cf->c->files; 123 RB_INSERT(client_files, &cf->c->files, cf); 124 cf->c->references++; 125 } 126 127 return (cf); 128 } 129 130 /* Free a file. */ 131 void 132 file_free(struct client_file *cf) 133 { 134 if (--cf->references != 0) 135 return; 136 137 evbuffer_free(cf->buffer); 138 free(cf->path); 139 140 if (cf->tree != NULL) 141 RB_REMOVE(client_files, cf->tree, cf); 142 if (cf->c != NULL) 143 server_client_unref(cf->c); 144 145 free(cf); 146 } 147 148 /* Event to fire the done callback. */ 149 static void 150 file_fire_done_cb(__unused int fd, __unused short events, void *arg) 151 { 152 struct client_file *cf = arg; 153 struct client *c = cf->c; 154 155 if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD))) 156 cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data); 157 file_free(cf); 158 } 159 160 /* Add an event to fire the done callback (used by the server). */ 161 void 162 file_fire_done(struct client_file *cf) 163 { 164 event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL); 165 } 166 167 /* Fire the read callback. */ 168 void 169 file_fire_read(struct client_file *cf) 170 { 171 if (cf->cb != NULL) 172 cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data); 173 } 174 175 /* Can this file be printed to? */ 176 int 177 file_can_print(struct client *c) 178 { 179 if (c == NULL) 180 return (0); 181 if (c->session != NULL && (~c->flags & CLIENT_CONTROL)) 182 return (0); 183 return (1); 184 } 185 186 /* Print a message to a file. */ 187 void 188 file_print(struct client *c, const char *fmt, ...) 189 { 190 va_list ap; 191 192 va_start(ap, fmt); 193 file_vprint(c, fmt, ap); 194 va_end(ap); 195 } 196 197 /* Print a message to a file. */ 198 void 199 file_vprint(struct client *c, const char *fmt, va_list ap) 200 { 201 struct client_file find, *cf; 202 struct msg_write_open msg; 203 204 if (!file_can_print(c)) 205 return; 206 207 find.stream = 1; 208 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { 209 cf = file_create_with_client(c, 1, NULL, NULL); 210 cf->path = xstrdup("-"); 211 212 evbuffer_add_vprintf(cf->buffer, fmt, ap); 213 214 msg.stream = 1; 215 msg.fd = STDOUT_FILENO; 216 msg.flags = 0; 217 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg); 218 } else { 219 evbuffer_add_vprintf(cf->buffer, fmt, ap); 220 file_push(cf); 221 } 222 } 223 224 /* Print a buffer to a file. */ 225 void 226 file_print_buffer(struct client *c, void *data, size_t size) 227 { 228 struct client_file find, *cf; 229 struct msg_write_open msg; 230 231 if (!file_can_print(c)) 232 return; 233 234 find.stream = 1; 235 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { 236 cf = file_create_with_client(c, 1, NULL, NULL); 237 cf->path = xstrdup("-"); 238 239 evbuffer_add(cf->buffer, data, size); 240 241 msg.stream = 1; 242 msg.fd = STDOUT_FILENO; 243 msg.flags = 0; 244 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg); 245 } else { 246 evbuffer_add(cf->buffer, data, size); 247 file_push(cf); 248 } 249 } 250 251 /* Report an error to a file. */ 252 void 253 file_error(struct client *c, const char *fmt, ...) 254 { 255 struct client_file find, *cf; 256 struct msg_write_open msg; 257 va_list ap; 258 259 if (!file_can_print(c)) 260 return; 261 262 va_start(ap, fmt); 263 264 find.stream = 2; 265 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { 266 cf = file_create_with_client(c, 2, NULL, NULL); 267 cf->path = xstrdup("-"); 268 269 evbuffer_add_vprintf(cf->buffer, fmt, ap); 270 271 msg.stream = 2; 272 msg.fd = STDERR_FILENO; 273 msg.flags = 0; 274 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg); 275 } else { 276 evbuffer_add_vprintf(cf->buffer, fmt, ap); 277 file_push(cf); 278 } 279 280 va_end(ap); 281 } 282 283 /* Write data to a file. */ 284 void 285 file_write(struct client *c, const char *path, int flags, const void *bdata, 286 size_t bsize, client_file_cb cb, void *cbdata) 287 { 288 struct client_file *cf; 289 struct msg_write_open *msg; 290 size_t msglen; 291 int fd = -1; 292 u_int stream = file_next_stream++; 293 FILE *f; 294 const char *mode; 295 296 if (strcmp(path, "-") == 0) { 297 cf = file_create_with_client(c, stream, cb, cbdata); 298 cf->path = xstrdup("-"); 299 300 fd = STDOUT_FILENO; 301 if (c == NULL || 302 (c->flags & CLIENT_ATTACHED) || 303 (c->flags & CLIENT_CONTROL)) { 304 cf->error = EBADF; 305 goto done; 306 } 307 goto skip; 308 } 309 310 cf = file_create_with_client(c, stream, cb, cbdata); 311 cf->path = file_get_path(c, path); 312 313 if (c == NULL || c->flags & CLIENT_ATTACHED) { 314 if (flags & O_APPEND) 315 mode = "ab"; 316 else 317 mode = "wb"; 318 f = fopen(cf->path, mode); 319 if (f == NULL) { 320 cf->error = errno; 321 goto done; 322 } 323 if (fwrite(bdata, 1, bsize, f) != bsize) { 324 fclose(f); 325 cf->error = EIO; 326 goto done; 327 } 328 fclose(f); 329 goto done; 330 } 331 332 skip: 333 evbuffer_add(cf->buffer, bdata, bsize); 334 335 msglen = strlen(cf->path) + 1 + sizeof *msg; 336 if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) { 337 cf->error = E2BIG; 338 goto done; 339 } 340 msg = xmalloc(msglen); 341 msg->stream = cf->stream; 342 msg->fd = fd; 343 msg->flags = flags; 344 memcpy(msg + 1, cf->path, msglen - sizeof *msg); 345 if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) { 346 free(msg); 347 cf->error = EINVAL; 348 goto done; 349 } 350 free(msg); 351 return; 352 353 done: 354 file_fire_done(cf); 355 } 356 357 /* Read a file. */ 358 void 359 file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) 360 { 361 struct client_file *cf; 362 struct msg_read_open *msg; 363 size_t msglen; 364 int fd = -1; 365 u_int stream = file_next_stream++; 366 FILE *f; 367 size_t size; 368 char buffer[BUFSIZ]; 369 370 if (strcmp(path, "-") == 0) { 371 cf = file_create_with_client(c, stream, cb, cbdata); 372 cf->path = xstrdup("-"); 373 374 fd = STDIN_FILENO; 375 if (c == NULL || 376 (c->flags & CLIENT_ATTACHED) || 377 (c->flags & CLIENT_CONTROL)) { 378 cf->error = EBADF; 379 goto done; 380 } 381 goto skip; 382 } 383 384 cf = file_create_with_client(c, stream, cb, cbdata); 385 cf->path = file_get_path(c, path); 386 387 if (c == NULL || c->flags & CLIENT_ATTACHED) { 388 f = fopen(cf->path, "rb"); 389 if (f == NULL) { 390 cf->error = errno; 391 goto done; 392 } 393 for (;;) { 394 size = fread(buffer, 1, sizeof buffer, f); 395 if (evbuffer_add(cf->buffer, buffer, size) != 0) { 396 cf->error = ENOMEM; 397 goto done; 398 } 399 if (size != sizeof buffer) 400 break; 401 } 402 if (ferror(f)) { 403 cf->error = EIO; 404 goto done; 405 } 406 fclose(f); 407 goto done; 408 } 409 410 skip: 411 msglen = strlen(cf->path) + 1 + sizeof *msg; 412 if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) { 413 cf->error = E2BIG; 414 goto done; 415 } 416 msg = xmalloc(msglen); 417 msg->stream = cf->stream; 418 msg->fd = fd; 419 memcpy(msg + 1, cf->path, msglen - sizeof *msg); 420 if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) { 421 free(msg); 422 cf->error = EINVAL; 423 goto done; 424 } 425 free(msg); 426 return; 427 428 done: 429 file_fire_done(cf); 430 } 431 432 /* Push event, fired if there is more writing to be done. */ 433 static void 434 file_push_cb(__unused int fd, __unused short events, void *arg) 435 { 436 struct client_file *cf = arg; 437 438 if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD) 439 file_push(cf); 440 file_free(cf); 441 } 442 443 /* Push uwritten data to the client for a file, if it will accept it. */ 444 void 445 file_push(struct client_file *cf) 446 { 447 struct msg_write_data *msg; 448 size_t msglen, sent, left; 449 struct msg_write_close close; 450 451 msg = xmalloc(sizeof *msg); 452 left = EVBUFFER_LENGTH(cf->buffer); 453 while (left != 0) { 454 sent = left; 455 if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) 456 sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; 457 458 msglen = (sizeof *msg) + sent; 459 msg = xrealloc(msg, msglen); 460 msg->stream = cf->stream; 461 memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent); 462 if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0) 463 break; 464 evbuffer_drain(cf->buffer, sent); 465 466 left = EVBUFFER_LENGTH(cf->buffer); 467 log_debug("file %d sent %zu, left %zu", cf->stream, sent, left); 468 } 469 if (left != 0) { 470 cf->references++; 471 event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL); 472 } else if (cf->stream > 2) { 473 close.stream = cf->stream; 474 proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close); 475 file_fire_done(cf); 476 } 477 free(msg); 478 } 479 480 /* Check if any files have data left to write. */ 481 int 482 file_write_left(struct client_files *files) 483 { 484 struct client_file *cf; 485 size_t left; 486 int waiting = 0; 487 488 RB_FOREACH(cf, client_files, files) { 489 if (cf->event == NULL) 490 continue; 491 left = EVBUFFER_LENGTH(cf->event->output); 492 if (left != 0) { 493 waiting++; 494 log_debug("file %u %zu bytes left", cf->stream, left); 495 } 496 } 497 return (waiting != 0); 498 } 499 500 /* Client file write error callback. */ 501 static void 502 file_write_error_callback(__unused struct bufferevent *bev, __unused short what, 503 void *arg) 504 { 505 struct client_file *cf = arg; 506 507 log_debug("write error file %d", cf->stream); 508 509 bufferevent_free(cf->event); 510 cf->event = NULL; 511 512 close(cf->fd); 513 cf->fd = -1; 514 515 if (cf->cb != NULL) 516 cf->cb(NULL, NULL, 0, -1, NULL, cf->data); 517 } 518 519 /* Client file write callback. */ 520 static void 521 file_write_callback(__unused struct bufferevent *bev, void *arg) 522 { 523 struct client_file *cf = arg; 524 525 log_debug("write check file %d", cf->stream); 526 527 if (cf->cb != NULL) 528 cf->cb(NULL, NULL, 0, -1, NULL, cf->data); 529 530 if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { 531 bufferevent_free(cf->event); 532 close(cf->fd); 533 RB_REMOVE(client_files, cf->tree, cf); 534 file_free(cf); 535 } 536 } 537 538 /* Handle a file write open message (client). */ 539 void 540 file_write_open(struct client_files *files, struct tmuxpeer *peer, 541 struct imsg *imsg, int allow_streams, int close_received, 542 client_file_cb cb, void *cbdata) 543 { 544 struct msg_write_open *msg = imsg->data; 545 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 546 const char *path; 547 struct msg_write_ready reply; 548 struct client_file find, *cf; 549 const int flags = O_NONBLOCK|O_WRONLY|O_CREAT; 550 int error = 0; 551 552 if (msglen < sizeof *msg) 553 fatalx("bad MSG_WRITE_OPEN size"); 554 if (msglen == sizeof *msg) 555 path = "-"; 556 else 557 path = (const char *)(msg + 1); 558 log_debug("open write file %d %s", msg->stream, path); 559 560 find.stream = msg->stream; 561 if ((cf = RB_FIND(client_files, files, &find)) != NULL) { 562 error = EBADF; 563 goto reply; 564 } 565 cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata); 566 if (cf->closed) { 567 error = EBADF; 568 goto reply; 569 } 570 571 cf->fd = -1; 572 if (msg->fd == -1) 573 cf->fd = open(path, msg->flags|flags, 0644); 574 else if (allow_streams) { 575 if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) 576 errno = EBADF; 577 else { 578 cf->fd = dup(msg->fd); 579 if (close_received) 580 close(msg->fd); /* can only be used once */ 581 } 582 } else 583 errno = EBADF; 584 if (cf->fd == -1) { 585 error = errno; 586 goto reply; 587 } 588 589 cf->event = bufferevent_new(cf->fd, NULL, file_write_callback, 590 file_write_error_callback, cf); 591 bufferevent_enable(cf->event, EV_WRITE); 592 goto reply; 593 594 reply: 595 reply.stream = msg->stream; 596 reply.error = error; 597 proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply); 598 } 599 600 /* Handle a file write data message (client). */ 601 void 602 file_write_data(struct client_files *files, struct imsg *imsg) 603 { 604 struct msg_write_data *msg = imsg->data; 605 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 606 struct client_file find, *cf; 607 size_t size = msglen - sizeof *msg; 608 609 if (msglen < sizeof *msg) 610 fatalx("bad MSG_WRITE size"); 611 find.stream = msg->stream; 612 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 613 fatalx("unknown stream number"); 614 log_debug("write %zu to file %d", size, cf->stream); 615 616 if (cf->event != NULL) 617 bufferevent_write(cf->event, msg + 1, size); 618 } 619 620 /* Handle a file write close message (client). */ 621 void 622 file_write_close(struct client_files *files, struct imsg *imsg) 623 { 624 struct msg_write_close *msg = imsg->data; 625 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 626 struct client_file find, *cf; 627 628 if (msglen != sizeof *msg) 629 fatalx("bad MSG_WRITE_CLOSE size"); 630 find.stream = msg->stream; 631 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 632 fatalx("unknown stream number"); 633 log_debug("close file %d", cf->stream); 634 635 if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { 636 if (cf->event != NULL) 637 bufferevent_free(cf->event); 638 if (cf->fd != -1) 639 close(cf->fd); 640 RB_REMOVE(client_files, files, cf); 641 file_free(cf); 642 } 643 } 644 645 /* Client file read error callback. */ 646 static void 647 file_read_error_callback(__unused struct bufferevent *bev, __unused short what, 648 void *arg) 649 { 650 struct client_file *cf = arg; 651 struct msg_read_done msg; 652 653 log_debug("read error file %d", cf->stream); 654 655 msg.stream = cf->stream; 656 msg.error = 0; 657 proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg); 658 659 bufferevent_free(cf->event); 660 close(cf->fd); 661 RB_REMOVE(client_files, cf->tree, cf); 662 file_free(cf); 663 } 664 665 /* Client file read callback. */ 666 static void 667 file_read_callback(__unused struct bufferevent *bev, void *arg) 668 { 669 struct client_file *cf = arg; 670 void *bdata; 671 size_t bsize; 672 struct msg_read_data *msg; 673 size_t msglen; 674 675 msg = xmalloc(sizeof *msg); 676 for (;;) { 677 bdata = EVBUFFER_DATA(cf->event->input); 678 bsize = EVBUFFER_LENGTH(cf->event->input); 679 680 if (bsize == 0) 681 break; 682 if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) 683 bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; 684 log_debug("read %zu from file %d", bsize, cf->stream); 685 686 msglen = (sizeof *msg) + bsize; 687 msg = xrealloc(msg, msglen); 688 msg->stream = cf->stream; 689 memcpy(msg + 1, bdata, bsize); 690 proc_send(cf->peer, MSG_READ, -1, msg, msglen); 691 692 evbuffer_drain(cf->event->input, bsize); 693 } 694 free(msg); 695 } 696 697 /* Handle a file read open message (client). */ 698 void 699 file_read_open(struct client_files *files, struct tmuxpeer *peer, 700 struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb, 701 void *cbdata) 702 { 703 struct msg_read_open *msg = imsg->data; 704 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 705 const char *path; 706 struct msg_read_done reply; 707 struct client_file find, *cf; 708 const int flags = O_NONBLOCK|O_RDONLY; 709 int error; 710 711 if (msglen < sizeof *msg) 712 fatalx("bad MSG_READ_OPEN size"); 713 if (msglen == sizeof *msg) 714 path = "-"; 715 else 716 path = (const char *)(msg + 1); 717 log_debug("open read file %d %s", msg->stream, path); 718 719 find.stream = msg->stream; 720 if ((cf = RB_FIND(client_files, files, &find)) != NULL) { 721 error = EBADF; 722 goto reply; 723 } 724 cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata); 725 if (cf->closed) { 726 error = EBADF; 727 goto reply; 728 } 729 730 cf->fd = -1; 731 if (msg->fd == -1) 732 cf->fd = open(path, flags); 733 else if (allow_streams) { 734 if (msg->fd != STDIN_FILENO) 735 errno = EBADF; 736 else { 737 cf->fd = dup(msg->fd); 738 if (close_received) 739 close(msg->fd); /* can only be used once */ 740 } 741 } else 742 errno = EBADF; 743 if (cf->fd == -1) { 744 error = errno; 745 goto reply; 746 } 747 748 cf->event = bufferevent_new(cf->fd, file_read_callback, NULL, 749 file_read_error_callback, cf); 750 bufferevent_enable(cf->event, EV_READ); 751 return; 752 753 reply: 754 reply.stream = msg->stream; 755 reply.error = error; 756 proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply); 757 } 758 759 /* Handle a write ready message (server). */ 760 void 761 file_write_ready(struct client_files *files, struct imsg *imsg) 762 { 763 struct msg_write_ready *msg = imsg->data; 764 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 765 struct client_file find, *cf; 766 767 if (msglen != sizeof *msg) 768 fatalx("bad MSG_WRITE_READY size"); 769 find.stream = msg->stream; 770 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 771 return; 772 if (msg->error != 0) { 773 cf->error = msg->error; 774 file_fire_done(cf); 775 } else 776 file_push(cf); 777 } 778 779 /* Handle read data message (server). */ 780 void 781 file_read_data(struct client_files *files, struct imsg *imsg) 782 { 783 struct msg_read_data *msg = imsg->data; 784 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 785 struct client_file find, *cf; 786 void *bdata = msg + 1; 787 size_t bsize = msglen - sizeof *msg; 788 789 if (msglen < sizeof *msg) 790 fatalx("bad MSG_READ_DATA size"); 791 find.stream = msg->stream; 792 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 793 return; 794 795 log_debug("file %d read %zu bytes", cf->stream, bsize); 796 if (cf->error == 0) { 797 if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { 798 cf->error = ENOMEM; 799 file_fire_done(cf); 800 } else 801 file_fire_read(cf); 802 } 803 } 804 805 /* Handle a read done message (server). */ 806 void 807 file_read_done(struct client_files *files, struct imsg *imsg) 808 { 809 struct msg_read_done *msg = imsg->data; 810 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 811 struct client_file find, *cf; 812 813 if (msglen != sizeof *msg) 814 fatalx("bad MSG_READ_DONE size"); 815 find.stream = msg->stream; 816 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 817 return; 818 819 log_debug("file %d read done", cf->stream); 820 cf->error = msg->error; 821 file_fire_done(cf); 822 } 823