1/* 2 * Copyright (c) 2001-2003 Damien Miller. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25/* XXX: memleaks */ 26/* XXX: signed vs unsigned */ 27/* XXX: remove all logging, only return status codes */ 28/* XXX: copy between two remote sites */ 29 30#include "includes.h"
|
31RCSID("$OpenBSD: sftp-client.c,v 1.42 2003/03/05 22:33:43 markus Exp $");
|
31RCSID("$OpenBSD: sftp-client.c,v 1.44 2003/06/28 16:23:06 deraadt Exp $"); |
32 33#include "openbsd-compat/sys-queue.h" 34 35#include "buffer.h" 36#include "bufaux.h" 37#include "getput.h" 38#include "xmalloc.h" 39#include "log.h" 40#include "atomicio.h" 41#include "progressmeter.h" 42 43#include "sftp.h" 44#include "sftp-common.h" 45#include "sftp-client.h" 46 47extern int showprogress; 48 49/* Minimum amount of data to read at at time */ 50#define MIN_READ_SIZE 512 51 52/* Maximum packet size */ 53#define MAX_MSG_LENGTH (256 * 1024) 54 55struct sftp_conn { 56 int fd_in; 57 int fd_out; 58 u_int transfer_buflen; 59 u_int num_requests; 60 u_int version; 61 u_int msg_id; 62}; 63 64static void 65send_msg(int fd, Buffer *m) 66{ 67 u_char mlen[4]; 68 69 if (buffer_len(m) > MAX_MSG_LENGTH) 70 fatal("Outbound message too long %u", buffer_len(m)); 71 72 /* Send length first */ 73 PUT_32BIT(mlen, buffer_len(m));
|
74 if (atomicio(write, fd, mlen, sizeof(mlen)) <= 0)
|
74 if (atomicio(vwrite, fd, mlen, sizeof(mlen)) <= 0) |
75 fatal("Couldn't send packet: %s", strerror(errno)); 76
|
77 if (atomicio(write, fd, buffer_ptr(m), buffer_len(m)) <= 0)
|
77 if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) <= 0) |
78 fatal("Couldn't send packet: %s", strerror(errno)); 79 80 buffer_clear(m); 81} 82 83static void 84get_msg(int fd, Buffer *m) 85{ 86 ssize_t len; 87 u_int msg_len; 88 89 buffer_append_space(m, 4); 90 len = atomicio(read, fd, buffer_ptr(m), 4); 91 if (len == 0) 92 fatal("Connection closed"); 93 else if (len == -1) 94 fatal("Couldn't read packet: %s", strerror(errno)); 95 96 msg_len = buffer_get_int(m); 97 if (msg_len > MAX_MSG_LENGTH) 98 fatal("Received message too long %u", msg_len); 99 100 buffer_append_space(m, msg_len); 101 len = atomicio(read, fd, buffer_ptr(m), msg_len); 102 if (len == 0) 103 fatal("Connection closed"); 104 else if (len == -1) 105 fatal("Read packet: %s", strerror(errno)); 106} 107 108static void 109send_string_request(int fd, u_int id, u_int code, char *s, 110 u_int len) 111{ 112 Buffer msg; 113 114 buffer_init(&msg); 115 buffer_put_char(&msg, code); 116 buffer_put_int(&msg, id); 117 buffer_put_string(&msg, s, len); 118 send_msg(fd, &msg); 119 debug3("Sent message fd %d T:%u I:%u", fd, code, id); 120 buffer_free(&msg); 121} 122 123static void 124send_string_attrs_request(int fd, u_int id, u_int code, char *s, 125 u_int len, Attrib *a) 126{ 127 Buffer msg; 128 129 buffer_init(&msg); 130 buffer_put_char(&msg, code); 131 buffer_put_int(&msg, id); 132 buffer_put_string(&msg, s, len); 133 encode_attrib(&msg, a); 134 send_msg(fd, &msg); 135 debug3("Sent message fd %d T:%u I:%u", fd, code, id); 136 buffer_free(&msg); 137} 138 139static u_int 140get_status(int fd, u_int expected_id) 141{ 142 Buffer msg; 143 u_int type, id, status; 144 145 buffer_init(&msg); 146 get_msg(fd, &msg); 147 type = buffer_get_char(&msg); 148 id = buffer_get_int(&msg); 149 150 if (id != expected_id) 151 fatal("ID mismatch (%u != %u)", id, expected_id); 152 if (type != SSH2_FXP_STATUS) 153 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", 154 SSH2_FXP_STATUS, type); 155 156 status = buffer_get_int(&msg); 157 buffer_free(&msg); 158 159 debug3("SSH2_FXP_STATUS %u", status); 160 161 return(status); 162} 163 164static char * 165get_handle(int fd, u_int expected_id, u_int *len) 166{ 167 Buffer msg; 168 u_int type, id; 169 char *handle; 170 171 buffer_init(&msg); 172 get_msg(fd, &msg); 173 type = buffer_get_char(&msg); 174 id = buffer_get_int(&msg); 175 176 if (id != expected_id) 177 fatal("ID mismatch (%u != %u)", id, expected_id); 178 if (type == SSH2_FXP_STATUS) { 179 int status = buffer_get_int(&msg); 180 181 error("Couldn't get handle: %s", fx2txt(status)); 182 return(NULL); 183 } else if (type != SSH2_FXP_HANDLE) 184 fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u", 185 SSH2_FXP_HANDLE, type); 186 187 handle = buffer_get_string(&msg, len); 188 buffer_free(&msg); 189 190 return(handle); 191} 192 193static Attrib * 194get_decode_stat(int fd, u_int expected_id, int quiet) 195{ 196 Buffer msg; 197 u_int type, id; 198 Attrib *a; 199 200 buffer_init(&msg); 201 get_msg(fd, &msg); 202 203 type = buffer_get_char(&msg); 204 id = buffer_get_int(&msg); 205 206 debug3("Received stat reply T:%u I:%u", type, id); 207 if (id != expected_id) 208 fatal("ID mismatch (%u != %u)", id, expected_id); 209 if (type == SSH2_FXP_STATUS) { 210 int status = buffer_get_int(&msg); 211 212 if (quiet) 213 debug("Couldn't stat remote file: %s", fx2txt(status)); 214 else 215 error("Couldn't stat remote file: %s", fx2txt(status)); 216 return(NULL); 217 } else if (type != SSH2_FXP_ATTRS) { 218 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", 219 SSH2_FXP_ATTRS, type); 220 } 221 a = decode_attrib(&msg); 222 buffer_free(&msg); 223 224 return(a); 225} 226 227struct sftp_conn * 228do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) 229{ 230 u_int type; 231 int version; 232 Buffer msg; 233 struct sftp_conn *ret; 234 235 buffer_init(&msg); 236 buffer_put_char(&msg, SSH2_FXP_INIT); 237 buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 238 send_msg(fd_out, &msg); 239 240 buffer_clear(&msg); 241 242 get_msg(fd_in, &msg); 243 244 /* Expecting a VERSION reply */ 245 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { 246 error("Invalid packet back from SSH2_FXP_INIT (type %u)", 247 type); 248 buffer_free(&msg); 249 return(NULL); 250 } 251 version = buffer_get_int(&msg); 252 253 debug2("Remote version: %d", version); 254 255 /* Check for extensions */ 256 while (buffer_len(&msg) > 0) { 257 char *name = buffer_get_string(&msg, NULL); 258 char *value = buffer_get_string(&msg, NULL); 259 260 debug2("Init extension: \"%s\"", name); 261 xfree(name); 262 xfree(value); 263 } 264 265 buffer_free(&msg); 266 267 ret = xmalloc(sizeof(*ret)); 268 ret->fd_in = fd_in; 269 ret->fd_out = fd_out; 270 ret->transfer_buflen = transfer_buflen; 271 ret->num_requests = num_requests; 272 ret->version = version; 273 ret->msg_id = 1; 274 275 /* Some filexfer v.0 servers don't support large packets */ 276 if (version == 0) 277 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480); 278 279 return(ret); 280} 281 282u_int 283sftp_proto_version(struct sftp_conn *conn) 284{ 285 return(conn->version); 286} 287 288int 289do_close(struct sftp_conn *conn, char *handle, u_int handle_len) 290{ 291 u_int id, status; 292 Buffer msg; 293 294 buffer_init(&msg); 295 296 id = conn->msg_id++; 297 buffer_put_char(&msg, SSH2_FXP_CLOSE); 298 buffer_put_int(&msg, id); 299 buffer_put_string(&msg, handle, handle_len); 300 send_msg(conn->fd_out, &msg); 301 debug3("Sent message SSH2_FXP_CLOSE I:%u", id); 302 303 status = get_status(conn->fd_in, id); 304 if (status != SSH2_FX_OK) 305 error("Couldn't close file: %s", fx2txt(status)); 306 307 buffer_free(&msg); 308 309 return(status); 310} 311 312 313static int 314do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, 315 SFTP_DIRENT ***dir) 316{ 317 Buffer msg; 318 u_int type, id, handle_len, i, expected_id, ents = 0; 319 char *handle; 320 321 id = conn->msg_id++; 322 323 buffer_init(&msg); 324 buffer_put_char(&msg, SSH2_FXP_OPENDIR); 325 buffer_put_int(&msg, id); 326 buffer_put_cstring(&msg, path); 327 send_msg(conn->fd_out, &msg); 328 329 buffer_clear(&msg); 330 331 handle = get_handle(conn->fd_in, id, &handle_len); 332 if (handle == NULL) 333 return(-1); 334 335 if (dir) { 336 ents = 0; 337 *dir = xmalloc(sizeof(**dir)); 338 (*dir)[0] = NULL; 339 } 340 341 for (;;) { 342 int count; 343 344 id = expected_id = conn->msg_id++; 345 346 debug3("Sending SSH2_FXP_READDIR I:%u", id); 347 348 buffer_clear(&msg); 349 buffer_put_char(&msg, SSH2_FXP_READDIR); 350 buffer_put_int(&msg, id); 351 buffer_put_string(&msg, handle, handle_len); 352 send_msg(conn->fd_out, &msg); 353 354 buffer_clear(&msg); 355 356 get_msg(conn->fd_in, &msg); 357 358 type = buffer_get_char(&msg); 359 id = buffer_get_int(&msg); 360 361 debug3("Received reply T:%u I:%u", type, id); 362 363 if (id != expected_id) 364 fatal("ID mismatch (%u != %u)", id, expected_id); 365 366 if (type == SSH2_FXP_STATUS) { 367 int status = buffer_get_int(&msg); 368 369 debug3("Received SSH2_FXP_STATUS %d", status); 370 371 if (status == SSH2_FX_EOF) { 372 break; 373 } else { 374 error("Couldn't read directory: %s", 375 fx2txt(status)); 376 do_close(conn, handle, handle_len); 377 xfree(handle); 378 return(status); 379 } 380 } else if (type != SSH2_FXP_NAME) 381 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 382 SSH2_FXP_NAME, type); 383 384 count = buffer_get_int(&msg); 385 if (count == 0) 386 break; 387 debug3("Received %d SSH2_FXP_NAME responses", count); 388 for (i = 0; i < count; i++) { 389 char *filename, *longname; 390 Attrib *a; 391 392 filename = buffer_get_string(&msg, NULL); 393 longname = buffer_get_string(&msg, NULL); 394 a = decode_attrib(&msg); 395 396 if (printflag) 397 printf("%s\n", longname); 398 399 if (dir) { 400 *dir = xrealloc(*dir, sizeof(**dir) * 401 (ents + 2)); 402 (*dir)[ents] = xmalloc(sizeof(***dir)); 403 (*dir)[ents]->filename = xstrdup(filename); 404 (*dir)[ents]->longname = xstrdup(longname); 405 memcpy(&(*dir)[ents]->a, a, sizeof(*a)); 406 (*dir)[++ents] = NULL; 407 } 408 409 xfree(filename); 410 xfree(longname); 411 } 412 } 413 414 buffer_free(&msg); 415 do_close(conn, handle, handle_len); 416 xfree(handle); 417 418 return(0); 419} 420 421int 422do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir) 423{ 424 return(do_lsreaddir(conn, path, 0, dir)); 425} 426 427void free_sftp_dirents(SFTP_DIRENT **s) 428{ 429 int i; 430 431 for (i = 0; s[i]; i++) { 432 xfree(s[i]->filename); 433 xfree(s[i]->longname); 434 xfree(s[i]); 435 } 436 xfree(s); 437} 438 439int 440do_rm(struct sftp_conn *conn, char *path) 441{ 442 u_int status, id; 443 444 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); 445 446 id = conn->msg_id++; 447 send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path, 448 strlen(path)); 449 status = get_status(conn->fd_in, id); 450 if (status != SSH2_FX_OK) 451 error("Couldn't delete file: %s", fx2txt(status)); 452 return(status); 453} 454 455int 456do_mkdir(struct sftp_conn *conn, char *path, Attrib *a) 457{ 458 u_int status, id; 459 460 id = conn->msg_id++; 461 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path, 462 strlen(path), a); 463 464 status = get_status(conn->fd_in, id); 465 if (status != SSH2_FX_OK) 466 error("Couldn't create directory: %s", fx2txt(status)); 467 468 return(status); 469} 470 471int 472do_rmdir(struct sftp_conn *conn, char *path) 473{ 474 u_int status, id; 475 476 id = conn->msg_id++; 477 send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path, 478 strlen(path)); 479 480 status = get_status(conn->fd_in, id); 481 if (status != SSH2_FX_OK) 482 error("Couldn't remove directory: %s", fx2txt(status)); 483 484 return(status); 485} 486 487Attrib * 488do_stat(struct sftp_conn *conn, char *path, int quiet) 489{ 490 u_int id; 491 492 id = conn->msg_id++; 493 494 send_string_request(conn->fd_out, id, 495 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, 496 path, strlen(path)); 497 498 return(get_decode_stat(conn->fd_in, id, quiet)); 499} 500 501Attrib * 502do_lstat(struct sftp_conn *conn, char *path, int quiet) 503{ 504 u_int id; 505 506 if (conn->version == 0) { 507 if (quiet) 508 debug("Server version does not support lstat operation"); 509 else
|
510 log("Server version does not support lstat operation");
|
510 logit("Server version does not support lstat operation"); |
511 return(do_stat(conn, path, quiet)); 512 } 513 514 id = conn->msg_id++; 515 send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path, 516 strlen(path)); 517 518 return(get_decode_stat(conn->fd_in, id, quiet)); 519} 520 521Attrib * 522do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) 523{ 524 u_int id; 525 526 id = conn->msg_id++; 527 send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle, 528 handle_len); 529 530 return(get_decode_stat(conn->fd_in, id, quiet)); 531} 532 533int 534do_setstat(struct sftp_conn *conn, char *path, Attrib *a) 535{ 536 u_int status, id; 537 538 id = conn->msg_id++; 539 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path, 540 strlen(path), a); 541 542 status = get_status(conn->fd_in, id); 543 if (status != SSH2_FX_OK) 544 error("Couldn't setstat on \"%s\": %s", path, 545 fx2txt(status)); 546 547 return(status); 548} 549 550int 551do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len, 552 Attrib *a) 553{ 554 u_int status, id; 555 556 id = conn->msg_id++; 557 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle, 558 handle_len, a); 559 560 status = get_status(conn->fd_in, id); 561 if (status != SSH2_FX_OK) 562 error("Couldn't fsetstat: %s", fx2txt(status)); 563 564 return(status); 565} 566 567char * 568do_realpath(struct sftp_conn *conn, char *path) 569{ 570 Buffer msg; 571 u_int type, expected_id, count, id; 572 char *filename, *longname; 573 Attrib *a; 574 575 expected_id = id = conn->msg_id++; 576 send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path, 577 strlen(path)); 578 579 buffer_init(&msg); 580 581 get_msg(conn->fd_in, &msg); 582 type = buffer_get_char(&msg); 583 id = buffer_get_int(&msg); 584 585 if (id != expected_id) 586 fatal("ID mismatch (%u != %u)", id, expected_id); 587 588 if (type == SSH2_FXP_STATUS) { 589 u_int status = buffer_get_int(&msg); 590 591 error("Couldn't canonicalise: %s", fx2txt(status)); 592 return(NULL); 593 } else if (type != SSH2_FXP_NAME) 594 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 595 SSH2_FXP_NAME, type); 596 597 count = buffer_get_int(&msg); 598 if (count != 1) 599 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); 600 601 filename = buffer_get_string(&msg, NULL); 602 longname = buffer_get_string(&msg, NULL); 603 a = decode_attrib(&msg); 604 605 debug3("SSH_FXP_REALPATH %s -> %s", path, filename); 606 607 xfree(longname); 608 609 buffer_free(&msg); 610 611 return(filename); 612} 613 614int 615do_rename(struct sftp_conn *conn, char *oldpath, char *newpath) 616{ 617 Buffer msg; 618 u_int status, id; 619 620 buffer_init(&msg); 621 622 /* Send rename request */ 623 id = conn->msg_id++; 624 buffer_put_char(&msg, SSH2_FXP_RENAME); 625 buffer_put_int(&msg, id); 626 buffer_put_cstring(&msg, oldpath); 627 buffer_put_cstring(&msg, newpath); 628 send_msg(conn->fd_out, &msg); 629 debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath, 630 newpath); 631 buffer_free(&msg); 632 633 status = get_status(conn->fd_in, id); 634 if (status != SSH2_FX_OK) 635 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, 636 newpath, fx2txt(status)); 637 638 return(status); 639} 640 641int 642do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath) 643{ 644 Buffer msg; 645 u_int status, id; 646 647 if (conn->version < 3) { 648 error("This server does not support the symlink operation"); 649 return(SSH2_FX_OP_UNSUPPORTED); 650 } 651 652 buffer_init(&msg); 653 654 /* Send rename request */ 655 id = conn->msg_id++; 656 buffer_put_char(&msg, SSH2_FXP_SYMLINK); 657 buffer_put_int(&msg, id); 658 buffer_put_cstring(&msg, oldpath); 659 buffer_put_cstring(&msg, newpath); 660 send_msg(conn->fd_out, &msg); 661 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, 662 newpath); 663 buffer_free(&msg); 664 665 status = get_status(conn->fd_in, id); 666 if (status != SSH2_FX_OK) 667 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, 668 newpath, fx2txt(status)); 669 670 return(status); 671} 672 673char * 674do_readlink(struct sftp_conn *conn, char *path) 675{ 676 Buffer msg; 677 u_int type, expected_id, count, id; 678 char *filename, *longname; 679 Attrib *a; 680 681 expected_id = id = conn->msg_id++; 682 send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path, 683 strlen(path)); 684 685 buffer_init(&msg); 686 687 get_msg(conn->fd_in, &msg); 688 type = buffer_get_char(&msg); 689 id = buffer_get_int(&msg); 690 691 if (id != expected_id) 692 fatal("ID mismatch (%u != %u)", id, expected_id); 693 694 if (type == SSH2_FXP_STATUS) { 695 u_int status = buffer_get_int(&msg); 696 697 error("Couldn't readlink: %s", fx2txt(status)); 698 return(NULL); 699 } else if (type != SSH2_FXP_NAME) 700 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 701 SSH2_FXP_NAME, type); 702 703 count = buffer_get_int(&msg); 704 if (count != 1) 705 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); 706 707 filename = buffer_get_string(&msg, NULL); 708 longname = buffer_get_string(&msg, NULL); 709 a = decode_attrib(&msg); 710 711 debug3("SSH_FXP_READLINK %s -> %s", path, filename); 712 713 xfree(longname); 714 715 buffer_free(&msg); 716 717 return(filename); 718} 719 720static void 721send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, 722 char *handle, u_int handle_len) 723{ 724 Buffer msg; 725 726 buffer_init(&msg); 727 buffer_clear(&msg); 728 buffer_put_char(&msg, SSH2_FXP_READ); 729 buffer_put_int(&msg, id); 730 buffer_put_string(&msg, handle, handle_len); 731 buffer_put_int64(&msg, offset); 732 buffer_put_int(&msg, len); 733 send_msg(fd_out, &msg); 734 buffer_free(&msg); 735} 736 737int 738do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 739 int pflag) 740{ 741 Attrib junk, *a; 742 Buffer msg; 743 char *handle; 744 int local_fd, status, num_req, max_req, write_error; 745 int read_error, write_errno; 746 u_int64_t offset, size; 747 u_int handle_len, mode, type, id, buflen; 748 off_t progress_counter; 749 struct request { 750 u_int id; 751 u_int len; 752 u_int64_t offset; 753 TAILQ_ENTRY(request) tq; 754 }; 755 TAILQ_HEAD(reqhead, request) requests; 756 struct request *req; 757 758 TAILQ_INIT(&requests); 759 760 a = do_stat(conn, remote_path, 0); 761 if (a == NULL) 762 return(-1); 763 764 /* XXX: should we preserve set[ug]id? */ 765 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 766 mode = a->perm & 0777; 767 else 768 mode = 0666; 769 770 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 771 (!S_ISREG(a->perm))) { 772 error("Cannot download non-regular file: %s", remote_path); 773 return(-1); 774 } 775 776 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) 777 size = a->size; 778 else 779 size = 0; 780 781 buflen = conn->transfer_buflen; 782 buffer_init(&msg); 783 784 /* Send open request */ 785 id = conn->msg_id++; 786 buffer_put_char(&msg, SSH2_FXP_OPEN); 787 buffer_put_int(&msg, id); 788 buffer_put_cstring(&msg, remote_path); 789 buffer_put_int(&msg, SSH2_FXF_READ); 790 attrib_clear(&junk); /* Send empty attributes */ 791 encode_attrib(&msg, &junk); 792 send_msg(conn->fd_out, &msg); 793 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 794 795 handle = get_handle(conn->fd_in, id, &handle_len); 796 if (handle == NULL) { 797 buffer_free(&msg); 798 return(-1); 799 } 800 801 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, 802 mode | S_IWRITE); 803 if (local_fd == -1) { 804 error("Couldn't open local file \"%s\" for writing: %s", 805 local_path, strerror(errno)); 806 buffer_free(&msg); 807 xfree(handle); 808 return(-1); 809 } 810 811 /* Read from remote and write to local */ 812 write_error = read_error = write_errno = num_req = offset = 0; 813 max_req = 1; 814 progress_counter = 0; 815 816 if (showprogress) { 817 if (size) 818 start_progress_meter(remote_path, size, 819 &progress_counter); 820 else 821 printf("Fetching %s to %s\n", remote_path, local_path); 822 } 823 824 while (num_req > 0 || max_req > 0) { 825 char *data; 826 u_int len; 827 828 /* Send some more requests */ 829 while (num_req < max_req) { 830 debug3("Request range %llu -> %llu (%d/%d)", 831 (unsigned long long)offset, 832 (unsigned long long)offset + buflen - 1, 833 num_req, max_req); 834 req = xmalloc(sizeof(*req)); 835 req->id = conn->msg_id++; 836 req->len = buflen; 837 req->offset = offset; 838 offset += buflen; 839 num_req++; 840 TAILQ_INSERT_TAIL(&requests, req, tq); 841 send_read_request(conn->fd_out, req->id, req->offset, 842 req->len, handle, handle_len); 843 } 844 845 buffer_clear(&msg); 846 get_msg(conn->fd_in, &msg); 847 type = buffer_get_char(&msg); 848 id = buffer_get_int(&msg); 849 debug3("Received reply T:%u I:%u R:%d", type, id, max_req); 850 851 /* Find the request in our queue */ 852 for(req = TAILQ_FIRST(&requests); 853 req != NULL && req->id != id; 854 req = TAILQ_NEXT(req, tq)) 855 ; 856 if (req == NULL) 857 fatal("Unexpected reply %u", id); 858 859 switch (type) { 860 case SSH2_FXP_STATUS: 861 status = buffer_get_int(&msg); 862 if (status != SSH2_FX_EOF) 863 read_error = 1; 864 max_req = 0; 865 TAILQ_REMOVE(&requests, req, tq); 866 xfree(req); 867 num_req--; 868 break; 869 case SSH2_FXP_DATA: 870 data = buffer_get_string(&msg, &len); 871 debug3("Received data %llu -> %llu", 872 (unsigned long long)req->offset, 873 (unsigned long long)req->offset + len - 1); 874 if (len > req->len) 875 fatal("Received more data than asked for " 876 "%u > %u", len, req->len); 877 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
|
878 atomicio(write, local_fd, data, len) != len) &&
|
878 atomicio(vwrite, local_fd, data, len) != len) && |
879 !write_error) { 880 write_errno = errno; 881 write_error = 1; 882 max_req = 0; 883 } 884 progress_counter += len; 885 xfree(data); 886 887 if (len == req->len) { 888 TAILQ_REMOVE(&requests, req, tq); 889 xfree(req); 890 num_req--; 891 } else { 892 /* Resend the request for the missing data */ 893 debug3("Short data block, re-requesting " 894 "%llu -> %llu (%2d)", 895 (unsigned long long)req->offset + len, 896 (unsigned long long)req->offset + 897 req->len - 1, num_req); 898 req->id = conn->msg_id++; 899 req->len -= len; 900 req->offset += len; 901 send_read_request(conn->fd_out, req->id, 902 req->offset, req->len, handle, handle_len); 903 /* Reduce the request size */ 904 if (len < buflen) 905 buflen = MAX(MIN_READ_SIZE, len); 906 } 907 if (max_req > 0) { /* max_req = 0 iff EOF received */ 908 if (size > 0 && offset > size) { 909 /* Only one request at a time 910 * after the expected EOF */ 911 debug3("Finish at %llu (%2d)", 912 (unsigned long long)offset, 913 num_req); 914 max_req = 1; 915 } 916 else if (max_req < conn->num_requests + 1) { 917 ++max_req; 918 } 919 } 920 break; 921 default: 922 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", 923 SSH2_FXP_DATA, type); 924 } 925 } 926 927 if (showprogress && size) 928 stop_progress_meter(); 929 930 /* Sanity check */ 931 if (TAILQ_FIRST(&requests) != NULL) 932 fatal("Transfer complete, but requests still in queue"); 933 934 if (read_error) { 935 error("Couldn't read from remote file \"%s\" : %s", 936 remote_path, fx2txt(status)); 937 do_close(conn, handle, handle_len); 938 } else if (write_error) { 939 error("Couldn't write to \"%s\": %s", local_path, 940 strerror(write_errno)); 941 status = -1; 942 do_close(conn, handle, handle_len); 943 } else { 944 status = do_close(conn, handle, handle_len); 945 946 /* Override umask and utimes if asked */ 947#ifdef HAVE_FCHMOD 948 if (pflag && fchmod(local_fd, mode) == -1) 949#else 950 if (pflag && chmod(local_path, mode) == -1) 951#endif /* HAVE_FCHMOD */ 952 error("Couldn't set mode on \"%s\": %s", local_path, 953 strerror(errno)); 954 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { 955 struct timeval tv[2]; 956 tv[0].tv_sec = a->atime; 957 tv[1].tv_sec = a->mtime; 958 tv[0].tv_usec = tv[1].tv_usec = 0; 959 if (utimes(local_path, tv) == -1) 960 error("Can't set times on \"%s\": %s", 961 local_path, strerror(errno)); 962 } 963 } 964 close(local_fd); 965 buffer_free(&msg); 966 xfree(handle); 967 968 return(status); 969} 970 971int 972do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 973 int pflag) 974{ 975 int local_fd, status; 976 u_int handle_len, id, type; 977 u_int64_t offset; 978 char *handle, *data; 979 Buffer msg; 980 struct stat sb; 981 Attrib a; 982 u_int32_t startid; 983 u_int32_t ackid; 984 struct outstanding_ack { 985 u_int id; 986 u_int len; 987 u_int64_t offset; 988 TAILQ_ENTRY(outstanding_ack) tq; 989 }; 990 TAILQ_HEAD(ackhead, outstanding_ack) acks; 991 struct outstanding_ack *ack; 992 993 TAILQ_INIT(&acks); 994 995 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { 996 error("Couldn't open local file \"%s\" for reading: %s", 997 local_path, strerror(errno)); 998 return(-1); 999 } 1000 if (fstat(local_fd, &sb) == -1) { 1001 error("Couldn't fstat local file \"%s\": %s", 1002 local_path, strerror(errno)); 1003 close(local_fd); 1004 return(-1); 1005 } 1006 if (!S_ISREG(sb.st_mode)) { 1007 error("%s is not a regular file", local_path); 1008 close(local_fd); 1009 return(-1); 1010 } 1011 stat_to_attrib(&sb, &a); 1012 1013 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 1014 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 1015 a.perm &= 0777; 1016 if (!pflag) 1017 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1018 1019 buffer_init(&msg); 1020 1021 /* Send open request */ 1022 id = conn->msg_id++; 1023 buffer_put_char(&msg, SSH2_FXP_OPEN); 1024 buffer_put_int(&msg, id); 1025 buffer_put_cstring(&msg, remote_path); 1026 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); 1027 encode_attrib(&msg, &a); 1028 send_msg(conn->fd_out, &msg); 1029 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 1030 1031 buffer_clear(&msg); 1032 1033 handle = get_handle(conn->fd_in, id, &handle_len); 1034 if (handle == NULL) { 1035 close(local_fd); 1036 buffer_free(&msg); 1037 return(-1); 1038 } 1039 1040 startid = ackid = id + 1; 1041 data = xmalloc(conn->transfer_buflen); 1042 1043 /* Read from local and write to remote */ 1044 offset = 0; 1045 if (showprogress) 1046 start_progress_meter(local_path, sb.st_size, &offset); 1047 else 1048 printf("Uploading %s to %s\n", local_path, remote_path); 1049 1050 for (;;) { 1051 int len; 1052 1053 /* 1054 * Can't use atomicio here because it returns 0 on EOF, thus losing 1055 * the last block of the file 1056 */ 1057 do 1058 len = read(local_fd, data, conn->transfer_buflen); 1059 while ((len == -1) && (errno == EINTR || errno == EAGAIN)); 1060 1061 if (len == -1) 1062 fatal("Couldn't read from \"%s\": %s", local_path, 1063 strerror(errno)); 1064 1065 if (len != 0) { 1066 ack = xmalloc(sizeof(*ack)); 1067 ack->id = ++id; 1068 ack->offset = offset; 1069 ack->len = len; 1070 TAILQ_INSERT_TAIL(&acks, ack, tq); 1071 1072 buffer_clear(&msg); 1073 buffer_put_char(&msg, SSH2_FXP_WRITE); 1074 buffer_put_int(&msg, ack->id); 1075 buffer_put_string(&msg, handle, handle_len); 1076 buffer_put_int64(&msg, offset); 1077 buffer_put_string(&msg, data, len); 1078 send_msg(conn->fd_out, &msg); 1079 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", 1080 id, (unsigned long long)offset, len); 1081 } else if (TAILQ_FIRST(&acks) == NULL) 1082 break; 1083 1084 if (ack == NULL) 1085 fatal("Unexpected ACK %u", id); 1086 1087 if (id == startid || len == 0 || 1088 id - ackid >= conn->num_requests) { 1089 u_int r_id; 1090 1091 buffer_clear(&msg); 1092 get_msg(conn->fd_in, &msg); 1093 type = buffer_get_char(&msg); 1094 r_id = buffer_get_int(&msg); 1095 1096 if (type != SSH2_FXP_STATUS) 1097 fatal("Expected SSH2_FXP_STATUS(%d) packet, " 1098 "got %d", SSH2_FXP_STATUS, type); 1099 1100 status = buffer_get_int(&msg); 1101 debug3("SSH2_FXP_STATUS %d", status); 1102 1103 /* Find the request in our queue */ 1104 for(ack = TAILQ_FIRST(&acks); 1105 ack != NULL && ack->id != r_id; 1106 ack = TAILQ_NEXT(ack, tq)) 1107 ; 1108 if (ack == NULL) 1109 fatal("Can't find request for ID %u", r_id); 1110 TAILQ_REMOVE(&acks, ack, tq); 1111 1112 if (status != SSH2_FX_OK) { 1113 error("Couldn't write to remote file \"%s\": %s", 1114 remote_path, fx2txt(status)); 1115 do_close(conn, handle, handle_len); 1116 close(local_fd); 1117 xfree(data); 1118 xfree(ack); 1119 goto done; 1120 } 1121 debug3("In write loop, ack for %u %u bytes at %llu", 1122 ack->id, ack->len, (unsigned long long)ack->offset); 1123 ++ackid; 1124 xfree(ack); 1125 } 1126 offset += len; 1127 } 1128 if (showprogress) 1129 stop_progress_meter(); 1130 xfree(data); 1131 1132 if (close(local_fd) == -1) { 1133 error("Couldn't close local file \"%s\": %s", local_path, 1134 strerror(errno)); 1135 do_close(conn, handle, handle_len); 1136 status = -1; 1137 goto done; 1138 } 1139 1140 /* Override umask and utimes if asked */ 1141 if (pflag) 1142 do_fsetstat(conn, handle, handle_len, &a); 1143 1144 status = do_close(conn, handle, handle_len); 1145 1146done: 1147 xfree(handle); 1148 buffer_free(&msg); 1149 return(status); 1150}
|